]> granicus.if.org Git - clang/commitdiff
[analyzer] Track null/uninitialized C++ objects used in method calls.
authorJordan Rose <jordan_rose@apple.com>
Fri, 3 Aug 2012 23:08:49 +0000 (23:08 +0000)
committerJordan Rose <jordan_rose@apple.com>
Fri, 3 Aug 2012 23:08:49 +0000 (23:08 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161278 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
lib/StaticAnalyzer/Core/CallEvent.cpp
test/Analysis/method-call-path-notes.cpp [new file with mode: 0644]
test/Analysis/misc-ps-cxx0x.cpp

index 7e665ceda54b4a7c8f72ea0a87fe18d17d218004..3e62a920b76edc73334d72899ea07b2babb1b54d 100644 (file)
@@ -232,7 +232,6 @@ BugReporterVisitor *getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
 
 const Stmt *GetDerefExpr(const ExplodedNode *N);
 const Stmt *GetDenomExpr(const ExplodedNode *N);
-const Stmt *GetCalleeExpr(const ExplodedNode *N);
 const Stmt *GetRetValExpr(const ExplodedNode *N);
 
 } // end namespace clang
index 8e50833a3e5ccaaf9bb3b45bb2efe3c457c40822..0843baa089f7cd2937f8e1a563baa298024815a5 100644 (file)
@@ -420,8 +420,17 @@ protected:
   CXXInstanceCall(const CXXInstanceCall &Other) : SimpleCall(Other) {}
 
 public:
+  /// \brief Returns the expression representing the implicit 'this' object.
+  virtual const Expr *getCXXThisExpr() const = 0;
+
   /// \brief Returns the value of the implicit 'this' object.
-  virtual SVal getCXXThisVal() const = 0;
+  SVal getCXXThisVal() const {
+    const Expr *Base = getCXXThisExpr();
+    // FIXME: This doesn't handle an overloaded ->* operator.
+    if (!Base)
+      return UnknownVal();
+    return getSVal(Base);
+  }
 
   virtual const Decl *getRuntimeDefinition() const;
 
@@ -453,7 +462,7 @@ public:
     return cast<CXXMemberCallExpr>(SimpleCall::getOriginExpr());
   }
 
-  virtual SVal getCXXThisVal() const;
+  virtual const Expr *getCXXThisExpr() const;
 
   virtual Kind getKind() const { return CE_CXXMember; }
 
@@ -492,7 +501,7 @@ public:
     return getOriginExpr()->getArg(Index + 1);
   }
 
-  virtual SVal getCXXThisVal() const;
+  virtual const Expr *getCXXThisExpr() const;
 
   virtual Kind getKind() const { return CE_CXXMemberOperator; }
 
index 439b92064f81277393a109d2d32f25da481a2475..70b6241dea07b214bfddb98490c8552b30cf3180 100644 (file)
@@ -51,7 +51,7 @@ private:
                                  bool IsFirstArgument, bool checkUninitFields,
                                  const CallEvent &Call, OwningPtr<BugType> &BT);
 
-  static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
+  static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
   void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
                           ExplodedNode *N) const;
 
@@ -66,15 +66,17 @@ private:
 };
 } // end anonymous namespace
 
-void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
-                                        const CallExpr *CE) {
+void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
+                                        const Expr *BadE) {
   ExplodedNode *N = C.generateSink();
   if (!N)
     return;
 
   BugReport *R = new BugReport(*BT, BT->getName(), N);
-  R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
-                               bugreporter::GetCalleeExpr(N), R));
+  if (BadE) {
+    R->addRange(BadE->getSourceRange());
+    R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, BadE, R));
+  }
   C.EmitReport(R);
 }
 
@@ -227,7 +229,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
     if (!BT_call_undef)
       BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
                                          "uninitalized pointer value"));
-    EmitBadCall(BT_call_undef.get(), C, CE);
+    emitBadCall(BT_call_undef.get(), C, Callee);
     return;
   }
 
@@ -235,7 +237,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
     if (!BT_call_null)
       BT_call_null.reset(
         new BuiltinBug("Called function pointer is null (null dereference)"));
-    EmitBadCall(BT_call_null.get(), C, CE);
+    emitBadCall(BT_call_null.get(), C, Callee);
   }
 }
 
