aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2013-11-23 01:01:34 +0000
committerTed Kremenek <kremenek@apple.com>2013-11-23 01:01:34 +0000
commit28eace65c09a51e24fb5651ee035e2c6c102da69 (patch)
treeab5a24003861c9eae641764eb089cff1647059d6
parent0078150c43f91c82cf43c8e65f6715b984e1a80f (diff)
downloadllvm-28eace65c09a51e24fb5651ee035e2c6c102da69.zip
llvm-28eace65c09a51e24fb5651ee035e2c6c102da69.tar.gz
llvm-28eace65c09a51e24fb5651ee035e2c6c102da69.tar.bz2
Add back experimental attribute objc_suppress_protocol_methods (slightly renamed).
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. llvm-svn: 195533
-rw-r--r--clang/include/clang/AST/DeclObjC.h3
-rw-r--r--clang/include/clang/Basic/Attr.td6
-rw-r--r--clang/lib/AST/DeclObjC.cpp19
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp27
-rw-r--r--clang/lib/Sema/SemaDeclObjC.cpp8
-rw-r--r--clang/test/SemaObjC/protocols-suppress-conformance.m79
6 files changed, 137 insertions, 5 deletions
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index eebed07..23778cb 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -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 {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 49c0450..5902932 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -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];
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index ca87bb8..a4857b6 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -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;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1dec334..7ae873d7 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -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;
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index 95e3b4f..6de4e5bb 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -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/clang/test/SemaObjC/protocols-suppress-conformance.m b/clang/test/SemaObjC/protocols-suppress-conformance.m
new file mode 100644
index 0000000..61c950a
--- /dev/null
+++ b/clang/test/SemaObjC/protocols-suppress-conformance.m
@@ -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