From: Fariborz Jahanian Date: Thu, 9 Feb 2012 21:30:24 +0000 (+0000) Subject: objc: If a method is not implemented in the category implementation but X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bb3d14e55d267bf5228699c7bf0c8ccdb71c8f46;p=clang objc: 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 unimplemented method. // rdar://10823023 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150206 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 04fbf751e1..0bb745a2a3 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -898,12 +898,15 @@ public: // Lookup a method. First, we search locally. If a method isn't // found, we search referenced protocols and class categories. - ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance) const; - ObjCMethodDecl *lookupInstanceMethod(Selector Sel) const { - return lookupMethod(Sel, true/*isInstance*/); - } - ObjCMethodDecl *lookupClassMethod(Selector Sel) const { - return lookupMethod(Sel, false/*isInstance*/); + ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance, + bool noCategoryLookup= false) const; + ObjCMethodDecl *lookupInstanceMethod(Selector Sel, + bool noCategoryLookup = false) const { + return lookupMethod(Sel, true/*isInstance*/, noCategoryLookup); + } + ObjCMethodDecl *lookupClassMethod(Selector Sel, + bool noCategoryLookup = false) const { + return lookupMethod(Sel, false/*isInstance*/, noCategoryLookup); } ObjCInterfaceDecl *lookupInheritedClass(const IdentifierInfo *ICName); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index fd9292339e..cfa8e1be94 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1964,7 +1964,7 @@ public: ObjCContainerDecl* IDecl, bool &IncompleteImpl, bool ImmediateClass, - bool WarnExactMatch=false); + bool WarnCategoryMethodImpl=false); /// CheckCategoryVsClassMethodMatches - Checks that methods implemented in /// category matches with those implemented in its primary class and diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index d0083855bb..6617dc7ac8 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -317,7 +317,8 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::lookupInheritedClass( /// lookupMethod - This method returns an instance/class method by looking in /// the class, its categories, and its super classes (using a linear search). ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, - bool isInstance) const { + bool isInstance, + bool noCategoryLookup) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; @@ -339,21 +340,22 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, E = Protocols.end(); I != E; ++I) if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) return MethodDecl; - - // Didn't find one yet - now look through categories. - ObjCCategoryDecl *CatDecl = ClassDecl->getCategoryList(); - while (CatDecl) { - if ((MethodDecl = CatDecl->getMethod(Sel, isInstance))) - return MethodDecl; - - // Didn't find one yet - look through protocols. - const ObjCList &Protocols = - CatDecl->getReferencedProtocols(); - for (ObjCList::iterator I = Protocols.begin(), - E = Protocols.end(); I != E; ++I) - if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) + if (!noCategoryLookup) { + // Didn't find one yet - now look through categories. + ObjCCategoryDecl *CatDecl = ClassDecl->getCategoryList(); + while (CatDecl) { + if ((MethodDecl = CatDecl->getMethod(Sel, isInstance))) return MethodDecl; - CatDecl = CatDecl->getNextClassCategory(); + + // Didn't find one yet - look through protocols. + const ObjCList &Protocols = + CatDecl->getReferencedProtocols(); + for (ObjCList::iterator I = Protocols.begin(), + E = Protocols.end(); I != E; ++I) + if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) + return MethodDecl; + CatDecl = CatDecl->getNextClassCategory(); + } } ClassDecl = ClassDecl->getSuperClass(); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index ccb106fd8d..f3db4be111 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1465,11 +1465,9 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, const llvm::DenseSet &InsMap, const llvm::DenseSet &ClsMap, ObjCContainerDecl *CDecl) { - ObjCInterfaceDecl *IDecl; - if (ObjCCategoryDecl *C = dyn_cast(CDecl)) - IDecl = C->getClassInterface(); - else - IDecl = dyn_cast(CDecl); + ObjCCategoryDecl *C = dyn_cast(CDecl); + ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() + : dyn_cast(CDecl); assert (IDecl && "CheckProtocolMethodDefs - IDecl is null"); ObjCInterfaceDecl *Super = IDecl->getSuperClass(); @@ -1504,20 +1502,27 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, !method->isSynthesized() && !InsMap.count(method->getSelector()) && (!Super || !Super->lookupInstanceMethod(method->getSelector()))) { + // 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. + // This is because method will be implemented in the primary class + // or one of its super class implementation. + // Ugly, but necessary. Method declared in protcol might have // have been synthesized due to a property declared in the class which // uses the protocol. - ObjCMethodDecl *MethodInClass = - IDecl->lookupInstanceMethod(method->getSelector()); - if (!MethodInClass || !MethodInClass->isSynthesized()) { - unsigned DIAG = diag::warn_unimplemented_protocol_method; - if (Diags.getDiagnosticLevel(DIAG, ImpLoc) - != DiagnosticsEngine::Ignored) { - WarnUndefinedMethod(ImpLoc, method, IncompleteImpl, DIAG); - Diag(method->getLocation(), diag::note_method_declared_at); - Diag(CDecl->getLocation(), diag::note_required_for_protocol_at) - << PDecl->getDeclName(); - } + if (ObjCMethodDecl *MethodInClass = + IDecl->lookupInstanceMethod(method->getSelector(), + true /*noCategoryLookup*/)) + if (C || MethodInClass->isSynthesized()) + continue; + unsigned DIAG = diag::warn_unimplemented_protocol_method; + if (Diags.getDiagnosticLevel(DIAG, ImpLoc) + != DiagnosticsEngine::Ignored) { + WarnUndefinedMethod(ImpLoc, method, IncompleteImpl, DIAG); + Diag(method->getLocation(), diag::note_method_declared_at); + Diag(CDecl->getLocation(), diag::note_required_for_protocol_at) + << PDecl->getDeclName(); } } } @@ -1529,6 +1534,10 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, if (method->getImplementationControl() != ObjCMethodDecl::Optional && !ClsMap.count(method->getSelector()) && (!Super || !Super->lookupClassMethod(method->getSelector()))) { + // See above comment for instance method lookups. + if (C && IDecl->lookupClassMethod(method->getSelector(), + true /*noCategoryLookup*/)) + continue; unsigned DIAG = diag::warn_unimplemented_protocol_method; if (Diags.getDiagnosticLevel(DIAG, ImpLoc) != DiagnosticsEngine::Ignored) { @@ -1542,7 +1551,7 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, // Check on this protocols's referenced protocols, recursively. for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), E = PDecl->protocol_end(); PI != E; ++PI) - CheckProtocolMethodDefs(ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap, IDecl); + CheckProtocolMethodDefs(ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap, CDecl); } /// MatchAllMethodDeclarations - Check methods declared in interface @@ -1556,7 +1565,7 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet &InsMap, ObjCContainerDecl* CDecl, bool &IncompleteImpl, bool ImmediateClass, - bool WarnExactMatch) { + bool WarnCategoryMethodImpl) { // Check and see if instance methods in class interface have been // implemented in the implementation class. If so, their types match. for (ObjCInterfaceDecl::instmeth_iterator I = CDecl->instmeth_begin(), @@ -1578,12 +1587,12 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet &InsMap, ObjCMethodDecl *MethodDecl = *I; // ImpMethodDecl may be null as in a @dynamic property. if (ImpMethodDecl) { - if (!WarnExactMatch) + if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, MethodDecl, isa(CDecl)); else if (!MethodDecl->isSynthesized()) WarnExactTypedMethods(ImpMethodDecl, MethodDecl, - isa(CDecl)); + isa(CDecl)); } } } @@ -1605,12 +1614,12 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet &InsMap, assert(CDecl->getClassMethod((*I)->getSelector()) && "Expected to find the method through lookup as well"); ObjCMethodDecl *MethodDecl = *I; - if (!WarnExactMatch) + if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, MethodDecl, isa(CDecl)); else WarnExactTypedMethods(ImpMethodDecl, MethodDecl, - isa(CDecl)); + isa(CDecl)); } } @@ -1621,7 +1630,8 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet &InsMap, MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, IMPDecl, const_cast(ClsExtDecl), - IncompleteImpl, false, WarnExactMatch); + IncompleteImpl, false, + WarnCategoryMethodImpl); // Check for any implementation of a methods declared in protocol. for (ObjCInterfaceDecl::all_protocol_iterator @@ -1629,11 +1639,12 @@ void Sema::MatchAllMethodDeclarations(const llvm::DenseSet &InsMap, E = I->all_referenced_protocol_end(); PI != E; ++PI) MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, IMPDecl, - (*PI), IncompleteImpl, false, WarnExactMatch); + (*PI), IncompleteImpl, false, + WarnCategoryMethodImpl); // FIXME. For now, we are not checking for extact match of methods // in category implementation and its primary class's super class. - if (!WarnExactMatch && I->getSuperClass()) + if (!WarnCategoryMethodImpl && I->getSuperClass()) MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, IMPDecl, I->getSuperClass(), IncompleteImpl, false); @@ -1670,7 +1681,8 @@ void Sema::CheckCategoryVsClassMethodMatches( bool IncompleteImpl = false; MatchAllMethodDeclarations(InsMap, ClsMap, InsMapSeen, ClsMapSeen, CatIMPDecl, IDecl, - IncompleteImpl, false, true /*WarnExactMatch*/); + IncompleteImpl, false, + true /*WarnCategoryMethodImpl*/); } void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, diff --git a/test/SemaObjC/method-undef-category-warn-1.m b/test/SemaObjC/method-undef-category-warn-1.m index 9ef83b2641..929f162cb9 100644 --- a/test/SemaObjC/method-undef-category-warn-1.m +++ b/test/SemaObjC/method-undef-category-warn-1.m @@ -28,3 +28,26 @@ @implementation MyClass1(CAT1) @end + +// rdar://10823023 +@class NSString; + +@protocol NSObject +- (NSString *)meth_inprotocol; +@end + +@interface NSObject +- (NSString *)description; ++ (NSString *) cls_description; +@end + +@protocol Foo +- (NSString *)description; ++ (NSString *) cls_description; +@end + +@interface NSObject (FooConformance) +@end + +@implementation NSObject (FooConformance) +@end