Skip to content

Commit 32514b4

Browse files
committed
[Function builders] Run syntactic diagnostics for function bodies.
When a function body has had a function builder applied to it, make sure that we run all of the syntactic diagnostics for expressions and statements within the body. Fixes rdar://problem/64493626.
1 parent 3998644 commit 32514b4

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,63 @@ BraceStmt *swift::applyFunctionBuilderTransform(
12231223
captured.first, captured.second)));
12241224
}
12251225

1226+
/// Produce any additional syntactic diagnostics for the body of a
1227+
static void performAddOnDiagnostics(BraceStmt *stmt, DeclContext *dc) {
1228+
class AddOnDiagnosticWalker : public ASTWalker {
1229+
SmallVector<DeclContext *, 4> dcStack;
1230+
1231+
public:
1232+
AddOnDiagnosticWalker(DeclContext *dc) {
1233+
dcStack.push_back(dc);
1234+
}
1235+
1236+
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
1237+
performSyntacticExprDiagnostics(
1238+
expr, dcStack.back(), /*isExprStmt=*/false);
1239+
1240+
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
1241+
if (closure->wasSeparatelyTypeChecked()) {
1242+
dcStack.push_back(closure);
1243+
return { true, expr };
1244+
}
1245+
}
1246+
1247+
return { false, expr };
1248+
}
1249+
1250+
Expr *walkToExprPost(Expr *expr) override {
1251+
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
1252+
if (closure->wasSeparatelyTypeChecked()) {
1253+
assert(dcStack.back() == closure);
1254+
dcStack.pop_back();
1255+
}
1256+
}
1257+
1258+
return expr;
1259+
}
1260+
1261+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
1262+
performStmtDiagnostics(dcStack.back()->getASTContext(), stmt);
1263+
return { true, stmt };
1264+
}
1265+
1266+
std::pair<bool, Pattern*> walkToPatternPre(Pattern *pattern) override {
1267+
return { false, pattern };
1268+
}
1269+
1270+
bool walkToTypeLocPre(TypeLoc &typeLoc) override { return false; }
1271+
1272+
bool walkToTypeReprPre(TypeRepr *typeRepr) override { return false; }
1273+
1274+
bool walkToParameterListPre(ParameterList *params) override {
1275+
return false;
1276+
}
1277+
};
1278+
1279+
AddOnDiagnosticWalker walker(dc);
1280+
stmt->walk(walker);
1281+
}
1282+
12261283
Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
12271284
FuncDecl *func, Type builderType) {
12281285
// Pre-check the body: pre-check any expressions in it and look
@@ -1363,6 +1420,7 @@ Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
13631420
if (auto result = cs.applySolution(
13641421
solutions.front(),
13651422
SolutionApplicationTarget(func))) {
1423+
performAddOnDiagnostics(result->getFunctionBody(), func);
13661424
return result->getFunctionBody();
13671425
}
13681426

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %swift -typecheck -verify -target %target-cpu-apple-macosx10.15 %s
2+
// REQUIRES: OS=macosx
3+
4+
enum Either<T,U> {
5+
case first(T)
6+
case second(U)
7+
}
8+
9+
struct Do<T> {
10+
var value: T
11+
}
12+
13+
@_functionBuilder
14+
struct TupleBuilder {
15+
static func buildBlock<T1>(_ t1: T1) -> (T1) {
16+
return (t1)
17+
}
18+
19+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
20+
return (t1, t2)
21+
}
22+
23+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
24+
-> (T1, T2, T3) {
25+
return (t1, t2, t3)
26+
}
27+
28+
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
29+
-> (T1, T2, T3, T4) {
30+
return (t1, t2, t3, t4)
31+
}
32+
33+
static func buildBlock<T1, T2, T3, T4, T5>(
34+
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
35+
) -> (T1, T2, T3, T4, T5) {
36+
return (t1, t2, t3, t4, t5)
37+
}
38+
39+
static func buildDo<T1>(_ t1: T1) -> Do<(T1)> {
40+
.init(value: t1)
41+
}
42+
43+
static func buildDo<T1, T2>(_ t1: T1, _ t2: T2) -> Do<(T1, T2)> {
44+
.init(value: (t1, t2))
45+
}
46+
47+
static func buildDo<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
48+
-> Do<(T1, T2, T3)> {
49+
.init(value: (t1, t2, t3))
50+
}
51+
52+
static func buildIf<T>(_ value: T?) -> T? { return value }
53+
54+
static func buildEither<T,U>(first value: T) -> Either<T,U> {
55+
return .first(value)
56+
}
57+
static func buildEither<T,U>(second value: U) -> Either<T,U> {
58+
return .second(value)
59+
}
60+
61+
static func buildArray<T>(_ array: [T]) -> [T] { return array }
62+
}
63+
64+
@available(macOS 10.14, *)
65+
enum Option {
66+
@available(macOS 10.15.4, *)
67+
case best
68+
}
69+
70+
@TupleBuilder
71+
func bestTuple() -> some Any { // expected-note{{add @available attribute to enclosing global function}}
72+
"Hello"
73+
Option.best // expected-error{{'best' is only available in macOS 10.15.4 or newer}}
74+
// expected-note@-1{{add 'if #available' version check}}
75+
}
76+
77+
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
78+
print(body(cond))
79+
}
80+
81+
tuplify(true) { x in
82+
x
83+
"Hello"
84+
Option.best // expected-error{{'best' is only available in macOS 10.15.4 or newer}}
85+
// expected-note@-1{{add 'if #available' version check}}
86+
}

0 commit comments

Comments
 (0)