Skip to content

Commit 2395225

Browse files
committed
[Type checker] Improve diagnostics for trailing closures.
The change to the forward-scanning rule regressed some diagnostics, because we no longer generated the special "trailing closure mismatch" diagnostic. Reinstate the special-case "trailing closure mismatch" diagnostic, but this time do so as part of the normal argument mismatch diagnostics so it is based on type information. While here, clean up the handling of missing-argument diagnostics to deal with (multiple) trailing closures properly, so that we can (e.g) suggest adding a new labeled trailing closure at the end, rather than producing nonsensical Fix-Its. And, note that SR-12291 is broken (again) by the forward-scan matching rules.
1 parent 6c359cc commit 2395225

11 files changed

+157
-93
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 100 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3915,9 +3915,9 @@ bool MissingArgumentsFailure::diagnoseAsError() {
39153915
Expr *fnExpr = nullptr;
39163916
Expr *argExpr = nullptr;
39173917
unsigned numArguments = 0;
3918-
bool hasTrailingClosure = false;
3918+
Optional<unsigned> firstTrailingClosure = None;
39193919

3920-
std::tie(fnExpr, argExpr, numArguments, hasTrailingClosure) =
3920+
std::tie(fnExpr, argExpr, numArguments, firstTrailingClosure) =
39213921
getCallInfo(getRawAnchor());
39223922

39233923
// TODO(diagnostics): We should be able to suggest this fix-it
@@ -3980,46 +3980,66 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
39803980
auto position = argument.first;
39813981
auto label = argument.second.getLabel();
39823982

3983-
SmallString<32> insertBuf;
3984-
llvm::raw_svector_ostream insertText(insertBuf);
3985-
3986-
if (position != 0)
3987-
insertText << ", ";
3988-
3989-
forFixIt(insertText, argument.second);
3990-
39913983
Expr *fnExpr = nullptr;
39923984
Expr *argExpr = nullptr;
3993-
unsigned insertableEndIdx = 0;
3994-
bool hasTrailingClosure = false;
3985+
unsigned numArgs = 0;
3986+
Optional<unsigned> firstTrailingClosure = None;
39953987

3996-
std::tie(fnExpr, argExpr, insertableEndIdx, hasTrailingClosure) =
3988+
std::tie(fnExpr, argExpr, numArgs, firstTrailingClosure) =
39973989
getCallInfo(anchor);
39983990

3999-
if (!argExpr)
3991+
if (!argExpr) {
40003992
return false;
3993+
}
3994+
3995+
// Will the parameter accept a trailing closure?
3996+
Type paramType = resolveType(argument.second.getPlainType());
3997+
bool paramAcceptsTrailingClosure = paramType
3998+
->lookThroughAllOptionalTypes()->is<AnyFunctionType>();
40013999

4002-
if (hasTrailingClosure)
4003-
insertableEndIdx -= 1;
4000+
// Determine whether we're inserting as a trailing closure.
4001+
bool insertingTrailingClosure =
4002+
firstTrailingClosure && position > *firstTrailingClosure;
4003+
4004+
SmallString<32> insertBuf;
4005+
llvm::raw_svector_ostream insertText(insertBuf);
40044006

4005-
if (position == 0 && insertableEndIdx != 0)
4007+
if (insertingTrailingClosure)
4008+
insertText << " ";
4009+
else if (position != 0)
4010+
insertText << ", ";
4011+
4012+
forFixIt(insertText, argument.second);
4013+
4014+
if (position == 0 && numArgs > 0 &&
4015+
(!firstTrailingClosure || position < *firstTrailingClosure))
40064016
insertText << ", ";
40074017

40084018
SourceLoc insertLoc;
4009-
if (auto *TE = dyn_cast<TupleExpr>(argExpr)) {
4010-
// fn():
4011-
// fn([argMissing])
4019+
4020+
if (position >= numArgs && insertingTrailingClosure) {
4021+
// Add a trailing closure to the end.
4022+
4023+
// fn { closure }:
4024+
// fn {closure} label: [argMissing]
4025+
// fn() { closure }:
4026+
// fn() {closure} label: [argMissing]
4027+
// fn(argX) { closure }:
4028+
// fn(argX) { closure } label: [argMissing]
4029+
insertLoc = Lexer::getLocForEndOfToken(
4030+
ctx.SourceMgr, argExpr->getEndLoc());
4031+
} else if (auto *TE = dyn_cast<TupleExpr>(argExpr)) {
40124032
// fn(argX, argY):
40134033
// fn([argMissing, ]argX, argY)
40144034
// fn(argX[, argMissing], argY)
4015-
// fn(argX, argY[, argMissing])
40164035
// fn(argX) { closure }:
40174036
// fn([argMissing, ]argX) { closure }
40184037
// fn(argX[, argMissing]) { closure }
4019-
// fn(argX[, closureLabel: ]{closure}[, argMissing)] // Not impl.
4020-
if (insertableEndIdx == 0)
4038+
// fn(argX, argY):
4039+
// fn(argX, argY[, argMissing])
4040+
if (numArgs == 0) {
40214041
insertLoc = TE->getRParenLoc();
4022-
else if (position != 0) {
4042+
} else if (position != 0) {
40234043
auto argPos = std::min(TE->getNumElements(), position) - 1;
40244044
insertLoc = Lexer::getLocForEndOfToken(
40254045
ctx.SourceMgr, TE->getElement(argPos)->getEndLoc());
@@ -4031,25 +4051,25 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
40314051
} else {
40324052
auto *PE = cast<ParenExpr>(argExpr);
40334053
if (PE->getRParenLoc().isValid()) {
4054+
// fn():
4055+
// fn([argMissing])
40344056
// fn(argX):
4035-
// fn([argMissing, ]argX)
40364057
// fn(argX[, argMissing])
4058+
// fn([argMissing, ]argX)
40374059
// fn() { closure }:
40384060
// fn([argMissing]) {closure}
4039-
// fn([closureLabel: ]{closure}[, argMissing]) // Not impl.
4040-
if (insertableEndIdx == 0)
4041-
insertLoc = PE->getRParenLoc();
4042-
else if (position == 0)
4043-
insertLoc = PE->getSubExpr()->getStartLoc();
4044-
else
4061+
if (position == 0) {
40454062
insertLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr,
4046-
PE->getSubExpr()->getEndLoc());
4063+
PE->getLParenLoc());
4064+
} else {
4065+
insertLoc = Lexer::getLocForEndOfToken(
4066+
ctx.SourceMgr, PE->getSubExpr()->getEndLoc());
4067+
}
40474068
} else {
40484069
// fn { closure }:
40494070
// fn[(argMissing)] { closure }
4050-
// fn[(closureLabel:] { closure }[, missingArg)] // Not impl.
40514071
assert(!isExpr<SubscriptExpr>(anchor) && "bracket less subscript");
4052-
assert(PE->hasTrailingClosure() &&
4072+
assert(firstTrailingClosure &&
40534073
"paren less ParenExpr without trailing closure");
40544074
insertBuf.insert(insertBuf.begin(), '(');
40554075
insertBuf.insert(insertBuf.end(), ')');
@@ -4061,16 +4081,28 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
40614081
if (insertLoc.isInvalid())
40624082
return false;
40634083

4084+
// If we are trying to insert a trailing closure but the parameter
4085+
// corresponding to the missing argument doesn't support a trailing closure,
4086+
// don't provide a Fix-It.
4087+
// FIXME: It's possible to parenthesize and relabel the argument list to
4088+
// accomodate this, but it's tricky.
4089+
bool shouldEmitFixIt =
4090+
!(insertingTrailingClosure && !paramAcceptsTrailingClosure);
4091+
40644092
if (label.empty()) {
4065-
emitDiagnosticAt(insertLoc, diag::missing_argument_positional, position + 1)
4066-
.fixItInsert(insertLoc, insertText.str());
4093+
auto diag = emitDiagnosticAt(
4094+
insertLoc, diag::missing_argument_positional, position + 1);
4095+
if (shouldEmitFixIt)
4096+
diag.fixItInsert(insertLoc, insertText.str());
40674097
} else if (isPropertyWrapperInitialization()) {
40684098
auto *TE = cast<TypeExpr>(fnExpr);
40694099
emitDiagnosticAt(TE->getLoc(), diag::property_wrapper_missing_arg_init,
40704100
label, resolveType(TE->getInstanceType())->getString());
40714101
} else {
4072-
emitDiagnosticAt(insertLoc, diag::missing_argument_named, label)
4073-
.fixItInsert(insertLoc, insertText.str());
4102+
auto diag = emitDiagnosticAt(
4103+
insertLoc, diag::missing_argument_named, label);
4104+
if (shouldEmitFixIt)
4105+
diag.fixItInsert(insertLoc, insertText.str());
40744106
}
40754107

40764108
if (auto selectedOverload =
@@ -4298,23 +4330,24 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument(
42984330
return TypeChecker::isConvertibleTo(argType, paramType, solution.getDC());
42994331
}
43004332

4301-
std::tuple<Expr *, Expr *, unsigned, bool>
4333+
std::tuple<Expr *, Expr *, unsigned, Optional<unsigned>>
43024334
MissingArgumentsFailure::getCallInfo(ASTNode anchor) const {
43034335
if (auto *call = getAsExpr<CallExpr>(anchor)) {
43044336
return std::make_tuple(call->getFn(), call->getArg(),
4305-
call->getNumArguments(), call->hasTrailingClosure());
4337+
call->getNumArguments(),
4338+
call->getUnlabeledTrailingClosureIndex());
43064339
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
43074340
return std::make_tuple(UME, UME->getArgument(), UME->getNumArguments(),
4308-
UME->hasTrailingClosure());
4341+
UME->getUnlabeledTrailingClosureIndex());
43094342
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
43104343
return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(),
4311-
SE->hasTrailingClosure());
4344+
SE->getUnlabeledTrailingClosureIndex());
43124345
} else if (auto *OLE = getAsExpr<ObjectLiteralExpr>(anchor)) {
43134346
return std::make_tuple(OLE, OLE->getArg(), OLE->getNumArguments(),
4314-
OLE->hasTrailingClosure());
4347+
OLE->getUnlabeledTrailingClosureIndex());
43154348
}
43164349

4317-
return std::make_tuple(nullptr, nullptr, 0, false);
4350+
return std::make_tuple(nullptr, nullptr, 0, None);
43184351
}
43194352

43204353
void MissingArgumentsFailure::forFixIt(
@@ -5509,6 +5542,9 @@ bool ArgumentMismatchFailure::diagnoseAsError() {
55095542
if (diagnosePropertyWrapperMismatch())
55105543
return true;
55115544

5545+
if (diagnoseTrailingClosureMismatch())
5546+
return true;
5547+
55125548
auto argType = getFromType();
55135549
auto paramType = getToType();
55145550

@@ -5743,6 +5779,26 @@ bool ArgumentMismatchFailure::diagnosePropertyWrapperMismatch() const {
57435779
return true;
57445780
}
57455781

5782+
bool ArgumentMismatchFailure::diagnoseTrailingClosureMismatch() const {
5783+
if (!Info.isTrailingClosure())
5784+
return false;
5785+
5786+
auto paramType = getToType();
5787+
if (paramType->lookThroughAllOptionalTypes()->is<AnyFunctionType>())
5788+
return false;
5789+
5790+
emitDiagnostic(diag::trailing_closure_bad_param, paramType)
5791+
.highlight(getSourceRange());
5792+
5793+
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
5794+
if (auto *decl = overload->choice.getDeclOrNull()) {
5795+
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
5796+
}
5797+
}
5798+
5799+
return true;
5800+
}
5801+
57465802
void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt(
57475803
const Expr *anchor) const {
57485804
// If this is an array literal, offer to remove the brackets and pass the

lib/Sema/CSDiagnostics.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,9 +1293,10 @@ class MissingArgumentsFailure final : public FailureDiagnostic {
12931293
bool isPropertyWrapperInitialization() const;
12941294

12951295
/// Gather information associated with expression that represents
1296-
/// a call - function, arguments, # of arguments and whether it has
1297-
/// a trailing closure.
1298-
std::tuple<Expr *, Expr *, unsigned, bool> getCallInfo(ASTNode anchor) const;
1296+
/// a call - function, arguments, # of arguments and the position of
1297+
/// the first trailing closure.
1298+
std::tuple<Expr *, Expr *, unsigned, Optional<unsigned>>
1299+
getCallInfo(ASTNode anchor) const;
12991300

13001301
/// Transform given argument into format suitable for a fix-it
13011302
/// text e.g. `[<label>:]? <#<type#>`
@@ -1815,6 +1816,10 @@ class ArgumentMismatchFailure : public ContextualFailure {
18151816
/// or now deprecated `init(initialValue:)`.
18161817
bool diagnosePropertyWrapperMismatch() const;
18171818

1819+
/// Tailored diagnostics for argument mismatches associated with trailing
1820+
/// closures being passed to non-closure parameters.
1821+
bool diagnoseTrailingClosureMismatch() const;
1822+
18181823
protected:
18191824
/// \returns The position of the argument being diagnosed, starting at 1.
18201825
unsigned getArgPosition() const { return Info.getArgPosition(); }

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,8 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
421421

422422
// If the parameter we are looking at does not support the (unlabeled)
423423
// trailing closure argument, this parameter is unfulfilled.
424-
if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx)) {
424+
if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) &&
425+
!ignoreNameMismatch) {
425426
haveUnfulfilledParams = true;
426427
return;
427428
}
@@ -739,7 +740,10 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
739740
// one argument requires label and another one doesn't, but caller
740741
// doesn't provide either, problem is going to be identified as
741742
// out-of-order argument instead of label mismatch.
742-
const auto expectedLabel = params[paramIdx].getLabel();
743+
const auto expectedLabel =
744+
fromArgIdx == unlabeledTrailingClosureArgIndex
745+
? Identifier()
746+
: params[paramIdx].getLabel();
743747
const auto argumentLabel = args[fromArgIdx].getLabel();
744748

745749
if (argumentLabel != expectedLabel) {

lib/Sema/ConstraintSystem.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,15 @@ class FunctionArgApplyInfo {
595595
return StringRef(scratch.data(), scratch.size());
596596
}
597597

598+
/// Whether the argument is a trailing closure.
599+
bool isTrailingClosure() const {
600+
if (auto trailingClosureArg =
601+
ArgListExpr->getUnlabeledTrailingClosureIndexOfPackedArgument())
602+
return ArgIdx >= *trailingClosureArg;
603+
604+
return false;
605+
}
606+
598607
/// \returns The interface type for the function being applied. Note that this
599608
/// may not a function type, for example it could be a generic parameter.
600609
Type getFnInterfaceType() const { return FnInterfaceType; }

test/Constraints/argument_matching.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ struct RelabelAndTrailingClosure {
14181418
func f2(aa: Int, bb: Int, _ cc: () -> Void = {}) {}
14191419

14201420
func test() {
1421-
f1(aax: 1, bbx: 2) {} // expected-error {{incorrect argument labels in call (have 'aax:bbx:_:', expected 'aa:bb:cc:')}} {{8-11=aa}} {{16-19=bb}} {{none}}
1421+
f1(aax: 1, bbx: 2) {} // expected-error {{incorrect argument labels in call (have 'aax:bbx:_:', expected 'aa:bb:_:')}} {{8-11=aa}} {{16-19=bb}} {{none}}
14221422
f2(aax: 1, bbx: 2) {} // expected-error {{incorrect argument labels in call (have 'aax:bbx:_:', expected 'aa:bb:_:')}} {{8-11=aa}} {{16-19=bb}} {{none}}
14231423

14241424
f1(aax: 1, bbx: 2) // expected-error {{incorrect argument labels in call (have 'aax:bbx:', expected 'aa:bb:')}} {{8-11=aa}} {{16-19=bb}} {{none}}

test/Constraints/closures.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -999,11 +999,13 @@ func rdar52204414() {
999999
// expected-error@-1 {{declared closure result 'Int' is incompatible with contextual type 'Void'}}
10001000
}
10011001

1002-
// SR-12291 - trailing closure is used as an argument to the last (positionally) parameter
1003-
func overloaded_with_default(a: () -> Int, b: Int = 0, c: Int = 0) {}
1004-
func overloaded_with_default(b: Int = 0, c: Int = 0, a: () -> Int) {}
1002+
// SR-12291 - trailing closure is used as an argument to the last (positionally) parameter.
1003+
// Note that this was accepted prior to Swift 5.3. SE-0286 changed the
1004+
// order of argument resolution and made it ambiguous.
1005+
func overloaded_with_default(a: () -> Int, b: Int = 0, c: Int = 0) {} // expected-note{{found this candidate}}
1006+
func overloaded_with_default(b: Int = 0, c: Int = 0, a: () -> Int) {} // expected-note{{found this candidate}}
10051007

1006-
overloaded_with_default { 0 } // Ok (could be ambiguous if trailing was allowed to match `a:` in first overload)
1008+
overloaded_with_default { 0 } // expected-error{{ambiguous use of 'overloaded_with_default'}}
10071009

10081010
func overloaded_with_default_and_autoclosure<T>(_ a: @autoclosure () -> T, b: Int = 0) {}
10091011
func overloaded_with_default_and_autoclosure<T>(b: Int = 0, c: @escaping () -> T?) {}

test/Constraints/diag_missing_arg.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,9 @@ trailingClosureSingle1() { 1 } // expected-error {{missing argument for paramete
3535

3636
func trailingClosureSingle2(x: () -> Int, y: Int) {} // expected-note * {{here}}
3737
trailingClosureSingle2 { 1 }
38-
// expected-error@-1 {{missing argument for parameter 'x' in call}} {{23-23=(x: <#() -> Int#>)}}
39-
// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
38+
// expected-error@-1 {{missing argument for parameter 'y' in call}} {{none}}
4039
trailingClosureSingle2() { 1 }
41-
// expected-error@-1 {{missing argument for parameter 'x' in call}} {{24-24=x: <#() -> Int#>}}
42-
// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
40+
// expected-error@-1 {{missing argument for parameter 'y' in call}} {{none}}
4341

4442
func trailingClosureMulti1(x: Int, y: Int, z: () -> Int) {} // expected-note * {{here}}
4543
trailingClosureMulti1(y: 1) { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=x: <#Int#>, }}
@@ -48,14 +46,17 @@ trailingClosureMulti1(x: 1, y: 1) // expected-error {{missing argument for param
4846

4947
func trailingClosureMulti2(x: Int, y: () -> Int, z: Int) {} // expected-note * {{here}}
5048
trailingClosureMulti2 { 1 }
51-
// expected-error@-1 {{missing arguments for parameters 'x', 'y' in call}}
52-
// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
49+
// expected-error@-1 {{missing arguments for parameters 'x', 'z' in call}}
5350
trailingClosureMulti2() { 1 }
54-
// expected-error@-1 {{missing arguments for parameters 'x', 'y' in call}}
55-
// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
51+
// expected-error@-1 {{missing arguments for parameters 'x', 'z' in call}}
5652
trailingClosureMulti2(x: 1) { 1 }
57-
// expected-error@-1 {{missing argument for parameter 'y' in call}} {{27-27=, y: <#() -> Int#>}}
58-
// expected-error@-2 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
53+
// expected-error@-1 {{missing argument for parameter 'z' in call}} {{none}}
54+
55+
func trailingClosureMulti3(x: (Int) -> Int, y: (Int) -> Int, z: (Int) -> Int) {} // expected-note 2 {{declared here}}
56+
trailingClosureMulti3 { $0 } y: { $0 } // expected-error{{missing argument for parameter 'z' in call}}{{39-39= z: <#(Int) -> Int#>}}
57+
58+
trailingClosureMulti3 { $0 } z: { $0 } // expected-error{{missing argument for parameter 'y' in call}}{{29-29= y: <#(Int) -> Int#>}}
59+
5960

6061
func param2Func(x: Int, y: Int) {} // expected-note * {{here}}
6162
param2Func(x: 1) // expected-error {{missing argument for parameter 'y' in call}} {{16-16=, y: <#Int#>}}

test/Constraints/overload_filtering.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,3 @@ func testUnresolvedMember(i: Int) -> X {
3838
// CHECK-NEXT: introducing single enabled disjunction term {{.*}} bound to decl overload_filtering.(file).X.init(_:_:)
3939
return .init(i, i)
4040
}
41-
42-
func trailing(x: Int = 0, y: () -> Void) { }
43-
func trailing(x: Int = 0, z: Float) { }
44-
45-
func testTrailing() {
46-
// CHECK: disabled disjunction term {{.*}} bound to decl overload_filtering.(file).trailing(x:z:)
47-
trailing() { }
48-
49-
// CHECK: disabled disjunction term {{.*}} bound to decl overload_filtering.(file).trailing(x:z:)
50-
trailing(x: 5) { }
51-
}

0 commit comments

Comments
 (0)