From: Fariborz Jahanian Date: Fri, 8 Apr 2011 18:25:29 +0000 (+0000) Subject: Warn for any kind of initialization if initializer does not X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b7bc34a83aff8af09f2a78aa6d1dcafe18ad8619;p=clang Warn for any kind of initialization if initializer does not implement lhs's protocols. // rdar://9091389. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@129142 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 84e7a63b02..241d76c448 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -629,6 +629,27 @@ public: return false; } + /// getImmSubClassOf - Returns Immediate sub-class of the specified interface class + /// if 'Super' is a superclass of this class. null if no such super class. + /// So in this example if 'this' is 'BClass' and 'Super' is 'AClass' then 'BClass' + /// is returned. + /// \code + /// @interface BClass : AClass + /// @end + /// \endcode + + ObjCInterfaceDecl *getImmSubClassOf(const ObjCInterfaceDecl *Super) { + ObjCInterfaceDecl *ImmSubClass = this; + ObjCInterfaceDecl *I = this->getSuperClass(); + while (I != NULL) { + if (Super == I) + return ImmSubClass; + ImmSubClass = I; + I = I->getSuperClass(); + } + return NULL; + } + ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared); ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName) { diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 01e8f8e396..39229a7b73 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4981,10 +4981,47 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectType *LHS, if (LHS->getNumProtocols() == 0) return true; - // Okay, we know the LHS has protocol qualifiers. If the RHS doesn't, then it - // isn't a superset. - if (RHS->getNumProtocols() == 0) - return true; // FIXME: should return false! + // Okay, we know the LHS has protocol qualifiers. If the RHS doesn't, + // more detailed analysis is required. + if (RHS->getNumProtocols() == 0) { + // OK, if LHS is a superclass of RHS *and* + // this superclass is assignment compatible with LHS. + // false otherwise. + ObjCInterfaceDecl *SuperClass = + RHS->getInterface()->getImmSubClassOf(LHS->getInterface()); + if (SuperClass) { + // OK if conversion of LHS to SuperClass results in narrowing of types + // ; i.e., SuperClass may implement at least one of the protocols + // in LHS's protocol list. Example, SuperObj = lhs is ok. + // But not SuperObj = lhs. + llvm::SmallPtrSet SuperClassInheritedProtocols; + CollectInheritedProtocols(SuperClass, SuperClassInheritedProtocols); + // If super class has no protocols, it is not a match. + if (SuperClassInheritedProtocols.empty()) + return false; + + for (ObjCObjectType::qual_iterator LHSPI = LHS->qual_begin(), + LHSPE = LHS->qual_end(); + LHSPI != LHSPE; LHSPI++) { + bool SuperImplementsProtocol = false; + ObjCProtocolDecl *LHSProto = (*LHSPI); + + for (llvm::SmallPtrSet::iterator I = + SuperClassInheritedProtocols.begin(), + E = SuperClassInheritedProtocols.end(); I != E; ++I) { + ObjCProtocolDecl *SuperClassProto = (*I); + if (SuperClassProto->lookupProtocolNamed(LHSProto->getIdentifier())) { + SuperImplementsProtocol = true; + break; + } + } + if (!SuperImplementsProtocol) + return false; + } + return true; + } + return false; + } for (ObjCObjectType::qual_iterator LHSPI = LHS->qual_begin(), LHSPE = LHS->qual_end(); diff --git a/test/CodeGenObjCXX/references.mm b/test/CodeGenObjCXX/references.mm index 8875fd6240..6265c7be76 100644 --- a/test/CodeGenObjCXX/references.mm +++ b/test/CodeGenObjCXX/references.mm @@ -30,7 +30,7 @@ void f(B* b) { @protocol P2 @end @protocol P3 @end @interface foo {} @end -@interface bar : foo {} @end +@interface bar : foo {} @end typedef bar baz; void f5(foo&); void f5b(foo&); diff --git a/test/SemaObjC/comptypes-4.m b/test/SemaObjC/comptypes-4.m index 56b23b2245..adc324c91e 100644 --- a/test/SemaObjC/comptypes-4.m +++ b/test/SemaObjC/comptypes-4.m @@ -12,7 +12,7 @@ int main() MyClass *obj_cp; obj_cp = obj_p; - obj_p = obj_cp; + obj_p = obj_cp; // expected-warning {{incompatible pointer types assigning to 'MyClass *' from 'MyClass *'}} if (obj_cp == obj_p) foo(); diff --git a/test/SemaObjC/unqualified-to-qualified-class-warn.m b/test/SemaObjC/unqualified-to-qualified-class-warn.m new file mode 100644 index 0000000000..5bbbfd9fcc --- /dev/null +++ b/test/SemaObjC/unqualified-to-qualified-class-warn.m @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://9091389 + +@protocol Fooable +- (void)foo; +@end + +@protocol SubFooable +@end + +@interface AClass +@end + +@interface BClass : AClass +@end + +@implementation BClass +- (void)foo { +} +@end + +void functionTakingAClassConformingToAProtocol(AClass *instance) { // expected-note {{passing argument to parameter 'instance' here}} +} + +int main () { + AClass *aobject = 0; + BClass *bobject = 0; + functionTakingAClassConformingToAProtocol(aobject); // expected-warning {{incompatible pointer types passing 'AClass *' to parameter of type 'AClass *'}} + functionTakingAClassConformingToAProtocol(bobject); // Shouldn't warn - does implement Fooable + return 0; +} diff --git a/test/SemaObjCXX/references.mm b/test/SemaObjCXX/references.mm index 585c75f3cd..3a522005ab 100644 --- a/test/SemaObjCXX/references.mm +++ b/test/SemaObjCXX/references.mm @@ -37,7 +37,7 @@ void f4(NSString &tmpstr) { @protocol P2 @end @protocol P3 @end @interface foo {} @end -@interface bar : foo {} @end +@interface bar : foo {} @end typedef bar baz; struct ToBar {