arphaman created this revision. arphaman added reviewers: erik.pilkington, MadCoder. Herald added subscribers: ributzka, jfb, jkorous. arphaman requested review of this revision. Herald added a project: clang.
Categories that add protocol conformances to classes with direct members should prohibit protocol conformances when the methods/properties that the protocol expects are actually declared as 'direct' in the class. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D92602 Files: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaDeclObjC.cpp clang/test/SemaObjC/category-direct-members-protocol-conformance.m
Index: clang/test/SemaObjC/category-direct-members-protocol-conformance.m =================================================================== --- /dev/null +++ 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) <P> // 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) <BaseP> // OK +@end + +@protocol P2 +- (void)indirect; +@end + +@interface I (Cat2) <P2> // OK +- (void)indirect; +@end + +@protocol P3 +- (void)indirect3; +@end + +@interface I (Cat3) <P3> // 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) <P> // 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 <PRecursive1> +@end + +@interface ExpDirect () <PRecursiveTop> // 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 () <PProp> // 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 () <PProp2> // expected-error {{class extension cannot conform to protocol 'PProp2' because of direct members declared in interface 'IProp2'}} +@end Index: clang/lib/Sema/SemaDeclObjC.cpp =================================================================== --- clang/lib/Sema/SemaDeclObjC.cpp +++ 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<ObjCProtocolList::iterator> &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<const Decl *, 4> 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<Decl *> allMethods, ArrayRef<DeclGroupPtrTy> allTUVars) { @@ -4012,6 +4061,8 @@ ObjCInterfaceDecl *CCPrimary = C->getClassInterface(); DiagnoseClassExtensionDupMethods(C, CCPrimary); } + + DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols()); } if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(ClassDecl)) { if (CDecl->getIdentifier()) Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1062,6 +1062,10 @@ InGroup<IgnoredAttributes>; 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 "
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits