[analzyer] Migrate CallAndMessageChecker to CheckerV2.

llvm-svn: 126626
This commit is contained in:
Argyrios Kyrtzidis 2011-02-28 01:28:13 +00:00
parent 64fe456a6b
commit 6d6801c5c7
9 changed files with 78 additions and 75 deletions

View File

@ -12,62 +12,51 @@
//
//===----------------------------------------------------------------------===//
#include "InternalChecks.h"
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/CheckerV2.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
using namespace clang;
using namespace ento;
namespace {
class CallAndMessageChecker
: public CheckerVisitor<CallAndMessageChecker> {
BugType *BT_call_null;
BugType *BT_call_undef;
BugType *BT_call_arg;
BugType *BT_msg_undef;
BugType *BT_msg_arg;
BugType *BT_msg_ret;
: public CheckerV2< check::PreStmt<CallExpr>, check::PreObjCMessage > {
mutable llvm::OwningPtr<BugType> BT_call_null;
mutable llvm::OwningPtr<BugType> BT_call_undef;
mutable llvm::OwningPtr<BugType> BT_call_arg;
mutable llvm::OwningPtr<BugType> BT_msg_undef;
mutable llvm::OwningPtr<BugType> BT_msg_arg;
mutable llvm::OwningPtr<BugType> BT_msg_ret;
public:
CallAndMessageChecker() :
BT_call_null(0), BT_call_undef(0), BT_call_arg(0),
BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {}
static void *getTag() {
static int x = 0;
return &x;
}
void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
bool evalNilReceiver(CheckerContext &C, ObjCMessage msg);
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
private:
void PreVisitProcessArgs(CheckerContext &C, CallOrObjCMessage callOrMsg,
const char *BT_desc, BugType *&BT);
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange,
const Expr *argEx, const char *BT_desc, BugType *&BT);
static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg,
const char *BT_desc, llvm::OwningPtr<BugType> &BT);
static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange,
const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT);
void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg,
ExplodedNode *N);
ExplodedNode *N) const;
void HandleNilReceiver(CheckerContext &C, const GRState *state,
ObjCMessage msg);
ObjCMessage msg) const;
void LazyInit_BT(const char *desc, BugType *&BT) {
static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) {
if (!BT)
BT = new BuiltinBug(desc);
BT.reset(new BuiltinBug(desc));
}
};
} // end anonymous namespace
void ento::RegisterCallAndMessageChecker(ExprEngine &Eng) {
Eng.registerCheck(new CallAndMessageChecker());
}
void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
const CallExpr *CE) {
ExplodedNode *N = C.generateSink();
@ -83,7 +72,7 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C,
CallOrObjCMessage callOrMsg,
const char *BT_desc,
BugType *&BT) {
llvm::OwningPtr<BugType> &BT) {
for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i)
if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i),
callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i),
@ -95,7 +84,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V, SourceRange argRange,
const Expr *argEx,
const char *BT_desc,
BugType *&BT) {
llvm::OwningPtr<BugType> &BT) {
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
@ -198,25 +187,25 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
return false;
}
void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
const CallExpr *CE){
void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const{
const Expr *Callee = CE->getCallee()->IgnoreParens();
SVal L = C.getState()->getSVal(Callee);
if (L.isUndef()) {
if (!BT_call_undef)
BT_call_undef =
new BuiltinBug("Called function pointer is an uninitalized pointer value");
EmitBadCall(BT_call_undef, C, CE);
BT_call_undef.reset(new BuiltinBug("Called function pointer is an "
"uninitalized pointer value"));
EmitBadCall(BT_call_undef.get(), C, CE);
return;
}
if (isa<loc::ConcreteInt>(L)) {
if (!BT_call_null)
BT_call_null =
new BuiltinBug("Called function pointer is null (null dereference)");
EmitBadCall(BT_call_null, C, CE);
BT_call_null.reset(
new BuiltinBug("Called function pointer is null (null dereference)"));
EmitBadCall(BT_call_null.get(), C, CE);
}
PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState()),
@ -224,18 +213,19 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C,
BT_call_arg);
}
void CallAndMessageChecker::preVisitObjCMessage(CheckerContext &C,
ObjCMessage msg) {
void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
CheckerContext &C) const {
const GRState *state = C.getState();
// FIXME: Handle 'super'?
if (const Expr *receiver = msg.getInstanceReceiver())
if (state->getSVal(receiver).isUndef()) {
if (const Expr *receiver = msg.getInstanceReceiver()) {
SVal recVal = state->getSVal(receiver);
if (recVal.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
if (!BT_msg_undef)
BT_msg_undef =
new BuiltinBug("Receiver in message expression is an uninitialized value");
BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is "
"an uninitialized value"));
EnhancedBugReport *R =
new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
R->addRange(receiver->getSourceRange());
@ -244,6 +234,19 @@ void CallAndMessageChecker::preVisitObjCMessage(CheckerContext &C,
C.EmitReport(R);
}
return;
} else {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
const GRState *notNilState, *nilState;
llvm::tie(notNilState, nilState) = state->assume(receiverVal);
// Handle receiver must be nil.
if (nilState && !notNilState) {
HandleNilReceiver(C, state, msg);
return;
}
}
}
const char *bugDesc = msg.isPropertySetter() ?
@ -253,20 +256,14 @@ void CallAndMessageChecker::preVisitObjCMessage(CheckerContext &C,
PreVisitProcessArgs(C, CallOrObjCMessage(msg, state), bugDesc, BT_msg_arg);
}
bool CallAndMessageChecker::evalNilReceiver(CheckerContext &C,
ObjCMessage msg) {
HandleNilReceiver(C, C.getState(), msg);
return true; // Nil receiver is not handled elsewhere.
}
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
const ObjCMessage &msg,
ExplodedNode *N) {
ExplodedNode *N) const {
if (!BT_msg_ret)
BT_msg_ret =
BT_msg_ret.reset(
new BuiltinBug("Receiver in message expression is "
"'nil' and returns a garbage value");
"'nil' and returns a garbage value"));
llvm::SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
@ -292,7 +289,7 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
const GRState *state,
ObjCMessage msg) {
ObjCMessage msg) const {
ASTContext &Ctx = C.getASTContext();
// Check the return type of the message expression. A message to nil will
@ -356,3 +353,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
C.addTransition(state);
}
void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
mgr.registerChecker<CallAndMessageChecker>();
}

