Skip to content

Commit 880a143

Browse files
authored
Merge pull request #38705 from hamishknight/to-many-avails
2 parents 0244a9b + 84a2c5c commit 880a143

File tree

11 files changed

+183
-57
lines changed

11 files changed

+183
-57
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,8 @@ ERROR(extra_argument_named,none,
12671267
ERROR(extra_argument_positional,none,
12681268
"extra argument in call", ())
12691269
ERROR(extra_arguments_in_call,none,
1270-
"extra arguments at positions %0 in call", (StringRef))
1270+
"extra %select{arguments|trailing closures}0 at positions %1 in call",
1271+
(bool, StringRef))
12711272
ERROR(extra_argument_to_nullary_call,none,
12721273
"argument passed to call that takes no arguments", ())
12731274
ERROR(extra_trailing_closure_in_call,none,

include/swift/Sema/IDETypeChecking.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,32 @@ namespace swift {
243243
SmallVector<SourceLoc, 4> labelLocs;
244244
SourceLoc lParenLoc;
245245
SourceLoc rParenLoc;
246-
bool hasTrailingClosure = false;
246+
Optional<unsigned> unlabeledTrailingClosureIdx;
247+
248+
/// The number of trailing closures in the argument list.
249+
unsigned getNumTrailingClosures() const {
250+
if (!unlabeledTrailingClosureIdx)
251+
return 0;
252+
return args.size() - *unlabeledTrailingClosureIdx;
253+
}
254+
255+
/// Whether any unlabeled or labeled trailing closures are present.
256+
bool hasAnyTrailingClosures() const {
257+
return unlabeledTrailingClosureIdx.hasValue();
258+
}
259+
260+
/// Whether the given index is for an unlabeled trailing closure.
261+
bool isUnlabeledTrailingClosureIdx(unsigned i) const {
262+
return unlabeledTrailingClosureIdx && *unlabeledTrailingClosureIdx == i;
263+
}
264+
265+
/// Whether the given index is for a labeled trailing closure in an
266+
/// argument list with multiple trailing closures.
267+
bool isLabeledTrailingClosureIdx(unsigned i) const {
268+
if (!unlabeledTrailingClosureIdx)
269+
return false;
270+
return i > *unlabeledTrailingClosureIdx && i < args.size();
271+
}
247272
};
248273

249274
/// When applying a solution to a constraint system, the type checker rewrites

lib/Sema/CSApply.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5837,8 +5837,9 @@ Expr *ExprRewriter::coerceCallArguments(
58375837
// Apply labels to arguments.
58385838
AnyFunctionType::relabelParams(args, argLabels);
58395839

5840-
auto unlabeledTrailingClosureIndex =
5840+
auto oldTrailingClosureIndex =
58415841
arg->getUnlabeledTrailingClosureIndexOfPackedArgument();
5842+
Optional<unsigned> newTrailingClosureIndex;
58425843

58435844
// Determine the parameter bindings that were applied.
58445845
auto *locatorPtr = cs.getConstraintLocator(locator);
@@ -5912,6 +5913,12 @@ Expr *ExprRewriter::coerceCallArguments(
59125913
auto arg = getArg(argIdx);
59135914
auto argType = cs.getType(arg);
59145915

5916+
// Update the trailing closure index if needed.
5917+
if (oldTrailingClosureIndex && *oldTrailingClosureIndex == argIdx) {
5918+
assert(!newTrailingClosureIndex);
5919+
newTrailingClosureIndex = newArgs.size();
5920+
}
5921+
59155922
// If the argument type exactly matches, this just works.
59165923
if (argType->isEqual(param.getPlainType())) {
59175924
variadicArgs.push_back(arg);
@@ -5974,6 +5981,12 @@ Expr *ExprRewriter::coerceCallArguments(
59745981
auto arg = getArg(argIdx);
59755982
auto argType = cs.getType(arg);
59765983

5984+
// Update the trailing closure index if needed.
5985+
if (oldTrailingClosureIndex && *oldTrailingClosureIndex == argIdx) {
5986+
assert(!newTrailingClosureIndex);
5987+
newTrailingClosureIndex = newArgs.size();
5988+
}
5989+
59775990
// Save the original label location.
59785991
newLabelLocs.push_back(getLabelLoc(argIdx));
59795992

@@ -6089,14 +6102,17 @@ Expr *ExprRewriter::coerceCallArguments(
60896102
assert(newArgs.size() == newParams.size());
60906103
assert(newArgs.size() == newLabels.size());
60916104
assert(newArgs.size() == newLabelLocs.size());
6105+
assert(oldTrailingClosureIndex.hasValue() ==
6106+
newTrailingClosureIndex.hasValue());
6107+
assert(!newTrailingClosureIndex || *newTrailingClosureIndex < newArgs.size());
60926108

60936109
// This is silly. SILGen gets confused if a 'self' parameter is wrapped
60946110
// in a ParenExpr sometimes.
60956111
if (!argTuple && !argParen &&
60966112
(params[0].getValueOwnership() == ValueOwnership::Default ||
60976113
params[0].getValueOwnership() == ValueOwnership::InOut)) {
60986114
assert(newArgs.size() == 1);
6099-
assert(!unlabeledTrailingClosureIndex);
6115+
assert(!newTrailingClosureIndex);
61006116
return newArgs[0];
61016117
}
61026118

@@ -6111,7 +6127,7 @@ Expr *ExprRewriter::coerceCallArguments(
61116127
bool isImplicit = arg->isImplicit();
61126128
arg = new (ctx) ParenExpr(
61136129
lParenLoc, newArgs[0], rParenLoc,
6114-
static_cast<bool>(unlabeledTrailingClosureIndex));
6130+
static_cast<bool>(newTrailingClosureIndex));
61156131
arg->setImplicit(isImplicit);
61166132
}
61176133
} else {
@@ -6126,7 +6142,7 @@ Expr *ExprRewriter::coerceCallArguments(
61266142
} else {
61276143
// Build a new TupleExpr, re-using source location information.
61286144
arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, newArgs, newLabels,
6129-
newLabelLocs, unlabeledTrailingClosureIndex,
6145+
newLabelLocs, newTrailingClosureIndex,
61306146
/*implicit=*/arg->isImplicit());
61316147
}
61326148
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5197,7 +5197,16 @@ bool ExtraneousArgumentsFailure::diagnoseAsError() {
51975197
},
51985198
[&] { OS << ", "; });
51995199

5200-
emitDiagnostic(diag::extra_arguments_in_call, OS.str());
5200+
bool areTrailingClosures = false;
5201+
if (auto *argExpr = getArgumentListExprFor(getLocator())) {
5202+
if (auto i = argExpr->getUnlabeledTrailingClosureIndexOfPackedArgument()) {
5203+
areTrailingClosures = llvm::all_of(ExtraArgs, [&](auto &pair) {
5204+
return pair.first >= i;
5205+
});
5206+
}
5207+
}
5208+
5209+
emitDiagnostic(diag::extra_arguments_in_call, areTrailingClosures, OS.str());
52015210

52025211
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
52035212
if (auto *decl = overload->choice.getDeclOrNull()) {
@@ -5248,9 +5257,12 @@ bool ExtraneousArgumentsFailure::diagnoseSingleExtraArgument() const {
52485257
auto argExpr = tuple ? tuple->getElement(index)
52495258
: cast<ParenExpr>(arguments)->getSubExpr();
52505259

5260+
auto trailingClosureIdx =
5261+
arguments->getUnlabeledTrailingClosureIndexOfPackedArgument();
5262+
auto isTrailingClosure = trailingClosureIdx && index >= *trailingClosureIdx;
5263+
52515264
auto loc = argExpr->getLoc();
5252-
if (tuple && index == tuple->getNumElements() - 1 &&
5253-
tuple->hasTrailingClosure()) {
5265+
if (isTrailingClosure) {
52545266
emitDiagnosticAt(loc, diag::extra_trailing_closure_in_call)
52555267
.highlight(argExpr->getSourceRange());
52565268
} else if (ContextualType->getNumParams() == 0) {

lib/Sema/CSGen.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4232,9 +4232,17 @@ OriginalArgumentList
42324232
swift::getOriginalArgumentList(Expr *expr) {
42334233
OriginalArgumentList result;
42344234

4235-
auto add = [&](Expr *arg, Identifier label, SourceLoc labelLoc) {
4236-
if (isa<DefaultArgumentExpr>(arg)) {
4235+
auto oldTrailingClosureIdx =
4236+
expr->getUnlabeledTrailingClosureIndexOfPackedArgument();
4237+
Optional<unsigned> newTrailingClosureIdx;
4238+
4239+
auto add = [&](unsigned i, Expr *arg, Identifier label, SourceLoc labelLoc) {
4240+
if (isa<DefaultArgumentExpr>(arg))
42374241
return;
4242+
4243+
if (oldTrailingClosureIdx && *oldTrailingClosureIdx == i) {
4244+
assert(!newTrailingClosureIdx);
4245+
newTrailingClosureIdx = result.args.size();
42384246
}
42394247

42404248
if (auto *varargExpr = dyn_cast<VarargExpansionExpr>(arg)) {
@@ -4260,24 +4268,25 @@ swift::getOriginalArgumentList(Expr *expr) {
42604268
if (auto *parenExpr = dyn_cast<ParenExpr>(expr)) {
42614269
result.lParenLoc = parenExpr->getLParenLoc();
42624270
result.rParenLoc = parenExpr->getRParenLoc();
4263-
result.hasTrailingClosure = parenExpr->hasTrailingClosure();
4264-
add(parenExpr->getSubExpr(), Identifier(), SourceLoc());
4271+
add(0, parenExpr->getSubExpr(), Identifier(), SourceLoc());
42654272
} else if (auto *tupleExpr = dyn_cast<TupleExpr>(expr)) {
42664273
result.lParenLoc = tupleExpr->getLParenLoc();
42674274
result.rParenLoc = tupleExpr->getRParenLoc();
4268-
result.hasTrailingClosure = tupleExpr->hasTrailingClosure();
42694275

42704276
auto args = tupleExpr->getElements();
42714277
auto labels = tupleExpr->getElementNames();
42724278
auto labelLocs = tupleExpr->getElementNameLocs();
42734279
for (unsigned i = 0, e = args.size(); i != e; ++i) {
42744280
// Implicit TupleExprs don't always store label locations.
4275-
add(args[i], labels[i],
4281+
add(i, args[i], labels[i],
42764282
labelLocs.empty() ? SourceLoc() : labelLocs[i]);
42774283
}
42784284
} else {
4279-
add(expr, Identifier(), SourceLoc());
4285+
add(0, expr, Identifier(), SourceLoc());
42804286
}
4287+
assert(oldTrailingClosureIdx.hasValue() == newTrailingClosureIdx.hasValue());
4288+
assert(!newTrailingClosureIdx || *newTrailingClosureIdx < result.args.size());
42814289

4290+
result.unlabeledTrailingClosureIdx = newTrailingClosureIdx;
42824291
return result;
42834292
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,9 +1906,7 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx,
19061906
assert(oldName || newName && "We can't have oldName and newName out of "
19071907
"bounds, otherwise n would be smaller");
19081908

1909-
if (oldName == newName ||
1910-
(argList.hasTrailingClosure && i == argList.args.size() - 1 &&
1911-
(numMissing > 0 || numExtra > 0 || numWrong > 0)))
1909+
if (oldName == newName || argList.isUnlabeledTrailingClosureIdx(i))
19121910
continue;
19131911

19141912
if (!oldName.hasValue() && newName.hasValue()) {
@@ -1988,11 +1986,17 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx,
19881986
if (i < newNames.size())
19891987
newName = newNames[i];
19901988

1991-
if (oldName == newName || (i == n-1 && argList.hasTrailingClosure))
1989+
if (oldName == newName || argList.isUnlabeledTrailingClosureIdx(i))
19921990
continue;
19931991

19941992
if (newName.empty()) {
1995-
// Delete the old name.
1993+
// If this is a labeled trailing closure, we need to replace with '_'.
1994+
if (argList.isLabeledTrailingClosureIdx(i)) {
1995+
diag.fixItReplace(argList.labelLocs[i], "_");
1996+
continue;
1997+
}
1998+
1999+
// Otherwise, delete the old name.
19962000
diag.fixItRemoveChars(argList.labelLocs[i],
19972001
argList.args[i]->getStartLoc());
19982002
continue;
@@ -2006,7 +2010,10 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx,
20062010
if (newNameIsReserved)
20072011
newStr += "`";
20082012

2009-
if (oldName.empty()) {
2013+
// If the argument was previously unlabeled, insert the new label. Note that
2014+
// we don't do this for labeled trailing closures as they write unlabeled
2015+
// args as '_:', and therefore need replacement.
2016+
if (oldName.empty() && !argList.isLabeledTrailingClosureIdx(i)) {
20102017
// Insert the name.
20112018
newStr += ": ";
20122019
diag.fixItInsert(argList.args[i]->getStartLoc(), newStr);

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,7 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
18721872
auto argList = getOriginalArgumentList(argExpr);
18731873

18741874
size_t numElementsWithinParens = argList.args.size();
1875-
numElementsWithinParens -= argList.hasTrailingClosure;
1875+
numElementsWithinParens -= argList.getNumTrailingClosures();
18761876
if (selfIndex >= numElementsWithinParens)
18771877
return;
18781878

@@ -2120,18 +2120,16 @@ static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
21202120
return;
21212121
}
21222122

2123-
auto argumentLabelsToCheck = llvm::makeArrayRef(argumentLabelIDs);
2124-
// The argument label for a trailing closure is ignored.
2125-
if (argList.hasTrailingClosure)
2126-
argumentLabelsToCheck = argumentLabelsToCheck.drop_back();
2127-
2128-
if (std::equal(argumentLabelsToCheck.begin(), argumentLabelsToCheck.end(),
2129-
argList.labels.begin())) {
2130-
// Already matching.
2131-
return;
2123+
// If any of the argument labels are mismatched, perform label correction.
2124+
for (auto i : indices(argList.args)) {
2125+
// The argument label of an unlabeled trailing closure is ignored.
2126+
if (argList.isUnlabeledTrailingClosureIdx(i))
2127+
continue;
2128+
if (argumentLabelIDs[i] != argList.labels[i]) {
2129+
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag);
2130+
return;
2131+
}
21322132
}
2133-
2134-
diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag);
21352133
}
21362134

21372135
// Must be kept in sync with diag::availability_decl_unavailable_rename and

lib/Sema/TypeCheckCodeCompletion.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,18 +288,18 @@ class SanitizeExpr : public ASTWalker {
288288
new (C) ParenExpr(argList.lParenLoc,
289289
argList.args[0],
290290
argList.rParenLoc,
291-
argList.hasTrailingClosure);
291+
argList.hasAnyTrailingClosures());
292292
result->setImplicit();
293293
return result;
294294
}
295295

296296
return TupleExpr::create(C,
297297
argList.lParenLoc,
298+
argList.rParenLoc,
298299
argList.args,
299300
argList.labels,
300301
argList.labelLocs,
301-
argList.rParenLoc,
302-
argList.hasTrailingClosure,
302+
argList.unlabeledTrailingClosureIdx,
303303
/*implicit=*/true);
304304
}
305305

test/Constraints/argument_matching.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,3 +1762,23 @@ func rdar70764991() {
17621762
bar(str, S.foo) // expected-error {{unnamed argument #1 must precede unnamed argument #2}} {{9-12=}} {{14-14=str}}
17631763
}
17641764
}
1765+
1766+
func testExtraTrailingClosure() {
1767+
func foo() {}
1768+
foo() {} // expected-error@:9 {{extra trailing closure passed in call}}
1769+
foo {} // expected-error@:7 {{extra trailing closure passed in call}}
1770+
foo {} x: {} // expected-error@:7 {{argument passed to call that takes no arguments}}
1771+
1772+
func bar(_ x: Int) {} // expected-note 2{{'bar' declared here}}
1773+
bar(5) {} // expected-error@:10 {{extra trailing closure passed in call}}
1774+
bar(0) {} x: {} // expected-error@:6 {{extra trailing closures at positions #2, #3 in call}}
1775+
bar(5, "") {} // expected-error@:6 {{extra arguments at positions #2, #3 in call}}
1776+
1777+
func baz(_ fn: () -> Void) {} // expected-note {{'baz' declared here}}
1778+
baz {} x: {} // expected-error@:13 {{extra trailing closure passed in call}}
1779+
baz({}) {} // expected-error@:11 {{extra trailing closure passed in call}}
1780+
baz({}) {} y: {} // expected-error@:6 {{extra trailing closures at positions #2, #3 in call}}
1781+
1782+
func qux(x: () -> Void, y: () -> Void, z: () -> Void) {} // expected-note {{'qux(x:y:z:)' declared here}}
1783+
qux() {} m: {} y: {} n: {} z: {} o: {} // expected-error@:6 {{extra trailing closures at positions #2, #4, #6 in call}}
1784+
}

test/Parse/trailing_closures.swift

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func test_multiple_trailing_syntax_without_labels() {
8282

8383
fn {} g: {} // Ok
8484

85-
fn {} _: {} // expected-error {{missing argument labels 'f:g:' in call}}
85+
fn {} _: {} // expected-error {{missing argument label 'g:' in call}} {{9-10=g}} {{none}}
8686

8787
fn {} g: <#T##() -> Void#> // expected-error {{editor placeholder in source file}}
8888

@@ -93,28 +93,15 @@ func test_multiple_trailing_syntax_without_labels() {
9393
func mixed_args_1(a: () -> Void, _: () -> Void) {}
9494
func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note {{'mixed_args_2(_:a:_:)' declared here}}
9595

96-
mixed_args_1
97-
{}
98-
_: {}
96+
mixed_args_1 {} _: {}
9997

100-
mixed_args_1
101-
{} // expected-error {{incorrect argument labels in call (have '_:a:', expected 'a:_:')}}
102-
a: {}
98+
mixed_args_1 {} a: {} // expected-error@:16 {{extraneous argument label 'a:' in call}} {{19-20=_}} {{none}}
10399

104-
mixed_args_2
105-
{}
106-
a: {}
107-
_: {}
100+
mixed_args_2 {} a: {} _: {}
108101

109-
mixed_args_2
110-
{} // expected-error {{missing argument for parameter 'a' in call}}
111-
_: {}
102+
mixed_args_2 {} _: {} // expected-error@:18 {{missing argument for parameter 'a' in call}} {{18-18= a: <#() -> Void#>}} {{none}}
112103

113-
// FIXME: not a good diagnostic
114-
mixed_args_2
115-
{} // expected-error {{missing argument label 'a:' in call}}
116-
_: {}
117-
_: {}
104+
mixed_args_2 {} _: {} _: {} // expected-error@:16 {{missing argument label 'a:' in call}} {{19-20=a}} {{none}}
118105
}
119106

120107
func produce(fn: () -> Int?, default d: () -> Int) -> Int { // expected-note {{declared here}}
@@ -129,7 +116,7 @@ func f() -> Int { 42 }
129116
// This should be interpreted as a trailing closure, instead of being
130117
// interpreted as a computed property with undesired initial value.
131118
struct TrickyTest {
132-
var x : Int = f () { // expected-error {{argument passed to call that takes no arguments}}
119+
var x : Int = f () { // expected-error {{extra trailing closure passed in call}}
133120
3
134121
}
135122
}

0 commit comments

Comments
 (0)