Skip to content

Commit aad3cf9

Browse files
committed
Factor out structural error diagnostics out to a helper method (allowing
the code to be actually readable since it unnests it greatly), and call it both before and after argument type validation. This allows us to capture many more structural errors than before, leading to much better diagnostics in a lot of cases. This also fixes the specific regressions introduced by 96a1e96.
1 parent 1586274 commit aad3cf9

File tree

11 files changed

+169
-139
lines changed

11 files changed

+169
-139
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 144 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,11 @@ namespace {
11901190
/// overloads.
11911191
void suggestPotentialOverloads(SourceLoc loc, bool isResult = false);
11921192

1193+
/// If the candidate set has been narrowed down to a specific structural
1194+
/// problem, e.g. that there are too few parameters specified or that
1195+
/// argument labels don't match up, diagnose that error and return true.
1196+
bool diagnoseAnyStructuralArgumentError(Expr *argExpr);
1197+
11931198
void dump() const LLVM_ATTRIBUTE_USED;
11941199

11951200
private:
@@ -1685,6 +1690,140 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) {
16851690
}
16861691
}
16871692

1693+
1694+
/// If the candidate set has been narrowed down to a specific structural
1695+
/// problem, e.g. that there are too few parameters specified or that argument
1696+
/// labels don't match up, diagnose that error and return true.
1697+
bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *argExpr) {
1698+
// TODO: We only handle the situation where there is exactly one candidate
1699+
// here.
1700+
if (size() != 1) return false;
1701+
1702+
// We only handle structural errors here.
1703+
if (closeness != CC_ArgumentLabelMismatch &&
1704+
closeness != CC_ArgumentCountMismatch)
1705+
return false;
1706+
1707+
auto args = decomposeArgParamType(argExpr->getType());
1708+
SmallVector<Identifier, 4> correctNames;
1709+
unsigned OOOArgIdx = ~0U, OOOPrevArgIdx = ~0U;
1710+
unsigned extraArgIdx = ~0U, missingParamIdx = ~0U;
1711+
1712+
// If we have a single candidate that failed to match the argument list,
1713+
// attempt to use matchCallArguments to diagnose the problem.
1714+
struct OurListener : public MatchCallArgumentListener {
1715+
SmallVectorImpl<Identifier> &correctNames;
1716+
unsigned &OOOArgIdx, &OOOPrevArgIdx;
1717+
unsigned &extraArgIdx, &missingParamIdx;
1718+
1719+
public:
1720+
OurListener(SmallVectorImpl<Identifier> &correctNames,
1721+
unsigned &OOOArgIdx, unsigned &OOOPrevArgIdx,
1722+
unsigned &extraArgIdx, unsigned &missingParamIdx)
1723+
: correctNames(correctNames),
1724+
OOOArgIdx(OOOArgIdx), OOOPrevArgIdx(OOOPrevArgIdx),
1725+
extraArgIdx(extraArgIdx), missingParamIdx(missingParamIdx) {}
1726+
void extraArgument(unsigned argIdx) override {
1727+
extraArgIdx = argIdx;
1728+
}
1729+
void missingArgument(unsigned paramIdx) override {
1730+
missingParamIdx = paramIdx;
1731+
}
1732+
void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override{
1733+
OOOArgIdx = argIdx;
1734+
OOOPrevArgIdx = prevArgIdx;
1735+
}
1736+
bool relabelArguments(ArrayRef<Identifier> newNames) override {
1737+
correctNames.append(newNames.begin(), newNames.end());
1738+
return true;
1739+
}
1740+
} listener(correctNames, OOOArgIdx, OOOPrevArgIdx,
1741+
extraArgIdx, missingParamIdx);
1742+
1743+
// Use matchCallArguments to determine how close the argument list is (in
1744+
// shape) to the specified candidates parameters. This ignores the
1745+
// concrete types of the arguments, looking only at the argument labels.
1746+
SmallVector<ParamBinding, 4> paramBindings;
1747+
auto params = decomposeArgParamType(candidates[0].getArgumentType());
1748+
if (!matchCallArguments(args, params, hasTrailingClosure,
1749+
/*allowFixes:*/true, listener, paramBindings))
1750+
return false;
1751+
1752+
1753+
// If we are missing an parameter, diagnose that.
1754+
if (missingParamIdx != ~0U) {
1755+
Identifier name = params[missingParamIdx].Label;
1756+
auto loc = argExpr->getStartLoc();
1757+
if (name.empty())
1758+
CS->TC.diagnose(loc, diag::missing_argument_positional,
1759+
missingParamIdx+1);
1760+
else
1761+
CS->TC.diagnose(loc, diag::missing_argument_named, name);
1762+
return true;
1763+
}
1764+
1765+
if (extraArgIdx != ~0U) {
1766+
auto name = args[extraArgIdx].Label;
1767+
Expr *arg = argExpr;
1768+
auto tuple = dyn_cast<TupleExpr>(argExpr);
1769+
if (tuple)
1770+
arg = tuple->getElement(extraArgIdx);
1771+
auto loc = arg->getLoc();
1772+
if (tuple && extraArgIdx == tuple->getNumElements()-1 &&
1773+
tuple->hasTrailingClosure())
1774+
CS->TC.diagnose(loc, diag::extra_trailing_closure_in_call)
1775+
.highlight(arg->getSourceRange());
1776+
else if (params.empty())
1777+
CS->TC.diagnose(loc, diag::extra_argument_to_nullary_call)
1778+
.highlight(argExpr->getSourceRange());
1779+
else if (name.empty())
1780+
CS->TC.diagnose(loc, diag::extra_argument_positional)
1781+
.highlight(arg->getSourceRange());
1782+
else
1783+
CS->TC.diagnose(loc, diag::extra_argument_named, name)
1784+
.highlight(arg->getSourceRange());
1785+
return true;
1786+
}
1787+
1788+
// If this is a argument label mismatch, then diagnose that error now.
1789+
if (!correctNames.empty() &&
1790+
CS->diagnoseArgumentLabelError(argExpr, correctNames,
1791+
/*isSubscript=*/false))
1792+
return true;
1793+
1794+
// If we have an out-of-order argument, diagnose it as such.
1795+
if (OOOArgIdx != ~0U && isa<TupleExpr>(argExpr)) {
1796+
auto tuple = cast<TupleExpr>(argExpr);
1797+
Identifier first = tuple->getElementName(OOOArgIdx);
1798+
Identifier second = tuple->getElementName(OOOPrevArgIdx);
1799+
1800+
SourceLoc diagLoc;
1801+
if (!first.empty())
1802+
diagLoc = tuple->getElementNameLoc(OOOArgIdx);
1803+
else
1804+
diagLoc = tuple->getElement(OOOArgIdx)->getStartLoc();
1805+
1806+
if (!second.empty()) {
1807+
CS->TC.diagnose(diagLoc, diag::argument_out_of_order, first, second)
1808+
.highlight(tuple->getElement(OOOArgIdx)->getSourceRange())
1809+
.highlight(SourceRange(tuple->getElementNameLoc(OOOPrevArgIdx),
1810+
tuple->getElement(OOOPrevArgIdx)->getEndLoc()));
1811+
return true;
1812+
}
1813+
1814+
CS->TC.diagnose(diagLoc, diag::argument_out_of_order_named_unnamed, first,
1815+
OOOPrevArgIdx)
1816+
.highlight(tuple->getElement(OOOArgIdx)->getSourceRange())
1817+
.highlight(tuple->getElement(OOOPrevArgIdx)->getSourceRange());
1818+
return true;
1819+
}
1820+
return false;
1821+
}
1822+
1823+
1824+
1825+
1826+
16881827
/// Flags that can be used to control name lookup.
16891828
enum TCCFlags {
16901829
/// Allow the result of the subexpression to be an lvalue. If this is not
@@ -3505,7 +3644,9 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
35053644

35063645
// Filter the candidate list based on the argument we may or may not have.
35073646
calleeInfo.filterContextualMemberList(callExpr->getArg());
3508-
3647+
3648+
if (calleeInfo.diagnoseAnyStructuralArgumentError(callExpr->getArg()))
3649+
return true;
35093650

35103651
Type argType; // Type of the argument list, if knowable.
35113652
if (auto FTy = fnType->getAs<AnyFunctionType>())
@@ -3529,124 +3670,8 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
35293670

35303671
calleeInfo.filterList(argExpr->getType());
35313672

3532-
3533-
// If we filtered this down to exactly one candidate, see if we can produce
3534-
// an extremely specific error about it.
3535-
if (calleeInfo.size() == 1) {
3536-
if (calleeInfo.closeness == CC_ArgumentLabelMismatch ||
3537-
calleeInfo.closeness == CC_ArgumentCountMismatch) {
3538-
auto args = decomposeArgParamType(argExpr->getType());
3539-
SmallVector<Identifier, 4> correctNames;
3540-
unsigned OOOArgIdx = ~0U, OOOPrevArgIdx = ~0U;
3541-
unsigned extraArgIdx = ~0U, missingParamIdx = ~0U;
3542-
3543-
// If we have a single candidate that failed to match the argument list,
3544-
// attempt to use matchCallArguments to diagnose the problem.
3545-
struct OurListener : public MatchCallArgumentListener {
3546-
SmallVectorImpl<Identifier> &correctNames;
3547-
unsigned &OOOArgIdx, &OOOPrevArgIdx;
3548-
unsigned &extraArgIdx, &missingParamIdx;
3549-
3550-
public:
3551-
OurListener(SmallVectorImpl<Identifier> &correctNames,
3552-
unsigned &OOOArgIdx, unsigned &OOOPrevArgIdx,
3553-
unsigned &extraArgIdx, unsigned &missingParamIdx)
3554-
: correctNames(correctNames),
3555-
OOOArgIdx(OOOArgIdx), OOOPrevArgIdx(OOOPrevArgIdx),
3556-
extraArgIdx(extraArgIdx), missingParamIdx(missingParamIdx) {}
3557-
void extraArgument(unsigned argIdx) override {
3558-
extraArgIdx = argIdx;
3559-
}
3560-
void missingArgument(unsigned paramIdx) override {
3561-
missingParamIdx = paramIdx;
3562-
}
3563-
void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override{
3564-
OOOArgIdx = argIdx;
3565-
OOOPrevArgIdx = prevArgIdx;
3566-
}
3567-
bool relabelArguments(ArrayRef<Identifier> newNames) override {
3568-
correctNames.append(newNames.begin(), newNames.end());
3569-
return true;
3570-
}
3571-
} listener(correctNames, OOOArgIdx, OOOPrevArgIdx,
3572-
extraArgIdx, missingParamIdx);
3573-
3574-
// Use matchCallArguments to determine how close the argument list is (in
3575-
// shape) to the specified candidates parameters. This ignores the
3576-
// concrete types of the arguments, looking only at the argument labels.
3577-
SmallVector<ParamBinding, 4> paramBindings;
3578-
auto params = decomposeArgParamType(calleeInfo[0].getArgumentType());
3579-
if (matchCallArguments(args, params, hasTrailingClosure,
3580-
/*allowFixes:*/true, listener, paramBindings)) {
3581-
3582-
// If we are missing an parameter, diagnose that.
3583-
if (missingParamIdx != ~0U) {
3584-
Identifier name = params[missingParamIdx].Label;
3585-
auto loc = callExpr->getArg()->getStartLoc();
3586-
if (name.empty())
3587-
diagnose(loc, diag::missing_argument_positional,
3588-
missingParamIdx+1);
3589-
else
3590-
diagnose(loc, diag::missing_argument_named, name);
3591-
return true;
3592-
}
3593-
3594-
if (extraArgIdx != ~0U) {
3595-
auto name = args[extraArgIdx].Label;
3596-
Expr *arg = argExpr;
3597-
auto tuple = dyn_cast<TupleExpr>(argExpr);
3598-
if (tuple)
3599-
arg = tuple->getElement(extraArgIdx);
3600-
auto loc = arg->getLoc();
3601-
if (tuple && extraArgIdx == tuple->getNumElements()-1 &&
3602-
tuple->hasTrailingClosure())
3603-
diagnose(loc, diag::extra_trailing_closure_in_call)
3604-
.highlight(arg->getSourceRange());
3605-
else if (name.empty())
3606-
diagnose(loc, diag::extra_argument_positional)
3607-
.highlight(arg->getSourceRange());
3608-
else
3609-
diagnose(loc, diag::extra_argument_named, name)
3610-
.highlight(arg->getSourceRange());
3611-
return true;
3612-
}
3613-
3614-
// If this is a argument label mismatch, then diagnose that error now.
3615-
if (!correctNames.empty() &&
3616-
CS->diagnoseArgumentLabelError(argExpr, correctNames,
3617-
/*isSubscript=*/false))
3618-
return true;
3619-
3620-
// If we have an out-of-order argument, diagnose it as such.
3621-
if (OOOArgIdx != ~0U && isa<TupleExpr>(callExpr->getArg())) {
3622-
auto tuple = cast<TupleExpr>(callExpr->getArg());
3623-
Identifier first = tuple->getElementName(OOOArgIdx);
3624-
Identifier second = tuple->getElementName(OOOPrevArgIdx);
3625-
3626-
SourceLoc diagLoc;
3627-
if (!first.empty())
3628-
diagLoc = tuple->getElementNameLoc(OOOArgIdx);
3629-
else
3630-
diagLoc = tuple->getElement(OOOArgIdx)->getStartLoc();
3631-
3632-
if (!second.empty()) {
3633-
diagnose(diagLoc,
3634-
diag::argument_out_of_order, first, second)
3635-
.highlight(tuple->getElement(OOOArgIdx)->getSourceRange())
3636-
.highlight(SourceRange(tuple->getElementNameLoc(OOOPrevArgIdx),
3637-
tuple->getElement(OOOPrevArgIdx)->getEndLoc()));
3638-
return true;
3639-
}
3640-
3641-
diagnose(diagLoc, diag::argument_out_of_order_named_unnamed, first,
3642-
OOOPrevArgIdx)
3643-
.highlight(tuple->getElement(OOOArgIdx)->getSourceRange())
3644-
.highlight(tuple->getElement(OOOPrevArgIdx)->getSourceRange());
3645-
return true;
3646-
}
3647-
}
3648-
}
3649-
}
3673+
if (calleeInfo.diagnoseAnyStructuralArgumentError(argExpr))
3674+
return true;
36503675

36513676
// If we have a failure where the candidate set differs on exactly one
36523677
// argument, and where we have a consistent mismatch across the candidate set

test/ClangModules/cfuncs_parse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ func test_cfunc1(i: Int) {
1212
func test_cfunc2(i: Int) {
1313
let f = cfunc2(i, 17)
1414
_ = f as Float
15-
// FIXME: Should report this error: {{cannot convert the expression's type '$T3' to type 'CLong'}}
16-
cfunc2(b:17, a:i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'Int32'}}
15+
cfunc2(b:17, a:i) // expected-error{{extraneous argument labels 'b:a:' in call}}
16+
cfunc2(17, i) // expected-error{{cannot convert value of type 'Int' to expected argument type 'Int32'}}
1717
}
1818

1919
func test_cfunc3_a() {

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func testMap() {
9898
}
9999

100100
// <rdar://problem/22414757> "UnresolvedDot" "in wrong phase" assertion from verifier
101-
[].reduce { $0 + $1 } // expected-error {{cannot convert value of type '(_, _) -> _' to expected argument type '(_, combine: @noescape (_, _) throws -> _)'}}
101+
[].reduce { $0 + $1 } // expected-error {{missing argument for parameter #1 in call}}
102102

103103

104104

test/Constraints/diagnostics.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,15 @@ func f7(a: Int)(b : Int) -> Int { // expected-warning{{curried function declarat
308308
f7(1)(b: 1)
309309
f7(1.0)(2) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
310310

311-
f7(1)(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
311+
f7(1)(1.0) // expected-error {{missing argument label 'b:' in call}}
312+
f7(1)(b: 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
312313

313314
let f8 = f7(2)
314315
f8(b: 1)
315316
f8(10) // expected-error {{missing argument label 'b:' in call}} {{4-4=b: }}
316-
f8(1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
317+
f8(1.0) // expected-error {{missing argument label 'b:' in call}}
318+
f8(b: 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
319+
317320

318321
class CurriedClass {
319322
func method1() {}
@@ -352,7 +355,7 @@ _ = CurriedClass.method3(c)(1, 2) // expected-error {{missing argument la
352355
_ = CurriedClass.method3(c)(1, b: 2)(32) // expected-error {{cannot call value of non-function type '()'}}
353356
_ = CurriedClass.method3(1, 2) // expected-error {{extra argument in call}}
354357
CurriedClass.method3(c)(1.0, b: 1) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}}
355-
CurriedClass.method3(c)(1) // expected-error {{cannot convert value of type 'Int' to expected argument type '(Int, b: Int)'}}
358+
CurriedClass.method3(c)(1) // expected-error {{missing argument for parameter 'b' in call}}
356359

357360
CurriedClass.method3(c)(c: 1.0) // expected-error {{missing argument for parameter 'b' in call}}
358361

@@ -361,8 +364,8 @@ extension CurriedClass {
361364
func f() {
362365
method3(1, b: 2)
363366
method3() // expected-error {{missing argument for parameter #1 in call}}
364-
method3(42) // expected-error {{cannot convert value of type 'Int' to expected argument type '(Int, b: Int)'}}
365-
method3(self) // expected-error {{cannot convert value of type 'CurriedClass' to expected argument type '(Int, b: Int)'}}
367+
method3(42) // expected-error {{missing argument for parameter 'b' in call}}
368+
method3(self) // expected-error {{missing argument for parameter 'b' in call}}
366369
}
367370
}
368371

@@ -513,7 +516,8 @@ class B {
513516

514517
func test(a : B) {
515518
B.f1(nil) // expected-error {{nil is not compatible with expected argument type 'AOpts'}}
516-
a.function(42, nil) //expected-error {{nil is not compatible with expected argument type 'AOpts'}}
519+
a.function(42, a: nil) //expected-error {{nil is not compatible with expected argument type 'AOpts'}}
520+
a.function(42, nil) //expected-error {{missing argument label 'a:' in call}}
517521
a.f2(nil) // expected-error {{nil is not compatible with expected argument type 'AOpts'}}
518522
}
519523

test/Generics/generic_types.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ func useNested(ii: Int, hni: HasNested<Int>,
195195
typealias HNI = HasNested<Int>
196196
var id = hni.f(1, u: 3.14159)
197197
id = (2, 3.14159)
198-
hni.f(1.5, 3.14159) // expected-error{{cannot convert value of type 'Double' to expected argument type 'Int'}}
198+
hni.f(1.5, 3.14159) // expected-error{{missing argument label 'u:' in call}}
199+
hni.f(1.5, u: 3.14159) // expected-error{{cannot convert value of type 'Double' to expected argument type 'Int'}}
199200

200201
// Generic constructor of a generic struct
201202
HNI(1, 2.71828) // expected-warning{{unused}}

test/TypeCoercion/overload_member.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func test_mixed_overload(a: A, x: X, y: Y) {
7171
x1 = x
7272
var y1 = a.mixed(y: y) // expected-error{{incorrect argument label in call (have 'y:', expected 'x:')}}
7373

74-
A.mixed(x) // expected-error{{cannot convert value of type 'X' to expected argument type 'Y'}}
74+
A.mixed(x) // expected-error{{missing argument label 'y:' in call}}
7575
var x2 = A.mixed(a)(x: x)
7676
x2 = x
7777
var y2 = A.mixed(y: y)
@@ -157,7 +157,7 @@ extension A {
157157
}
158158

159159
class func test_mixed_overload_static(a a: A, x: X, y: Y) {
160-
mixed(x) // expected-error{{cannot convert value of type 'X' to expected argument type 'Y'}}
160+
mixed(x) // expected-error{{missing argument label 'y:' in call}}
161161
var x2 = mixed(a)(x: x)
162162
x2 = x
163163
var y2 = mixed(y: y)

test/decl/func/complete_object_init.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class B2 : A {
9494

9595
func testConstructB2(i: Int) {
9696
var b2a = B2()
97-
var b2b = B2(int: i) // expected-error{{extra argument 'int' in call}}
97+
var b2b = B2(int: i) // expected-error{{argument passed to call that takes no arguments}}
9898

9999
var b2: B2 = b2a
100100
}

0 commit comments

Comments
 (0)