Skip to content

Commit 5dded3f

Browse files
committedDec 9, 2015
Fix <rdar://problem/23718816> QoI: "Extra argument" error when accidentally currying a method
It is a somewhat common case where folks are accidentally referring to an instance member with the Type as the base. This forms a curried member, which then produced head scratching error messages downstream. Now that the prep work has gone in, the first part of this is now straight-forward to fix: simply check for this case and diagnose it with a custom error, which makes it more clear what the mistake was. The other half of this problem (tracked by 22108559) affects cases where the method you're calling takes a single argument. This isn't fixed yet, but I'm adding a testcase for it anyway.
1 parent f8a76d1 commit 5dded3f

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed
 

‎include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,10 @@ ERROR(argument_out_of_order,sema_tcc,none,
618618
ERROR(argument_out_of_order_named_unnamed,sema_tcc,none,
619619
"argument %0 must precede unnamed parameter #%1", (Identifier, unsigned))
620620

621+
ERROR(instance_member_use_on_type,sema_tcc,none,
622+
"use of instance member %1 on type %0; "
623+
"did you mean to use a value of type %0 instead?", (Type, Identifier))
624+
621625
ERROR(missing_argument_named,sema_tcc,none,
622626
"missing argument for parameter %0 in call", (Identifier))
623627
ERROR(missing_argument_positional,sema_tcc,none,

‎lib/Sema/CSDiag.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ namespace {
11931193
/// If the candidate set has been narrowed down to a specific structural
11941194
/// problem, e.g. that there are too few parameters specified or that
11951195
/// argument labels don't match up, diagnose that error and return true.
1196-
bool diagnoseAnyStructuralArgumentError(Expr *argExpr);
1196+
bool diagnoseAnyStructuralArgumentError(Expr *fnExpr, Expr *argExpr);
11971197

11981198
void dump() const LLVM_ATTRIBUTE_USED;
11991199

@@ -1694,7 +1694,8 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) {
16941694
/// If the candidate set has been narrowed down to a specific structural
16951695
/// problem, e.g. that there are too few parameters specified or that argument
16961696
/// labels don't match up, diagnose that error and return true.
1697-
bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *argExpr) {
1697+
bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *fnExpr,
1698+
Expr *argExpr) {
16981699
// TODO: We only handle the situation where there is exactly one candidate
16991700
// here.
17001701
if (size() != 1) return false;
@@ -1705,6 +1706,31 @@ bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *argExpr) {
17051706
return false;
17061707

17071708
auto args = decomposeArgParamType(argExpr->getType());
1709+
auto params = decomposeArgParamType(candidates[0].getArgumentType());
1710+
1711+
// It is a somewhat common error to try to access an instance method as a
1712+
// curried member on the type, instead of using an instance, e.g. the user
1713+
// wrote:
1714+
//
1715+
// Foo.doThing(42, b: 19)
1716+
//
1717+
// instead of:
1718+
//
1719+
// myFoo.doThing(42, b: 19)
1720+
//
1721+
// Check for this situation and handle it gracefully.
1722+
if (params.size() == 1 && candidates[0].decl->isInstanceMember() &&
1723+
candidates[0].level == 0) {
1724+
if (auto UDE = dyn_cast<UnresolvedDotExpr>(fnExpr))
1725+
if (isa<TypeExpr>(UDE->getBase())) {
1726+
auto baseType = candidates[0].getArgumentType();
1727+
CS->TC.diagnose(UDE->getLoc(), diag::instance_member_use_on_type,
1728+
baseType, UDE->getName())
1729+
.highlight(UDE->getBase()->getSourceRange());
1730+
return true;
1731+
}
1732+
}
1733+
17081734
SmallVector<Identifier, 4> correctNames;
17091735
unsigned OOOArgIdx = ~0U, OOOPrevArgIdx = ~0U;
17101736
unsigned extraArgIdx = ~0U, missingParamIdx = ~0U;
@@ -1744,7 +1770,6 @@ bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *argExpr) {
17441770
// shape) to the specified candidates parameters. This ignores the
17451771
// concrete types of the arguments, looking only at the argument labels.
17461772
SmallVector<ParamBinding, 4> paramBindings;
1747-
auto params = decomposeArgParamType(candidates[0].getArgumentType());
17481773
if (!matchCallArguments(args, params, hasTrailingClosure,
17491774
/*allowFixes:*/true, listener, paramBindings))
17501775
return false;
@@ -3645,7 +3670,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
36453670
// Filter the candidate list based on the argument we may or may not have.
36463671
calleeInfo.filterContextualMemberList(callExpr->getArg());
36473672

3648-
if (calleeInfo.diagnoseAnyStructuralArgumentError(callExpr->getArg()))
3673+
if (calleeInfo.diagnoseAnyStructuralArgumentError(callExpr->getFn(),
3674+
callExpr->getArg()))
36493675
return true;
36503676

36513677
Type argType; // Type of the argument list, if knowable.
@@ -3670,7 +3696,7 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
36703696

36713697
calleeInfo.filterList(argExpr->getType());
36723698

3673-
if (calleeInfo.diagnoseAnyStructuralArgumentError(argExpr))
3699+
if (calleeInfo.diagnoseAnyStructuralArgumentError(callExpr->getFn(), argExpr))
36743700
return true;
36753701

36763702
// If we have a failure where the candidate set differs on exactly one

‎test/Constraints/diagnostics.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func validateSaveButton(text: String) {
166166
// <rdar://problem/20201968> QoI: poor diagnostic when calling a class method via a metatype
167167
class r20201968C {
168168
func blah() {
169-
r20201968C.blah() // expected-error {{missing argument for parameter #1 in call}}
169+
r20201968C.blah() // expected-error {{use of instance member 'blah' on type 'r20201968C'; did you mean to use a value of type 'r20201968C' instead?}}
170170
}
171171
}
172172

@@ -344,7 +344,7 @@ CurriedClass.method1(2.0)(1) // expected-error {{cannot convert value of t
344344
CurriedClass.method2(c)(32)(b: 1)
345345
_ = CurriedClass.method2(c)
346346
_ = CurriedClass.method2(c)(32)
347-
_ = CurriedClass.method2(1,2) // expected-error {{extra argument in call}}
347+
_ = CurriedClass.method2(1,2) // expected-error {{use of instance member 'method2' on type 'CurriedClass'; did you mean to use a value of type 'CurriedClass' instead?}}
348348
CurriedClass.method2(c)(1.0)(b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
349349
CurriedClass.method2(c)(1)(b: 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
350350
CurriedClass.method2(c)(2)(c: 1.0) // expected-error {{incorrect argument label in call (have 'c:', expected 'b:')}}
@@ -353,7 +353,7 @@ CurriedClass.method3(c)(32, b: 1)
353353
_ = CurriedClass.method3(c)
354354
_ = CurriedClass.method3(c)(1, 2) // expected-error {{missing argument label 'b:' in call}} {{32-32=b: }}
355355
_ = CurriedClass.method3(c)(1, b: 2)(32) // expected-error {{cannot call value of non-function type '()'}}
356-
_ = CurriedClass.method3(1, 2) // expected-error {{extra argument in call}}
356+
_ = CurriedClass.method3(1, 2) // expected-error {{use of instance member 'method3' on type 'CurriedClass'; did you mean to use a value of type 'CurriedClass' instead?}}
357357
CurriedClass.method3(c)(1.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
358358
CurriedClass.method3(c)(1) // expected-error {{missing argument for parameter 'b' in call}}
359359

@@ -369,6 +369,22 @@ extension CurriedClass {
369369
}
370370
}
371371

372+
extension CurriedClass {
373+
func m1(a : Int, b : Int) {}
374+
375+
func m2(a : Int) {}
376+
}
377+
378+
// <rdar://problem/23718816> QoI: "Extra argument" error when accidentally currying a method
379+
CurriedClass.m1(2, b: 42) // expected-error {{use of instance member 'm1' on type 'CurriedClass'; did you mean to use a value of type 'CurriedClass' instead?}}
380+
381+
382+
// <rdar://problem/22108559> QoI: Confusing error message when calling an instance method as a class method
383+
CurriedClass.m2(12) // expected-error {{cannot convert value of type 'Int' to expected argument type 'CurriedClass'}}
384+
385+
386+
387+
372388

373389
// <rdar://problem/19870975> Incorrect diagnostic for failed member lookups within closures passed as arguments ("(_) -> _")
374390
func ident<T>(t: T) -> T {}

0 commit comments

Comments
 (0)
Please sign in to comment.