blob: 2401a72741b2b77cf3930c3d0cc707cf1f3fa286 [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_ANALYSIS_CHECKER
#define LLVM_CLANG_ANALYSIS_CHECKER
#include "clang/Analysis/Support/SaveAndRestore.h"
#include "clang/Checker/PathSensitive/GRCoreEngine.h"
#include "clang/Checker/PathSensitive/GRState.h"
#include "clang/Checker/PathSensitive/GRExprEngine.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
//===----------------------------------------------------------------------===//
// Checker interface.
//===----------------------------------------------------------------------===//
namespace clang {
class GRExprEngine;
class CheckerContext {
ExplodedNodeSet &Dst;
GRStmtNodeBuilder &B;
GRExprEngine &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;
bool DoneEvaluating; // FIXME: This is not a permanent API change.
public:
CheckerContext(ExplodedNodeSet &dst, GRStmtNodeBuilder &builder,
GRExprEngine &eng, ExplodedNode *pred,
const void *tag, ProgramPoint::Kind K,
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()) {}
~CheckerContext();
GRExprEngine &getEngine() {
return Eng;
}
AnalysisManager &getAnalysisManager() {
return Eng.getAnalysisManager();
}
ConstraintManager &getConstraintManager() {
return Eng.getConstraintManager();
}
StoreManager &getStoreManager() {
return Eng.getStoreManager();
}
ExplodedNodeSet &getNodeSet() { return Dst; }
GRStmtNodeBuilder &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();
}
ValueManager &getValueManager() {
return Eng.getValueManager();
}
SValuator &getSValuator() {
return Eng.getSValuator();
}
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)))
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 GRStmtNodeBuilder.
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);
}
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 GRExprEngine;
// FIXME: Remove the 'tag' option.
void GR_Visit(ExplodedNodeSet &Dst,
GRStmtNodeBuilder &Builder,
GRExprEngine &Eng,
const Stmt *S,
ExplodedNode *Pred, void *tag, bool isPrevisit) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
isPrevisit ? ProgramPoint::PreStmtKind :
ProgramPoint::PostStmtKind, S);
if (isPrevisit)
_PreVisit(C, S);
else
_PostVisit(C, S);
}
bool GR_EvalNilReceiver(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
GRExprEngine &Eng, const ObjCMessageExpr *ME,
ExplodedNode *Pred, const GRState *state, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
ME, state);
return EvalNilReceiver(C, ME);
}
bool GR_EvalCallExpr(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
GRExprEngine &Eng, const CallExpr *CE,
ExplodedNode *Pred, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
CE);
return EvalCallExpr(C, CE);
}
// FIXME: Remove the 'tag' option.
void GR_VisitBind(ExplodedNodeSet &Dst,
GRStmtNodeBuilder &Builder, GRExprEngine &Eng,
const Stmt *AssignE,
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, StoreE);
assert(isPrevisit && "Only previsit supported for now.");
PreVisitBind(C, AssignE, StoreE, location, val);
}
// FIXME: Remove the 'tag' option.
void GR_VisitLocation(ExplodedNodeSet &Dst,
GRStmtNodeBuilder &Builder,
GRExprEngine &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, S, state);
VisitLocation(C, S, location);
}
void GR_EvalDeadSymbols(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
GRExprEngine &Eng, const Stmt *S, ExplodedNode *Pred,
SymbolReaper &SymReaper, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag,
ProgramPoint::PostPurgeDeadSymbolsKind, S);
EvalDeadSymbols(C, S, 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 *AssignE,
const Stmt *StoreE, SVal location, SVal val) {}
virtual void EvalDeadSymbols(CheckerContext &C, const Stmt *S,
SymbolReaper &SymReaper) {}
virtual void EvalEndPath(GREndPathNodeBuilder &B, void *tag,
GRExprEngine &Eng) {}
virtual void VisitBranchCondition(GRBranchNodeBuilder &Builder,
GRExprEngine &Eng,
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) {
return state;
}
};
} // end clang namespace
#endif