Skip to content

Commit 83f9a52

Browse files
authored
Merge pull request #28362 from owenv/improve_trailing_closure_in_condition_diag_2
[Parse] Reapply trailing closure in condition fix and downgrade to warning
2 parents 62ae2bf + 067a8ec commit 83f9a52

File tree

4 files changed

+150
-43
lines changed

4 files changed

+150
-43
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,9 +3476,10 @@ NOTE(add_where_newline, none,
34763476
NOTE(duplicate_where, none,
34773477
"duplicate the 'where' on both patterns to check both patterns",())
34783478

3479-
ERROR(trailing_closure_requires_parens,none,
3480-
"trailing closure requires parentheses for disambiguation in this"
3481-
" context", ())
3479+
WARNING(trailing_closure_requires_parens,none,
3480+
"trailing closure in this context is confusable with the body of the"
3481+
" statement; pass as a parenthesized argument to silence this warning",
3482+
())
34823483

34833484
ERROR(opaque_type_var_no_init,none,
34843485
"property declares an opaque return type, but has no initializer "

lib/Parse/ParseExpr.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -965,23 +965,40 @@ static bool isValidTrailingClosure(bool isExprBasic, Parser &P){
965965
// the token after the { is on the same line as the {.
966966
if (P.peekToken().isAtStartOfLine())
967967
return false;
968-
969-
968+
970969
// Determine if the {} goes with the expression by eating it, and looking
971-
// to see if it is immediately followed by '{', 'where', or comma. If so,
972-
// we consider it to be part of the proceeding expression.
970+
// to see if it is immediately followed by a token which indicates we should
971+
// consider it part of the preceding expression
973972
Parser::BacktrackingScope backtrack(P);
974973
P.consumeToken(tok::l_brace);
975974
P.skipUntil(tok::r_brace);
976975
SourceLoc endLoc;
977-
if (!P.consumeIf(tok::r_brace, endLoc) ||
978-
P.Tok.isNot(tok::l_brace, tok::kw_where, tok::comma)) {
976+
if (!P.consumeIf(tok::r_brace, endLoc))
979977
return false;
980-
}
981978

982-
// Recoverable case. Just return true here and Sema will emit a diagnostic
983-
// later. see: Sema/MiscDiagnostics.cpp#checkStmtConditionTrailingClosure
984-
return true;
979+
switch (P.Tok.getKind()) {
980+
case tok::l_brace:
981+
case tok::kw_where:
982+
case tok::comma:
983+
return true;
984+
case tok::l_square:
985+
case tok::l_paren:
986+
case tok::period:
987+
case tok::period_prefix:
988+
case tok::kw_is:
989+
case tok::kw_as:
990+
case tok::question_postfix:
991+
case tok::question_infix:
992+
case tok::exclaim_postfix:
993+
case tok::colon:
994+
case tok::equal:
995+
case tok::oper_postfix:
996+
case tok::oper_binary_spaced:
997+
case tok::oper_binary_unspaced:
998+
return !P.Tok.isAtStartOfLine();
999+
default:
1000+
return false;
1001+
}
9851002
}
9861003

9871004

lib/Sema/MiscDiagnostics.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3030,7 +3030,7 @@ void swift::fixItEncloseTrailingClosure(ASTContext &ctx,
30303030
static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) {
30313031
if (E == nullptr || isa<ErrorExpr>(E)) return;
30323032

3033-
// Shallow walker. just dig into implicit expression.
3033+
// Walk into expressions which might have invalid trailing closures
30343034
class DiagnoseWalker : public ASTWalker {
30353035
ASTContext &Ctx;
30363036

@@ -3065,13 +3065,22 @@ static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) {
30653065
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
30663066

30673067
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
3068-
// Dig into implicit expression.
3069-
if (E->isImplicit()) return { true, E };
3070-
// Diagnose call expression.
3071-
if (auto CE = dyn_cast<CallExpr>(E))
3072-
diagnoseIt(CE);
3073-
// Don't dig any further.
3074-
return { false, E };
3068+
switch (E->getKind()) {
3069+
case ExprKind::Paren:
3070+
case ExprKind::Tuple:
3071+
case ExprKind::Array:
3072+
case ExprKind::Dictionary:
3073+
case ExprKind::InterpolatedStringLiteral:
3074+
// If a trailing closure appears as a child of one of these types of
3075+
// expression, don't diagnose it as there is no ambiguity.
3076+
return {E->isImplicit(), E};
3077+
case ExprKind::Call:
3078+
diagnoseIt(cast<CallExpr>(E));
3079+
break;
3080+
default:
3081+
break;
3082+
}
3083+
return {true, E};
30753084
}
30763085
};
30773086

@@ -3091,9 +3100,13 @@ static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) {
30913100
static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Stmt *S) {
30923101
if (auto LCS = dyn_cast<LabeledConditionalStmt>(S)) {
30933102
for (auto elt : LCS->getCond()) {
3094-
if (elt.getKind() == StmtConditionElement::CK_PatternBinding)
3103+
3104+
if (elt.getKind() == StmtConditionElement::CK_PatternBinding) {
30953105
checkStmtConditionTrailingClosure(ctx, elt.getInitializer());
3096-
else if (elt.getKind() == StmtConditionElement::CK_Boolean)
3106+
if (auto *exprPattern = dyn_cast<ExprPattern>(elt.getPattern())) {
3107+
checkStmtConditionTrailingClosure(ctx, exprPattern->getMatchExpr());
3108+
}
3109+
} else if (elt.getKind() == StmtConditionElement::CK_Boolean)
30973110
checkStmtConditionTrailingClosure(ctx, elt.getBoolean());
30983111
// No trailing closure for CK_Availability: e.g. `if #available() {}`.
30993112
}

test/expr/closure/trailing.swift

Lines changed: 96 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ limitXY(someInt, toGamut: intArray) {} // expected-error{{extra trailing closur
137137
// <rdar://problem/23036383> QoI: Invalid trailing closures in stmt-conditions produce lowsy diagnostics
138138
func retBool(x: () -> Int) -> Bool {}
139139
func maybeInt(_: () -> Int) -> Int? {}
140+
func twoClosureArgs(_:()->Void, _:()->Void) -> Bool {}
140141
class Foo23036383 {
141142
init() {}
142143
func map(_: (Int) -> Int) -> Int? {}
@@ -153,55 +154,130 @@ func r23036383(foo: Foo23036383?, obj: Foo23036383) {
153154
if retBool(x: { 1 }) { } // OK
154155
if (retBool { 1 }) { } // OK
155156

156-
if retBool{ 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{13-13=(x: }} {{18-18=)}}
157+
if retBool{ 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{13-13=(x: }} {{18-18=)}}
157158
}
158-
if retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{13-14=(x: }} {{19-19=)}}
159+
if retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{13-14=(x: }} {{19-19=)}}
159160
}
160-
if retBool() { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{14-16=x: }} {{21-21=)}}
161-
} else if retBool( ) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{21-24=x: }} {{29-29=)}}
161+
if retBool() { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{14-16=x: }} {{21-21=)}}
162+
} else if retBool( ) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{21-24=x: }} {{29-29=)}}
162163
}
163164

164-
if let _ = maybeInt { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}}
165+
if let _ = maybeInt { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}}
165166
}
166-
if let _ = maybeInt { 1 } , true { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}}
167+
if let _ = maybeInt { 1 } , true { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}}
167168
}
168169

169-
if let _ = foo?.map {$0+1} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{29-29=)}}
170+
if let _ = foo?.map {$0+1} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}}
170171
}
171-
if let _ = foo?.map() {$0+1} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{23-25=}} {{31-31=)}}
172+
if let _ = foo?.map() {$0+1} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{23-25=}} {{31-31=)}}
172173
}
173-
if let _ = foo, retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{26-27=(x: }} {{32-32=)}}
174+
if let _ = foo, retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{26-27=(x: }} {{32-32=)}}
174175
}
175176

176-
if obj.meth1(x: 1) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{20-22=, }} {{27-27=)}}
177+
if obj.meth1(x: 1) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{20-22=, }} {{27-27=)}}
177178
}
178-
if obj.meth2(1) { 0 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{17-19=, y: }} {{24-24=)}}
179+
if obj.meth2(1) { 0 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{17-19=, y: }} {{24-24=)}}
179180
}
180181

181-
for _ in obj.filter {$0 > 4} { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(by: }} {{31-31=)}}
182+
for _ in obj.filter {$0 > 4} { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(by: }} {{31-31=)}}
182183
}
183-
for _ in obj.filter {$0 > 4} where true { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(by: }} {{31-31=)}}
184+
for _ in obj.filter {$0 > 4} where true { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(by: }} {{31-31=)}}
184185
}
185-
for _ in [1,2] where retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{31-32=(x: }} {{37-37=)}}
186+
for _ in [1,2] where retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{31-32=(x: }} {{37-37=)}}
186187
}
187188