View File

@ -75,6 +75,10 @@ def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">,
let ParentPackage = Core in {
def CallAndMessageChecker : Checker<"CallAndMsg">,
HelpText<"Check for errors of call and objc message expressions">,
DescFile<"CallAndMessageChecker.cpp">;
def AdjustedReturnValueChecker : Checker<"AdjustRet">,
HelpText<"Check to see if the return value of a function call is different than the caller expects">,
DescFile<"AdjustedReturnValueChecker.cpp">;

View File

@ -321,7 +321,6 @@ static void RegisterInternalChecks(ExprEngine &Eng) {
// object.
// CallAndMessageChecker should be registered before AttrNonNullChecker,
// where we assume arguments are not undefined.
RegisterCallAndMessageChecker(Eng);
RegisterDereferenceChecker(Eng);
}

View File

@ -22,7 +22,6 @@ namespace ento {
class ExprEngine;
// Foundational checks that handle basic semantics.
void RegisterCallAndMessageChecker(ExprEngine &Eng);
void RegisterDereferenceChecker(ExprEngine &Eng);
} // end GR namespace

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=range -analyzer-store=region -verify %s
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=range -analyzer-store=region -verify %s
// <rdar://problem/6888289> - This test case shows that a nil instance
// variable can possibly be initialized by a method.

View File

@ -1,9 +1,9 @@
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin8 %s
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin8 %s
// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple thumbv6-apple-darwin4.0.0-iphoneos -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple thumbv6-apple-darwin4.0.0-iphoneos -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin8 %s
// RUN: %clang_cc1 -triple i386-apple-darwin8 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin8 %s
// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple thumbv6-apple-darwin4.0.0-iphoneos -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s 2>&1 | FileCheck -check-prefix=darwin9 %s
// RUN: %clang_cc1 -triple thumbv6-apple-darwin4.0.0-iphoneos -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s 2>&1 | FileCheck -check-prefix=darwin9 %s
@interface MyClass {}
- (void *)voidPtrM;

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s -verify
// RUN: %clang_cc1 -analyze -analyzer-checker=core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s -verify
// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=basic %s -verify
// RUN: %clang_cc1 -analyze -analyzer-checker=core,core.experimental -analyzer-check-objc-mem -analyzer-constraints=basic -analyzer-store=region %s -verify
typedef struct Foo { int x; } Bar;

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=basic -verify %s
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=basic -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-check-objc-mem -analyzer-store=region -verify %s
//===----------------------------------------------------------------------===//
// The following code is reduced using delta-debugging from

View File

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -analyze -verify -analyzer-store=basic -analyzer-check-objc-mem %s
// RUN: %clang_cc1 -analyze -verify -analyzer-store=region -analyzer-check-objc-mem %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -analyzer-store=basic -analyzer-check-objc-mem %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -analyzer-store=region -analyzer-check-objc-mem %s
// Delta-Debugging reduced preamble.
typedef signed char BOOL;