[analyzer] Replace boolean IsSink parameters with 'generateSink' methods.
Generating a sink is significantly different behavior from generating a normal node, and a simple boolean parameter can be rather opaque. Per offline discussion with Anna, adding new generation methods is the clearest way to communicate intent. No functionality change. llvm-svn: 162215
This commit is contained in:
parent
10ff96ce8c
commit
4b4613cbec
|
@ -144,20 +144,15 @@ public:
|
|||
/// \brief Generates a new transition in the program state graph
|
||||
/// (ExplodedGraph). Uses the default CheckerContext predecessor node.
|
||||
///
|
||||
/// @param State The state of the generated node.
|
||||
/// @param State The state of the generated node. If not specified, the state
|
||||
/// will not be changed, but the new node will have the checker's tag.
|
||||
/// @param Tag The tag is used to uniquely identify the creation site. If no
|
||||
/// tag is specified, a default tag, unique to the given checker,
|
||||
/// will be used. Tags are used to prevent states generated at
|
||||
/// different sites from caching out.
|
||||
ExplodedNode *addTransition(ProgramStateRef State,
|
||||
ExplodedNode *addTransition(ProgramStateRef State = 0,
|
||||
const ProgramPointTag *Tag = 0) {
|
||||
return addTransitionImpl(State, false, 0, Tag);
|
||||
}
|
||||
|
||||
/// \brief Generates a default transition (containing checker tag but no
|
||||
/// checker state changes).
|
||||
ExplodedNode *addTransition() {
|
||||
return addTransition(getState());
|
||||
return addTransitionImpl(State ? State : getState(), false, 0, Tag);
|
||||
}
|
||||
|
||||
/// \brief Generates a new transition with the given predecessor.
|
||||
|
@ -171,15 +166,16 @@ public:
|
|||
/// the given path.
|
||||
ExplodedNode *addTransition(ProgramStateRef State,
|
||||
ExplodedNode *Pred,
|
||||
const ProgramPointTag *Tag = 0,
|
||||
bool IsSink = false) {
|
||||
return addTransitionImpl(State, IsSink, Pred, Tag);
|
||||
const ProgramPointTag *Tag = 0) {
|
||||
return addTransitionImpl(State, false, Pred, Tag);
|
||||
}
|
||||
|
||||
/// \brief Generate a sink node. Generating sink stops exploration of the
|
||||
/// \brief Generate a sink node. Generating a sink stops exploration of the
|
||||
/// given path.
|
||||
ExplodedNode *generateSink(ProgramStateRef state = 0) {
|
||||
return addTransitionImpl(state ? state : getState(), true);
|
||||
ExplodedNode *generateSink(ProgramStateRef State = 0,
|
||||
ExplodedNode *Pred = 0,
|
||||
const ProgramPointTag *Tag = 0) {
|
||||
return addTransitionImpl(State ? State : getState(), true, Pred, Tag);
|
||||
}
|
||||
|
||||
/// \brief Emit the diagnostics report.
|
||||
|
@ -226,9 +222,15 @@ private:
|
|||
return Pred;
|
||||
|
||||
Changed = true;
|
||||
ExplodedNode *node = NB.generateNode(Tag ? Location.withTag(Tag) : Location,
|
||||
State,
|
||||
P ? P : Pred, MarkAsSink);
|
||||
const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location);
|
||||
if (!P)
|
||||
P = Pred;
|
||||
|
||||
ExplodedNode *node;
|
||||
if (MarkAsSink)
|
||||
node = NB.generateSink(LocalLoc, State, P);
|
||||
else
|
||||
node = NB.generateNode(LocalLoc, State, P);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -265,14 +265,21 @@ public:
|
|||
virtual ~NodeBuilder() {}
|
||||
|
||||
/// \brief Generates a node in the ExplodedGraph.
|
||||
///
|
||||
/// When a node is marked as sink, the exploration from the node is stopped -
|
||||
/// the node becomes the last node on the path.
|
||||
ExplodedNode *generateNode(const ProgramPoint &PP,
|
||||
ProgramStateRef State,
|
||||
ExplodedNode *Pred,
|
||||
bool MarkAsSink = false) {
|
||||
return generateNodeImpl(PP, State, Pred, MarkAsSink);
|
||||
ExplodedNode *Pred) {
|
||||
return generateNodeImpl(PP, State, Pred, false);
|
||||
}
|
||||
|
||||
/// \brief Generates a sink in the ExplodedGraph.
|
||||
///
|
||||
/// When a node is marked as sink, the exploration from the node is stopped -
|
||||
/// the node becomes the last node on the path and certain kinds of bugs are
|
||||
/// suppressed.
|
||||
ExplodedNode *generateSink(const ProgramPoint &PP,
|
||||
ProgramStateRef State,
|
||||
ExplodedNode *Pred) {
|
||||
return generateNodeImpl(PP, State, Pred, true);
|
||||
}
|
||||
|
||||
const ExplodedNodeSet &getResults() {
|
||||
|
@ -317,13 +324,18 @@ public:
|
|||
NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet,
|
||||
const NodeBuilderContext &Ctx, ProgramPoint &L)
|
||||
: NodeBuilder(Pred, DstSet, Ctx), Location(L) {}
|
||||
|
||||
ExplodedNode *generateNode(ProgramStateRef State,
|
||||
ExplodedNode *Pred,
|
||||
const ProgramPointTag *Tag = 0,
|
||||
bool MarkAsSink = false) {
|
||||
ProgramPoint LocalLoc = (Tag ? Location.withTag(Tag): Location);
|
||||
const ProgramPointTag *Tag = 0) {
|
||||
const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location);
|
||||
return NodeBuilder::generateNode(LocalLoc, State, Pred);
|
||||
}
|
||||
|
||||
ExplodedNode *N = generateNodeImpl(LocalLoc, State, Pred, MarkAsSink);
|
||||
ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred,
|
||||
const ProgramPointTag *Tag = 0) {
|
||||
const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location);
|
||||
ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred);
|
||||
if (N && N->isSink())
|
||||
sinksGenerated.push_back(N);
|
||||
return N;
|
||||
|
@ -336,7 +348,7 @@ public:
|
|||
|
||||
/// \class StmtNodeBuilder
|
||||
/// \brief This builder class is useful for generating nodes that resulted from
|
||||
/// visiting a statement. The main difference from it's parent NodeBuilder is
|
||||
/// visiting a statement. The main difference from its parent NodeBuilder is
|
||||
/// that it creates a statement specific ProgramPoint.
|
||||
class StmtNodeBuilder: public NodeBuilder {
|
||||
NodeBuilder *EnclosingBldr;
|
||||
|
@ -363,22 +375,27 @@ public:
|
|||
|
||||
virtual ~StmtNodeBuilder();
|
||||
|
||||
using NodeBuilder::generateNode;
|
||||
using NodeBuilder::generateSink;
|
||||
|
||||
ExplodedNode *generateNode(const Stmt *S,
|
||||
ExplodedNode *Pred,
|
||||
ProgramStateRef St,
|
||||
bool MarkAsSink = false,
|
||||
const ProgramPointTag *tag = 0,
|
||||
ProgramPoint::Kind K = ProgramPoint::PostStmtKind){
|
||||
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
|
||||
Pred->getLocationContext(), tag);
|
||||
return generateNodeImpl(L, St, Pred, MarkAsSink);
|
||||
return NodeBuilder::generateNode(L, St, Pred);
|
||||
}
|
||||
|
||||
ExplodedNode *generateNode(const ProgramPoint &PP,
|
||||
ExplodedNode *generateSink(const Stmt *S,
|
||||
ExplodedNode *Pred,
|
||||
ProgramStateRef State,
|
||||
bool MarkAsSink = false) {
|
||||
return generateNodeImpl(PP, State, Pred, MarkAsSink);
|
||||
ProgramStateRef St,
|
||||
const ProgramPointTag *tag = 0,
|
||||
ProgramPoint::Kind K = ProgramPoint::PostStmtKind){
|
||||
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
|
||||
Pred->getLocationContext(), tag);
|
||||
return NodeBuilder::generateSink(L, St, Pred);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -192,8 +192,7 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE,
|
|||
QualType T = CE->getType();
|
||||
if (!T->isVoidType())
|
||||
Res = Eng.getSValBuilder().makeTruthVal(true, T);
|
||||
B.generateNode(CE, predNew, stateNew->BindExpr(CE, LCtx, Res),
|
||||
false, this);
|
||||
B.generateNode(CE, predNew, stateNew->BindExpr(CE, LCtx, Res), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,8 +204,7 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE,
|
|||
if (!T->isVoidType())
|
||||
Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType());
|
||||
StmtNodeBuilder B(N, Dst, Eng.getBuilderContext());
|
||||
B.generateNode(CE, N, stateNotEqual->BindExpr(CE, LCtx, Res),
|
||||
false, this);
|
||||
B.generateNode(CE, N, stateNotEqual->BindExpr(CE, LCtx, Res), this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3432,7 +3432,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
|
|||
V = V ^ RefVal::ErrorOverAutorelease;
|
||||
state = setRefBinding(state, Sym, V);
|
||||
|
||||
ExplodedNode *N = Ctx.addTransition(state, Pred, Tag, /*IsSink=*/true);
|
||||
ExplodedNode *N = Ctx.generateSink(state, Pred, Tag);
|
||||
if (N) {
|
||||
SmallString<128> sbuf;
|
||||
llvm::raw_svector_ostream os(sbuf);
|
||||
|
|
|
@ -277,7 +277,7 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
|
|||
// up. Since no symbols are dead, we can optimize and not clean out
|
||||
// the constraint manager.
|
||||
StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext);
|
||||
Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K);
|
||||
Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, &cleanupTag, K);
|
||||
|
||||
} else {
|
||||
// Call checkers with the non-cleaned state so that they could query the
|
||||
|
@ -309,8 +309,7 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
|
|||
// generate a transition to that state.
|
||||
ProgramStateRef CleanedCheckerSt =
|
||||
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
|
||||
Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false,
|
||||
&cleanupTag, K);
|
||||
Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, &cleanupTag, K);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -525,8 +524,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
case Stmt::SEHExceptStmtClass:
|
||||
case Stmt::LambdaExprClass:
|
||||
case Stmt::SEHFinallyStmtClass: {
|
||||
const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState(),
|
||||
/* sink */ true);
|
||||
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
|
||||
Engine.addAbortedBlock(node, currentBuilderContext->getBlock());
|
||||
break;
|
||||
}
|
||||
|
@ -885,7 +883,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
case Stmt::CXXThrowExprClass:
|
||||
// FIXME: This is not complete. We basically treat @throw as
|
||||
// an abort.
|
||||
Bldr.generateNode(S, Pred, Pred->getState(), /*IsSink=*/true);
|
||||
Bldr.generateSink(S, Pred, Pred->getState());
|
||||
break;
|
||||
|
||||
case Stmt::ReturnStmtClass:
|
||||
|
@ -1033,7 +1031,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
|
|||
if (nodeBuilder.getContext().getCurrentBlockCount() >= AMgr.getMaxVisit()) {
|
||||
static SimpleProgramPointTag tag("ExprEngine : Block count exceeded");
|
||||
const ExplodedNode *Sink =
|
||||
nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
|
||||
nodeBuilder.generateSink(pred->getState(), pred, &tag);
|
||||
|
||||
// Check if we stopped at the top level function or not.
|
||||
// Root node should have the location context of the top most function.
|
||||
|
@ -1417,7 +1415,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
|||
V = UnknownVal();
|
||||
}
|
||||
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0,
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
return;
|
||||
}
|
||||
|
@ -1429,19 +1427,18 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
|||
}
|
||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||
SVal V = svalBuilder.getFunctionPointer(FD);
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0,
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
return;
|
||||
}
|
||||
if (isa<FieldDecl>(D)) {
|
||||
// FIXME: Compute lvalue of fields.
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()),
|
||||
false, 0, ProgramPoint::PostLValueKind);
|
||||
// FIXME: Compute lvalue of field pointers-to-member.
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()), 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
return;
|
||||
}
|
||||
|
||||
assert (false &&
|
||||
"ValueDecl support for this ValueDecl not implemented.");
|
||||
llvm_unreachable("Support for this Decl not implemented.");
|
||||
}
|
||||
|
||||
/// VisitArraySubscriptExpr - Transfer function for array accesses
|
||||
|
@ -1466,8 +1463,8 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
|
|||
state->getSVal(Idx, LCtx),
|
||||
state->getSVal(Base, LCtx));
|
||||
assert(A->isGLValue());
|
||||
Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V),
|
||||
false, 0, ProgramPoint::PostLValueKind);
|
||||
Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1530,7 +1527,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
|
|||
L = UnknownVal();
|
||||
}
|
||||
|
||||
Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0,
|
||||
Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
} else {
|
||||
Bldr.takeNodes(Pred);
|
||||
|
@ -1574,7 +1571,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
|
|||
LocReg = LocRegVal->getRegion();
|
||||
|
||||
const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0);
|
||||
Bldr.generateNode(L, PredI, state, false);
|
||||
Bldr.generateNode(L, state, PredI);
|
||||
}
|
||||
|
||||
Dst.insert(TmpDst);
|
||||
|
@ -1679,8 +1676,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
|
|||
// This is important. We must nuke the old binding.
|
||||
Bldr.generateNode(NodeEx, *NI,
|
||||
state->BindExpr(BoundEx, LCtx, UnknownVal()),
|
||||
false, tag,
|
||||
ProgramPoint::PostLoadKind);
|
||||
tag, ProgramPoint::PostLoadKind);
|
||||
}
|
||||
else {
|
||||
if (LoadTy.isNull())
|
||||
|
@ -1688,7 +1684,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
|
|||
SVal V = state->getSVal(cast<Loc>(location), LoadTy);
|
||||
Bldr.generateNode(NodeEx, *NI,
|
||||
state->bindExprAndLocation(BoundEx, LCtx, location, V),
|
||||
false, tag, ProgramPoint::PostLoadKind);
|
||||
tag, ProgramPoint::PostLoadKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1720,9 +1716,8 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst,
|
|||
// instead "int *p" is noted as
|
||||
// "Variable 'p' initialized to a null pointer value"
|
||||
|
||||
// FIXME: why is 'tag' not used instead of etag?
|
||||
static SimpleProgramPointTag etag("ExprEngine: Location");
|
||||
Bldr.generateNode(NodeEx, Pred, state, false, &etag);
|
||||
static SimpleProgramPointTag tag("ExprEngine: Location");
|
||||
Bldr.generateNode(NodeEx, Pred, state, &tag);
|
||||
}
|
||||
ExplodedNodeSet Tmp;
|
||||
getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad,
|
||||
|
@ -1763,14 +1758,14 @@ void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
|||
if (ProgramStateRef StateTrue = state->assume(*SEV, true)) {
|
||||
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
|
||||
StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val);
|
||||
Bldr.generateNode(Ex, Pred, StateTrue, false, tags.first);
|
||||
Bldr.generateNode(Ex, Pred, StateTrue, tags.first);
|
||||
}
|
||||
|
||||
// Next, assume that the condition is false.
|
||||
if (ProgramStateRef StateFalse = state->assume(*SEV, false)) {
|
||||
SVal Val = svalBuilder.makeIntVal(0U, Ex->getType());
|
||||
StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val);
|
||||
Bldr.generateNode(Ex, Pred, StateFalse, false, tags.second);
|
||||
Bldr.generateNode(Ex, Pred, StateFalse, tags.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,8 +211,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
|||
StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
|
||||
Bldr.generateNode(BE, Pred,
|
||||
State->BindExpr(BE, Pred->getLocationContext(), V),
|
||||
false, 0,
|
||||
ProgramPoint::PostLValueKind);
|
||||
0, ProgramPoint::PostLValueKind);
|
||||
|
||||
// FIXME: Move all post/pre visits to ::Visit().
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
|
||||
|
@ -347,7 +346,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
|
|||
if (T->isReferenceType()) {
|
||||
// A bad_cast exception is thrown if input value is a reference.
|
||||
// Currently, we model this, by generating a sink.
|
||||
Bldr.generateNode(CastE, Pred, state, true);
|
||||
Bldr.generateSink(CastE, Pred, state);
|
||||
continue;
|
||||
} else {
|
||||
// If the cast fails on a pointer, bind to 0.
|
||||
|
|
|
@ -187,7 +187,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
|||
if (Msg->getSelector() == RaiseSel) {
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
Bldr.generateNode(currentStmt, Pred, State, true);
|
||||
Bldr.generateSink(currentStmt, Pred, State);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
|
|||
if (RaisesException) {
|
||||
// If we raise an exception, for now treat it as a sink.
|
||||
// Eventually we will want to handle exceptions properly.
|
||||
Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
|
||||
Bldr.generateSink(currentStmt, Pred, Pred->getState());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue