]> granicus.if.org Git - clang/commitdiff
[analyzer] Add a checker to manage dynamic type propagation.
authorAnna Zaks <ganna@apple.com>
Mon, 6 Aug 2012 23:25:39 +0000 (23:25 +0000)
committerAnna Zaks <ganna@apple.com>
Mon, 6 Aug 2012 23:25:39 +0000 (23:25 +0000)
Instead of sprinkling dynamic type info propagation throughout
ExprEngine, the added checker would add the more precise type
information on known APIs (Ex: ObjC alloc, new) and propagate
the type info in other cases (ex: ObjC init method, casts (the second is
not implemented yet)).

Add handling of ObjC alloc, new and init to the checker.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161357 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
lib/StaticAnalyzer/Checkers/CMakeLists.txt
lib/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp [new file with mode: 0644]
lib/StaticAnalyzer/Core/CallEvent.cpp
lib/StaticAnalyzer/Core/ExprEngineC.cpp
lib/StaticAnalyzer/Core/ProgramState.cpp
test/Analysis/inlining/InlineObjCInstanceMethod.m
test/Analysis/inlining/ObjCDynTypePopagation.m [new file with mode: 0644]

index b051d33cbd848bc2c88260c853483df5904e8874..0669b05570645c349d926b17a3eccdb4a3b0555f 100644 (file)
@@ -92,6 +92,10 @@ public:
     return Pred->getLocationContext();
   }
 
+  const StackFrameContext *getCurrentStackFrame() const {
+    return getLocationContext()->getCurrentStackFrame();
+  }
+
   BugReporter &getBugReporter() {
     return Eng.getBugReporter();
   }
@@ -132,6 +136,11 @@ public:
     return 0;
   }
 
+  /// \brief Get the value of arbitrary expressions at this point in the path.
+  SVal getSVal(const Stmt *S) const {
+    return getState()->getSVal(S, getLocationContext());
+  }
+
   /// \brief Generates a new transition in the program state graph
   /// (ExplodedGraph). Uses the default CheckerContext predecessor node.
   ///
index 9d82ec69837b4d64bcd421a1bbb386bcd12d8caa..b38f3ac3113362d4d777998001c49871ba0f4561 100644 (file)
@@ -331,10 +331,18 @@ public:
   bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const;
   bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const;
 
-  /// Get dynamic type information for a region.
+  /// \brief Get dynamic type information for a region.
   DynamicTypeInfo getDynamicTypeInfo(const MemRegion *Reg) const;
-  /// Add dynamic type information to the region and return the new state.
-  ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg, QualType NewTy)const;
+
+  /// \brief Add dynamic type information to the region; return the new state.
+  ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg,
+                                     DynamicTypeInfo NewTy) const;
+
+  /// \brief Add dynamic type information to the region; return the new state.
+  ProgramStateRef addDynamicTypeInfo(const MemRegion *Reg,
+                                     QualType NewTy) const {
+    return addDynamicTypeInfo(Reg, DynamicTypeInfo(NewTy));
+  }
 
   //==---------------------------------------------------------------------==//
   // Accessing the Generic Data Map (GDM).
index 66ee42f7e9aa12c6712047413ca71c0c79ecfcca..7fe51d3a4ce3c0ba6609402e753f7e55c1b0b0a6 100644 (file)
@@ -29,6 +29,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   DebugCheckers.cpp
   DereferenceChecker.cpp
   DivZeroChecker.cpp
+  DynamicTypePropagation.cpp
   ExprInspectionChecker.cpp
   FixedAddressChecker.cpp
   GenericTaintChecker.cpp
index 959c44f60d0f080a44a88e5121d091747faddfa5..8110bd0e18c216c4b75043510860208d16d11432 100644 (file)
@@ -84,6 +84,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">,
   HelpText<"Check that addresses to stack memory do not escape the function">,
   DescFile<"StackAddrEscapeChecker.cpp">;
 
