Skip to content

Commit b7d0264

Browse files
authored
Merge pull request #28591 from DougGregor/nsobject-protocol-conformance
2 parents f1c31a7 + 345d1f4 commit b7d0264

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,10 @@ ERROR(type_does_not_conform,none,
17371737
ERROR(cannot_use_nil_with_this_type,none,
17381738
"'nil' cannot be used in context expecting type %0", (Type))
17391739

1740+
ERROR(type_cannot_conform_to_nsobject,none,
1741+
"cannot declare conformance to 'NSObjectProtocol' in Swift; %0 should "
1742+
"inherit 'NSObject' instead", (Type))
1743+
17401744
ERROR(use_of_equal_instead_of_equality,none,
17411745
"use of '=' in a boolean context, did you mean '=='?", ())
17421746

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2578,6 +2578,14 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
25782578
}
25792579
}
25802580

2581+
/// Whether this protocol is the Objective-C "NSObject" protocol.
2582+
static bool isNSObjectProtocol(ProtocolDecl *proto) {
2583+
if (proto->getNameStr() != "NSObjectProtocol")
2584+
return false;
2585+
2586+
return proto->hasClangNode();
2587+
}
2588+
25812589
bool swift::
25822590
printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
25832591
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) {
@@ -2707,6 +2715,7 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
27072715
// If this conformance has nothing to complain, return.
27082716
if (LocalMissing.empty())
27092717
return;
2718+
27102719
SourceLoc ComplainLoc = Loc;
27112720
bool EditorMode = getASTContext().LangOpts.DiagnosticsEditorMode;
27122721
llvm::SetVector<ValueDecl*> MissingWitnesses(GlobalMissingWitnesses.begin(),
@@ -2748,6 +2757,11 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
27482757
auto &SM = DC->getASTContext().SourceMgr;
27492758
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
27502759
for (auto VD : MissingWitnesses) {
2760+
// Don't ever emit a diagnostic for a requirement in the NSObject
2761+
// protocol. They're not implementable.
2762+
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
2763+
continue;
2764+
27512765
// Whether this VD has a stub printed.
27522766
bool AddFixit = !NoStubRequirements.count(VD);
27532767
bool SameFile = VD->getLoc().isValid() ?
@@ -2773,6 +2787,7 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
27732787
}
27742788
continue;
27752789
}
2790+
27762791
// Issue diagnostics for witness values.
27772792
Type RequirementType =
27782793
getRequirementTypeForDisplay(DC->getParentModule(), Conf, VD);
@@ -4052,6 +4067,45 @@ static void diagnoseConformanceFailure(Type T,
40524067
}
40534068
}
40544069

4070+
// One cannot meaningfully declare conformance to the NSObject protocol
4071+
// in Swift. Suggest inheritance from NSObject instead.
4072+
if (isNSObjectProtocol(Proto)) {
4073+
if (T->getClassOrBoundGenericClass()) {
4074+
auto diag =
4075+
diags.diagnose(ComplainLoc, diag::type_cannot_conform_to_nsobject,
4076+
T);
4077+
4078+
// Try to suggest inheriting from NSObject instead.
4079+
auto classDecl = dyn_cast<ClassDecl>(DC);
4080+
if (!classDecl)
4081+
return;
4082+
4083+
auto inheritedClause = classDecl->getInherited();
4084+
for (unsigned i : indices(inheritedClause)) {
4085+
auto &inherited = inheritedClause[i];
4086+
4087+
// Find the inherited type.
4088+
InheritedTypeRequest request{classDecl, i, TypeResolutionStage::Interface};
4089+
Type inheritedTy = evaluateOrDefault(ctx.evaluator, request, Type());
4090+
4091+
// If it's a class, we cannot suggest a different class to inherit
4092+
// from.
4093+
if (inheritedTy->getClassOrBoundGenericClass())
4094+
return;
4095+
4096+
// Is it the NSObject protocol?
4097+
if (auto protoTy = inheritedTy->getAs<ProtocolType>()) {
4098+
if (isNSObjectProtocol(protoTy->getDecl())) {
4099+
diag.fixItReplace(inherited.getSourceRange(), "NSObject");
4100+
return;
4101+
}
4102+
}
4103+
}
4104+
4105+
return;
4106+
}
4107+
}
4108+
40554109
diags.diagnose(ComplainLoc, diag::type_does_not_conform,
40564110
T, Proto->getDeclaredType());
40574111
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -parse-as-library -swift-version 4 %s -verify
2+
3+
// REQUIRES: objc_interop
4+
5+
import Foundation
6+
7+
class A: NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'A' should inherit 'NSObject' instead}}{{10-26=NSObject}}
8+
9+
@objc protocol Other: NSObjectProtocol { }
10+
11+
class B: Other { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'B' should inherit 'NSObject' instead}}
12+
13+
class C { }
14+
15+
class D: C, NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'D' should inherit 'NSObject' instead}}
16+
17+
class E { }
18+
19+
extension E: NSObjectProtocol { } // expected-error{{cannot declare conformance to 'NSObjectProtocol' in Swift; 'E' should inherit 'NSObject' instead}}

0 commit comments

Comments
 (0)