[analyzer] Add generic preCall and postCall checks.

llvm-svn: 159562
This commit is contained in:
Jordan Rose 2012-07-02 19:28:16 +00:00
parent f3c12ac3b1
commit afe7c2c2bf
7 changed files with 197 additions and 28 deletions

View File

@ -150,6 +150,36 @@ public:
}
};
class PreCall {
template <typename CHECKER>
static void _checkCall(void *checker, const CallEvent &msg,
CheckerContext &C) {
((const CHECKER *)checker)->checkPreCall(msg, C);
}
public:
template <typename CHECKER>
static void _register(CHECKER *checker, CheckerManager &mgr) {
mgr._registerForPreCall(
CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>));
}
};
class PostCall {
template <typename CHECKER>
static void _checkCall(void *checker, const CallEvent &msg,
CheckerContext &C) {
((const CHECKER *)checker)->checkPostCall(msg, C);
}
public:
template <typename CHECKER>
static void _register(CHECKER *checker, CheckerManager &mgr) {
mgr._registerForPostCall(
CheckerManager::CheckCallFunc(checker, _checkCall<CHECKER>));
}
};
class Location {
template <typename CHECKER>
static void _checkLocation(void *checker,
@ -372,16 +402,14 @@ template <typename CHECK1, typename CHECK2=check::_VoidCheck,
typename CHECK11=check::_VoidCheck,typename CHECK12=check::_VoidCheck,
typename CHECK13=check::_VoidCheck,typename CHECK14=check::_VoidCheck,
typename CHECK15=check::_VoidCheck,typename CHECK16=check::_VoidCheck,
typename CHECK17=check::_VoidCheck,typename CHECK18=check::_VoidCheck>
typename CHECK17=check::_VoidCheck,typename CHECK18=check::_VoidCheck,
typename CHECK19=check::_VoidCheck,typename CHECK20=check::_VoidCheck,
typename CHECK21=check::_VoidCheck,typename CHECK22=check::_VoidCheck,
typename CHECK23=check::_VoidCheck,typename CHECK24=check::_VoidCheck>
class Checker;
template <>
class Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
check::_VoidCheck, check::_VoidCheck, check::_VoidCheck>
class Checker<check::_VoidCheck>
: public CheckerBase
{
virtual void anchor();
@ -393,19 +421,22 @@ template <typename CHECK1, typename CHECK2, typename CHECK3, typename CHECK4,
typename CHECK5, typename CHECK6, typename CHECK7, typename CHECK8,
typename CHECK9, typename CHECK10,typename CHECK11,typename CHECK12,
typename CHECK13,typename CHECK14,typename CHECK15,typename CHECK16,
typename CHECK17,typename CHECK18>
typename CHECK17,typename CHECK18,typename CHECK19,typename CHECK20,
typename CHECK21,typename CHECK22,typename CHECK23,typename CHECK24>
class Checker
: public CHECK1,
public Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7, CHECK8,
CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,CHECK14,CHECK15,
CHECK16,CHECK17,CHECK18> {
public Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7,
CHECK8, CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,
CHECK14,CHECK15,CHECK16,CHECK17,CHECK18,CHECK19,
CHECK20,CHECK21,CHECK22,CHECK23,CHECK24> {
public:
template <typename CHECKER>
static void _register(CHECKER *checker, CheckerManager &mgr) {
CHECK1::_register(checker, mgr);
Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7, CHECK8,
CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,CHECK14,CHECK15,
CHECK16,CHECK17,CHECK18>::_register(checker, mgr);
Checker<CHECK2, CHECK3, CHECK4, CHECK5, CHECK6, CHECK7,
CHECK8, CHECK9, CHECK10,CHECK11,CHECK12,CHECK13,
CHECK14,CHECK15,CHECK16,CHECK17,CHECK18,CHECK19,
CHECK20,CHECK21,CHECK22,CHECK23,CHECK24>::_register(checker, mgr);
}
};

View File

@ -221,6 +221,23 @@ public:
const ExplodedNodeSet &Src,
const ObjCMethodCall &msg, ExprEngine &Eng);
/// \brief Run checkers for pre-visiting obj-c messages.
void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
const CallEvent &Call, ExprEngine &Eng) {
runCheckersForCallEvent(/*isPreVisit=*/true, Dst, Src, Call, Eng);
}
/// \brief Run checkers for post-visiting obj-c messages.
void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
const CallEvent &Call, ExprEngine &Eng) {
runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng);
}
/// \brief Run checkers for visiting obj-c messages.
void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call, ExprEngine &Eng);
/// \brief Run checkers for load/store of a location.
void runCheckersForLocation(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
@ -340,6 +357,9 @@ public:
typedef CheckerFn<void (const ObjCMethodCall &, CheckerContext &)>
CheckObjCMessageFunc;
typedef CheckerFn<void (const CallEvent &, CheckerContext &)>
CheckCallFunc;
typedef CheckerFn<void (const SVal &location, bool isLoad,
const Stmt *S,
CheckerContext &)>
@ -397,6 +417,9 @@ public:
void _registerForPreObjCMessage(CheckObjCMessageFunc checkfn);
void _registerForPostObjCMessage(CheckObjCMessageFunc checkfn);
void _registerForPreCall(CheckCallFunc checkfn);
void _registerForPostCall(CheckCallFunc checkfn);
void _registerForLocation(CheckLocationFunc checkfn);
void _registerForBind(CheckBindFunc checkfn);
@ -518,6 +541,9 @@ private:
std::vector<CheckObjCMessageFunc> PreObjCMessageCheckers;
std::vector<CheckObjCMessageFunc> PostObjCMessageCheckers;
std::vector<CheckCallFunc> PreCallCheckers;
std::vector<CheckCallFunc> PostCallCheckers;
std::vector<CheckLocationFunc> LocationCheckers;
std::vector<CheckBindFunc> BindCheckers;

View File

@ -37,6 +37,8 @@ class CheckerDocumentation : public Checker< check::PreStmt<DeclStmt>,
check::PostStmt<CallExpr>,
check::PreObjCMessage,
check::PostObjCMessage,
check::PreCall,
check::PostCall,
check::BranchCondition,
check::Location,
check::Bind,
@ -72,15 +74,43 @@ public:
/// which does not include the control flow statements such as IfStmt. The
/// callback can be specialized to be called with any subclass of Stmt.
///
/// check::PostStmt<DeclStmt>
/// check::PostStmt<CallExpr>
void checkPostStmt(const CallExpr *DS, CheckerContext &C) const;
/// \brief Pre-visit the Objective C messages.
/// \brief Pre-visit the Objective C message.
///
/// This will be called before the analyzer core processes the method call.
/// This is called for any action which produces an Objective-C message send,
/// including explicit message syntax and property access. See the subclasses
/// of ObjCMethodCall for more details.
///
/// check::PreObjCMessage
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
/// \brief Post-visit the Objective C messages.
/// \brief Post-visit the Objective C message.
/// \sa checkPreObjCMessage()
///
/// check::PostObjCMessage
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
/// \brief Pre-visit an abstract "call" event.
///
/// This is used for checkers that want to check arguments or attributed
/// behavior for functions and methods no matter how they are being invoked.
///
/// Note that this includes ALL cross-body invocations, so if you want to
/// limit your checks to, say, function calls, you can either test for that
/// or fall back to the explicit callback (i.e. check::PreStmt).
///
/// check::PreCall
void checkPreCall(const CallEvent &Call, CheckerContext &C) const {}
/// \brief Post-visit an abstract "call" event.
/// \sa checkPreObjCMessage()
///
/// check::PostCall
void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
/// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}

View File

@ -25,6 +25,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const {
return !StmtCheckers.empty() ||
!PreObjCMessageCheckers.empty() ||
!PostObjCMessageCheckers.empty() ||
!PreCallCheckers.empty() ||
!PostCallCheckers.empty() ||
!LocationCheckers.empty() ||
!BindCheckers.empty() ||
!EndAnalysisCheckers.empty() ||
@ -216,6 +218,54 @@ void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
expandGraphWithCheckers(C, Dst, Src);
}
namespace {
// FIXME: This has all the same signatures as CheckObjCMessageContext.
// Is there a way we can merge the two?
struct CheckCallContext {
typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
bool IsPreVisit;
const CheckersTy &Checkers;
const CallEvent &Call;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
const CallEvent &call, ExprEngine &eng)
: IsPreVisit(isPreVisit), Checkers(checkers), Call(call), Eng(eng) { }
void runChecker(CheckerManager::CheckCallFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
// FIXME: This will be wrong as soon as we handle any calls without
// associated statements.
ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind
: ProgramPoint::PostStmtKind;
assert(Call.getOriginExpr() && "Calls without stmts not yet handled");
const ProgramPoint &L =
ProgramPoint::getProgramPoint(Call.getOriginExpr(),
K, Pred->getLocationContext(),
checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
checkFn(Call, C);
}
};
}
/// \brief Run checkers for visiting an abstract call event.
void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call,
ExprEngine &Eng) {
CheckCallContext C(isPreVisit,
isPreVisit ? PreCallCheckers
: PostCallCheckers,
Call, Eng);
expandGraphWithCheckers(C, Dst, Src);
}
namespace {
struct CheckLocationContext {
typedef std::vector<CheckerManager::CheckLocationFunc> CheckersTy;
@ -584,6 +634,13 @@ void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
PostObjCMessageCheckers.push_back(checkfn);
}
void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
PreCallCheckers.push_back(checkfn);
}
void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
PostCallCheckers.push_back(checkfn);
}
void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
LocationCheckers.push_back(checkfn);
}

