Skip to content

Commit 2a01fc9

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 5549619 commit 2a01fc9

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
@@ -1387,6 +1387,63 @@ BraceStmt *swift::applyFunctionBuilderTransform(
13871387
captured.first, captured.second)));
13881388
}
13891389

1390+
/// Produce any additional syntactic diagnostics for the body of a
1391+
static void performAddOnDiagnostics(BraceStmt *stmt, DeclContext *dc) {
1392+
class AddOnDiagnosticWalker : public ASTWalker {
1393+
SmallVector<DeclContext *, 4> dcStack;
1394+
1395+
public:
1396+
AddOnDiagnosticWalker(DeclContext *dc) {
1397+
dcStack.push_back(dc);
1398+
}
1399+
1400+
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
1401+
performSyntacticExprDiagnostics(
1402+
expr, dcStack.back(), /*isExprStmt=*/false);
1403+
1404+
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
1405+
if (closure->wasSeparatelyTypeChecked()) {
1406+
dcStack.push_back(closure);
1407+
return { true, expr };
1408+
}
1409+
}
1410+
1411+
return { false, expr };
1412+
}
1413+
1414+
Expr *walkToExprPost(Expr *expr) override {
1415+
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
1416+
if (closure->wasSeparatelyTypeChecked()) {
1417+
assert(dcStack.back() == closure);
1418+
dcStack.pop_back();
1419+
}
1420+
}
1421+
1422+
return expr;
1423+
}
1424+
1425+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
1426+
performStmtDiagnostics(dcStack.back()->getASTContext(), stmt);
1427+
return { true, stmt };
1428+
}
1429+
1430+
std::pair<bool, Pattern*> walkToPatternPre(Pattern *pattern) override {
1431+
return { false, pattern };
1432+
}
1433+
1434+
bool walkToTypeLocPre(TypeLoc &typeLoc) override { return false; }
1435+
1436+
bool walkToTypeReprPre(TypeRepr *typeRepr) override { return false; }
1437+
1438+
bool walkToParameterListPre(ParameterList *params) override {
1439+
return false;
1440+
}
1441+
};
1442+
1443+
AddOnDiagnosticWalker walker(dc);
1444+
stmt->walk(walker);
1445+
}
1446+
13901447
Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
13911448
FuncDecl *func, Type builderType) {
13921449
// Pre-check the body: pre-check any expressions in it and look
@@ -1505,6 +1562,7 @@ Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
15051562
if (auto result = cs.applySolution(
15061563
solutions.front(),
15071564
SolutionApplicationTarget(func))) {
1565+
performAddOnDiagnostics(result->getFunctionBody(), func);
15081566
return result->getFunctionBody();
15091567
}
15101568

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)