Skip to content

Commit df02ddd

Browse files
committed
[Conformance checking] Diagnose the actual problem with near-misses.
Use the diagnostics machinery of the protocol conformance checker to say why each near-miss actually missed, e.g., a type conflict. This gives better information regarding how to fix the actual problem. Yet more QoI for rdar://problem/25159872.
1 parent 4d59d71 commit df02ddd

File tree

3 files changed

+52
-20
lines changed

3 files changed

+52
-20
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ NOTE(ambiguous_witnesses_type,none,
12041204
NOTE(protocol_witness_exact_match,none,
12051205
"candidate exactly matches%0", (StringRef))
12061206
NOTE(protocol_witness_renamed,none,
1207-
"candidate matches (with renaming)%0", (StringRef))
1207+
"rename to %0 to satisfy this requirement%1", (DeclName, StringRef))
12081208
NOTE(protocol_witness_kind_conflict,none,
12091209
"candidate is not %select{an initializer|a function|a variable|"
12101210
"a subscript}0", (RequirementKind))
@@ -1272,9 +1272,6 @@ NOTE(protocol_conformance_implied_here,none,
12721272
WARNING(optional_req_near_match,none,
12731273
"%0 %1 nearly matches optional requirement %2 of protocol %3",
12741274
(DescriptiveDeclKind, DeclName, DeclName, DeclName))
1275-
NOTE(optional_req_near_match_rename,none,
1276-
"rename to %0 to satisfy this requirement",
1277-
(DeclName))
12781275
NOTE(optional_req_nonobjc_near_match_add_objc,none,
12791276
"add '@objc' to provide an Objective-C entrypoint", ())
12801277
NOTE(optional_req_nonobjc_to_objc,none,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,9 +1708,18 @@ diagnoseMatch(TypeChecker &tc, Module *module,
17081708
withAssocTypes);
17091709
break;
17101710

1711-
case MatchKind::RenamedMatch:
1712-
tc.diagnose(match.Witness, diag::protocol_witness_renamed, withAssocTypes);
1711+
case MatchKind::RenamedMatch: {
1712+
auto diag = tc.diagnose(match.Witness, diag::protocol_witness_renamed,
1713+
req->getFullName(), withAssocTypes);
1714+
1715+
// Fix the name.
1716+
fixDeclarationName(diag, match.Witness, req->getFullName());
1717+
1718+
// Also fix the Objective-C name, if needed.
1719+
if (req->isObjC())
1720+
fixDeclarationObjCName(diag, match.Witness, req->getObjCRuntimeName());
17131721
break;
1722+
}
17141723

17151724
case MatchKind::KindConflict:
17161725
tc.diagnose(match.Witness, diag::protocol_witness_kind_conflict,
@@ -4344,7 +4353,9 @@ static bool shouldWarnAboutPotentialWitness(ValueDecl *req,
43444353
}
43454354

43464355
/// Diagnose a potential witness.
4347-
static void diagnosePotentialWitness(TypeChecker &tc, ValueDecl *req,
4356+
static void diagnosePotentialWitness(TypeChecker &tc,
4357+
NormalProtocolConformance *conformance,
4358+
ValueDecl *req,
43484359
ValueDecl *witness,
43494360
Accessibility accessibility) {
43504361
auto proto = cast<ProtocolDecl>(req->getDeclContext());
@@ -4356,20 +4367,18 @@ static void diagnosePotentialWitness(TypeChecker &tc, ValueDecl *req,
43564367
req->getFullName(),
43574368
proto->getFullName());
43584369

4359-
if (req->getFullName() != witness->getFullName()) {
4360-
// Note to fix the names.
4361-
auto diag = tc.diagnose(witness, diag::optional_req_near_match_rename,
4362-
req->getFullName());
4363-
fixDeclarationName(diag, witness, req->getFullName());
4364-
4365-
// Also fix the Objective-C name, if needed.
4366-
if (req->isObjC())
4367-
fixDeclarationObjCName(diag, witness, req->getObjCRuntimeName());
4368-
} else if (req->isObjC() && !witness->isObjC()) {
4369-
// Note to add @objc.
4370+
// Describe why the witness didn't satisfy the requirement.
4371+
auto match = matchWitness(tc, conformance->getProtocol(), conformance,
4372+
conformance->getDeclContext(), req, witness);
4373+
if (match.Kind == MatchKind::ExactMatch &&
4374+
req->isObjC() && !witness->isObjC()) {
4375+
// Special case: note to add @objc.
43704376
auto diag = tc.diagnose(witness,
43714377
diag::optional_req_nonobjc_near_match_add_objc);
4372-
fixDeclarationObjCName(diag, witness, req->getObjCRuntimeName());
4378+
fixDeclarationObjCName(diag, witness, req->getObjCRuntimeName());
4379+
} else {
4380+
diagnoseMatch(tc, conformance->getDeclContext()->getParentModule(),
4381+
conformance, req, match);
43734382
}
43744383

43754384
// If moving the declaration can help, suggest that.
@@ -4559,7 +4568,18 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
45594568
// If we have something to complain about, do so.
45604569
if (!bestOptionalReqs.empty()) {
45614570
auto req = bestOptionalReqs[0];
4562-
diagnosePotentialWitness(*this, req, value, defaultAccessibility);
4571+
bool diagnosed = false;
4572+
for (auto conformance : conformances) {
4573+
if (conformance->getProtocol() == req->getDeclContext()) {
4574+
diagnosePotentialWitness(*this,
4575+
conformance->getRootNormalConformance(),
4576+
req, value, defaultAccessibility);
4577+
diagnosed = true;
4578+
break;
4579+
}
4580+
}
4581+
assert(diagnosed && "Failed to find conformance to diagnose?");
4582+
(void)diagnosed;
45634583

45644584
// Remove this optional requirement from the list. We don't want to
45654585
// complain about it twice.

test/decl/protocol/conforms/near_miss_objc.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,18 @@ class C6a : P6 {
104104
// expected-note@-3{{move 'methodWithInt(_:forSomeClass:dividingDouble:)' to an extension to silence this warning}}
105105
// expected-note@-4{{make 'methodWithInt(_:forSomeClass:dividingDouble:)' private to silence this warning}}{{3-3=private }}
106106
}
107+
108+
// Use the first note to always describe why it didn't match.
109+
@objc protocol P7 {
110+
optional func method(foo: Float)
111+
// expected-note@-1{{requirement 'method(foo:)' declared here}}
112+
}
113+
114+
class C7a : P7 {
115+
@objc func method(foo: Double) { }
116+
// expected-warning@-1{{instance method 'method(foo:)' nearly matches optional requirement 'method(foo:)' of protocol 'P7'}}
117+
// expected-note@-2{{candidate has non-matching type '(foo: Double) -> ()'}}
118+
// expected-note@-3{{move 'method(foo:)' to an extension to silence this warning}}
119+
// expected-note@-4{{make 'method(foo:)' private to silence this warning}}
120+
}
121+

0 commit comments

Comments
 (0)