+def DynamicTypePropagation : Checker<"DynamicTypePropagation">,
+  HelpText<"Generate dynamic type information">,
+  DescFile<"DynamicTypePropagation.cpp">;
+
 } // end "core"
 
 let ParentPackage = CoreExperimental in {
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
new file mode 100644 (file)
index 0000000..56610b5
--- /dev/null
@@ -0,0 +1,116 @@
+//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker defines the rules for dynamic type gathering and propagation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/Basic/Builtins.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DynamicTypePropagation : public Checker< check::PostCall > {
+  const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+                                                    CheckerContext &C) const;
+public:
+  void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
+};
+}
+
+void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
+                                           CheckerContext &C) const {
+  // We can obtain perfect type info for return values from some calls.
+  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+
+    // Get the returned value if it's a region.
+    SVal Result = C.getSVal(Call.getOriginExpr());
+    const MemRegion *RetReg = Result.getAsRegion();
+    if (!RetReg)
+      return;
+
+    ProgramStateRef State = C.getState();
+
+    switch (Msg->getMethodFamily()) {
+    default:
+      break;
+
+    // We assume that the type of the object returned by alloc and new are the
+    // pointer to the object of the class specified in the receiver of the
+    // message.
+    case OMF_alloc:
+    case OMF_new: {
+      // Get the type of object that will get created.
+      const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
+      const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
+      if (!ObjTy)
+        return;
+      QualType DynResTy =
+                 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
+      C.addTransition(State->addDynamicTypeInfo(RetReg, DynResTy));
+      break;
+    }
+    case OMF_init: {
+      // Assume, the result of the init method has the same dynamic type as
+      // the receiver and propagate the dynamic type info.
+      const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
+      assert(RecReg);
+      DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
+      C.addTransition(State->addDynamicTypeInfo(RetReg, RecDynType));
+      break;
+    }
+    }
+  }
+}
+
+const ObjCObjectType *
+DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+                                                    CheckerContext &C) const {
+  if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
+    if (const ObjCObjectType *ObjTy
+          = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
+    return ObjTy;
+  }
+
+  if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
+    if (const ObjCObjectType *ObjTy
+          = MsgE->getSuperType()->getAs<ObjCObjectType>())
+      return ObjTy;
+  }
+
+  const Expr *RecE = MsgE->getInstanceReceiver();
+  if (!RecE)
+    return 0;
+
+  RecE= RecE->IgnoreParenImpCasts();
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
+    const StackFrameContext *SFCtx = C.getCurrentStackFrame();
+    // Are we calling [self alloc]? If this is self, get the type of the
+    // enclosing ObjC class.
+    if (DRE->getDecl() == SFCtx->getSelfDecl()) {
+      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
+        if (const ObjCObjectType *ObjTy =
+            dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
+          return ObjTy;
+    }
+  }
+  return 0;
+}
+
+void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
+  mgr.registerChecker<DynamicTypePropagation>();
+}
index 05147670c11f0e664b9ff6bb31eeffbbc2ae5417..396e0f6d8d247d47319a79eca162f4cb1f405470 100644 (file)
@@ -583,8 +583,8 @@ SVal ObjCMethodCall::getReceiverSVal() const {
   if (!isInstanceMessage())
     return UnknownVal();
     
-  if (const Expr *Base = getOriginExpr()->getInstanceReceiver())
-    return getSVal(Base);
+  if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
+    return getSVal(RecE);
 
   // An instance message with no expression means we are sending to super.
   // In this case the object reference is the same as 'self'.
@@ -676,8 +676,8 @@ const Decl *ObjCMethodCall::getRuntimeDefinition() const {
       if (!Receiver)
         return 0;
 
-      DynamicTypeInfo TI = getState()->getDynamicTypeInfo(Receiver);
-      ReceiverT = dyn_cast<ObjCObjectPointerType>(TI.getType().getTypePtr());
+      QualType DynType = getState()->getDynamicTypeInfo(Receiver).getType();
+      ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getTypePtr());
     }
 
     // Lookup the method implementation.
