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