Skip to content

Commit a9ebaef

Browse files
authored
Merge pull request #18227 from xedin/diagnose-missing-labels-using-fixes
[ConstraintSystem] Use fixes to diagnose missing/extraneous/incorrect argument labels
2 parents 32a93c7 + 84cd99a commit a9ebaef

15 files changed

+140
-38
lines changed

lib/Sema/CSApply.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7907,21 +7907,43 @@ Expr *ConstraintSystem::coerceToRValue(Expr *expr) {
79077907
/// Emit the fixes computed as part of the solution, returning true if we were
79087908
/// able to emit an error message, or false if none of the fixits worked out.
79097909
bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
7910-
bool diagnosed = false;
7911-
for (unsigned i = 0, e = solution.Fixes.size(); i != e; ++i)
7912-
diagnosed |= applySolutionFix(E, solution, i);
7910+
llvm::SmallDenseMap<Expr *,
7911+
SmallVector<std::pair<Fix, ConstraintLocator *>, 4>>
7912+
fixesPerExpr;
7913+
7914+
for (const auto &fix : solution.Fixes)
7915+
fixesPerExpr[fix.second->getAnchor()].push_back(fix);
7916+
7917+
auto diagnoseExprFailures = [&](Expr *expr) -> bool {
7918+
auto fixes = fixesPerExpr.find(expr);
7919+
if (fixes == fixesPerExpr.end())
7920+
return false;
7921+
7922+
bool diagnosed = false;
7923+
for (auto &fix : fixes->second)
7924+
diagnosed |= applySolutionFix(expr, solution, fix);
7925+
return diagnosed;
7926+
};
79137927

7928+
bool diagnosed = false;
7929+
E->forEachChildExpr([&](Expr *subExpr) -> Expr * {
7930+
// Diagnose root expression at the end to
7931+
// preserve ordering.
7932+
if (subExpr != E)
7933+
diagnosed |= diagnoseExprFailures(subExpr);
7934+
return subExpr;
7935+
});
7936+
7937+
diagnosed |= diagnoseExprFailures(E);
79147938
return diagnosed;
79157939
}
79167940

7917-
/// \brief Apply the specified Fix # to this solution, producing a fixit hint
7918-
/// diagnostic for it and returning true. If the fixit hint turned out to be
7941+
/// \brief Apply the specified Fix to this solution, producing a fix-it hint
7942+
/// diagnostic for it and returning true. If the fix-it hint turned out to be
79197943
/// bogus, this returns false and doesn't emit anything.
7920-
bool ConstraintSystem::applySolutionFix(Expr *expr,
7921-
const Solution &solution,
7922-
unsigned fixNo) {
7923-
auto &fix = solution.Fixes[fixNo];
7924-
7944+
bool ConstraintSystem::applySolutionFix(
7945+
Expr *expr, const Solution &solution,
7946+
std::pair<Fix, ConstraintLocator *> &fix) {
79257947
// Some fixes need more information from the locator.
79267948
ConstraintLocator *locator = fix.second;
79277949

@@ -8098,6 +8120,13 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
80988120
getASTContext().TheAnyType);
80998121
return true;
81008122
}
8123+
8124+
case FixKind::RelabelArguments: {
8125+
auto *call = cast<CallExpr>(locator->getAnchor());
8126+
return diagnoseArgumentLabelError(getASTContext(), call->getArg(),
8127+
fix.first.getArgumentLabels(*this),
8128+
isa<SubscriptExpr>(call->getFn()));
8129+
}
81018130
}
81028131

81038132
// FIXME: It would be really nice to emit a follow-up note showing where

lib/Sema/CSSimplify.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,15 @@ matchCallArguments(ArrayRef<AnyFunctionType::Param> args,
499499
auto fromArgIdx = boundArgIdx;
500500
auto toArgIdx = argIdx;
501501

502+
// If there is no re-ordering going on, and index is past
503+
// the number of parameters, it could only mean that this
504+
// is variadic parameter, so let's just move on.
505+
if (fromArgIdx == toArgIdx && toArgIdx >= params.size()) {
506+
assert(args[fromArgIdx].getLabel().empty());
507+
argIdx++;
508+
continue;
509+
}
510+
502511
// First let's double check if out-of-order argument is nothing
503512
// more than a simple label mismatch, because in situation where
504513
// one argument requires label and another one doesn't, but caller
@@ -692,6 +701,40 @@ getCalleeDeclAndArgs(ConstraintSystem &cs,
692701
return std::make_tuple(nullptr, 0, argLabels, hasTrailingClosure);
693702
}
694703

704+
class ArgumentFailureTracker : public MatchCallArgumentListener {
705+
ConstraintSystem &CS;
706+
ConstraintLocatorBuilder Locator;
707+
708+
public:
709+
ArgumentFailureTracker(ConstraintSystem &cs, ConstraintLocatorBuilder locator)
710+
: CS(cs), Locator(locator) {}
711+
712+
bool missingLabel(unsigned paramIndex) override {
713+
return !CS.shouldAttemptFixes();
714+
}
715+
716+
bool extraneousLabel(unsigned paramIndex) override {
717+
return !CS.shouldAttemptFixes();
718+
}
719+
720+
bool incorrectLabel(unsigned paramIndex) override {
721+
return !CS.shouldAttemptFixes();
722+
}
723+
724+
bool relabelArguments(ArrayRef<Identifier> newLabels) override {
725+
if (!CS.shouldAttemptFixes())
726+
return true;
727+
728+
auto *anchor = Locator.getBaseLocator()->getAnchor();
729+
if (!anchor || !isa<CallExpr>(anchor))
730+
return true;
731+
732+
CS.recordFix(Fix::fixArgumentLabels(CS, newLabels),
733+
CS.getConstraintLocator(anchor));
734+
return false;
735+
}
736+
};
737+
695738
// Match the argument of a call to the parameter.
696739
static ConstraintSystem::TypeMatchResult
697740
matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
@@ -747,7 +790,7 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
747790
auto args = decomposeArgType(argType, argLabels);
748791

749792
// Match up the call arguments to the parameters.
750-
MatchCallArgumentListener listener;
793+
ArgumentFailureTracker listener(cs, locator);
751794
SmallVector<ParamBinding, 4> parameterBindings;
752795
if (constraints::matchCallArguments(args, params,
753796
defaultMap,
@@ -4910,6 +4953,7 @@ ConstraintSystem::simplifyFixConstraint(Fix fix, Type type1, Type type2,
49104953
case FixKind::ExplicitlyEscaping:
49114954
case FixKind::ExplicitlyEscapingToAny:
49124955
case FixKind::CoerceToCheckedCast:
4956+
case FixKind::RelabelArguments:
49134957
llvm_unreachable("handled elsewhere");
49144958
}
49154959

lib/Sema/Constraint.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,13 @@ Fix Fix::getUnwrapOptionalBase(ConstraintSystem &cs, DeclName memberName) {
493493
return Fix(FixKind::UnwrapOptionalBase, index);
494494
}
495495

496+
Fix Fix::fixArgumentLabels(ConstraintSystem &cs,
497+
ArrayRef<Identifier> newLabels) {
498+
unsigned index = cs.FixedArgLabels.size();
499+
cs.FixedArgLabels.push_back(newLabels);
500+
return Fix(FixKind::RelabelArguments, index);
501+
}
502+
496503
Type Fix::getTypeArgument(ConstraintSystem &cs) const {
497504
assert(getKind() == FixKind::ForceDowncast);
498505
return cs.FixedTypes[Data];
@@ -504,6 +511,11 @@ DeclName Fix::getDeclNameArgument(ConstraintSystem &cs) const {
504511
return cs.FixedDeclNames[Data];
505512
}
506513

514+
ArrayRef<Identifier> Fix::getArgumentLabels(ConstraintSystem &cs) const {
515+
assert(getKind() == FixKind::RelabelArguments);
516+
return cs.FixedArgLabels[Data];
517+
}
518+
507519
StringRef Fix::getName(FixKind kind) {
508520
switch (kind) {
509521
case FixKind::ForceOptional:
@@ -519,6 +531,8 @@ StringRef Fix::getName(FixKind kind) {
519531
case FixKind::ExplicitlyEscaping:
520532
case FixKind::ExplicitlyEscapingToAny:
521533
return "fix: add @escaping";
534+
case FixKind::RelabelArguments:
535+
return "fix: re-label argument(s)";
522536
}
523537

524538
llvm_unreachable("Unhandled FixKind in switch.");

lib/Sema/Constraint.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ enum class FixKind : uint8_t {
249249
ExplicitlyEscaping,
250250
/// Mark function type as explicitly '@escaping' to be convertable to 'Any'.
251251
ExplicitlyEscapingToAny,
252+
253+
/// Arguments have labeling failures - missing/extraneous or incorrect
254+
/// labels attached to the, fix it by suggesting proper labels.
255+
RelabelArguments,
252256
};
253257

254258
/// Describes a fix that can be applied to a constraint before visiting it.
@@ -276,6 +280,11 @@ class Fix {
276280
/// with the given name.
277281
static Fix getUnwrapOptionalBase(ConstraintSystem &cs, DeclName memberName);
278282

283+
/// Produce a new fix that re-labels existing arguments so they much
284+
/// what parameters expect.
285+
static Fix fixArgumentLabels(ConstraintSystem &cs,
286+
ArrayRef<Identifier> newLabels);
287+
279288
/// Retrieve the kind of fix.
280289
FixKind getKind() const { return Kind; }
281290

@@ -285,6 +294,9 @@ class Fix {
285294
/// If this fix has a name argument, retrieve it.
286295
DeclName getDeclNameArgument(ConstraintSystem &cs) const;
287296

297+
/// If this fix is an argument re-labeling, retrieve new labels.
298+
ArrayRef<Identifier> getArgumentLabels(ConstraintSystem &cs) const;
299+
288300
/// Return a string representation of a fix.
289301
static llvm::StringRef getName(FixKind kind);
290302

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,9 @@ class ConstraintSystem {
10081008
/// Declaration names used in fixes.
10091009
std::vector<DeclName> FixedDeclNames;
10101010

1011+
/// Argument labels fixed by the constraint solver.
1012+
SmallVector<std::vector<Identifier>, 4> FixedArgLabels;
1013+
10111014
/// \brief The set of remembered disjunction choices used to reach
10121015
/// the current constraint system.
10131016
SmallVector<std::pair<ConstraintLocator*, unsigned>, 32>
@@ -1515,10 +1518,11 @@ class ConstraintSystem {
15151518
/// able to emit an error message, or false if none of the fixits worked out.
15161519
bool applySolutionFixes(Expr *E, const Solution &solution);
15171520

1518-
/// \brief Apply the specified Fix # to this solution, producing a fixit hint
1519-
/// diagnostic for it and returning true. If the fixit hint turned out to be
1521+
/// \brief Apply the specified Fix to this solution, producing a fix-it hint
1522+
/// diagnostic for it and returning true. If the fix-it hint turned out to be
15201523
/// bogus, this returns false and doesn't emit anything.
1521-
bool applySolutionFix(Expr *expr, const Solution &solution, unsigned fixNo);
1524+
bool applySolutionFix(Expr *expr, const Solution &solution,
1525+
std::pair<Fix, ConstraintLocator *> &fix);
15221526

15231527
/// \brief If there is more than one viable solution,
15241528
/// attempt to pick the best solution and remove all of the rest.

test/ClangImporter/objc_factory_method.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ func testInstanceTypeFactoryMethodInherited() {
2222
_ = NSObjectFactorySub() // okay, prefers init method
2323
_ = NSObjectFactorySub(integer: 1)
2424
_ = NSObjectFactorySub(double: 314159)
25-
_ = NSObjectFactorySub(float: 314159) // expected-error{{argument labels '(float:)' do not match any available overloads}}
26-
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double)}}
25+
_ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}}
2726
let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument labels '(buildingWidgets:)' do not match any available overloads}}
2827
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double)}}
2928
_ = a

test/ClangImporter/objc_implicit_with.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ func testInstanceTypeFactoryMethodInherited() {
3333
_ = NSObjectFactorySub() // okay, prefers init method
3434
_ = NSObjectFactorySub(integer: 1)
3535
_ = NSObjectFactorySub(double: 314159)
36-
_ = NSObjectFactorySub(float: 314159) // expected-error{{argument labels '(float:)' do not match any available overloads}}
37-
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double)}}
36+
_ = NSObjectFactorySub(float: 314159) // expected-error{{incorrect argument label in call (have 'float:', expected 'integer:')}}
3837
let a = NSObjectFactorySub(buildingWidgets: ()) // expected-error{{argument labels '(buildingWidgets:)' do not match any available overloads}}
3938
// expected-note @-1 {{overloads for 'NSObjectFactorySub' exist with these partially matching parameter lists: (integer: Int), (double: Double)}}
4039
_ = a

test/Constraints/diagnostics_swift4.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ extension C_2505 {
2424
class C2_2505: P_2505 {
2525
}
2626

27-
let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{argument labels '(arg:)' do not match any available overloads}}
28-
// expected-note@-1 {{overloads for 'C_2505' exist with these partially matching parameter lists: (Any), (from: [T])}}
27+
let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{incorrect argument label in call (have 'arg:', expected 'from:')}}
2928

3029
// rdar://problem/31898542 - Swift 4: 'type of expression is ambiguous without more context' errors, without a fixit
3130

test/Constraints/dynamic_lookup.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,5 @@ func testOverloadedWithUnavailable(ao: AnyObject) {
338338

339339
func dynamicInitCrash(ao: AnyObject.Type) {
340340
let sdk = ao.init(blahblah: ())
341-
// expected-error@-1 {{argument labels '(blahblah:)' do not match any available overloads}}
342-
// expected-note@-2 {{overloads for 'AnyObject.Type.init' exist with these partially matching parameter lists}}
341+
// expected-error@-1 {{incorrect argument label in call (have 'blahblah:', expected 'toMemory:')}}
343342
}

test/Constraints/keyword_arguments.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ struct X1 {
88
init(_ a: Int) { }
99
func f1(_ a: Int) {}
1010
}
11-
X1(a: 5).f1(b: 5) // expected-error{{extraneous argument label 'a:' in call}}{{4-7=}}
11+
X1(a: 5).f1(b: 5)
12+
// expected-error@-1 {{extraneous argument label 'a:' in call}} {{4-7=}}
13+
// expected-error@-2 {{extraneous argument label 'b:' in call}} {{13-16=}}
1214

1315
// <rdar://problem/16801056>
1416
enum Policy {
@@ -33,7 +35,9 @@ struct X2 {
3335
init(a: Int) { }
3436
func f2(b: Int) { }
3537
}
36-
X2(5).f2(5) // expected-error{{missing argument label 'a:' in call}}{{4-4=a: }}
38+
X2(5).f2(5)
39+
// expected-error@-1 {{missing argument label 'a:' in call}} {{4-4=a: }}
40+
// expected-error@-2 {{missing argument label 'b:' in call}} {{10-10=b: }}
3741

3842

3943
// -------------------------------------------
@@ -401,3 +405,8 @@ _ = acceptTuple2(tuple1)
401405
_ = acceptTuple2((1, "hello", 3.14159))
402406

403407

408+
func generic_and_missing_label(x: Int) {}
409+
func generic_and_missing_label<T>(x: T) {}
410+
411+
generic_and_missing_label(42)
412+
// expected-error@-1 {{missing argument label 'x:' in call}} {{27-27=x: }}

test/Constraints/optional.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ func sr2752(x: String?, y: String?) {
209209
var sr3248 : ((Int) -> ())!
210210
sr3248?(a: 2) // expected-error {{extraneous argument label 'a:' in call}}
211211
sr3248!(a: 2) // expected-error {{extraneous argument label 'a:' in call}}
212-
sr3248(a: 2) // expected-error {{cannot call value of non-function type '((Int) -> ())?'}}
212+
sr3248(a: 2) // expected-error {{extraneous argument label 'a:' in call}}
213213

214214
struct SR_3248 {
215215
var callback: (([AnyObject]) -> Void)!

test/IDE/complete_unresolved_members.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ var a = {() in
207207
OptionSetTaker5([.#^UNRESOLVED_18^#], .Option4, .South, .West)
208208
}
209209
var Container = OptionTakerContainer1()
210-
Container.OptionSetTaker1(.#^UNRESOLVED_19^#
210+
Container.OptionSetTaker1(.#^UNRESOLVED_19^#)
211211
Container.EnumTaker1(.#^UNRESOLVED_20^#
212212

213213
func parserSync() {}

test/decl/inherit/initializer.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ class NotInherited1 : D {
5858

5959
func testNotInherited1() {
6060
var n1 = NotInherited1(int: 5)
61-
var n2 = NotInherited1(double: 2.71828) // expected-error{{argument labels '(double:)' do not match any available overloads}}
62-
// expected-note @-1 {{overloads for 'NotInherited1' exist with these partially matching parameter lists: (int: Int), (float: Float)}}
61+
var n2 = NotInherited1(double: 2.71828) // expected-error{{incorrect argument label in call (have 'double:', expected 'float:')}}
6362
}
6463

6564
class NotInherited1Sub : NotInherited1 {
@@ -71,8 +70,7 @@ class NotInherited1Sub : NotInherited1 {
7170
func testNotInherited1Sub() {
7271
var n1 = NotInherited1Sub(int: 5)
7372
var n2 = NotInherited1Sub(float: 3.14159)
74-
var n3 = NotInherited1Sub(double: 2.71828) // expected-error{{argument labels '(double:)' do not match any available overloads}}
75-
// expected-note @-1 {{overloads for 'NotInherited1Sub' exist with these partially matching parameter lists: (int: Int), (float: Float)}}
73+
var n3 = NotInherited1Sub(double: 2.71828) // expected-error{{incorrect argument label in call (have 'double:', expected 'float:')}}
7674
}
7775

7876
// Having a stored property without an initial value prevents

test/expr/postfix/dot/init_ref_delegation.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ func foo<T: C>(_ x: T, y: T.Type) where T: P {
294294
var cs2 = T.init(x: 0) // expected-error{{'required' initializer}}
295295
var cs3 = T.init() // expected-error{{'required' initializer}}
296296
var cs4 = T.init(proto: "")
297-
var cs5 = T.init(notfound: "") // expected-error{{argument labels '(notfound:)' do not match any available overloads}}
298-
// expected-note @-1 {{overloads for 'T.Type.init' exist with these partially matching parameter lists: (x: Int), (required: Double), (proto: String)}}
297+
var cs5 = T.init(notfound: "") // expected-error{{incorrect argument label in call (have 'notfound:', expected 'proto:')}}
299298

300299
var csf1: (Double) -> T = T.init
301300
var csf2: (Int) -> T = T.init // expected-error{{'required' initializer}}

validation-test/stdlib/FixedPointDiagnostics.swift.gyb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,19 @@ func test_truncatingBitPatternAPIIsStableAcrossPlatforms() {
3535
_ = UInt8(truncatingBitPattern: UInt(0))
3636
_ = UInt16(truncatingBitPattern: UInt(0))
3737
_ = UInt32(truncatingBitPattern: UInt(0))
38-
UInt64(truncatingBitPattern: UInt(0)) // expected-error {{argument labels '(truncatingBitPattern:)' do not match any available overloads}}
39-
// expected-note @-1 {{overloads for 'UInt64' exist with these partially matching parameter lists}}
38+
UInt64(truncatingBitPattern: UInt(0)) // expected-error {{extraneous argument label 'truncatingBitPattern:' in call}}
4039
UInt(truncatingBitPattern: UInt(0)) // expected-error {{}} expected-note * {{}}
4140

4241
_ = Int8(truncatingBitPattern: UInt(0))
4342
_ = Int16(truncatingBitPattern: UInt(0))
4443
_ = Int32(truncatingBitPattern: UInt(0))
45-
Int64(truncatingBitPattern: UInt(0)) // expected-error {{argument labels '(truncatingBitPattern:)' do not match any available overloads}}
46-
// expected-note @-1 {{overloads for 'Int64' exist with}}
44+
Int64(truncatingBitPattern: UInt(0)) // expected-error {{extraneous argument label 'truncatingBitPattern:' in call}}
4745
Int(truncatingBitPattern: UInt(0)) // expected-error {{}} expected-note * {{}}
4846

4947
_ = UInt8(truncatingBitPattern: Int(0))
5048
_ = UInt16(truncatingBitPattern: Int(0))
5149
_ = UInt32(truncatingBitPattern: Int(0))
52-
UInt64(truncatingBitPattern: Int(0)) // expected-error {{argument labels '(truncatingBitPattern:)' do not match any available overloads}}
53-
// expected-note @-1 {{overloads for 'UInt64' exist with these partially matching parameter lists}}
50+
UInt64(truncatingBitPattern: Int(0)) // expected-error {{extraneous argument label 'truncatingBitPattern:' in call}}
5451
UInt(truncatingBitPattern: Int(0)) // expected-error {{}} expected-note * {{}}
5552

5653
_ = Int8(truncatingBitPattern: Int(0))

0 commit comments

Comments
 (0)