Skip to content

Commit d500f9c

Browse files
authored
Merge pull request #37373 from xedin/rdar-76058892
[CSSimplify] Increase fix impact when passing closure to a non-function type parameter
2 parents 4886bd5 + c346bbd commit d500f9c

File tree

4 files changed

+84
-4
lines changed

4 files changed

+84
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ FIXIT(insert_closure_return_type_placeholder,
279279
"%select{| () }0-> <#Result#> %select{|in }0",
280280
(bool))
281281

282+
NOTE(use_of_anon_closure_param,none,
283+
"anonymous closure parameter %0 is used here", (Identifier))
284+
282285
ERROR(incorrect_explicit_closure_result,none,
283286
"declared closure result %0 is incompatible with contextual type %1",
284287
(Type, Type))

lib/Sema/CSDiagnostics.cpp

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4984,20 +4984,69 @@ bool ExtraneousArgumentsFailure::diagnoseAsError() {
49844984
params->getStartLoc(), diag::closure_argument_list_tuple, fnType,
49854985
fnType->getNumParams(), params->size(), (params->size() == 1));
49864986

4987-
bool onlyAnonymousParams =
4987+
// Unsed parameter is represented by `_` before `in`.
4988+
bool onlyUnusedParams =
49884989
std::all_of(params->begin(), params->end(),
49894990
[](ParamDecl *param) { return !param->hasName(); });
49904991

49914992
// If closure expects no parameters but N was given,
4992-
// and all of them are anonymous let's suggest removing them.
4993-
if (fnType->getNumParams() == 0 && onlyAnonymousParams) {
4993+
// and all of them are unused, let's suggest removing them.
4994+
if (fnType->getNumParams() == 0 && onlyUnusedParams) {
49944995
auto inLoc = closure->getInLoc();
49954996
auto &sourceMgr = getASTContext().SourceMgr;
49964997

4997-
if (inLoc.isValid())
4998+
if (inLoc.isValid()) {
49984999
diag.fixItRemoveChars(params->getStartLoc(),
49995000
Lexer::getLocForEndOfToken(sourceMgr, inLoc));
5001+
return true;
5002+
}
50005003
}
5004+
5005+
diag.flush();
5006+
5007+
// If all of the parameters are anonymous, let's point out references
5008+
// to make it explicit where parameters are used in complex closure body,
5009+
// which helps in situations where braces are missing for potential inner
5010+
// closures e.g.
5011+
//
5012+
// func a(_: () -> Void) {}
5013+
// func b(_: (Int) -> Void) {}
5014+
//
5015+
// a {
5016+
// ...
5017+
// b($0.member)
5018+
// }
5019+
//
5020+
// Here `$0` is associated with `a` since braces around `member` reference
5021+
// are missing.
5022+
if (!closure->hasSingleExpressionBody() &&
5023+
llvm::all_of(params->getArray(),
5024+
[](ParamDecl *P) { return P->isAnonClosureParam(); })) {
5025+
if (auto *body = closure->getBody()) {
5026+
struct ParamRefFinder : public ASTWalker {
5027+
DiagnosticEngine &D;
5028+
ParameterList *Params;
5029+
5030+
ParamRefFinder(DiagnosticEngine &diags, ParameterList *params)
5031+
: D(diags), Params(params) {}
5032+
5033+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
5034+
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
5035+
if (llvm::is_contained(Params->getArray(), DRE->getDecl())) {
5036+
auto *P = cast<ParamDecl>(DRE->getDecl());
5037+
D.diagnose(DRE->getLoc(), diag::use_of_anon_closure_param,
5038+
P->getName());
5039+
}
5040+
}
5041+
return {true, E};
5042+
}
5043+
};
5044+
5045+
ParamRefFinder finder(getASTContext().Diags, params);
5046+
body->walk(finder);
5047+
}
5048+
}
5049+
50015050
return true;
50025051
}
50035052

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11393,6 +11393,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1139311393
}))
1139411394
impact += 3;
1139511395

11396+
// Passing a closure to a parameter that doesn't expect one should
11397+
// be scored lower because there might be an overload that expects
11398+
// a closure but has other issues e.g. wrong number of parameters.
11399+
if (!type2->lookThroughAllOptionalTypes()->is<FunctionType>()) {
11400+
auto argument = simplifyLocatorToAnchor(fix->getLocator());
11401+
if (isExpr<ClosureExpr>(argument)) {
11402+
impact += 2;
11403+
}
11404+
}
11405+
1139611406
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
1139711407
}
1139811408

test/Constraints/closures.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,3 +1114,21 @@ func rdar77022842(argA: Bool? = nil, argB: Bool? = nil) {
11141114
// expected-error@-3 {{expected expression in conditional}}
11151115
} // expected-error {{expected '{' after 'if' condition}}
11161116
}
1117+
1118+
// rdar://76058892 - spurious ambiguity diagnostic
1119+
func rdar76058892() {
1120+
struct S {
1121+
var test: Int = 0
1122+
}
1123+
1124+
func test(_: Int) {}
1125+
func test(_: () -> String) {}
1126+
1127+
func experiment(arr: [S]?) {
1128+
test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}}
1129+
if let arr = arr {
1130+
arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}}
1131+
}
1132+
}
1133+
}
1134+
}

0 commit comments

Comments
 (0)