View File

@ -55,14 +55,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNodeSet DstPreVisit;
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit,
Call, *this);
ExplodedNodeSet DstInvalidated;
for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), E = DstPreVisit.end();
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
defaultEvalCall(DstInvalidated, *I, Call);
getCheckerManager().runCheckersForPostStmt(destNodes, DstInvalidated,
CE, *this);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
Call, *this);
getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
}
void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,

View File

@ -359,7 +359,20 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
const SimpleCall &Call) {
getCheckerManager().runCheckersForEvalCall(Dst, Pred, Call, *this);
// Run any pre-call checks using the generic call interface.
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this);
// Actually evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call, and get a callback at
// defaultEvalCall if all of them fail.
ExplodedNodeSet dstCallEvaluated;
getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
Call, *this);
// Finally, run any post-call checks.
getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated,
Call, *this);
}
void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,

View File

@ -148,13 +148,16 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
ExplodedNodeSet dstPrevisit;
getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
msg, *this);
ExplodedNodeSet dstGenericPrevisit;
getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
msg, *this);
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext);
StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext);
for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
DE = dstPrevisit.end(); DI != DE; ++DI) {
for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
bool RaisesException = false;
@ -235,9 +238,13 @@ void ExprEngine::VisitObjCMessage(const ObjCMethodCall &msg,
}
}
ExplodedNodeSet dstPostvisit;
getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, msg, *this);
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
msg, *this);
}
void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
@ -280,7 +287,7 @@ void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
state = msg.invalidateRegions(BlockCount, state);
// And create the new node.
Bldr.generateNode(msg.getOriginExpr(), Pred, state, GenSink);
Bldr.generateNode(currentStmt, Pred, state, GenSink);
assert(Bldr.hasGeneratedNodes());
}