/// we used an immutable set to keep the table then it wouldn't add significant
/// memory cost and it would be handy for lookups.
+typedef llvm::DenseSet<IdentifierInfo*> ProtocolNameSet;
+typedef llvm::OwningPtr<ProtocolNameSet> LazyProtocolNameSet;
+
+/// Recursively populates a set with all conformed protocols in a class
+/// hierarchy that have the 'objc_protocol_requires_explicit_implementation'
+/// attribute.
+static void findProtocolsWithExplicitImpls(const ObjCInterfaceDecl *Super,
+ ProtocolNameSet &PNS) {
+ if (!Super)
+ return;
+
+ for (ObjCInterfaceDecl::all_protocol_iterator
+ I = Super->all_referenced_protocol_begin(),
+ E = Super->all_referenced_protocol_end(); I != E; ++I) {
+ const ObjCProtocolDecl *PDecl = *I;
+ if (PDecl->hasAttr<ObjCExplicitProtocolImplAttr>())
+ PNS.insert(PDecl->getIdentifier());
+ }
+}
+
/// CheckProtocolMethodDefs - This routine checks unimplemented methods
/// Declared in protocol, and those referenced by it.
static void CheckProtocolMethodDefs(Sema &S,
const Sema::SelectorSet &InsMap,
const Sema::SelectorSet &ClsMap,
ObjCContainerDecl *CDecl,
- bool isExplicitProtocol = true) {
+ LazyProtocolNameSet &ProtocolsExplictImpl) {
ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl);
ObjCInterfaceDecl *IDecl = C ? C->getClassInterface()
: dyn_cast<ObjCInterfaceDecl>(CDecl);
ObjCInterfaceDecl *Super = IDecl->getSuperClass();
ObjCInterfaceDecl *NSIDecl = 0;
+
+ // If this protocol is marked 'objc_protocol_requires_explicit_implementation'
+ // then we should check if any class in the super class hierarchy also
+ // conforms to this protocol, either directly or via protocol inheritance.
+ // If so, we can skip checking this protocol completely because we
+ // know that a parent class already satisfies this protocol.
+ //
+ // Note: we could generalize this logic for all protocols, and merely
+ // add the limit on looking at the super class chain for just
+ // specially marked protocols. This may be a good optimization. This
+ // change is restricted to 'objc_protocol_requires_explicit_implementation'
+ // protocols for now for controlled evaluation.
+ if (PDecl->hasAttr<ObjCExplicitProtocolImplAttr>()) {
+ if (!ProtocolsExplictImpl.isValid()) {
+ ProtocolsExplictImpl.reset(new ProtocolNameSet);
+ findProtocolsWithExplicitImpls(Super, *ProtocolsExplictImpl);
+ }
+ if (ProtocolsExplictImpl->find(PDecl->getIdentifier()) !=
+ ProtocolsExplictImpl->end())
+ return;
+
+ // If no super class conforms to the protocol, we should not search
+ // for methods in the super class to implicitly satisfy the protocol.
+ Super = NULL;
+ }
+
if (S.getLangOpts().ObjCRuntime.isNeXTFamily()) {
// check to see if class implements forwardInvocation method and objects
// of this class are derived from 'NSProxy' so that to forward requests
// the method was implemented by a base class or an inherited
// protocol. This lookup is slow, but occurs rarely in correct code
// and otherwise would terminate in a warning.
- if (isExplicitProtocol && PDecl->hasAttr<ObjCExplicitProtocolImplAttr>())
- Super = NULL;
// check unimplemented instance methods.
if (!NSIDecl)
for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(),
E = PDecl->protocol_end(); PI != E; ++PI)
CheckProtocolMethodDefs(S, ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap,
- CDecl, /* isExplicitProtocl */ false);
+ CDecl, ProtocolsExplictImpl);
}
/// MatchAllMethodDeclarations - Check methods declared in interface
// Check and see if class methods in class interface have been
// implemented in the implementation class.
+ LazyProtocolNameSet ExplicitImplProtocols;
+
if (ObjCInterfaceDecl *I = dyn_cast<ObjCInterfaceDecl> (CDecl)) {
for (ObjCInterfaceDecl::all_protocol_iterator
PI = I->all_referenced_protocol_begin(),
E = I->all_referenced_protocol_end(); PI != E; ++PI)
CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), *PI,
- IncompleteImpl, InsMap, ClsMap, I);
+ IncompleteImpl, InsMap, ClsMap, I,
+ ExplicitImplProtocols);
// Check class extensions (unnamed categories)
for (ObjCInterfaceDecl::visible_extensions_iterator
Ext = I->visible_extensions_begin(),
for (ObjCCategoryDecl::protocol_iterator PI = C->protocol_begin(),
E = C->protocol_end(); PI != E; ++PI)
CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), *PI,
- IncompleteImpl, InsMap, ClsMap, CDecl);
+ IncompleteImpl, InsMap, ClsMap, CDecl,
+ ExplicitImplProtocols);
DiagnoseUnimplementedProperties(S, IMPDecl, CDecl,
/* SynthesizeProperties */ false);
}
// to be explicitly implemented in the adopting class.
__attribute__((objc_protocol_requires_explicit_implementation))
@protocol Protocol
-- (void) theBestOfTimes; // expected-note 2 {{method 'theBestOfTimes' declared here}}
+- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
@property (readonly) id theWorstOfTimes; // expected-note {{property declared here}}
@end
@property (readonly) id theWorstOfTimes;
@end
-// This class subclasses ClassA (which adopts 'Protocol'),
-// but does not provide the needed implementation.
+// This class subclasses ClassA (which also adopts 'Protocol').
@interface ClassB : ClassA <Protocol>
@end
-@implementation ClassB // expected-warning {{method 'theBestOfTimes' in protocol 'Protocol' not implemented}} expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation}}
+@implementation ClassB // expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation}}
@end
@interface ClassB_Good : ClassA <Protocol>
__attribute__((objc_protocol_requires_explicit_implementation)) // expected-error{{attribute 'objc_protocol_requires_explicit_implementation' can only be applied to @protocol definitions, not forward declarations}}
@protocol NotDefined;
+// Another complete hierarchy.
+ __attribute__((objc_protocol_requires_explicit_implementation))
+@protocol Ex2FooBar
+- (void)methodA;
+@end
+
+ __attribute__((objc_protocol_requires_explicit_implementation))
+@protocol Ex2ProtocolA
+- (void)methodB; // expected-note {{method 'methodB' declared here}}
+@end
+
+ __attribute__((objc_protocol_requires_explicit_implementation))
+@protocol Ex2ProtocolB <Ex2ProtocolA>
+- (void)methodA; // expected-note {{method 'methodA' declared here}}
+@end
+
+// NOT required
+@protocol Ex2ProtocolC <Ex2ProtocolA>
+- (void)methodB;
+- (void)methodA;
+@end
+
+@interface Ex2ClassA <Ex2ProtocolC, Ex2FooBar>
+@end
+@implementation Ex2ClassA
+- (void)methodB {}
+- (void)methodA {}
+@end
+
+@interface Ex2ClassB : Ex2ClassA <Ex2ProtocolB>
+@end
+
+@implementation Ex2ClassB // expected-warning {{method 'methodB' in protocol 'Ex2ProtocolA' not implemented}}\
+ // expected-warning {{method 'methodA' in protocol 'Ex2ProtocolB' not implemented}}
+@end