188-
while retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{16-17=(x: }} {{22-22=)}}
189+
while retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{16-17=(x: }} {{22-22=)}}
189190
}
190-
while let _ = foo, retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{29-30=(x: }} {{35-35=)}}
191+
while let _ = foo, retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{29-30=(x: }} {{35-35=)}}
191192
}
192193

193-
switch retBool { return 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{17-18=(x: }} {{30-30=)}}
194+
switch retBool { return 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{17-18=(x: }} {{30-30=)}}
194195
default: break
195196
}
196197

197198
do {
198199
throw MyErr.A;
199-
} catch MyErr.A where retBool { 1 } { // expected-error {{trailing closure requires parentheses for disambiguation in this context}} {{32-33=(x: }} {{38-38=)}}
200+
} catch MyErr.A where retBool { 1 } { // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{32-33=(x: }} {{38-38=)}}
200201
} catch { }
201202

202203
if let _ = maybeInt { 1 }, retBool { 1 } { }
203-
// expected-error@-1 {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}}
204-
// expected-error@-2 {{trailing closure requires parentheses for disambiguation in this context}} {{37-38=(x: }} {{43-43=)}}
204+
// expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{28-28=)}}
205+
// expected-warning@-2 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{37-38=(x: }} {{43-43=)}}
206+
207+
if let _ = foo?.map {$0+1}.map {$0+1} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{33-34=(}} {{40-40=)}}
208+
// expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}}
209+
210+
if let _ = foo?.map {$0+1}.map({$0+1}) {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}}
211+
212+
if let _ = foo?.map {$0+1}.map({$0+1}).map{$0+1} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{45-45=(}} {{51-51=)}}
213+
// expected-warning@-1 {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{22-23=(}} {{29-29=)}}
214+
215+
if twoClosureArgs({}) {} {} // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}} {{23-25=, }} {{27-27=)}}
216+
217+
if let _ = (foo?.map {$0+1}.map({$0+1}).map{$0+1}) {} // OK
218+
219+
if let _ = (foo?.map {$0+1}.map({$0+1})).map({$0+1}) {} // OK
220+
}
221+
222+
func id<T>(fn: () -> T) -> T { return fn() }
223+
func any<T>(fn: () -> T) -> Any { return fn() }
224+
225+
func testSR8736() {
226+
if !id { true } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
227+
228+
if id { true } == true { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
229+
230+
if true == id { true } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
231+
232+
if id { true } ? true : false { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
233+
234+
if true ? id { true } : false { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
235+
236+
if true ? true : id { false } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
237+
238+
if id { [false,true] }[0] { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
239+
240+
if id { { true } } () { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
241+
242+
if any { true } as! Bool { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
243+
244+
if let _ = any { "test" } as? Int { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
245+
246+
if any { "test" } is Int { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
247+
248+
if let _ = id { [] as [Int]? }?.first { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
249+
250+
if id { true as Bool? }! { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
251+
252+
if case id { 1 } = 1 { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
253+
254+
if case 1 = id { 1 } { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
255+
256+
if case 1 = id { 1 } /*comment*/ { return } // expected-warning {{trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning}}
257+
258+
if case (id { 1 }) = 1 { return } // OK
259+
260+
if case 1 = (id { 1 }) { return } // OK
261+
262+
if [id { true }].count == 0 { return } // OK
263+
264+
if [id { true } : "test"].keys.count == 0 { return } // OK
265+
266+
if "\(id { true })" == "foo" { return } // OK
267+
268+
if (id { true }) { return } // OK
269+
270+
if (id { true }) { }
271+
[1, 2, 3].count // expected-warning {{expression of type 'Int' is unused}}
272+
273+
if true { }
274+
() // OK
275+
276+
if true
277+
{
278+
279+
}
280+
() // OK
205281
}
206282

207283
func overloadOnLabel(a: () -> Void) {}

0 commit comments

Comments
 (0)