Skip to content

Commit 65f3ba5

Browse files
authored
[Sema] Unify fix-it logic for enclosing trailing closure in argument parens (#4961)
`tryDiagnoseTrailingClosureAmbiguity` and `checkStmtConditionTrailingClosure` had similar logic. Added swift::fixItEncloseTrailingClosure function.
1 parent 5bc8ca7 commit 65f3ba5

File tree

3 files changed

+69
-84
lines changed

3 files changed

+69
-84
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -376,47 +376,12 @@ tryDiagnoseTrailingClosureAmbiguity(TypeChecker &tc, const Expr *expr,
376376

377377
// If we got here, then all of the choices have unique labels. Offer them in
378378
// order.
379-
SourceManager &sourceMgr = tc.Context.SourceMgr;
380-
SourceLoc replaceStartLoc;
381-
SourceLoc closureStartLoc;
382-
bool hasOtherArgs, needsParens;
383-
if (auto tupleArgs = dyn_cast<TupleExpr>(callExpr->getArg())) {
384-
assert(tupleArgs->getNumElements() >= 2);
385-
const Expr *lastBeforeClosure = tupleArgs->getElements().drop_back().back();
386-
replaceStartLoc =
387-
Lexer::getLocForEndOfToken(sourceMgr, lastBeforeClosure->getEndLoc());
388-
closureStartLoc = tupleArgs->getElements().back()->getStartLoc();
389-
hasOtherArgs = true;
390-
needsParens = false;
391-
} else {
392-
auto parenArgs = cast<ParenExpr>(callExpr->getArg());
393-
replaceStartLoc = parenArgs->getRParenLoc();
394-
closureStartLoc = parenArgs->getSubExpr()->getStartLoc();
395-
hasOtherArgs = false;
396-
needsParens = parenArgs->getRParenLoc().isInvalid();
397-
}
398-
if (!replaceStartLoc.isValid()) {
399-
assert(callExpr->getFn()->getEndLoc().isValid());
400-
replaceStartLoc =
401-
Lexer::getLocForEndOfToken(sourceMgr, callExpr->getFn()->getEndLoc());
402-
}
403-
404379
for (const auto &choicePair : choicesByLabel) {
405-
SmallString<64> insertionString;
406-
if (needsParens)
407-
insertionString += "(";
408-
else if (hasOtherArgs)
409-
insertionString += ", ";
410-
411-
if (!choicePair.first.empty()) {
412-
insertionString += choicePair.first.str();
413-
insertionString += ": ";
414-
}
415-
416-
tc.diagnose(expr->getLoc(), diag::ambiguous_because_of_trailing_closure,
417-
choicePair.first.empty(), choicePair.second->getFullName())
418-
.fixItReplaceChars(replaceStartLoc, closureStartLoc, insertionString)
419-
.fixItInsertAfter(callExpr->getEndLoc(), ")");
380+
auto diag = tc.diagnose(expr->getLoc(),
381+
diag::ambiguous_because_of_trailing_closure,
382+
choicePair.first.empty(),
383+
choicePair.second->getFullName());
384+
swift::fixItEncloseTrailingClosure(tc, diag, callExpr, choicePair.first);
420385
}
421386

422387
return true;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3108,6 +3108,51 @@ static void checkSwitch(TypeChecker &TC, const SwitchStmt *stmt) {
31083108
}
31093109
}
31103110

3111+
void swift::fixItEncloseTrailingClosure(TypeChecker &TC,
3112+
InFlightDiagnostic &diag,
3113+
const CallExpr *call,
3114+
Identifier closureLabel) {
3115+
auto argsExpr = call->getArg();
3116+
if (auto TSE = dyn_cast<TupleShuffleExpr>(argsExpr))
3117+
argsExpr = TSE->getSubExpr();
3118+
3119+
SmallString<32> replacement;
3120+
SourceLoc lastLoc;
3121+
SourceRange closureRange;
3122+
if (auto PE = dyn_cast<ParenExpr>(argsExpr)) {
3123+
assert(PE->hasTrailingClosure() && "must have trailing closure");
3124+
closureRange = PE->getSubExpr()->getSourceRange();
3125+
lastLoc = PE->getLParenLoc(); // e.g funcName() { 1 }
3126+
if (!lastLoc.isValid()) {
3127+
// Bare trailing closure: e.g. funcName { 1 }
3128+
replacement = "(";
3129+
lastLoc = call->getFn()->getEndLoc();
3130+
}
3131+
} else if (auto TE = dyn_cast<TupleExpr>(argsExpr)) {
3132+
// Tuple + trailing closure: e.g. funcName(x: 1) { 1 }
3133+
assert(TE->hasTrailingClosure() && "must have trailing closure");
3134+
auto numElements = TE->getNumElements();
3135+
assert(numElements >= 2 && "Unexpected num of elements in TupleExpr");
3136+
closureRange = TE->getElement(numElements - 1)->getSourceRange();
3137+
lastLoc = TE->getElement(numElements - 2)->getEndLoc();
3138+
replacement = ", ";
3139+
} else {
3140+
// Can't be here.
3141+
return;
3142+
}
3143+
3144+
// Add argument label of the closure.
3145+
if (!closureLabel.empty()) {
3146+
replacement += closureLabel.str();
3147+
replacement += ": ";
3148+
}
3149+
3150+
lastLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr, lastLoc);
3151+
diag
3152+
.fixItReplaceChars(lastLoc, closureRange.Start, replacement)
3153+
.fixItInsertAfter(closureRange.End, ")");
3154+
}
3155+
31113156
// Perform checkStmtConditionTrailingClosure for single expression.
31123157
static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) {
31133158
if (E == nullptr || isa<ErrorExpr>(E)) return;
@@ -3117,63 +3162,31 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) {
31173162
TypeChecker &TC;
31183163

31193164
void diagnoseIt(const CallExpr *E) {
3165+
if (!E->hasTrailingClosure()) return;
3166+
31203167
auto argsExpr = E->getArg();
31213168
auto argsTy = argsExpr->getType();
3122-
31233169
// Ignore invalid argument type. Some diagnostics are already emitted.
31243170
if (!argsTy || argsTy->is<ErrorType>()) return;
31253171

31263172
if (auto TSE = dyn_cast<TupleShuffleExpr>(argsExpr))
31273173
argsExpr = TSE->getSubExpr();
31283174

3129-
SmallString<16> replacement;
3130-
SourceLoc lastLoc;
3131-
SourceRange closureRange;
3132-
if (auto PE = dyn_cast<ParenExpr>(argsExpr)) {
3133-
// Ignore non-trailing-closure.
3134-
if (!PE->hasTrailingClosure()) return;
3135-
3136-
closureRange = PE->getSubExpr()->getSourceRange();
3137-
lastLoc = PE->getLParenLoc();
3138-
if (lastLoc.isValid()) {
3139-
// Empty paren: e.g. if funcName() { 1 } { ... }
3140-
replacement = "";
3141-
} else {
3142-
// Bare trailing closure: e.g. if funcName { 1 } { ... }
3143-
replacement = "(";
3144-
lastLoc = E->getFn()->getEndLoc();
3145-
}
3146-
} else if (auto TE = dyn_cast<TupleExpr>(argsExpr)) {
3147-
// Ignore non-trailing-closure.
3148-
if (!TE->hasTrailingClosure()) return;
3149-
3150-
// Tuple + trailing closure: e.g. if funcName(x: 1) { 1 } { ... }
3151-
auto numElements = TE->getNumElements();
3152-
assert(numElements >= 2 && "Unexpected num of elements in TupleExpr");
3153-
closureRange = TE->getElement(numElements - 1)->getSourceRange();
3154-
lastLoc = TE->getElement(numElements - 2)->getEndLoc();
3155-
replacement = ", ";
3156-
} else {
3157-
// Can't be here.
3158-
return;
3159-
}
3175+
SourceLoc closureLoc;
3176+
if (auto PE = dyn_cast<ParenExpr>(argsExpr))
3177+
closureLoc = PE->getSubExpr()->getStartLoc();
3178+
else if (auto TE = dyn_cast<TupleExpr>(argsExpr))
3179+
closureLoc = TE->getElements().back()->getStartLoc();
31603180

3161-
// Add argument label of the closure that is going to be enclosed in
3162-
// parens.
3181+
Identifier closureLabel;
31633182
if (auto TT = argsTy->getAs<TupleType>()) {
31643183
assert(TT->getNumElements() != 0 && "Unexpected empty TupleType");
3165-
auto closureLabel = TT->getElement(TT->getNumElements() - 1).getName();
3166-
if (!closureLabel.empty()) {
3167-
replacement += closureLabel.str();
3168-
replacement += ": ";
3169-
}
3184+
closureLabel = TT->getElement(TT->getNumElements() - 1).getName();
31703185
}
31713186

3172-
// Emit diagnostics.
3173-
lastLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr, lastLoc);
3174-
TC.diagnose(closureRange.Start, diag::trailing_closure_requires_parens)
3175-
.fixItReplaceChars(lastLoc, closureRange.Start, replacement)
3176-
.fixItInsertAfter(closureRange.End, ")");
3187+
auto diag = TC.diagnose(closureLoc,
3188+
diag::trailing_closure_requires_parens);
3189+
fixItEncloseTrailingClosure(TC, diag, E, closureLabel);
31773190
}
31783191

31793192
public:

lib/Sema/MiscDiagnostics.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace swift {
2424
class AbstractFunctionDecl;
2525
class ApplyExpr;
2626
class AvailableAttr;
27+
class CallExpr;
2728
class DeclContext;
2829
class Expr;
2930
class InFlightDiagnostic;
@@ -86,6 +87,12 @@ bool fixItOverrideDeclarationTypes(TypeChecker &TC,
8687
InFlightDiagnostic &diag,
8788
ValueDecl *decl,
8889
const ValueDecl *base);
90+
91+
/// Emit fix-its to enclose trailing closure in argument parens.
92+
void fixItEncloseTrailingClosure(TypeChecker &TC,
93+
InFlightDiagnostic &diag,
94+
const CallExpr *call,
95+
Identifier closureLabel);
8996
} // namespace swift
9097

9198
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

0 commit comments

Comments
 (0)