@@ -715,7 +715,6 @@ void ObjCMethodCall::getInitialStackFrameContents(
   }
 }
 
-
 CallEventRef<SimpleCall>
 CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
                                 const LocationContext *LCtx) {
index 740dec5ec41556ab91e470de7938002d215407ea..7ec151ef6d7eae8904f6e86be8f9b18973779f8f 100644 (file)
@@ -308,9 +308,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
         const LocationContext *LCtx = Pred->getLocationContext();
         SVal V = state->getSVal(Ex, LCtx);
         V = svalBuilder.evalCast(V, T, ExTy);
-        if (const MemRegion *R = V.getAsRegion()) {
-          state = state->addDynamicTypeInfo(R, T);
-        }
         state = state->BindExpr(CastE, LCtx, V);
         Bldr.generateNode(CastE, Pred, state);
         continue;
index b44c470a90807f0f16a6fb26666be3922e97d6dc..c916c1a0207582338f0aa8bdc55f73df14aeddac 100644 (file)
@@ -762,12 +762,12 @@ DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const {
 }
 
 ProgramStateRef ProgramState::addDynamicTypeInfo(const MemRegion *Reg,
-                                                 QualType NewTy) const {
+                                                 DynamicTypeInfo NewTy) const {
   if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) {
     SymbolRef Sym = SR->getSymbol();
     // TODO: Instead of resetting the type info, check the old type info and
     // merge and pick the most precise type.
-    ProgramStateRef NewState = set<DynamicTypeMap>(Sym, DynamicTypeInfo(NewTy));
+    ProgramStateRef NewState = set<DynamicTypeMap>(Sym, NewTy);
     assert(NewState);
     return NewState;
   }
index 651d7c5b5a288c8b7822d4ab06b61e260939e38c..d7b184e00202237ea6d1ae6bd5cd2f2c33290c3d 100644 (file)
@@ -29,7 +29,7 @@
 // Method is called on inited object.
 + (int)testAllocInit2 {
   MyClass *a = [[MyClass alloc] init];
-  return 5/[a getInt]; // todo
+  return 5/[a getInt]; // expected-warning {{Division by zero}}
 }
 
 // Method is called on a parameter.
diff --git a/test/Analysis/inlining/ObjCDynTypePopagation.m b/test/Analysis/inlining/ObjCDynTypePopagation.m
new file mode 100644 (file)
index 0000000..89c05c9
--- /dev/null
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=dynamic -verify %s
+
+typedef signed char BOOL;
+
+void clang_analyzer_eval(BOOL);
+
+@protocol NSObject  - (BOOL)isEqual:(id)object; @end
+@interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
++(id)new;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+@end
+
+@interface MyParent : NSObject
+- (int)getZeroOverridden;
+@end
+@implementation MyParent
+- (int) getZeroOverridden {
+   return 1;
+}
+- (int) getZero {
+   return 0;
+}
+@end
+
+@interface MyClass : MyParent
+- (int) getZeroOverridden;
+@end
+
+MyClass *getObj();
+
+@implementation MyClass
+- (int) getZeroOverridden {
+   return 0;
+}
+
+/* Test that we get the right type from call to alloc. */
+
++ (void) testAllocSelf {
+ id a = [self alloc];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
+
++ (void) testAllocClass {
+  id a = [MyClass alloc];
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testAllocSuperOverriden {
+  id a = [super alloc];
+  // Evaluates to 1 in the parent.
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{FALSE}} 
+}
+
++ (void) testAllocSuper {
+  id a = [super alloc];
+  clang_analyzer_eval([a getZero] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testAllocInit {
+  id a = [[self alloc] init];
+  clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
++ (void) testNewSelf {
+ id a = [self new];
+ clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}}
+}
+
+@end
\ No newline at end of file