[analyzer] Track null/uninitialized C++ objects used in method calls.

llvm-svn: 161278
This commit is contained in:
Jordan Rose 2012-08-03 23:08:49 +00:00
parent 80880ac7ee
commit 92e1449b55
6 changed files with 64 additions and 57 deletions

View 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

View 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; }

View 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;
}
}

View 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);
}

View File

@ -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}}
}

View 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}}
}