summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2013-11-21 07:20:42 +0000
committerTed Kremenek <kremenek@apple.com>2013-11-21 07:20:42 +0000
commita4bd9a0c0ed8287bd03937e9458ef9160aa675a0 (patch)
tree50540e001b0a1982818804844d9d14a143749302
parent2d3379e394ee4a787773c4b310dfd8f5a70e59e6 (diff)
downloadbcm5719-llvm-a4bd9a0c0ed8287bd03937e9458ef9160aa675a0.tar.gz
bcm5719-llvm-a4bd9a0c0ed8287bd03937e9458ef9160aa675a0.zip
Add new attribute 'objc_suppress_protocol' to suppress protocol conformance for a class.
The idea is to allow a class to stipulate that its methods (and those of its parents) cannot be used for protocol conformance in a subclass. A subclass is then explicitly required to re-implement those methods of they are present in the class marked with this attribute. Currently the attribute can only be applied to an @interface, and not a category or class extension. This is by design. Unlike protocol conformance, where a category can add explicit conformance of a protocol to class, this anti-conformance really needs to be observed uniformly by all clients of the class. That's because the absence of the attribute implies more permissive checking of protocol conformance. This unfortunately required changing method lookup in ObjCInterfaceDecl to take an optional protocol parameter. This should not slow down method lookup in most cases, and is just used for protocol conformance. llvm-svn: 195323
-rw-r--r--clang/include/clang/AST/DeclObjC.h13
-rw-r--r--clang/include/clang/Basic/Attr.td6
-rw-r--r--clang/lib/AST/DeclObjC.cpp26
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp29
-rw-r--r--clang/lib/Sema/SemaDeclObjC.cpp13
-rw-r--r--clang/test/SemaObjC/protocols-suppress-conformance.m27
6 files changed, 100 insertions, 14 deletions
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index 2e760d658e4..10e5dc11ee6 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -1142,14 +1142,17 @@ public:
// found, we search referenced protocols and class categories.
ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance,
bool shallowCategoryLookup= false,
- const ObjCCategoryDecl *C= 0) const;
+ const ObjCCategoryDecl *C = 0,
+ const ObjCProtocolDecl *P = 0) const;
ObjCMethodDecl *lookupInstanceMethod(Selector Sel,
- bool shallowCategoryLookup = false) const {
- return lookupMethod(Sel, true/*isInstance*/, shallowCategoryLookup);
+ bool shallowCategoryLookup = false,
+ ObjCProtocolDecl *P = 0) const {
+ return lookupMethod(Sel, true/*isInstance*/, shallowCategoryLookup, 0, P);
}
ObjCMethodDecl *lookupClassMethod(Selector Sel,
- bool shallowCategoryLookup = false) const {
- return lookupMethod(Sel, false/*isInstance*/, shallowCategoryLookup);
+ bool shallowCategoryLookup = false,
+ ObjCProtocolDecl *P = 0) const {
+ return lookupMethod(Sel, false/*isInstance*/, shallowCategoryLookup, 0, P);
}
ObjCInterfaceDecl *lookupInheritedClass(const IdentifierInfo *ICName);
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index bcca1d25ef7..a6be491c5a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -613,6 +613,12 @@ def ObjCRootClass : InheritableAttr {
let Subjects = [ObjCInterface];
}
+def ObjCSuppressProtocol : InheritableAttr {
+ let Spellings = [GNU<"objc_suppress_protocol">];
+ let Subjects = [ObjCInterface];
+ let Args = [IdentifierArgument<"Protocol", 1>];
+}
+
def Overloadable : Attr {
let Spellings = [GNU<"overloadable">];
}
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index b2b5b70197b..d759c0795b8 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -457,9 +457,11 @@ ObjCInterfaceDecl::lookupNestedProtocol(IdentifierInfo *Name) {
/// When argument category "C" is specified, any implicit method found
/// in this category is ignored.
ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel,
- bool isInstance,
- bool shallowCategoryLookup,
- const ObjCCategoryDecl *C) const {
+ bool isInstance,
+ bool shallowCategoryLookup,
+ const ObjCCategoryDecl *C,
+ const ObjCProtocolDecl *P) const
+{
// FIXME: Should make sure no callers ever do this.
if (!hasDefinition())
return 0;
@@ -470,7 +472,23 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel,
if (data().ExternallyCompleted)
LoadExternalDefinition();
- while (ClassDecl != NULL) {
+ while (ClassDecl) {
+ // If we are looking for a method that is part of protocol conformance,
+ // check if the class 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 38bffa2f99e..0c025b78497 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2134,6 +2134,30 @@ 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 = NULL;
+ if (Attr.getNumArgs() == 1) {
+ Parm = 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)) {
@@ -4690,7 +4714,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 f44fb325114..2a781b5bd7d 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -1664,7 +1664,8 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
if (method->getImplementationControl() != ObjCMethodDecl::Optional &&
!method->isPropertyAccessor() &&
!InsMap.count(method->getSelector()) &&
- (!Super || !Super->lookupInstanceMethod(method->getSelector()))) {
+ (!Super || !Super->lookupInstanceMethod(method->getSelector(),
+ false, PDecl))) {
// 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.
@@ -1676,7 +1677,8 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
// uses the protocol.
if (ObjCMethodDecl *MethodInClass =
IDecl->lookupInstanceMethod(method->getSelector(),
- true /*shallowCategoryLookup*/))
+ true /*shallowCategoryLookup*/,
+ PDecl))
if (C || MethodInClass->isPropertyAccessor())
continue;
unsigned DIAG = diag::warn_unimplemented_protocol_method;
@@ -1695,10 +1697,13 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc,
ObjCMethodDecl *method = *I;
if (method->getImplementationControl() != ObjCMethodDecl::Optional &&
!ClsMap.count(method->getSelector()) &&
- (!Super || !Super->lookupClassMethod(method->getSelector()))) {
+ (!Super || !Super->lookupClassMethod(method->getSelector(),
+ /* shallowCategoryLookup */ false,
+ PDecl))) {
// See above comment for instance method lookups.
if (C && IDecl->lookupClassMethod(method->getSelector(),
- true /*shallowCategoryLookup*/))
+ true /*shallowCategoryLookup*/,
+ PDecl))
continue;
unsigned DIAG = diag::warn_unimplemented_protocol_method;
if (Diags.getDiagnosticLevel(DIAG, ImpLoc) !=
diff --git a/clang/test/SemaObjC/protocols-suppress-conformance.m b/clang/test/SemaObjC/protocols-suppress-conformance.m
new file mode 100644
index 00000000000..77c7e254c27
--- /dev/null
+++ b/clang/test/SemaObjC/protocols-suppress-conformance.m
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-objc-root-class
+
+@protocol FooProto
+- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
+@end
+
+__attribute__((objc_suppress_protocol(FooProto)))
+@interface Bar
+- (void) theBestOfTimes;
+@end
+
+@interface Bar2 : Bar
+@end
+
+@interface Baz : Bar2 <FooProto> // expected-note {{required for direct or indirect protocol 'FooProto'}}
+@end
+
+@interface Baz2 : Bar2 <FooProto>
+- (void) theBestOfTimes;
+@end
+
+@implementation Baz // expected-warning {{method 'theBestOfTimes' in protocol not implemented}}
+@end
+
+@implementation Baz2 // no-warning
+- (void) theBestOfTimes {}
+@end \ No newline at end of file
OpenPOWER on IntegriCloud