blob: a536955c958ad3bed4db4cfcfefd677be55ad497 [file] [log] [blame]
//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines Checker and CheckerVisitor, classes used for creating
// domain-specific checks.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_GR_CHECKER
#define LLVM_CLANG_GR_CHECKER
#include "clang/Analysis/Support/SaveAndRestore.h"
#include "clang/EntoSA/PathSensitive/ExprEngine.h"
//===----------------------------------------------------------------------===//
// Checker interface.
//===----------------------------------------------------------------------===//
namespace clang {
namespace ento {
class CheckerContext {
ExplodedNodeSet &Dst;
StmtNodeBuilder &B;
ExprEngine &Eng;
ExplodedNode *Pred;
SaveAndRestore<bool> OldSink;
SaveAndRestore<const void*> OldTag;
SaveAndRestore<ProgramPoint::Kind> OldPointKind;
SaveOr OldHasGen;
const GRState *ST;
const Stmt *statement;
const unsigned size;
public:
bool *respondsToCallback;
public:
CheckerContext(ExplodedNodeSet &dst, StmtNodeBuilder &builder,
ExprEngine &eng, ExplodedNode *pred,
const void *tag, ProgramPoint::Kind K,
bool *respondsToCB = 0,
const Stmt *stmt = 0, const GRState *st = 0)
: Dst(dst), B(builder), Eng(eng), Pred(pred),
OldSink(B.BuildSinks),
OldTag(B.Tag, tag),
OldPointKind(B.PointKind, K),
OldHasGen(B.HasGeneratedNode),
ST(st), statement(stmt), size(Dst.size()),
respondsToCallback(respondsToCB) {}
~CheckerContext();
ExprEngine &getEngine() {
return Eng;
}
AnalysisManager &getAnalysisManager() {
return Eng.getAnalysisManager();
}
ConstraintManager &getConstraintManager() {
return Eng.getConstraintManager();
}
StoreManager &getStoreManager() {
return Eng.getStoreManager();
}
ExplodedNodeSet &getNodeSet() { return Dst; }
StmtNodeBuilder &getNodeBuilder() { return B; }
ExplodedNode *&getPredecessor() { return Pred; }
const GRState *getState() { return ST ? ST : B.GetState(Pred); }
ASTContext &getASTContext() {
return Eng.getContext();
}
BugReporter &getBugReporter() {
return Eng.getBugReporter();
}
SourceManager &getSourceManager() {
return getBugReporter().getSourceManager();
}
SValBuilder &getSValBuilder() {
return Eng.getSValBuilder();
}
ExplodedNode *generateNode(bool autoTransition = true) {
assert(statement && "Only transitions with statements currently supported");
ExplodedNode *N = generateNodeImpl(statement, getState(), false);
if (N && autoTransition)
Dst.Add(N);
return N;
}
ExplodedNode *generateNode(const Stmt *stmt, const GRState *state,
bool autoTransition = true) {
assert(state);
ExplodedNode *N = generateNodeImpl(stmt, state, false);
if (N && autoTransition)
addTransition(N);
return N;
}
ExplodedNode *generateNode(const GRState *state, ExplodedNode *pred,
bool autoTransition = true) {
assert(statement && "Only transitions with statements currently supported");
ExplodedNode *N = generateNodeImpl(statement, state, pred, false);
if (N && autoTransition)
addTransition(N);
return N;
}
ExplodedNode *generateNode(const GRState *state, bool autoTransition = true) {
assert(statement && "Only transitions with statements currently supported");
ExplodedNode *N = generateNodeImpl(statement, state, false);
if (N && autoTransition)
addTransition(N);
return N;
}
ExplodedNode *generateSink(const Stmt *stmt, const GRState *state = 0) {
return generateNodeImpl(stmt, state ? state : getState(), true);
}
ExplodedNode *generateSink(const GRState *state = 0) {
assert(statement && "Only transitions with statements currently supported");
return generateNodeImpl(statement, state ? state : getState(), true);
}
void addTransition(ExplodedNode *node) {
Dst.Add(node);
}
void addTransition(const GRState *state) {
assert(state);
// If the 'state' is not new, we need to check if the cached state 'ST'
// is new.
if (state != getState() || (ST && ST != B.GetState(Pred)))
// state is new or equals to ST.
generateNode(state, true);
else
Dst.Add(Pred);
}
// Generate a node with a new program point different from the one that will
// be created by the StmtNodeBuilder.
void addTransition(const GRState *state, ProgramPoint Loc) {
ExplodedNode *N = B.generateNode(Loc, state, Pred);
if (N)
addTransition(N);
}
void EmitReport(BugReport *R) {
Eng.getBugReporter().EmitReport(R);
}
AnalysisContext *getCurrentAnalysisContext() const {
return Pred->getLocationContext()->getAnalysisContext();
}
private:
ExplodedNode *generateNodeImpl(const Stmt* stmt, const GRState *state,
bool markAsSink) {
ExplodedNode *node = B.generateNode(stmt, state, Pred);
if (markAsSink && node)
node->markAsSink();
return node;
}
ExplodedNode *generateNodeImpl(const Stmt* stmt, const GRState *state,
ExplodedNode *pred, bool markAsSink) {
ExplodedNode *node = B.generateNode(stmt, state, pred);
if (markAsSink && node)
node->markAsSink();
return node;
}
};
class Checker {
private:
friend class ExprEngine;
// FIXME: Remove the 'tag' option.
void GR_Visit(ExplodedNodeSet &Dst,
StmtNodeBuilder &Builder,
ExprEngine &Eng,
const Stmt *S,
ExplodedNode *Pred, void *tag, bool isPrevisit,
bool& respondsToCallback) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
isPrevisit ? ProgramPoint::PreStmtKind :
ProgramPoint::PostStmtKind, &respondsToCallback, S);
if (isPrevisit)
_PreVisit(C, S);
else
_PostVisit(C, S);
}
bool GR_evalNilReceiver(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder,
ExprEngine &Eng, const ObjCMessageExpr *ME,
ExplodedNode *Pred, const GRState *state, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
0, ME, state);
return evalNilReceiver(C, ME);
}
bool GR_evalCallExpr(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder,
ExprEngine &Eng, const CallExpr *CE,
ExplodedNode *Pred, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
0, CE);
return evalCallExpr(C, CE);
}
// FIXME: Remove the 'tag' option.
void GR_VisitBind(ExplodedNodeSet &Dst,
StmtNodeBuilder &Builder, ExprEngine &Eng,
const Stmt *StoreE, ExplodedNode *Pred, void *tag,
SVal location, SVal val,
bool isPrevisit) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
isPrevisit ? ProgramPoint::PreStmtKind :
ProgramPoint::PostStmtKind, 0, StoreE);
assert(isPrevisit && "Only previsit supported for now.");
PreVisitBind(C, StoreE, location, val);
}
// FIXME: Remove the 'tag' option.
void GR_visitLocation(ExplodedNodeSet &Dst,
StmtNodeBuilder &Builder,
ExprEngine &Eng,
const Stmt *S,
ExplodedNode *Pred, const GRState *state,
SVal location,
void *tag, bool isLoad) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
isLoad ? ProgramPoint::PreLoadKind :
ProgramPoint::PreStoreKind, 0, S, state);
visitLocation(C, S, location);
}
void GR_evalDeadSymbols(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder,
ExprEngine &Eng, const Stmt *S, ExplodedNode *Pred,
SymbolReaper &SymReaper, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
ProgramPoint::PostPurgeDeadSymbolsKind, 0, S);
evalDeadSymbols(C, SymReaper);
}
public:
virtual ~Checker();
virtual void _PreVisit(CheckerContext &C, const Stmt *S) {}
virtual void _PostVisit(CheckerContext &C, const Stmt *S) {}
virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location) {}
virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE,
SVal location, SVal val) {}
virtual void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper) {}
virtual void evalEndPath(EndPathNodeBuilder &B, void *tag,
ExprEngine &Eng) {}
virtual void MarkLiveSymbols(const GRState *state, SymbolReaper &SymReaper) {}
virtual void VisitBranchCondition(BranchNodeBuilder &Builder,
ExprEngine &Eng,
const Stmt *Condition, void *tag) {}
virtual bool evalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) {
return false;
}
virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE) {
return false;
}
virtual const GRState *evalAssume(const GRState *state, SVal Cond,
bool Assumption, bool *respondsToCallback) {
*respondsToCallback = false;
return state;
}
virtual bool WantsRegionChangeUpdate(const GRState *state) { return false; }
virtual const GRState *EvalRegionChanges(const GRState *state,
const MemRegion * const *Begin,
const MemRegion * const *End,
bool *respondsToCallback) {
*respondsToCallback = false;
return state;
}
virtual void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B,
ExprEngine &Eng) {}
};
} // end GR namespace
} // end clang namespace
#endif