r291867 - [analyzer] Support inlining of '[self classMethod]' and '[[self class] classMethod]'
Anna Zaks via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 12 16:50:47 PST 2017
Author: zaks
Date: Thu Jan 12 18:50:47 2017
New Revision: 291867
URL: http://llvm.org/viewvc/llvm-project?rev=291867&view=rev
Log:
[analyzer] Support inlining of '[self classMethod]' and '[[self class] classMethod]'
Differential Revision: https://reviews.llvm.org/D28495
Modified:
cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
cfe/trunk/test/Analysis/inlining/InlineObjCClassMethod.m
Modified: cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp?rev=291867&r1=291866&r2=291867&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp Thu Jan 12 18:50:47 2017
@@ -896,6 +896,38 @@ bool ObjCMethodCall::canBeOverridenInSub
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 @@ RuntimeDefinition ObjCMethodCall::getRun
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,7 +954,7 @@ RuntimeDefinition ObjCMethodCall::getRun
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())
@@ -929,7 +962,32 @@ RuntimeDefinition ObjCMethodCall::getRun
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 associate the meta type with the symbol
+ // using the dynamic type info tracking and use it here. We can add a new
+ // SVal for ObjC 'Class' values that know what interface declaration they
+ // come from. Then 'self' in a class method would be filled in with
+ // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
+ // do proper dynamic dispatch for class methods just like we do for
+ // instance methods now.
+ 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
Modified: cfe/trunk/test/Analysis/inlining/InlineObjCClassMethod.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/InlineObjCClassMethod.m?rev=291867&r1=291866&r2=291867&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/inlining/InlineObjCClassMethod.m (original)
+++ cfe/trunk/test/Analysis/inlining/InlineObjCClassMethod.m Thu Jan 12 18:50:47 2017
@@ -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 @@ int foo2() {
@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,80 @@ void rdar15037033() {
+ (void)forwardDeclaredVariadicMethod:(int)x, ... {
clang_analyzer_checkInlined(0); // no-warning
}
-
@end
+ at interface SelfClassTestParent : NSObject
+-(unsigned)returns10;
++(unsigned)returns20;
++(unsigned)returns30;
+ at end
+
+ at implementation SelfClassTestParent
+-(unsigned)returns10 { return 100; }
++(unsigned)returns20 { return 100; }
++(unsigned)returns30 { return 100; }
+ at end
+
+ at interface SelfClassTest : SelfClassTestParent
+-(unsigned)returns10;
++(unsigned)returns20;
++(unsigned)returns30;
+ at end
+
+ at 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}}
+ 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}}
+}
+ at end
-
+ at interface Parent : NSObject
++ (int)a;
++ (int)b;
+ at end
+ at interface Child : Parent
+ at end
+ at interface Other : NSObject
++(void)run;
+ at end
+int main(int argc, const char * argv[]) {
+ @autoreleasepool {
+ [Other run];
+ }
+ return 0;
+}
+ at implementation Other
++(void)run {
+ int result = [Child a];
+ // TODO: This should return 100.
+ clang_analyzer_eval(result == 12); // expected-warning{{TRUE}}
+}
+ at end
+ at implementation Parent
++ (int)a; {
+ return [self b];
+}
++ (int)b; {
+ return 12;
+}
+ at end
+ at implementation Child
++ (int)b; {
+ return 100;
+}
+ at end
More information about the cfe-commits
mailing list