Skip to content

Commit e6d6e0e

Browse files
committed
Offer fix-its to disambiguate based on a trailing closure's label.
(by making it a normal argument with a label and not a trailing closure) Diagnostic part of rdar://problem/25607552. A later commit will keep us from getting in this situation quite so much when default arguments are involved.
1 parent a14e329 commit e6d6e0e

File tree

3 files changed

+163
-1
lines changed

3 files changed

+163
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,10 @@ ERROR(ambiguous_decl_ref,none,
22352235
"ambiguous use of %0", (DeclName))
22362236
ERROR(ambiguous_operator_ref,none,
22372237
"ambiguous use of operator %0", (DeclName))
2238+
NOTE(ambiguous_because_of_trailing_closure,none,
2239+
"%select{use an explicit argument label instead of a trailing closure|"
2240+
"avoid using a trailing closure}0 to call %1",
2241+
(bool, DeclName))
22382242

22392243
// Cannot capture inout-ness of a parameter
22402244
// Partial application of foreign functions not supported

lib/Sema/CSDiag.cpp

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,93 @@ static DeclName getOverloadChoiceName(ArrayRef<OverloadChoice> choices) {
334334
return name;
335335
}
336336

337+
/// Returns true if any diagnostics were emitted.
338+
static bool
339+
tryDiagnoseTrailingClosureAmbiguity(TypeChecker &tc, const Expr *expr,
340+
ArrayRef<OverloadChoice> choices) {
341+
auto *callExpr = dyn_cast<CallExpr>(expr);
342+
if (!callExpr)
343+
return false;
344+
if (!callExpr->hasTrailingClosure())
345+
return false;
346+
347+
llvm::SmallMapVector<Identifier, const ValueDecl *, 8> choicesByLabel;
348+
for (const OverloadChoice &choice : choices) {
349+
auto *callee = dyn_cast<AbstractFunctionDecl>(choice.getDecl());
350+
if (!callee)
351+
return false;
352+
353+
const ParameterList *paramList = callee->getParameterLists().back();
354+
const ParamDecl *param = paramList->getArray().back();
355+
356+
// Sanity-check that the trailing closure corresponds to this parameter.
357+
if (!param->getType()->is<AnyFunctionType>())
358+
return false;
359+
360+
Identifier trailingClosureLabel = param->getArgumentName();
361+
auto &choiceForLabel = choicesByLabel[trailingClosureLabel];
362+
363+
// FIXME: Cargo-culted from diagnoseAmbiguity: apparently the same decl can
364+
// appear more than once?
365+
if (choiceForLabel == callee)
366+
continue;
367+
368+
// If just providing the trailing closure label won't solve the ambiguity,
369+
// don't bother offering the fix-it.
370+
if (choiceForLabel != nullptr)
371+
return false;
372+
373+
choiceForLabel = callee;
374+
}
375+
376+
// If we got here, then all of the choices have unique labels. Offer them in
377+
// order.
378+
SourceManager &sourceMgr = tc.Context.SourceMgr;
379+
SourceLoc replaceStartLoc;
380+
SourceLoc closureStartLoc;
381+
bool hasOtherArgs, needsParens;
382+
if (auto tupleArgs = dyn_cast<TupleExpr>(callExpr->getArg())) {
383+
assert(tupleArgs->getNumElements() >= 2);
384+
const Expr *lastBeforeClosure = tupleArgs->getElements().drop_back().back();
385+
replaceStartLoc =
386+
Lexer::getLocForEndOfToken(sourceMgr, lastBeforeClosure->getEndLoc());
387+
closureStartLoc = tupleArgs->getElements().back()->getStartLoc();
388+
hasOtherArgs = true;
389+
needsParens = false;
390+
} else {
391+
auto parenArgs = cast<ParenExpr>(callExpr->getArg());
392+
replaceStartLoc = parenArgs->getRParenLoc();
393+
closureStartLoc = parenArgs->getSubExpr()->getStartLoc();
394+
hasOtherArgs = false;
395+
needsParens = parenArgs->getRParenLoc().isInvalid();
396+
}
397+
if (!replaceStartLoc.isValid()) {
398+
assert(callExpr->getFn()->getEndLoc().isValid());
399+
replaceStartLoc =
400+
Lexer::getLocForEndOfToken(sourceMgr, callExpr->getFn()->getEndLoc());
401+
}
402+
403+
for (const auto &choicePair : choicesByLabel) {
404+
SmallString<64> insertionString;
405+
if (needsParens)
406+
insertionString += "(";
407+
else if (hasOtherArgs)
408+
insertionString += ", ";
409+
410+
if (!choicePair.first.empty()) {
411+
insertionString += choicePair.first.str();
412+
insertionString += ": ";
413+
}
414+
415+
tc.diagnose(expr->getLoc(), diag::ambiguous_because_of_trailing_closure,
416+
choicePair.first.empty(), choicePair.second->getFullName())
417+
.fixItReplaceChars(replaceStartLoc, closureStartLoc, insertionString)
418+
.fixItInsertAfter(callExpr->getEndLoc(), ")");
419+
}
420+
421+
return true;
422+
}
423+
337424
static bool diagnoseAmbiguity(ConstraintSystem &cs,
338425
ArrayRef<Solution> solutions,
339426
Expr *expr) {
@@ -424,9 +511,12 @@ static bool diagnoseAmbiguity(ConstraintSystem &cs,
424511
: diag::ambiguous_decl_ref,
425512
name);
426513

514+
if (tryDiagnoseTrailingClosureAmbiguity(tc, expr, overload.choices))
515+
return true;
516+
427517
// Emit candidates. Use a SmallPtrSet to make sure only emit a particular
428518
// candidate once. FIXME: Why is one candidate getting into the overload
429-
// set multiple times?
519+
// set multiple times? (See also tryDiagnoseTrailingClosureAmbiguity.)
430520
SmallPtrSet<Decl*, 8> EmittedDecls;
431521
for (auto choice : overload.choices) {
432522
switch (choice.getKind()) {

test/expr/closure/trailing.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,71 @@ func r23036383(foo: Foo23036383?, obj: Foo23036383) {
180180
// expected-error@-1 {{trailing closure requires parentheses for disambiguation in this context}} {{22-23=(}} {{28-28=)}}
181181
// expected-error@-2 {{trailing closure requires parentheses for disambiguation in this context}} {{37-38=(x: }} {{43-43=)}}
182182
}
183+
184+
func overloadOnLabel(a: () -> Void) {}
185+
func overloadOnLabel(b: () -> Void) {}
186+
func overloadOnLabel(c: () -> Void) {}
187+
188+
func overloadOnLabel2(a: () -> Void) {}
189+
func overloadOnLabel2(_: () -> Void) {}
190+
191+
func overloadOnLabelArgs(_: Int, a: () -> Void) {}
192+
func overloadOnLabelArgs(_: Int, b: () -> Void) {}
193+
194+
func overloadOnLabelArgs2(_: Int, a: () -> Void) {}
195+
func overloadOnLabelArgs2(_: Int, _: () -> Void) {}
196+
197+
func overloadOnLabelDefaultArgs(x: Int = 0, a: () -> Void) {}
198+
func overloadOnLabelDefaultArgs(x: Int = 1, b: () -> Void) {}
199+
200+
func overloadOnLabelSomeDefaultArgs(_: Int, x: Int = 0, a: () -> Void) {}
201+
func overloadOnLabelSomeDefaultArgs(_: Int, x: Int = 1, b: () -> Void) {}
202+
203+
func overloadOnDefaultArgsOnly(x: Int = 0, a: () -> Void) {} // expected-note 2 {{found this candidate}}
204+
func overloadOnDefaultArgsOnly(y: Int = 1, a: () -> Void) {} // expected-note 2 {{found this candidate}}
205+
206+
func overloadOnDefaultArgsOnly2(x: Int = 0, a: () -> Void) {} // expected-note 2 {{found this candidate}}
207+
func overloadOnDefaultArgsOnly2(y: Bool = true, a: () -> Void) {} // expected-note 2 {{found this candidate}}
208+
209+
func overloadOnDefaultArgsOnly3(x: Int = 0, a: () -> Void) {} // expected-note 2 {{found this candidate}}
210+
func overloadOnDefaultArgsOnly3(x: Bool = true, a: () -> Void) {} // expected-note 2 {{found this candidate}}
211+
212+
func overloadOnSomeDefaultArgsOnly(_: Int, x: Int = 0, a: () -> Void) {} // expected-note {{found this candidate}}
213+
func overloadOnSomeDefaultArgsOnly(_: Int, y: Int = 1, a: () -> Void) {} // expected-note {{found this candidate}}
214+
215+
func overloadOnSomeDefaultArgsOnly2(_: Int, x: Int = 0, a: () -> Void) {} // expected-note {{found this candidate}}
216+
func overloadOnSomeDefaultArgsOnly2(_: Int, y: Bool = true, a: () -> Void) {} // expected-note {{found this candidate}}
217+
218+
func overloadOnSomeDefaultArgsOnly3(_: Int, x: Int = 0, a: () -> Void) {} // expected-note {{found this candidate}}
219+
func overloadOnSomeDefaultArgsOnly3(_: Int, x: Bool = true, a: () -> Void) {} // expected-note {{found this candidate}}
220+
221+
func testOverloadAmbiguity() {
222+
overloadOnLabel {} // expected-error {{ambiguous use of 'overloadOnLabel'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(a:)'}} {{18-19=(a: }} {{21-21=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(b:)'}} {{18-19=(b: }} {{21-21=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(c:)'}} {{18-19=(c: }} {{21-21=)}}
223+
overloadOnLabel() {} // expected-error {{ambiguous use of 'overloadOnLabel'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(a:)'}} {{19-21=a: }} {{23-23=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(b:)'}} {{19-21=b: }} {{23-23=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel(c:)'}} {{19-21=c: }} {{23-23=)}}
224+
overloadOnLabel2 {} // expected-error {{ambiguous use of 'overloadOnLabel2'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel2(a:)'}} {{19-20=(a: }} {{22-22=)}} expected-note {{avoid using a trailing closure to call 'overloadOnLabel2'}} {{19-20=(}} {{22-22=)}}
225+
overloadOnLabel2() {} // expected-error {{ambiguous use of 'overloadOnLabel2'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabel2(a:)'}} {{20-22=a: }} {{24-24=)}} expected-note {{avoid using a trailing closure to call 'overloadOnLabel2'}} {{20-22=}} {{24-24=)}}
226+
overloadOnLabelArgs(1) {} // expected-error {{ambiguous use of 'overloadOnLabelArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelArgs(_:a:)'}} {{24-26=, a: }} {{28-28=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelArgs(_:b:)'}} {{24-26=, b: }} {{28-28=)}}
227+
overloadOnLabelArgs2(1) {} // expected-error {{ambiguous use of 'overloadOnLabelArgs2'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelArgs2(_:a:)'}} {{25-27=, a: }} {{29-29=)}} expected-note {{avoid using a trailing closure to call 'overloadOnLabelArgs2'}} {{25-27=, }} {{29-29=)}}
228+
overloadOnLabelDefaultArgs {} // expected-error {{ambiguous use of 'overloadOnLabelDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:a:)'}} {{29-30=(a: }} {{32-32=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:b:)'}} {{29-30=(b: }} {{32-32=)}}
229+
overloadOnLabelDefaultArgs() {} // expected-error {{ambiguous use of 'overloadOnLabelDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:a:)'}} {{30-32=a: }} {{34-34=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:b:)'}} {{30-32=b: }} {{34-34=)}}
230+
overloadOnLabelDefaultArgs(x: 2) {} // expected-error {{ambiguous use of 'overloadOnLabelDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:a:)'}} {{34-36=, a: }} {{38-38=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelDefaultArgs(x:b:)'}} {{34-36=, b: }} {{38-38=)}}
231+
overloadOnLabelSomeDefaultArgs(1) {} // expected-error {{ambiguous use of 'overloadOnLabelSomeDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:a:)'}} {{35-37=, a: }} {{39-39=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:b:)'}} {{35-37=, b: }} {{39-39=)}}
232+
overloadOnLabelSomeDefaultArgs(1, x: 2) {} // expected-error {{ambiguous use of 'overloadOnLabelSomeDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:a:)'}} {{41-43=, a: }} {{45-45=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:b:)'}} {{41-43=, b: }} {{45-45=)}}
233+
234+
overloadOnLabelSomeDefaultArgs( // expected-error {{ambiguous use of 'overloadOnLabelSomeDefaultArgs'}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:a:)'}} {{12-5=, a: }} {{4-4=)}} expected-note {{use an explicit argument label instead of a trailing closure to call 'overloadOnLabelSomeDefaultArgs(_:x:b:)'}} {{12-5=, b: }} {{4-4=)}}
235+
1, x: 2
236+
) {
237+
// some
238+
}
239+
240+
overloadOnDefaultArgsOnly {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly'}}
241+
overloadOnDefaultArgsOnly() {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly'}}
242+
overloadOnDefaultArgsOnly2 {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly2'}}
243+
overloadOnDefaultArgsOnly2() {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly2'}}
244+
overloadOnDefaultArgsOnly3 {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly3(x:a:)'}}
245+
overloadOnDefaultArgsOnly3() {} // expected-error {{ambiguous use of 'overloadOnDefaultArgsOnly3(x:a:)'}}
246+
247+
overloadOnSomeDefaultArgsOnly(1) {} // expected-error {{ambiguous use of 'overloadOnSomeDefaultArgsOnly'}}
248+
overloadOnSomeDefaultArgsOnly2(1) {} // expected-error {{ambiguous use of 'overloadOnSomeDefaultArgsOnly2'}}
249+
overloadOnSomeDefaultArgsOnly3(1) {} // expected-error {{ambiguous use of 'overloadOnSomeDefaultArgsOnly3(_:x:a:)'}}
250+
}

0 commit comments

Comments
 (0)