Skip to content

Commit a318acb

Browse files
committed
QoI: Improve diagnostics for failed @objc inference.
When we cannot infer an @objc name due to an ambiguity, provide a specific error with Fix-Its for various courses of action (pick a specific name, make it @nonobjc). Also, be sure to suppress redundant diagnostics for Objective-C selector checking when checking protocol conformances: a match-with-renaming will emit the appropriate '@objc' when it's needed, so we don't need a follow-up diagnostic here. Finishes rdar://problem/26518216.
1 parent a42b940 commit a318acb

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2701,6 +2701,13 @@ ERROR(objc_override_property_name_mismatch,none,
27012701
"Objective-C property has a different name from the "
27022702
"property it overrides (%0 vs. %1)", (Identifier, Identifier))
27032703

2704+
ERROR(objc_ambiguous_inference,none,
2705+
"ambiguous inference of Objective-C name for %0 %1 (%2 vs %3)",
2706+
(DescriptiveDeclKind, DeclName, ObjCSelector, ObjCSelector))
2707+
NOTE(objc_ambiguous_inference_candidate,none,
2708+
"%0 (in protocol %1) provides Objective-C name %2",
2709+
(DeclName, DeclName, ObjCSelector))
2710+
27042711
ERROR(nonlocal_bridged_to_objc,none,
27052712
"conformance of %0 to %1 can only be written in module %2",
27062713
(Identifier, Identifier, Identifier))

lib/Sema/TypeCheckDecl.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,18 +2269,42 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
22692269
// When no override determined the Objective-C name, look for
22702270
// requirements for which this declaration is a witness.
22712271
Optional<ObjCSelector> requirementObjCName;
2272+
ValueDecl *firstReq = nullptr;
22722273
for (auto req : tc.findWitnessedObjCRequirements(decl,
22732274
/*onlyFirst=*/false)) {
22742275
// If this is the first requirement, take its name.
22752276
if (!requirementObjCName) {
22762277
requirementObjCName = req->getObjCRuntimeName();
2278+
firstReq = req;
22772279
continue;
22782280
}
22792281

22802282
// If this requirement has a different name from one we've seen,
2281-
// bail out and let protocol-conformance diagnostics handle this.
2282-
if (*requirementObjCName != *req->getObjCRuntimeName())
2283-
return;
2283+
// note the ambiguity.
2284+
if (*requirementObjCName != *req->getObjCRuntimeName()) {
2285+
tc.diagnose(decl, diag::objc_ambiguous_inference,
2286+
decl->getDescriptiveKind(), decl->getFullName(),
2287+
*requirementObjCName, *req->getObjCRuntimeName());
2288+
2289+
// Note the candidates and what Objective-C names they provide.
2290+
auto diagnoseCandidate = [&](ValueDecl *req) {
2291+
auto proto = cast<ProtocolDecl>(req->getDeclContext());
2292+
auto diag = tc.diagnose(decl,
2293+
diag::objc_ambiguous_inference_candidate,
2294+
req->getFullName(),
2295+
proto->getFullName(),
2296+
*req->getObjCRuntimeName());
2297+
fixDeclarationObjCName(diag, decl, req->getObjCRuntimeName());
2298+
};
2299+
diagnoseCandidate(firstReq);
2300+
diagnoseCandidate(req);
2301+
2302+
// Suggest '@nonobjc' to suppress this error, and not try to
2303+
// infer @objc for anything.
2304+
tc.diagnose(decl, diag::optional_req_near_match_nonobjc, true)
2305+
.fixItInsert(decl->getAttributeInsertionLoc(false), "@nonobjc ");
2306+
break;
2307+
}
22842308
}
22852309

22862310
// If we have a name, install it via an @objc attribute.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,6 +3653,7 @@ void ConformanceChecker::checkConformance() {
36533653

36543654
// Objective-C checking for @objc requirements.
36553655
if (requirement->isObjC() &&
3656+
requirement->getFullName() == witness->getFullName() &&
36563657
!requirement->getAttrs().isUnavailable(TC.Context)) {
36573658
// The witness must also be @objc.
36583659
if (!witness->isObjC()) {

test/decl/protocol/objc.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ class C4b : P4 {
130130
// Don't infer when there is an ambiguity.
131131
class C4_5a : P4, P5 {
132132
func method(x: Int, y: Int) { }
133-
// expected-error@-1{{Objective-C method 'methodWithX:y:' provided by method 'method(x:y:)' does not match the requirement's selector ('foo:bar:')}}
134-
// expected-error@-2{{Objective-C method 'methodWithX:y:' provided by method 'method(x:y:)' does not match the requirement's selector ('wibble:wobble:')}}
133+
// expected-error@-1{{ambiguous inference of Objective-C name for instance method 'method(x:y:)' ('foo:bar:' vs 'wibble:wobble:')}}
134+
// expected-note@-2{{'method(x:y:)' (in protocol 'P4') provides Objective-C name 'foo:bar:'}}{{3-3=@objc(foo:bar:) }}
135+
// expected-note@-3{{'method(x:y:)' (in protocol 'P5') provides Objective-C name 'wibble:wobble:'}}{{3-3=@objc(wibble:wobble:) }}
136+
// expected-note@-4{{add '@nonobjc' to silence this error}}{{3-3=@nonobjc }}
137+
// expected-error@-5{{Objective-C method 'foo:bar:' provided by method 'method(x:y:)' does not match the requirement's selector ('wibble:wobble:')}}
138+
}
139+
140+
// Don't complain about a selector mismatch in cases where the
141+
// selector will be inferred.
142+
@objc protocol P6 {
143+
func doSomething(_ sender: AnyObject?) // expected-note{{requirement 'doSomething' declared here}}
144+
}
145+
146+
class C6a : P6 {
147+
func doSomething(sender: AnyObject?) { } // expected-error{{method 'doSomething(sender:)' has different argument names from those required by protocol 'P6' ('doSomething')}}{{20-20=_ }}{{none}}
135148
}

0 commit comments

Comments
 (0)