forked from OSchip/llvm-project
[analyzer] Track null/uninitialized C++ objects used in method calls.
llvm-svn: 161278
This commit is contained in:
parent
80880ac7ee
commit
92e1449b55
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
|
@ -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}}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue