Skip to content

Commit 2dddfcb

Browse files
authored
Merge pull request #28013 from xedin/trailing-closures-not-so-trailing
[Diagnostics] Port invalid trailing closure use diagnostics
2 parents 7e7e09b + 88c477b commit 2dddfcb

File tree

10 files changed

+162
-41
lines changed

10 files changed

+162
-41
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,32 +2021,6 @@ class ArgumentMatcher : public MatchCallArgumentListener {
20212021
return true;
20222022
}
20232023

2024-
bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override {
2025-
Expr *arg = ArgExpr;
2026-
2027-
auto tuple = dyn_cast<TupleExpr>(ArgExpr);
2028-
if (tuple)
2029-
arg = tuple->getElement(argIdx);
2030-
2031-
if (argIdx >= Parameters.size()) {
2032-
TC.diagnose(arg->getLoc(), diag::extra_trailing_closure_in_call)
2033-
.highlight(arg->getSourceRange());
2034-
} else {
2035-
auto &param = Parameters[paramIdx];
2036-
TC.diagnose(arg->getLoc(), diag::trailing_closure_bad_param,
2037-
param.getPlainType())
2038-
.highlight(arg->getSourceRange());
2039-
2040-
auto candidate = CandidateInfo[0];
2041-
if (candidate.getDecl())
2042-
TC.diagnose(candidate.getDecl(), diag::decl_declared_here,
2043-
candidate.getDecl()->getFullName());
2044-
}
2045-
Diagnosed = true;
2046-
2047-
return true;
2048-
}
2049-
20502024
bool diagnose() {
20512025
// Use matchCallArguments to determine how close the argument list is (in
20522026
// shape) to the specified candidates parameters. This ignores the

lib/Sema/CSDiagnostics.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5491,3 +5491,20 @@ bool ExtraneousCallFailure::diagnoseAsError() {
54915491
removeParensFixIt(diagnostic);
54925492
return true;
54935493
}
5494+
5495+
bool InvalidUseOfTrailingClosure::diagnoseAsError() {
5496+
auto *anchor = getAnchor();
5497+
auto &cs = getConstraintSystem();
5498+
5499+
emitDiagnostic(anchor->getLoc(), diag::trailing_closure_bad_param,
5500+
getToType())
5501+
.highlight(anchor->getSourceRange());
5502+
5503+
if (auto overload = getChoiceFor(cs.getCalleeLocator(getLocator()))) {
5504+
if (auto *decl = overload->choice.getDeclOrNull()) {
5505+
emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName());
5506+
}
5507+
}
5508+
5509+
return true;
5510+
}

lib/Sema/CSDiagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,15 @@ class ExtraneousCallFailure final : public FailureDiagnostic {
19211921
bool diagnoseAsError() override;
19221922
};
19231923

1924+
class InvalidUseOfTrailingClosure final : public ArgumentMismatchFailure {
1925+
public:
1926+
InvalidUseOfTrailingClosure(Expr *root, ConstraintSystem &cs, Type argType,
1927+
Type paramType, ConstraintLocator *locator)
1928+
: ArgumentMismatchFailure(root, cs, argType, paramType, locator) {}
1929+
1930+
bool diagnoseAsError() override;
1931+
};
1932+
19241933
} // end namespace constraints
19251934
} // end namespace swift
19261935

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,3 +994,18 @@ RemoveInvalidCall *RemoveInvalidCall::create(ConstraintSystem &cs,
994994
ConstraintLocator *locator) {
995995
return new (cs.getAllocator()) RemoveInvalidCall(cs, locator);
996996
}
997+
998+
bool AllowInvalidUseOfTrailingClosure::diagnose(Expr *expr, bool asNote) const {
999+
auto &cs = getConstraintSystem();
1000+
InvalidUseOfTrailingClosure failure(expr, cs, getFromType(), getToType(),
1001+
getLocator());
1002+
return failure.diagnose(asNote);
1003+
}
1004+
1005+
AllowInvalidUseOfTrailingClosure *
1006+
AllowInvalidUseOfTrailingClosure::create(ConstraintSystem &cs, Type argType,
1007+
Type paramType,
1008+
ConstraintLocator *locator) {
1009+
return new (cs.getAllocator())
1010+
AllowInvalidUseOfTrailingClosure(cs, argType, paramType, locator);
1011+
}

lib/Sema/CSFix.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ enum class FixKind : uint8_t {
216216
/// Remove extraneous call to something which can't be invoked e.g.
217217
/// a variable, a property etc.
218218
RemoveCall,
219+
220+
AllowInvalidUseOfTrailingClosure,
219221
};
220222

221223
class ConstraintFix {
@@ -1487,6 +1489,24 @@ class RemoveInvalidCall final : public ConstraintFix {
14871489
ConstraintLocator *locator);
14881490
};
14891491

1492+
class AllowInvalidUseOfTrailingClosure final : public AllowArgumentMismatch {
1493+
AllowInvalidUseOfTrailingClosure(ConstraintSystem &cs, Type argType,
1494+
Type paramType, ConstraintLocator *locator)
1495+
: AllowArgumentMismatch(cs, FixKind::AllowInvalidUseOfTrailingClosure,
1496+
argType, paramType, locator) {}
1497+
1498+
public:
1499+
std::string getName() const {
1500+
return "allow invalid use of trailing closure";
1501+
}
1502+
1503+
bool diagnose(Expr *root, bool asNote = false) const;
1504+
1505+
static AllowInvalidUseOfTrailingClosure *create(ConstraintSystem &cs,
1506+
Type argType, Type paramType,
1507+
ConstraintLocator *locator);
1508+
};
1509+
14901510
} // end namespace constraints
14911511
} // end namespace swift
14921512

lib/Sema/CSSimplify.cpp

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,17 +430,56 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
430430

431431
// If we have a trailing closure, it maps to the last parameter.
432432
if (hasTrailingClosure && numParams > 0) {
433+
unsigned lastParamIdx = numParams - 1;
434+
bool lastAcceptsTrailingClosure =
435+
acceptsTrailingClosure(params[lastParamIdx]);
436+
437+
// If the last parameter is defaulted, this might be
438+
// an attempt to use a trailing closure with previous
439+
// parameter that accepts a function type e.g.
440+
//
441+
// func foo(_: () -> Int, _ x: Int = 0) {}
442+
// foo { 42 }
443+
if (!lastAcceptsTrailingClosure && numParams > 1 &&
444+
paramInfo.hasDefaultArgument(lastParamIdx)) {
445+
auto paramType = params[lastParamIdx - 1].getPlainType();
446+
// If the parameter before defaulted last accepts.
447+
if (paramType->is<AnyFunctionType>()) {
448+
lastAcceptsTrailingClosure = true;
449+
lastParamIdx -= 1;
450+
}
451+
}
452+
453+
bool isExtraClosure = false;
433454
// If there is no suitable last parameter to accept the trailing closure,
434455
// notify the listener and bail if we need to.
435-
if (!acceptsTrailingClosure(params[numParams - 1])) {
436-
if (listener.trailingClosureMismatch(numParams - 1, numArgs - 1))
456+
if (!lastAcceptsTrailingClosure) {
457+
if (numArgs > numParams) {
458+
// Argument before the trailing closure.
459+
unsigned prevArg = numArgs - 2;
460+
auto &arg = args[prevArg];
461+
// If the argument before trailing closure matches
462+
// last parameter, this is just a special case of
463+
// an extraneous argument.
464+
const auto param = params[numParams - 1];
465+
if (param.hasLabel() && param.getLabel() == arg.getLabel()) {
466+
isExtraClosure = true;
467+
if (listener.extraArgument(numArgs - 1))
468+
return true;
469+
}
470+
}
471+
472+
if (!isExtraClosure &&
473+
listener.trailingClosureMismatch(lastParamIdx, numArgs - 1))
437474
return true;
438475
}
439476

440477
// Claim the parameter/argument pair.
441478
claimedArgs[numArgs-1] = true;
442479
++numClaimedArgs;
443-
parameterBindings[numParams-1].push_back(numArgs-1);
480+
// Let's claim the trailing closure unless it's an extra argument.
481+
if (!isExtraClosure)
482+
parameterBindings[lastParamIdx].push_back(numArgs - 1);
444483
}
445484

446485
// Mark through the parameters, binding them to their arguments.
@@ -923,6 +962,27 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
923962
return false;
924963
}
925964

