diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-08-03 23:08:49 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-08-03 23:08:49 +0000 |
commit | 9da59a67a27a4d3fc9d59552f07808a32f85e9d3 (patch) | |
tree | 4be5d7b80e8b3e2a8e435b4e80b5a56682947c28 | |
parent | 7ad4848d4744b8d60289f3e359250cebdaaf7114 (diff) |
[analyzer] Track null/uninitialized C++ objects used in method calls.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161278 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h | 1 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h | 15 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 24 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 16 | ||||
-rw-r--r-- | test/Analysis/method-call-path-notes.cpp | 36 | ||||
-rw-r--r-- | test/Analysis/misc-ps-cxx0x.cpp | 29 |
6 files changed, 64 insertions, 57 deletions
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index 7e665ceda5..3e62a920b7 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -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 diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 8e50833a3e..0843baa089 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -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; } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 439b92064f..70b6241dea 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -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; } } diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index fb00a226a2..05147670c1 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -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 index 0000000000..1e17b838cc --- /dev/null +++ b/test/Analysis/method-call-path-notes.cpp @@ -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}} +} diff --git a/test/Analysis/misc-ps-cxx0x.cpp b/test/Analysis/misc-ps-cxx0x.cpp index 8e31c8db8c..164af5dc3d 100644 --- a/test/Analysis/misc-ps-cxx0x.cpp +++ b/test/Analysis/misc-ps-cxx0x.cpp @@ -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}} -} - |