]> granicus.if.org Git - clang/commitdiff
Add back experimental attribute objc_suppress_protocol_methods (slightly renamed).
authorTed Kremenek <kremenek@apple.com>
Sat, 23 Nov 2013 01:01:34 +0000 (01:01 +0000)
committerTed Kremenek <kremenek@apple.com>
Sat, 23 Nov 2013 01:01:34 +0000 (01:01 +0000)
This is still an experimental attribute, but I wanted it in tree
for review.  It may still get yanked.

This attribute can only be applied to a class @interface, not
a class extension or category.  It does not change the type
system rules for Objective-C, but rather the implementation checking
for Objective-C classes that explicitly conform to a protocol.
During protocol conformance checking, clang recursively searches
up the class hierarchy for the set of methods that compose
a protocol.  This attribute will cause the compiler to not consider
the methods contributed by a super class, its categories, and those
from its ancestor classes.  Thus this attribute is used to force
subclasses to redeclare (and hopefully re-implement) methods if
they decide to explicitly conform to a protocol where some of those
methods may be provided by a super class.

This attribute intentionally leaves out properties, which are associated
with state.  This attribute only considers methods (at least right now)
that are non-property accessors.  These represent methods that "do something"
as dictated by the protocol.  This may be further refined, and this
should be considered a WIP until documentation gets written or this
gets removed.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@195533 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclObjC.h
include/clang/Basic/Attr.td
lib/AST/DeclObjC.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaDeclObjC.cpp
test/SemaObjC/protocols-suppress-conformance.m [new file with mode: 0644]

index eebed07aa9ace4e75aa8dc674469a1c2a670cfe2..23778cb368a5c26fe14f75fb86028d96c98e9c1e 100644 (file)
@@ -1143,7 +1143,8 @@ public:
   ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance,
                                bool shallowCategoryLookup = false,
                                bool followSuper = true,
-                               const ObjCCategoryDecl *C = 0) const;
+                               const ObjCCategoryDecl *C = 0,
+                               const ObjCProtocolDecl *P = 0) const;
 
   /// Lookup an instance method for a given selector.
   ObjCMethodDecl *lookupInstanceMethod(Selector Sel) const {
index 49c04500924e43ff5134d1f2777ed235dd80024f..5902932be2de6731a6841e923003594fde3d3ca8 100644 (file)
@@ -642,6 +642,12 @@ def ObjCRootClass : InheritableAttr {
   let Subjects = [ObjCInterface];
 }
 
+def ObjCSuppressProtocol : InheritableAttr {
+  let Spellings = [GNU<"objc_suppress_protocol_methods">];
+  let Subjects = [ObjCInterface];
+  let Args = [IdentifierArgument<"Protocol", 1>];
+}
+
 def Overloadable : Attr {
   let Spellings = [GNU<"overloadable">];
   let Subjects = [Function];
index ca87bb8197f59659766df9bca1ef65f99c1c2474..a4857b607315661ec9119986285066c5db8d258d 100644 (file)
@@ -460,7 +460,8 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel,
                                                 bool isInstance,
                                                 bool shallowCategoryLookup,
                                                 bool followSuper,
-                                                const ObjCCategoryDecl *C) const
+                                                const ObjCCategoryDecl *C,
+                                                const ObjCProtocolDecl *P) const
 {
   // FIXME: Should make sure no callers ever do this.
   if (!hasDefinition())
@@ -473,6 +474,22 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel,
     LoadExternalDefinition();
 
   while (ClassDecl) {
+    // If we are looking for a method that is part of protocol conformance,
+    // check if the superclass has been marked to suppress conformance
+    // of that protocol.
+    if (P && ClassDecl->hasAttrs()) {
+      const AttrVec &V = ClassDecl->getAttrs();
+      const IdentifierInfo *PI = P->getIdentifier();
+      for (AttrVec::const_iterator I = V.begin(), E = V.end(); I != E; ++I) {
+        if (const ObjCSuppressProtocolAttr *A =
+            dyn_cast<ObjCSuppressProtocolAttr>(*I)){
+          if (A->getProtocol() == PI) {
+            return 0;
+          }
+        }
+      }
+    }
+
     if ((MethodDecl = ClassDecl->getMethod(Sel, isInstance)))
       return MethodDecl;
 
index 1dec334bc7e298bb1c7b02bb8919db439219d331..7ae873d7f9ea979b0e6ef904bdb1a99c906c6f1a 100644 (file)
@@ -2134,6 +2134,28 @@ static void handleObjCRootClassAttr(Sema &S, Decl *D,
                                Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleObjCSuppresProtocolAttr(Sema &S, Decl *D,
+                                          const AttributeList &Attr) {
+  if (!isa<ObjCInterfaceDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
+      << Attr.getName() << ExpectedObjectiveCInterface;
+    return;
+  }
+
+  IdentifierLoc *Parm = (Attr.getNumArgs() == 1 && Attr.isArgIdent(0))
+                        ? Attr.getArgAsIdent(0) : 0;
+
+  if (!Parm) {
+    S.Diag(D->getLocStart(), diag::err_objc_attr_not_id) << Attr.getName() << 1;
+    return;
+  }
+
+  D->addAttr(::new (S.Context)
+             ObjCSuppressProtocolAttr(Attr.getRange(), S.Context, Parm->Ident,
+                                      Attr.getAttributeSpellingListIndex()));
+}
+
+
 static void handleObjCRequiresPropertyDefsAttr(Sema &S, Decl *D,
                                                const AttributeList &Attr) {
   if (!isa<ObjCInterfaceDecl>(D)) {
@@ -4713,7 +4735,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_ObjCRootClass:
     handleObjCRootClassAttr(S, D, Attr);
     break;
-  case AttributeList::AT_ObjCRequiresPropertyDefs: 
+  case AttributeList::AT_ObjCSuppressProtocol:
+    handleObjCSuppresProtocolAttr(S, D, Attr);
+    break;
+  case AttributeList::AT_ObjCRequiresPropertyDefs:
     handleObjCRequiresPropertyDefsAttr (S, D, Attr); 
     break;
   case AttributeList::AT_Unused:      handleUnusedAttr      (S, D, Attr); break;
index 95e3b4f4669fc99ca3768910c8283c1d096378d1..6de4e5bb21a32e993c1a6ea5f8232b43b90b22bd 100644 (file)
@@ -1667,7 +1667,9 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
           (!Super || !Super->lookupMethod(method->getSelector(),
                                           true /* instance */,
                                           false /* shallowCategory */,
-                                          true /* followsSuper */))) {
+                                          true /* followsSuper */,
+                                          NULL /* category */,
+                                          PDecl /* protocol */))) {
             // If a method is not implemented in the category implementation but
             // has been declared in its primary class, superclass,
             // or in one of their protocols, no need to issue the warning. 
@@ -1703,7 +1705,9 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
         (!Super || !Super->lookupMethod(method->getSelector(),
                                         false /* class method */,
                                         false /* shallowCategoryLookup */,
-                                        true  /* followSuper */))) {
+                                        true  /* followSuper */,
+                                        NULL /* category */,
+                                        PDecl /* protocol */))) {
       // See above comment for instance method lookups.
       if (C && IDecl->lookupMethod(method->getSelector(),
                                    false /* class */,
diff --git a/test/SemaObjC/protocols-suppress-conformance.m b/test/SemaObjC/protocols-suppress-conformance.m
new file mode 100644 (file)
index 0000000..61c950a
--- /dev/null
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1  -triple x86_64-apple-darwin11 -fsyntax-only -verify %s -Wno-objc-root-class
+
+@protocol Protocol
+- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
+@property (readonly) id theWorstOfTimes; // expected-note {{property declared here}}
+@end
+
+// In this example, the root class provides all the methods for
+// a protocol, and the immediate subclass adopts the attribute.
+//
+// The further subclasses should not have access to the root class's
+// methods for checking protocol conformance.
+//
+// ClassC states protocol conformance, but does not redeclare the method.
+// For this case we get a warning.
+//
+// ClassD states protocol conformance, but does redeclare the method.
+// For this case we do not get a warning.
+//
+
+@interface ClassA <Protocol>
+- (void) theBestOfTimes;
+//@property (readonly) id theWorstOfTimes;
+@end
+
+__attribute__((objc_suppress_protocol_methods(Protocol))) @interface ClassB : ClassA @end
+
+@interface ClassC : ClassB <Protocol> @end // expected-note {{required for direct or indirect protocol 'Protocol'}}
+
+@interface ClassD : ClassB <Protocol>
+- (void) theBestOfTimes;
+@property (readonly) id theWorstOfTimes;
+@end
+
+@implementation ClassA // expected-warning {{auto property synthesis will not synthesize property declared in a protocol}}
+- (void) theBestOfTimes {}
+@end
+
+@implementation ClassC @end // expected-warning {{method 'theBestOfTimes' in protocol not implemented}}
+
+@implementation ClassD // no-warning
+- (void) theBestOfTimes {}
+@end
+
+// In this example, the class both conforms to the protocl and adopts
+// the attribute.  This illustrates that the attribute does not
+// interfere with the protocol conformance checking for the class
+// itself.
+__attribute__((objc_suppress_protocol_methods(Protocol)))
+@interface AdoptsAndConforms <Protocol>
+- (void) theBestOfTimes;
+@property (readonly) id theWorstOfTimes;
+@end
+
+@implementation AdoptsAndConforms // no-warning
+- (void) theBestOfTimes {}
+@end
+
+// This attribute cannot be added to a class extension or category.
+@interface ClassE
+-(void) theBestOfTimes;
+@end
+
+__attribute__((objc_supress_protocol(Protocol)))
+@interface ClassE () @end // expected-error {{attributes may not be specified on a category}}
+
+__attribute__((objc_supress_protocol(Protocol)))
+@interface ClassE (MyCat) @end // expected-error {{attributes may not be specified on a category}}
+
+// The attribute requires one or more identifiers.
+__attribute__((objc_suppress_protocol_methods()))
+@interface ClassF @end // expected-error {{parameter of 'objc_suppress_protocol_methods' attribute must be a single name of an Objective-C protocol}}
+
+// The attribute requires one or more identifiers.
+__attribute__((objc_suppress_protocol_methods(ProtoA, ProtoB))) // expected-error {{use of undeclared identifier 'ProtoB'}}
+@interface ClassG @end
+__attribute__((objc_suppress_protocol_methods(1+2)))
+@interface ClassH @end // expected-error {{parameter of 'objc_suppress_protocol_methods' attribute must be a single name of an Objective-C protocol}}
+  
\ No newline at end of file