-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Diagnostic] Improve diagnostic for trailing closures in statement conditions #25165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2957,7 +2957,7 @@ void swift::fixItEncloseTrailingClosure(TypeChecker &TC, | |
static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { | ||
if (E == nullptr || isa<ErrorExpr>(E)) return; | ||
|
||
// Shallow walker. just dig into implicit expression. | ||
// Walk into expressions which might have invalid trailing closures | ||
class DiagnoseWalker : public ASTWalker { | ||
TypeChecker &TC; | ||
|
||
|
@@ -2992,13 +2992,22 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { | |
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; } | ||
|
||
std::pair<bool, Expr *> walkToExprPre(Expr *E) override { | ||
// Dig into implicit expression. | ||
if (E->isImplicit()) return { true, E }; | ||
// Diagnose call expression. | ||
if (auto CE = dyn_cast<CallExpr>(E)) | ||
diagnoseIt(CE); | ||
// Don't dig any further. | ||
return { false, E }; | ||
switch (E->getKind()) { | ||
case ExprKind::Paren: | ||
case ExprKind::Tuple: | ||
case ExprKind::Array: | ||
case ExprKind::Dictionary: | ||
case ExprKind::InterpolatedStringLiteral: | ||
// If a trailing closure appears as a child of one of these types of | ||
// expression, don't diagnose it as there is no ambiguity. | ||
return {E->isImplicit(), E}; | ||
case ExprKind::Call: | ||
diagnoseIt(cast<CallExpr>(E)); | ||
break; | ||
default: | ||
break; | ||
} | ||
return {true, E}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is a change in behavior; is that intentional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. The intent is to always walk into an expression, unless it's a non-implicit parent/tuple/etc. expression. |
||
} | ||
}; | ||
|
||
|
@@ -3018,9 +3027,12 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) { | |
static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Stmt *S) { | ||
if (auto LCS = dyn_cast<LabeledConditionalStmt>(S)) { | ||
for (auto elt : LCS->getCond()) { | ||
if (elt.getKind() == StmtConditionElement::CK_PatternBinding) | ||
if (elt.getKind() == StmtConditionElement::CK_PatternBinding) { | ||
checkStmtConditionTrailingClosure(TC, elt.getInitializer()); | ||
else if (elt.getKind() == StmtConditionElement::CK_Boolean) | ||
if (auto *exprPattern = dyn_cast<ExprPattern>(elt.getPattern())) { | ||
checkStmtConditionTrailingClosure(TC, exprPattern->getMatchExpr()); | ||
} | ||
} else if (elt.getKind() == StmtConditionElement::CK_Boolean) | ||
checkStmtConditionTrailingClosure(TC, elt.getBoolean()); | ||
// No trailing closure for CK_Availability: e.g. `if #available() {}`. | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To handle binary expressions et al., I think we should maintain "not walk into" list, instead of walk into specific expression kinds.
I mean, currently we walk into only
CallExpr
(andDotSyntaxCallExpr
). But I think we should walk into exceptParenExpr
,TupleExpr
,ArrayExpr
,DictionaryExpr
, etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up adopting a solution like this, my one concern is that a future new expression kind might cause a false positive to be diagnosed if it isn't included in the 'skipped' list. This should be mitigated by the fact that isValidTrailingClosure() still whitelists the individual tokens which may follow a closing brace for something to be considered a possibly valid trailing closure. It doesn't completely eliminate the possibility of future breakage though.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's OK. False positive is better than false negative in this case. The user can easily workaround it by not using trailing closure (or enclose whole expression into parentheses).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…as long as we don't break source compat.