]> granicus.if.org Git - clang/commitdiff
Adjust logic for 'objc_protocol_requires_explicit_implementation' for inherited proto...
authorTed Kremenek <kremenek@apple.com>
Wed, 5 Mar 2014 08:13:08 +0000 (08:13 +0000)
committerTed Kremenek <kremenek@apple.com>
Wed, 5 Mar 2014 08:13:08 +0000 (08:13 +0000)
Per more discussion, 'objc_protocol_requires_explicit_implementation' is
refinement that it mainly adds that requirement that a protocol must be
explicitly satisfied at the moment the first class in the class hierarchy
conforms to it.  Any subclasses that also conform to that protocol,
either directly or via conforming to a protocol that inherits that protocol,
do not need to re-implement that protocol.

Doing this requires first doing a pass on the super class hierarchy,
gathering the set of protocols conformed to by the super classes,
and then culling those out when determining conformance.  This
two-pass algorithm could be generalized for all protocol checking,
and could possibly be a performance win in some cases.  For now
we restrict this change to protocols with this attribute to isolate
the change in logic (especially as the design continues to evolve).

This change needs to be adjusted for properties as well; this
only impacts methods right now.

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

lib/Sema/SemaDeclObjC.cpp
test/SemaObjC/protocols-suppress-conformance.m

index 86e16212a3a5959d468c1ae5825e257cf57214d0..57f40aaca5493dd7040008c964a4ea59d63c7d99 100644 (file)
@@ -1632,6 +1632,26 @@ void Sema::WarnExactTypedMethods(ObjCMethodDecl *ImpMethodDecl,
 /// 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,
@@ -1641,7 +1661,7 @@ 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);
@@ -1649,6 +1669,32 @@ static void CheckProtocolMethodDefs(Sema &S,
   
   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
@@ -1674,8 +1720,6 @@ static void CheckProtocolMethodDefs(Sema &S,
   // 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)
@@ -1744,7 +1788,7 @@ static void CheckProtocolMethodDefs(Sema &S,
   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
@@ -1958,12 +2002,15 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
   // 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(),
@@ -1978,7 +2025,8 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
       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);
     } 
index 460b78fff37bba9a7b73bcd2e13d8b51f93df446..3bb892780c15ef8637c5da711b1465e154461553 100644 (file)
@@ -4,7 +4,7 @@
 // to be explicitly implemented in the adopting class.
 __attribute__((objc_protocol_requires_explicit_implementation))
 @protocol Protocol
-- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
+- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
 @property (readonly) id theWorstOfTimes; // expected-note {{property declared here}}
 @end
 
@@ -16,12 +16,11 @@ __attribute__((objc_protocol_requires_explicit_implementation))
 @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>
@@ -171,4 +170,39 @@ __attribute__((objc_protocol_requires_explicit_implementation))
 __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