[clang] db226cd - [objc] diagnose protocol conformance in categories with direct members

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 4 15:56:21 PST 2020


Author: Alex Lorenz
Date: 2020-12-04T15:55:34-08:00
New Revision: db226cdf4cf91f350267da1a5b95dda42dd23413

URL: https://github.com/llvm/llvm-project/commit/db226cdf4cf91f350267da1a5b95dda42dd23413
DIFF: https://github.com/llvm/llvm-project/commit/db226cdf4cf91f350267da1a5b95dda42dd23413.diff

LOG: [objc] diagnose protocol conformance in categories with direct members
in their corresponding class interfaces

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.

Differential Revision: https://reviews.llvm.org/D92602

Added: 
    clang/test/SemaObjC/category-direct-members-protocol-conformance.m

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDeclObjC.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 21660965abf7..01a521fc603e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1062,6 +1062,10 @@ def warn_objc_direct_property_ignored : Warning<
   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 "

diff  --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index a48919685ba2..60253a82e93a 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -3912,6 +3912,55 @@ static void DiagnoseVariableSizedIvars(Sema &S, ObjCContainerDecl *OCD) {
   }
 }
 
+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 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
       ObjCInterfaceDecl *CCPrimary = C->getClassInterface();
       DiagnoseClassExtensionDupMethods(C, CCPrimary);
     }
+
+    DiagnoseCategoryDirectMembersProtocolConformance(*this, C, C->protocols());
   }
   if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(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
index 000000000000..dfee42f58869
--- /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))
+ at interface RootClass
+
+- (void)baseMethod;
+
+ at end
+
+__attribute__((objc_direct_members))
+ at interface I : RootClass
+
+- (void)direct; // expected-note {{direct member declared here}}
+
+ at end
+
+ at protocol P
+- (void)direct;
+ at end
+
+ at interface I (Cat1) <P> // expected-error {{category 'Cat1' cannot conform to protocol 'P' because of direct members declared in interface 'I'}}
+ at end
+
+ at protocol BaseP
+- (void)baseMethod;
+ at end
+
+ at interface I (CatBase) <BaseP> // OK
+ at end
+
+ at protocol P2
+- (void)indirect;
+ at end
+
+ at interface I (Cat2) <P2> // OK
+- (void)indirect;
+ at end
+
+ at protocol P3
+- (void)indirect3;
+ at end
+
+ at interface I (Cat3) <P3> // OK
+ at end
+
+ at 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}}
+
+ at end
+
+ at interface ExpDirect (CatExpDirect) <P> // expected-error {{category 'CatExpDirect' cannot conform to protocol 'P' because of direct members declared in interface 'ExpDirect'}}
+ at end
+
+ at protocol PRecursive1
+- (void)directRecursive;
+ at end
+
+ at protocol PRecursiveTop <PRecursive1>
+ at end
+
+ at interface ExpDirect () <PRecursiveTop> // expected-error {{class extension cannot conform to protocol 'PRecursive1' because of direct members declared in interface 'ExpDirect'}}
+ at end
+
+
+ at protocol PProp
+
+ at property (nonatomic, readonly) I *name;
+
+ at end
+
+__attribute__((objc_direct_members))
+ at interface IProp1 : RootClass
+
+ at property (nonatomic, readonly) I *name; // expected-note {{direct member declared here}}
+
+ at end
+
+ at interface IProp1 () <PProp> // expected-error {{class extension cannot conform to protocol 'PProp' because of direct members declared in interface 'IProp1'}}
+ at end
+
+
+ at protocol PProp2
+
+ at property (nonatomic, readonly, class) I *name;
+
+ at end
+
+ at interface IProp2 : RootClass
+
+ at property (nonatomic, readonly, class, direct) I *name; // expected-note {{direct member declared here}}
+
+ at end
+
+ at interface IProp2 () <PProp2> // expected-error {{class extension cannot conform to protocol 'PProp2' because of direct members declared in interface 'IProp2'}}
+ at end


        


More information about the cfe-commits mailing list