zaks.anna created this revision. zaks.anna added reviewers: dergachev.a, dcoughlin. zaks.anna added a subscriber: cfe-commits.
https://reviews.llvm.org/D28495 Files: lib/StaticAnalyzer/Core/CallEvent.cpp test/Analysis/inlining/InlineObjCClassMethod.m
Index: test/Analysis/inlining/InlineObjCClassMethod.m =================================================================== --- test/Analysis/inlining/InlineObjCClassMethod.m +++ test/Analysis/inlining/InlineObjCClassMethod.m @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ipa=dynamic-bifurcate -verify %s void clang_analyzer_checkInlined(int); +void clang_analyzer_eval(int); // Test inlining of ObjC class methods. @@ -194,7 +195,9 @@ @implementation SelfUsedInParent + (int)getNum {return 5;} + (int)foo { - return [self getNum]; + int r = [self getNum]; + clang_analyzer_eval(r == 5); // expected-warning{{TRUE}} + return r; } @end @interface SelfUsedInParentChild : SelfUsedInParent @@ -229,8 +232,41 @@ + (void)forwardDeclaredVariadicMethod:(int)x, ... { clang_analyzer_checkInlined(0); // no-warning } +@end + +@interface SelfClassTestParent : NSObject +-(unsigned)returns10; ++(unsigned)returns20; ++(unsigned)returns30; +@end +@implementation SelfClassTestParent +-(unsigned)returns10 { return 100; } ++(unsigned)returns20 { return 100; } ++(unsigned)returns30 { return 100; } @end +@interface SelfClassTest : SelfClassTestParent +-(unsigned)returns10; ++(unsigned)returns20; ++(unsigned)returns30; +@end +@implementation SelfClassTest +-(unsigned)returns10 { return 10; } ++(unsigned)returns20 { return 20; } ++(unsigned)returns30 { return 30; } ++(void)classMethod { + unsigned result1 = [self returns20]; + clang_analyzer_eval(result1 == 20); // expected-warning{{TRUE}} + unsigned result2 = [[self class] returns30]; + clang_analyzer_eval(result2 == 30); // expected-warning{{TRUE}} + unsigned result3 = [[super class] returns30]; + clang_analyzer_eval(result3 == 100); // expected-warning{{UNKNOWN}} +} +-(void)instanceMethod { + unsigned result0 = [self returns10]; + clang_analyzer_eval(result0 == 10); // expected-warning{{TRUE}} +} +@end Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -896,6 +896,38 @@ llvm_unreachable("The while loop should always terminate."); } +static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) { + if (!MD) + return MD; + + // Find the redeclaration that defines the method. + if (!MD->hasBody()) { + for (auto I : MD->redecls()) + if (I->hasBody()) + MD = cast<ObjCMethodDecl>(I); + } + return MD; +} + +static bool isCallToSelfClass(const ObjCMessageExpr *ME) { + const Expr* InstRec = ME->getInstanceReceiver(); + if (!InstRec) + return false; + const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts()); + + // Check that receiver is called 'self'. + if (!InstRecIg || !InstRecIg->getFoundDecl() || + !InstRecIg->getFoundDecl()->getName().equals("self")) + return false; + + // Check that the method name is 'class'. + if (ME->getSelector().getNumArgs() != 0 || + !ME->getSelector().getNameForSlot(0).equals("class")) + return false; + + return true; +} + RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { const ObjCMessageExpr *E = getOriginExpr(); assert(E); @@ -910,6 +942,7 @@ const MemRegion *Receiver = nullptr; if (!SupersType.isNull()) { + // The receiver is guaranteed to be 'super' in this case. // Super always means the type of immediate predecessor to the method // where the call occurs. ReceiverT = cast<ObjCObjectPointerType>(SupersType); @@ -921,15 +954,35 @@ DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver); QualType DynType = DTI.getType(); CanBeSubClassed = DTI.canBeASubClass(); - ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); + ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType()); if (ReceiverT && CanBeSubClassed) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) if (!canBeOverridenInSubclass(IDecl, Sel)) CanBeSubClassed = false; } - // Lookup the method implementation. + // Handle special cases of '[self classMethod]' and + // '[[self class] classMethod]', which are treated by the compiler as + // instance (not class) messages. We will statically dispatch to those. + if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) { + // For [self classMethod], return the compiler visible declaration. + if (PT->getObjectType()->isObjCClass() && + Receiver == getSelfSVal().getAsRegion()) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + + // Similarly, handle [[self class] classMethod]. + // TODO: We are currently doing a syntactic match for this pattern with is + // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m + // shows. A better way would be to accosiate the meta type with the symbol + // using the dynamic type info tracking and use it here. + if (E->getInstanceReceiver()) + if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver())) + if (isCallToSelfClass(M)) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + } + + // Lookup the instance method implementation. if (ReceiverT) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { // Repeatedly calling lookupPrivateMethod() is expensive, especially
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits