[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:
Jordan Rose 2012-08-20 18:43:42 +00:00
parent 10ff96ce8c
commit 4b4613cbec
7 changed files with 83 additions and 72 deletions

View File

@ -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;
}
};

View File

@ -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);
}
};

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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.

View File

@ -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;
}