diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1062,6 +1062,10 @@ InGroup; def err_objc_direct_dynamic_property : Error< "direct property cannot be @dynamic">; +def err_objc_direct_protocol_conformance : Error< + "%select{category %1|class extension}0 cannot conform to protocol %2 because " + "of direct members declared in interface %3">; +def note_direct_member_here : Note<"direct member declared here">; def warn_conflicting_overriding_ret_types : Warning< "conflicting return type in " diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -3912,6 +3912,55 @@ } } +static void DiagnoseCategoryDirectMembersProtocolConformance( + Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl); + +static void DiagnoseCategoryDirectMembersProtocolConformance( + Sema &S, ObjCCategoryDecl *CDecl, + const llvm::iterator_range &Protocols) { + for (auto *PI : Protocols) + DiagnoseCategoryDirectMembersProtocolConformance(S, PI, CDecl); +} + +static void DiagnoseCategoryDirectMembersProtocolConformance( + Sema &S, ObjCProtocolDecl *PDecl, ObjCCategoryDecl *CDecl) { + if (!PDecl->isThisDeclarationADefinition() && PDecl->getDefinition()) + PDecl = PDecl->getDefinition(); + + llvm::SmallVector DirectMembers; + const auto *IDecl = CDecl->getClassInterface(); + for (auto *MD : PDecl->methods()) { + if (!MD->isPropertyAccessor()) { + if (const auto *CMD = + IDecl->getMethod(MD->getSelector(), MD->isInstanceMethod())) { + if (CMD->isDirectMethod()) + DirectMembers.push_back(CMD); + } + } + } + for (auto *PD : PDecl->properties()) { + if (const auto *CPD = IDecl->FindPropertyVisibleInPrimaryClass( + PD->getIdentifier(), + PD->isClassProperty() + ? ObjCPropertyQueryKind::OBJC_PR_query_class + : ObjCPropertyQueryKind::OBJC_PR_query_instance)) { + if (CPD->isDirectProperty()) + DirectMembers.push_back(CPD); + } + } + if (!DirectMembers.empty()) { + S.Diag(CDecl->getLocation(), diag::err_objc_direct_protocol_conformance) + << CDecl->IsClassExtension() << CDecl << PDecl << IDecl; + for (const auto *MD : DirectMembers) + S.Diag(MD->getLocation(), diag::note_direct_member_here); + return; + } + + // Check on this protocols's referenced protocols, recursively. + DiagnoseCategoryDirectMembersProtocolConformance(S, CDecl, + PDecl->protocols()); +} + // Note: For class/category implementations, allMethods is always null. Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, ArrayRef allTUVars) { @@ -4012,6 +4061,8 @@ ObjCInterfaceDecl *CCPrimary = C->getClassInterface(); DiagnoseClassExtensionDupMethods(C, CCPrimary); } + + DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols()); } if (ObjCContainerDecl *CDecl = dyn_cast(ClassDecl)) { if (CDecl->getIdentifier()) diff --git a/clang/test/SemaObjC/category-direct-members-protocol-conformance.m b/clang/test/SemaObjC/category-direct-members-protocol-conformance.m new file mode 100644 --- /dev/null +++ b/clang/test/SemaObjC/category-direct-members-protocol-conformance.m @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +__attribute__((objc_root_class)) +@interface RootClass + +- (void)baseMethod; + +@end + +__attribute__((objc_direct_members)) +@interface I : RootClass + +- (void)direct; // expected-note {{direct member declared here}} + +@end + +@protocol P +- (void)direct; +@end + +@interface I (Cat1)

// expected-error {{category 'Cat1' cannot conform to protocol 'P' because of direct members declared in interface 'I'}} +@end + +@protocol BaseP +- (void)baseMethod; +@end + +@interface I (CatBase) // OK +@end + +@protocol P2 +- (void)indirect; +@end + +@interface I (Cat2) // OK +- (void)indirect; +@end + +@protocol P3 +- (void)indirect3; +@end + +@interface I (Cat3) // OK +@end + +@interface ExpDirect : RootClass + +- (void)direct __attribute__((objc_direct)); // expected-note {{direct member declared here}} + +- (void)directRecursive __attribute__((objc_direct)); // expected-note {{direct member declared here}} + +@end + +@interface ExpDirect (CatExpDirect)

// expected-error {{category 'CatExpDirect' cannot conform to protocol 'P' because of direct members declared in interface 'ExpDirect'}} +@end + +@protocol PRecursive1 +- (void)directRecursive; +@end + +@protocol PRecursiveTop +@end + +@interface ExpDirect () // expected-error {{class extension cannot conform to protocol 'PRecursive1' because of direct members declared in interface 'ExpDirect'}} +@end + + +@protocol PProp + +@property (nonatomic, readonly) I *name; + +@end + +__attribute__((objc_direct_members)) +@interface IProp1 : RootClass + +@property (nonatomic, readonly) I *name; // expected-note {{direct member declared here}} + +@end + +@interface IProp1 () // expected-error {{class extension cannot conform to protocol 'PProp' because of direct members declared in interface 'IProp1'}} +@end + + +@protocol PProp2 + +@property (nonatomic, readonly, class) I *name; + +@end + +@interface IProp2 : RootClass + +@property (nonatomic, readonly, class, direct) I *name; // expected-note {{direct member declared here}} + +@end + +@interface IProp2 () // expected-error {{class extension cannot conform to protocol 'PProp2' because of direct members declared in interface 'IProp2'}} +@end