forked from OSchip/llvm-project
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
This commit is contained in:
parent
0078150c43
commit
28eace65c0
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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}}
|
||||
|
Loading…
Reference in New Issue