965+
bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override {
966+
if (!CS.shouldAttemptFixes())
967+
return true;
968+
969+
auto argType = Arguments[argIdx].getPlainType();
970+
argType.visit([&](Type type) {
971+
if (auto *typeVar = type->getAs<TypeVariableType>())
972+
CS.recordHole(typeVar);
973+
});
974+
975+
const auto &param = Parameters[paramIdx];
976+
977+
auto *argLoc = CS.getConstraintLocator(
978+
Locator.withPathElement(LocatorPathElt::ApplyArgToParam(
979+
argIdx, paramIdx, param.getParameterFlags())));
980+
981+
auto *fix = AllowInvalidUseOfTrailingClosure::create(
982+
CS, argType, param.getPlainType(), argLoc);
983+
return CS.recordFix(fix, /*impact=*/3);
984+
}
985+
926986
ArrayRef<std::pair<unsigned, AnyFunctionType::Param>>
927987
getExtraneousArguments() const {
928988
return ExtraArguments;
@@ -2696,6 +2756,10 @@ bool ConstraintSystem::repairFailures(
26962756

26972757
case ConstraintLocator::ApplyArgToParam: {
26982758
auto loc = getConstraintLocator(locator);
2759+
2760+
if (hasFixFor(loc, FixKind::AllowInvalidUseOfTrailingClosure))
2761+
return true;
2762+
26992763
if (repairByInsertingExplicitCall(lhs, rhs))
27002764
break;
27012765

@@ -8054,6 +8118,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
80548118
case FixKind::GenericArgumentsMismatch:
80558119
case FixKind::AllowMutatingMemberOnRValueBase:
80568120
case FixKind::AllowTupleSplatForSingleParameter:
8121+
case FixKind::AllowInvalidUseOfTrailingClosure:
80578122
llvm_unreachable("handled elsewhere");
80588123
}
80598124

lib/Sema/ConstraintSystem.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,10 +2094,15 @@ class ConstraintSystem {
20942094

20952095
/// Determine whether constraint system already has a fix recorded
20962096
/// for a particular location.
2097-
bool hasFixFor(ConstraintLocator *locator) const {
2098-
return llvm::any_of(Fixes, [&locator](const ConstraintFix *fix) {
2099-
return fix->getLocator() == locator;
2100-
});
2097+
bool hasFixFor(ConstraintLocator *locator,
2098+
Optional<FixKind> expectedKind = None) const {
2099+
return llvm::any_of(
2100+
Fixes, [&locator, &expectedKind](const ConstraintFix *fix) {
2101+
if (fix->getLocator() == locator) {
2102+
return !expectedKind || fix->getKind() == *expectedKind;
2103+
}
2104+
return false;
2105+
});
21012106
}
21022107

21032108
/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the

test/Constraints/closures.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,3 +907,10 @@ do {
907907
// via the 'T -> U => T -> ()' implicit conversion.
908908
let badResult = { (fn: () -> ()) in fn }
909909
// expected-error@-1 {{expression resolves to an unused function}}
910+
911+
// rdar://problem/55102498 - closure's result type can't be inferred if the last parameter has a default value
912+
func test_trailing_closure_with_defaulted_last() {
913+
func foo<T>(fn: () -> T, value: Int = 0) {}
914+
foo { 42 } // Ok
915+
foo(fn: { 42 }) // Ok
916+
}

test/Constraints/diag_missing_arg.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,28 @@ trailingClosureSingle1 { 1 } // expected-error {{missing argument for parameter
3434
trailingClosureSingle1() { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{24-24=x: <#Int#>}}
3535

3636
func trailingClosureSingle2(x: () -> Int, y: Int) {} // expected-note * {{here}}
37-
trailingClosureSingle2 { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
38-
trailingClosureSingle2() { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
37+
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}}
40+
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}}
3943

4044
func trailingClosureMulti1(x: Int, y: Int, z: () -> Int) {} // expected-note * {{here}}
4145
trailingClosureMulti1(y: 1) { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=x: <#Int#>, }}
4246
trailingClosureMulti1(x: 1) { 1 } // expected-error {{missing argument for parameter 'y' in call}} {{27-27=, y: <#Int#>}}
4347
trailingClosureMulti1(x: 1, y: 1) // expected-error {{missing argument for parameter 'z' in call}} {{33-33=, z: <#() -> Int#>}}
4448

4549
func trailingClosureMulti2(x: Int, y: () -> Int, z: Int) {} // expected-note * {{here}}
46-
trailingClosureMulti2 { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
47-
trailingClosureMulti2() { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
48-
trailingClosureMulti2(x: 1) { 1 } // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
50+
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}}
53+
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}}
56+
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}}
4959

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

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -700,11 +700,10 @@ let a = safeAssign // expected-error {{generic parameter 'T' could not be inferr
700700

701701
// <rdar://problem/21692808> QoI: Incorrect 'add ()' fixit with trailing closure
702702
struct Radar21692808<Element> {
703-
init(count: Int, value: Element) {}
703+
init(count: Int, value: Element) {} // expected-note {{'init(count:value:)' declared here}}
704704
}
705705
func radar21692808() -> Radar21692808<Int> {
706-
return Radar21692808<Int>(count: 1) { // expected-error {{cannot invoke initializer for type 'Radar21692808<Int>' with an argument list of type '(count: Int, @escaping () -> Int)'}}
707-
// expected-note @-1 {{expected an argument list of type '(count: Int, value: Element)'}}
706+
return Radar21692808<Int>(count: 1) { // expected-error {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}
708707
return 1
709708
}
710709
}

0 commit comments

Comments
 (0)