@@ -243,22 +245,20 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
                                          CheckerContext &C) const {
   // If this is a call to a C++ method, check if the callee is null or
   // undefined.
-  // FIXME: Generalize this to CXXInstanceCall once it supports
-  // getCXXThisVal().
-  if (const CXXMemberCall *CC = dyn_cast<CXXMemberCall>(&Call)) {
+  if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
     SVal V = CC->getCXXThisVal();
     if (V.isUndef()) {
       if (!BT_cxx_call_undef)
         BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is "
                                                "uninitialized"));
-      EmitBadCall(BT_cxx_call_undef.get(), C, CC->getOriginExpr());
+      emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
       return;
     }
     if (V.isZeroConstant()) {
       if (!BT_cxx_call_null)
         BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer "
                                               "is null"));
-      EmitBadCall(BT_cxx_call_null.get(), C, CC->getOriginExpr());
+      emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
       return;
     }
   }
index fb00a226a2102874a3635d64cf68c558fe17d494..05147670c11f0e664b9ff6bb31eeffbbc2ae5417 100644 (file)
@@ -419,21 +419,13 @@ void CXXInstanceCall::getInitialStackFrameContents(
 
 
 
-SVal CXXMemberCall::getCXXThisVal() const {
-  const Expr *Base = getOriginExpr()->getImplicitObjectArgument();
-
-  // FIXME: Will eventually need to cope with member pointers.  This is
-  // a limitation in getImplicitObjectArgument().
-  if (!Base)
-    return UnknownVal();
-
-  return getSVal(Base);
+const Expr *CXXMemberCall::getCXXThisExpr() const {
+  return getOriginExpr()->getImplicitObjectArgument();
 }
 
 
-SVal CXXMemberOperatorCall::getCXXThisVal() const {
-  const Expr *Base = getOriginExpr()->getArg(0);
-  return getSVal(Base);
+const Expr *CXXMemberOperatorCall::getCXXThisExpr() const {
+  return getOriginExpr()->getArg(0);
 }
 
 
diff --git a/test/Analysis/method-call-path-notes.cpp b/test/Analysis/method-call-path-notes.cpp
new file mode 100644 (file)
index 0000000..1e17b83
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-output=text -verify %s
+
+// Test warning about null or uninitialized pointer values used as instance member
+// calls.
+class TestInstanceCall {
+public:
+  void foo() {}
+};
+
+void test_ic() {
+  TestInstanceCall *p; // expected-note {{Variable 'p' declared without an initial value}}
+  p->foo(); // expected-warning {{Called C++ object pointer is uninitialized}} expected-note {{Called C++ object pointer is uninitialized}}
+}
+
+void test_ic_null() {
+  TestInstanceCall *p = 0; // expected-note {{Variable 'p' initialized to a null pointer value}}
+  p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note {{Called C++ object pointer is null}}
+}
+
+void test_ic_set_to_null() {
+  TestInstanceCall *p;
+  p = 0; // expected-note {{Null pointer value stored to 'p'}}
+  p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note {{Called C++ object pointer is null}}
+}
+
+void test_ic_null(TestInstanceCall *p) {
+  if (!p) // expected-note {{Taking true branch}}
+    p->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note{{Called C++ object pointer is null}}
+}
+
+void test_ic_member_ptr() {
+  TestInstanceCall *p = 0; // expected-note {{Variable 'p' initialized to a null pointer value}}
+  typedef void (TestInstanceCall::*IC_Ptr)();
+  IC_Ptr bar = &TestInstanceCall::foo;
+  (p->*bar)(); // expected-warning {{Called C++ object pointer is null}} expected-note{{Called C++ object pointer is null}}
+}
index 8e31c8db8c9c5ab92a2844960c91e73d15cf9569..164af5dc3dfe20e2ee81c922bd87c1dda88ce095 100644 (file)
@@ -87,32 +87,3 @@ void rdar11817693::operator=(const rdar11817693& src) {
   operator=(dynamic_cast<const rdar11817693_BaseBase&>(src));
 }
 
-// Test warning about null or uninitialized pointer values used as instance member
-// calls.
-class TestInstanceCall {
-public:
-  void foo() {}
-};
-
-void test_ic() {
-  TestInstanceCall *p;
-  p->foo(); // expected-warning {{Called C++ object pointer is uninitialized}}
-}
-
-void test_ic_null() {
-  TestInstanceCall *p = 0;
-  p->foo(); // expected-warning {{Called C++ object pointer is null}}
-}
-
-void test_ic_null(TestInstanceCall *p) {
-  if (!p)
-    p->foo(); // expected-warning {{Called C++ object pointer is null}}
-}
-
-void test_ic_member_ptr() {
-  TestInstanceCall *p = 0;
-  typedef void (TestInstanceCall::*IC_Ptr)();
-  IC_Ptr bar = &TestInstanceCall::foo;
-  (p->*bar)(); // expected-warning {{Called C++ object pointer is null}}
-}
-