Skip to content

Commit 2750e69

Browse files
committed
[CSSimplify] Inject .callAsFunction upon trailing closure mismatch
Detect that extraneous closure belongs to `.init` call on a callable type and let the solver inject `.callAsFunction` before fixing it. This prevents incorrect "extraneous trailing closure" diagnostic when issue is located inside of the closure itself.
1 parent ea4ac2c commit 2750e69

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,8 +1105,10 @@ constraints::getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) {
11051105
class ArgumentFailureTracker : public MatchCallArgumentListener {
11061106
protected:
11071107
ConstraintSystem &CS;
1108+
NullablePtr<ValueDecl> Callee;
11081109
SmallVectorImpl<AnyFunctionType::Param> &Arguments;
11091110
ArrayRef<AnyFunctionType::Param> Parameters;
1111+
Optional<unsigned> UnlabeledTrailingClosureArgIndex;
11101112
ConstraintLocatorBuilder Locator;
11111113

11121114
private:
@@ -1138,11 +1140,14 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
11381140
}
11391141

11401142
public:
1141-
ArgumentFailureTracker(ConstraintSystem &cs,
1143+
ArgumentFailureTracker(ConstraintSystem &cs, ValueDecl *callee,
11421144
SmallVectorImpl<AnyFunctionType::Param> &args,
11431145
ArrayRef<AnyFunctionType::Param> params,
1146+
Optional<unsigned> unlabeledTrailingClosureArgIndex,
11441147
ConstraintLocatorBuilder locator)
1145-
: CS(cs), Arguments(args), Parameters(params), Locator(locator) {}
1148+
: CS(cs), Callee(callee), Arguments(args), Parameters(params),
1149+
UnlabeledTrailingClosureArgIndex(unlabeledTrailingClosureArgIndex),
1150+
Locator(locator) {}
11461151

11471152
~ArgumentFailureTracker() override {
11481153
if (!MissingArguments.empty()) {
@@ -1172,6 +1177,19 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
11721177
if (!CS.shouldAttemptFixes())
11731178
return true;
11741179

1180+
// If this is a trailing closure, let's check if the call is
1181+
// to an init of a callable type. If so, let's not record it
1182+
// as extraneous since it would be matched against implicitly
1183+
// injected `.callAsFunction` call.
1184+
if (UnlabeledTrailingClosureArgIndex &&
1185+
argIdx == *UnlabeledTrailingClosureArgIndex && Callee) {
1186+
if (auto *ctor = dyn_cast<ConstructorDecl>(Callee.get())) {
1187+
auto resultTy = ctor->getResultInterfaceType();
1188+
if (resultTy->isCallableNominalType(CS.DC))
1189+
return true;
1190+
}
1191+
}
1192+
11751193
ExtraArguments.push_back(std::make_pair(argIdx, Arguments[argIdx]));
11761194
return false;
11771195
}
@@ -1280,12 +1298,15 @@ class CompletionArgumentTracker : public ArgumentFailureTracker {
12801298
struct CompletionArgInfo ArgInfo;
12811299

12821300
public:
1283-
CompletionArgumentTracker(ConstraintSystem &cs,
1301+
CompletionArgumentTracker(ConstraintSystem &cs, ValueDecl *callee,
12841302
SmallVectorImpl<AnyFunctionType::Param> &args,
12851303
ArrayRef<AnyFunctionType::Param> params,
1304+
Optional<unsigned> unlabeledTrailingClosureArgIndex,
12861305
ConstraintLocatorBuilder locator,
12871306
struct CompletionArgInfo ArgInfo)
1288-
: ArgumentFailureTracker(cs, args, params, locator), ArgInfo(ArgInfo) {}
1307+
: ArgumentFailureTracker(cs, callee, args, params,
1308+
unlabeledTrailingClosureArgIndex, locator),
1309+
ArgInfo(ArgInfo) {}
12891310

12901311
Optional<unsigned> missingArgument(unsigned paramIdx,
12911312
unsigned argInsertIdx) override {
@@ -1695,14 +1716,16 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
16951716
if (cs.isForCodeCompletion()) {
16961717
if (auto completionInfo = getCompletionArgInfo(locator.getAnchor(), cs)) {
16971718
listener = std::make_unique<CompletionArgumentTracker>(
1698-
cs, argsWithLabels, params, locator, *completionInfo);
1719+
cs, callee, argsWithLabels, params,
1720+
argList->getFirstTrailingClosureIndex(), locator, *completionInfo);
16991721
}
17001722
}
17011723
if (!listener) {
17021724
// We didn't create an argument tracker for code completion. Create a
17031725
// normal one.
1704-
listener = std::make_unique<ArgumentFailureTracker>(cs, argsWithLabels,
1705-
params, locator);
1726+
listener = std::make_unique<ArgumentFailureTracker>(
1727+
cs, callee, argsWithLabels, params,
1728+
argList->getFirstTrailingClosureIndex(), locator);
17061729
}
17071730
auto callArgumentMatch = constraints::matchCallArguments(
17081731
argsWithLabels, params, paramInfo,

test/Constraints/callAsFunction.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ struct MyLayout {
1616
init(alignment: Align? = .center, spacing: Double? = 0.0) {}
1717

1818
func callAsFunction<V: View>(content: () -> V) -> MyLayout { .init() }
19+
// expected-note@-1 {{where 'V' = 'Int'}}
1920
func callAsFunction<V: View>(answer: () -> Int,
2021
content: () -> V) -> MyLayout { .init() }
22+
// expected-note@-2 {{where 'V' = 'Int'}}
2123
}
2224

2325
struct Test {
@@ -61,6 +63,31 @@ struct Test {
6163
EmptyView() // Ok
6264
}
6365
}
66+
67+
var body6: MyLayout {
68+
MyLayout(spacing: 1.0) {
69+
_ = EmptyView()
70+
return 42
71+
} // expected-error {{instance method 'callAsFunction(content:)' requires that 'Int' conform to 'View'}}
72+
}
73+
74+
var body7: MyLayout {
75+
MyLayout(alignment: .center) {
76+
42
77+
} content: {
78+
_ = EmptyView()
79+
return 42
80+
} // expected-error {{instance method 'callAsFunction(answer:content:)' requires that 'Int' conform to 'View'}}
81+
}
82+
83+
var body8: MyLayout {
84+
MyLayout {
85+
let x = ""
86+
return x // expected-error {{cannot convert return expression of type 'String' to return type 'Int'}}
87+
} content: {
88+
EmptyView()
89+
}
90+
}
6491
}
6592

6693
// rdar://92912878 - filtering prevents disambiguation of `.callAsFunction`

0 commit comments

Comments
 (0)