Newer
Older
//=-- GRExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- 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 a meta-engine for path-sensitive dataflow analysis that
// is built on GREngine, but provides the boilerplate to execute transfer
// functions and build the ExplodedGraph at the expression level.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Analysis/PathSensitive/GRTransferFuncs.h"
#include "llvm/Support/Streams.h"
using namespace clang;
using llvm::dyn_cast;
using llvm::cast;
using llvm::APSInt;
Ted Kremenek
committed
GRExprEngine::StateTy
GRExprEngine::SetValue(StateTy St, Expr* S, const RValue& V) {
if (!StateCleaned) {
St = RemoveDeadBindings(CurrentStmt, St);
StateCleaned = true;
}
bool isBlkExpr = false;
if (S == CurrentStmt) {
isBlkExpr = getCFG().isBlkExpr(S);
if (!isBlkExpr)
return St;
}
return StateMgr.SetValue(St, S, isBlkExpr, V);
}
const GRExprEngine::StateTy::BufferTy&
GRExprEngine::SetValue(StateTy St, Expr* S, const RValue::BufferTy& RB,
StateTy::BufferTy& RetBuf) {
assert (RetBuf.empty());
for (RValue::BufferTy::const_iterator I=RB.begin(), E=RB.end(); I!=E; ++I)
RetBuf.push_back(SetValue(St, S, *I));
return RetBuf;
}
GRExprEngine::StateTy
GRExprEngine::SetValue(StateTy St, const LValue& LV, const RValue& V) {
return St;
if (!StateCleaned) {
St = RemoveDeadBindings(CurrentStmt, St);
StateCleaned = true;
}
return StateMgr.SetValue(St, LV, V);
}
void GRExprEngine::ProcessBranch(Expr* Condition, Stmt* Term,
BranchNodeBuilder& builder) {
// Remove old bindings for subexpressions.
StateTy PrevState = StateMgr.RemoveSubExprBindings(builder.getState());
Ted Kremenek
committed
// Check for NULL conditions; e.g. "for(;;)"
if (!Condition) {
builder.markInfeasible(false);
// Get the current block counter.
GRBlockCounter BC = builder.getBlockCounter();
unsigned BlockID = builder.getTargetBlock(true)->getBlockID();
unsigned NumVisited = BC.getNumVisited(BlockID);
if (NumVisited < 1) builder.generateNode(PrevState, true);
else builder.markInfeasible(true);
return;
}
RValue V = GetValue(PrevState, Condition);
switch (V.getBaseKind()) {
default:
break;
builder.generateNode(PrevState, true);
builder.generateNode(PrevState, false);
return;
case RValue::UninitializedKind: {
NodeTy* N = builder.generateNode(PrevState, true);
if (N) {
N->markAsSink();
UninitBranches.insert(N);
}
builder.markInfeasible(false);
return;
}
}
Ted Kremenek
committed
// Get the current block counter.
GRBlockCounter BC = builder.getBlockCounter();
unsigned BlockID = builder.getTargetBlock(true)->getBlockID();
unsigned NumVisited = BC.getNumVisited(BlockID);
if (isa<nonlval::ConcreteInt>(V) ||
BC.getNumVisited(builder.getTargetBlock(true)->getBlockID()) < 1) {
// Process the true branch.
bool isFeasible = true;
StateTy St = Assume(PrevState, V, true, isFeasible);
if (isFeasible)
builder.generateNode(St, true);
else
builder.markInfeasible(true);
else
builder.markInfeasible(true);
BlockID = builder.getTargetBlock(false)->getBlockID();
NumVisited = BC.getNumVisited(BlockID);
if (isa<nonlval::ConcreteInt>(V) ||
BC.getNumVisited(builder.getTargetBlock(false)->getBlockID()) < 1) {
// Process the false branch.
bool isFeasible = false;
StateTy St = Assume(PrevState, V, false, isFeasible);
if (isFeasible)
builder.generateNode(St, false);
else
builder.markInfeasible(false);
}
else
builder.markInfeasible(false);
}
/// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump.
void GRExprEngine::ProcessIndirectGoto(IndirectGotoNodeBuilder& builder) {
StateTy St = builder.getState();
LValue V = cast<LValue>(GetValue(St, builder.getTarget()));
// Three possibilities:
//
// (1) We know the computed label.
// (2) The label is NULL (or some other constant), or Uninitialized.
// (3) We have no clue about the label. Dispatch to all targets.
//
typedef IndirectGotoNodeBuilder::iterator iterator;
if (isa<lval::GotoLabel>(V)) {
LabelStmt* L = cast<lval::GotoLabel>(V).getLabel();
for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) {
if (I.getLabel() == L) {
builder.generateNode(I, St);
return;
}
}
assert (false && "No block with label.");
return;
}
if (isa<lval::ConcreteInt>(V) || isa<UninitializedVal>(V)) {
// Dispatch to the first target and mark it as a sink.
NodeTy* N = builder.generateNode(builder.begin(), St, true);
UninitBranches.insert(N);
return;
}
// This is really a catch-all. We don't support symbolics yet.
assert (isa<UnknownVal>(V));
for (iterator I=builder.begin(), E=builder.end(); I != E; ++I)
/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
void GRExprEngine::ProcessSwitch(SwitchNodeBuilder& builder) {
typedef SwitchNodeBuilder::iterator iterator;
StateTy St = builder.getState();
Expr* CondE = builder.getCondition();
NonLValue CondV = cast<NonLValue>(GetValue(St, CondE));
if (isa<UninitializedVal>(CondV)) {
NodeTy* N = builder.generateDefaultCaseNode(St, true);
UninitBranches.insert(N);
return;
}
StateTy DefaultSt = St;
// While most of this can be assumed (such as the signedness), having it
// just computed makes sure everything makes the same assumptions end-to-end.
unsigned bits = getContext().getTypeSize(CondE->getType(),
CondE->getExprLoc());
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
APSInt V1(bits, false);
APSInt V2 = V1;
for (iterator I=builder.begin(), E=builder.end(); I!=E; ++I) {
CaseStmt* Case = cast<CaseStmt>(I.getCase());
// Evaluate the case.
if (!Case->getLHS()->isIntegerConstantExpr(V1, getContext(), 0, true)) {
assert (false && "Case condition must evaluate to an integer constant.");
return;
}
// Get the RHS of the case, if it exists.
if (Expr* E = Case->getRHS()) {
if (!E->isIntegerConstantExpr(V2, getContext(), 0, true)) {
assert (false &&
"Case condition (RHS) must evaluate to an integer constant.");
return ;
}
assert (V1 <= V2);
}
else V2 = V1;
// FIXME: Eventually we should replace the logic below with a range
// comparison, rather than concretize the values within the range.
// This should be easy once we have "ranges" for NonLValues.
do {
nonlval::ConcreteInt CaseVal(ValMgr.getValue(V1));
NonLValue Res = EvalBinaryOp(BinaryOperator::EQ, CondV, CaseVal);
// Now "assume" that the case matches.
bool isFeasible = false;
StateTy StNew = Assume(St, Res, true, isFeasible);
if (isFeasible) {
builder.generateCaseStmtNode(I, StNew);
// If CondV evaluates to a constant, then we know that this
// is the *only* case that we can take, so stop evaluating the
// others.
if (isa<nonlval::ConcreteInt>(CondV))
return;
}
// Now "assume" that the case doesn't match. Add this state
// to the default state (if it is feasible).
StNew = Assume(DefaultSt, Res, false, isFeasible);
if (isFeasible)
DefaultSt = StNew;
// Concretize the next value in the range.
++V1;
} while (V1 < V2);
}
// If we reach here, than we know that the default branch is
// possible.
builder.generateDefaultCaseNode(DefaultSt);
}
void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred,
NodeSet& Dst) {
bool hasR2;
StateTy PrevState = Pred->getState();
RValue R1 = GetValue(PrevState, B->getLHS());
RValue R2 = GetValue(PrevState, B->getRHS(), hasR2);
if (hasR2) {
if (isa<UninitializedVal>(R2) || isa<UnknownVal>(R2)) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, R2));
return;
}
}
else if (isa<UninitializedVal>(R1) || isa<UnknownVal>(R1)) {
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
Nodify(Dst, B, Pred, SetValue(PrevState, B, R1));
return;
}
// R1 is an expression that can evaluate to either 'true' or 'false'.
if (B->getOpcode() == BinaryOperator::LAnd) {
// hasR2 == 'false' means that LHS evaluated to 'false' and that
// we short-circuited, leading to a value of '0' for the '&&' expression.
if (hasR2 == false) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(0U, B)));
return;
}
}
else {
assert (B->getOpcode() == BinaryOperator::LOr);
// hasR2 == 'false' means that the LHS evaluate to 'true' and that
// we short-circuited, leading to a value of '1' for the '||' expression.
if (hasR2 == false) {
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(1U, B)));
return;
}
}
// If we reach here we did not short-circuit. Assume R2 == true and
// R2 == false.
bool isFeasible;
StateTy St = Assume(PrevState, R2, true, isFeasible);
if (isFeasible)
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(1U, B)));
St = Assume(PrevState, R2, false, isFeasible);
if (isFeasible)
Nodify(Dst, B, Pred, SetValue(PrevState, B, GetRValueConstant(0U, B)));
}
void GRExprEngine::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) {
Builder = &builder;
Ted Kremenek
committed
StmtEntryNode = builder.getLastNode();
CurrentStmt = S;
NodeSet Dst;
StateCleaned = false;
Visit(S, StmtEntryNode, Dst);
// If no nodes were generated, generate a new node that has all the
// dead mappings removed.
if (Dst.size() == 1 && *Dst.begin() == StmtEntryNode) {
StateTy St = RemoveDeadBindings(S, StmtEntryNode->getState());
builder.generateNode(S, St, StmtEntryNode);
}
Ted Kremenek
committed
CurrentStmt = NULL;
StmtEntryNode = NULL;
Builder = NULL;
GRExprEngine::NodeTy*
GRExprEngine::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred, StateTy St) {
Ted Kremenek
committed
// If the state hasn't changed, don't generate a new node.
return NULL;
NodeTy* N = Builder->generateNode(S, St, Pred);
Dst.Add(N);
return N;
}
void GRExprEngine::Nodify(NodeSet& Dst, Stmt* S, NodeTy* Pred,
const StateTy::BufferTy& SB) {
for (StateTy::BufferTy::const_iterator I=SB.begin(), E=SB.end(); I!=E; ++I)
Nodify(Dst, S, Pred, *I);
}
void GRExprEngine::VisitDeclRefExpr(DeclRefExpr* D, NodeTy* Pred, NodeSet& Dst){
if (D != CurrentStmt) {
Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
return;
}
// If we are here, we are loading the value of the decl and binding
// it to the block-level expression.
StateTy St = Pred->getState();
Nodify(Dst, D, Pred, SetValue(St, D, GetValue(St, D)));
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred,
CallExpr::arg_iterator I, CallExpr::arg_iterator E,
NodeSet& Dst) {
if (I != E) {
NodeSet DstTmp;
Visit(*I, Pred, DstTmp);
++I;
for (NodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI!=DE; ++DI)
VisitCall(CE, *DI, I, E, Dst);
return;
}
// If we reach here we have processed all of the arguments. Evaluate
// the callee expression.
NodeSet DstTmp;
Visit(CE->getCallee(), Pred, DstTmp);
// Finally, evaluate the function call.
for (NodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI!=DE; ++DI) {
StateTy St = (*DI)->getState();
LValue L = GetLValue(St, CE->getCallee());
// Check for uninitialized control-flow.
if (isa<UninitializedVal>(L)) {
NodeTy* N = Builder->generateNode(CE, St, *DI);
N->markAsSink();
UninitBranches.insert(N);
continue;
}
// Note: EvalCall must handle the case where the callee is "UnknownVal."
Nodify(Dst, CE, *DI, EvalCall(CE, (*DI)->getState()));
}
}
void GRExprEngine::VisitCast(Expr* CastE, Expr* E, NodeTy* Pred, NodeSet& Dst) {
NodeSet S1;
Visit(E, Pred, S1);
QualType T = CastE->getType();
// Check for redundant casts or casting to "void"
if (T->isVoidType() ||
E->getType() == T ||
(T->isPointerType() && E->getType()->isFunctionType())) {
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1)
Dst.Add(*I1);
return;
}
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N = *I1;
StateTy St = N->getState();
const RValue& V = GetValue(St, E);
Nodify(Dst, CastE, N, SetValue(St, CastE, EvalCast(ValMgr, V, CastE)));
void GRExprEngine::VisitDeclStmt(DeclStmt* DS, GRExprEngine::NodeTy* Pred,
GRExprEngine::NodeSet& Dst) {
StateTy St = Pred->getState();
for (const ScopedDecl* D = DS->getDecl(); D; D = D->getNextDeclarator())
if (const VarDecl* VD = dyn_cast<VarDecl>(D)) {
// FIXME: Add support for local arrays.
if (VD->getType()->isArrayType())
continue;
St = SetValue(St, lval::DeclVal(VD),
E ? GetValue(St, E) : UninitializedVal());
Nodify(Dst, DS, Pred, St);
if (Dst.empty())
Dst.Add(Pred);
}
void GRExprEngine::VisitGuardedExpr(Expr* S, Expr* LHS, Expr* RHS,
NodeTy* Pred, NodeSet& Dst) {
StateTy St = Pred->getState();
RValue R = GetValue(St, LHS);
if (isa<UnknownVal>(R)) R = GetValue(St, RHS);
Nodify(Dst, S, Pred, SetValue(St, S, R));
}
/// VisitSizeOfAlignOfTypeExpr - Transfer function for sizeof(type).
void GRExprEngine::VisitSizeOfAlignOfTypeExpr(SizeOfAlignOfTypeExpr* S,
NodeTy* Pred,
NodeSet& Dst) {
// 6.5.3.4 sizeof: "The result type is an integer."
QualType T = S->getArgumentType();
// FIXME: Add support for VLAs.
if (!T.getTypePtr()->isConstantSizeType())
return;
SourceLocation L = S->getExprLoc();
uint64_t size = getContext().getTypeSize(T, L) / 8;
Nodify(Dst, S, Pred,
SetValue(Pred->getState(), S,
Ted Kremenek
committed
NonLValue::GetValue(ValMgr, size, S->getType(), L)));
}
void GRExprEngine::VisitDeref(UnaryOperator* U, NodeTy* Pred, NodeSet& Dst) {
Expr* E = U->getSubExpr()->IgnoreParens();
NodeSet DstTmp;
if (!isa<DeclRefExpr>(E))
DstTmp.Add(Pred);
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
Visit(E, Pred, DstTmp);
for (NodeSet::iterator I=DstTmp.begin(), DE=DstTmp.end(); I != DE; ++I) {
NodeTy* N = *I;
StateTy St = N->getState();
// FIXME: Bifurcate when dereferencing a symbolic with no constraints?
LValue L = cast<LValue>(GetValue(St, E));
if (isa<UninitializedVal>(L)) {
NodeTy* Succ = Builder->generateNode(U, St, N);
if (Succ) {
Succ->markAsSink();
UninitDeref.insert(Succ);
}
continue;
}
if (L.isUnknown()) {
Dst.Add(N);
continue;
}
// After a dereference, one of two possible situations arise:
// (1) A crash, because the pointer was NULL.
// (2) The pointer is not NULL, and the dereference works.
//
// We add these assumptions.
bool isFeasibleNotNull;
// "Assume" that the pointer is Not-NULL.
StateTy StNotNull = Assume(St, L, true, isFeasibleNotNull);
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
if (isFeasibleNotNull) {
QualType T = U->getType();
// FIXME: Currently symbolic analysis "generates" new symbols
// for the contents of values. We need a better approach.
Nodify(Dst, U, N, SetValue(StNotNull, U, GetValue(StNotNull, L, &T)));
}
bool isFeasibleNull;
// "Assume" that the pointer is NULL.
StateTy StNull = Assume(St, L, false, isFeasibleNull);
if (isFeasibleNull) {
// We don't use "Nodify" here because the node will be a sink
// and we have no intention of processing it later.
NodeTy* NullNode = Builder->generateNode(U, StNull, N);
if (NullNode) {
NullNode->markAsSink();
if (isFeasibleNotNull)
ImplicitNullDeref.insert(NullNode);
else
ExplicitNullDeref.insert(NullNode);
}
}
}
}
void GRExprEngine::VisitUnaryOperator(UnaryOperator* U,
NodeTy* Pred, NodeSet& Dst) {
NodeSet S1;
assert (U->getOpcode() != UnaryOperator::Deref);
switch (U->getOpcode()) {
case UnaryOperator::PostInc:
case UnaryOperator::PostDec:
case UnaryOperator::PreInc:
case UnaryOperator::PreDec:
case UnaryOperator::AddrOf:
// Evalue subexpression as an LValue.
VisitLValue(U->getSubExpr(), Pred, S1);
break;
case UnaryOperator::SizeOf:
case UnaryOperator::AlignOf:
// Do not evaluate subexpression.
S1.Add(Pred);
break;
default:
Visit(U->getSubExpr(), Pred, S1);
break;
}
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N1 = *I1;
StateTy St = N1->getState();
if (U->isIncrementDecrementOp()) {
// Handle ++ and -- (both pre- and post-increment).
const LValue& L1 = GetLValue(St, U->getSubExpr());
QualType T = U->getType();
RValue R1 = GetValue(St, L1, &T);
BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add
: BinaryOperator::Sub;
RValue Result = EvalBinaryOp(Op, R1, GetRValueConstant(1U, U));
if (U->isPostfix())
Nodify(Dst, U, N1, SetValue(SetValue(St, U, R1), L1, Result));
Nodify(Dst, U, N1, SetValue(SetValue(St, U, Result), L1, Result));
continue;
}
// Handle all other unary operators.
switch (U->getOpcode()) {
case UnaryOperator::Minus: {
const NonLValue& R1 = cast<NonLValue>(GetValue(St, U->getSubExpr()));
Ted Kremenek
committed
Nodify(Dst, U, N1, SetValue(St, U, EvalMinus(ValMgr, U, R1)));
case UnaryOperator::Plus: {
const NonLValue& R1 = cast<NonLValue>(GetValue(St, U->getSubExpr()));
Nodify(Dst, U, N1, SetValue(St, U, EvalPlus(ValMgr, U, R1)));
break;
}
case UnaryOperator::Not: {
const NonLValue& R1 = cast<NonLValue>(GetValue(St, U->getSubExpr()));
Ted Kremenek
committed
Nodify(Dst, U, N1, SetValue(St, U, EvalComplement(ValMgr, R1)));
case UnaryOperator::LNot: {
// C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
//
// Note: technically we do "E == 0", but this is the same in the
// transfer functions as "0 == E".
RValue V1 = GetValue(St, U->getSubExpr());
if (isa<LValue>(V1)) {
const LValue& L1 = cast<LValue>(V1);
lval::ConcreteInt V2(ValMgr.getZeroWithPtrWidth());
Nodify(Dst, U, N1,
SetValue(St, U, EvalBinaryOp(BinaryOperator::EQ,
L1, V2)));
}
else {
const NonLValue& R1 = cast<NonLValue>(V1);
nonlval::ConcreteInt V2(ValMgr.getZeroWithPtrWidth());
Nodify(Dst, U, N1,
SetValue(St, U, EvalBinaryOp(BinaryOperator::EQ,
R1, V2)));
}
break;
}
case UnaryOperator::SizeOf: {
// 6.5.3.4 sizeof: "The result type is an integer."
QualType T = U->getSubExpr()->getType();
// FIXME: Add support for VLAs.
if (!T.getTypePtr()->isConstantSizeType())
return;
SourceLocation L = U->getExprLoc();
uint64_t size = getContext().getTypeSize(T, L) / 8;
Nodify(Dst, U, N1,
SetValue(St, U, NonLValue::GetValue(ValMgr, size,
Ted Kremenek
committed
U->getType(), L)));
break;
}
case UnaryOperator::AddrOf: {
const LValue& L1 = GetLValue(St, U->getSubExpr());
Nodify(Dst, U, N1, SetValue(St, U, L1));
break;
}
default: ;
assert (false && "Not implemented.");
}
}
}
void GRExprEngine::VisitLValue(Expr* E, NodeTy* Pred, NodeSet& Dst) {
E = E->IgnoreParens();
if (isa<DeclRefExpr>(E)) {
Dst.Add(Pred);
if (UnaryOperator* U = dyn_cast<UnaryOperator>(E)) {
if (U->getOpcode() == UnaryOperator::Deref) {
E = U->getSubExpr()->IgnoreParens();
if (isa<DeclRefExpr>(E))
Dst.Add(Pred);
else
Visit(E, Pred, Dst);
return;
}
}
Visit(E, Pred, Dst);
}
void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
GRExprEngine::NodeTy* Pred,
GRExprEngine::NodeSet& Dst) {
Ted Kremenek
committed
NodeSet S1;
if (B->isAssignmentOp())
VisitLValue(B->getLHS(), Pred, S1);
else
Visit(B->getLHS(), Pred, S1);
Ted Kremenek
committed
for (NodeSet::iterator I1=S1.begin(), E1=S1.end(); I1 != E1; ++I1) {
NodeTy* N1 = *I1;
Ted Kremenek
committed
// When getting the value for the LHS, check if we are in an assignment.
// In such cases, we want to (initially) treat the LHS as an LValue,
// so we use GetLValue instead of GetValue so that DeclRefExpr's are
// evaluated to LValueDecl's instead of to an NonLValue.
const RValue& V1 =
Ted Kremenek
committed
B->isAssignmentOp() ? GetLValue(N1->getState(), B->getLHS())
: GetValue(N1->getState(), B->getLHS());
Ted Kremenek
committed
NodeSet S2;
Visit(B->getRHS(), N1, S2);
for (NodeSet::iterator I2=S2.begin(), E2=S2.end(); I2 != E2; ++I2) {
Ted Kremenek
committed
NodeTy* N2 = *I2;
StateTy St = N2->getState();
const RValue& V2 = GetValue(St, B->getRHS());
Ted Kremenek
committed
BinaryOperator::Opcode Op = B->getOpcode();
if (Op <= BinaryOperator::Or) {
if (isa<UnknownVal>(V1) || isa<UninitializedVal>(V1)) {
Nodify(Dst, B, N2, SetValue(St, B, V1));
continue;
}
Nodify(Dst, B, N2, SetValue(St, B, EvalBinaryOp(Op, V1, V2)));
continue;
}
switch (Op) {
Ted Kremenek
committed
case BinaryOperator::Assign: {
const LValue& L1 = cast<LValue>(V1);
if (isa<UninitializedVal>(L1))
HandleUninitializedStore(B, N2);
else
Nodify(Dst, B, N2, SetValue(SetValue(St, B, V2), L1, V2));
Ted Kremenek
committed
break;
}
default: { // Compound assignment operators.
assert (B->isCompoundAssignmentOp());
const LValue& L1 = cast<LValue>(V1);
if (isa<UninitializedVal>(L1)) {
HandleUninitializedStore(B, N2);
break;
}
if (isa<UninitializedVal>(V2)) {
Nodify(Dst, B, N2, SetValue(SetValue(St, B, V2), L1, V2));
break;
}
RValue Result = cast<NonLValue>(UnknownVal());
if (Op >= BinaryOperator::AndAssign)
((int&) Op) -= (BinaryOperator::AndAssign - BinaryOperator::And);
else
((int&) Op) -= BinaryOperator::MulAssign;
if (B->getType()->isPointerType()) { // Perform pointer arithmetic.
const NonLValue& R2 = cast<NonLValue>(V2);
Result = EvalBinaryOp(Op, L1, R2);
Ted Kremenek
committed
else if (isa<LValue>(V2)) {
const LValue& L2 = cast<LValue>(V2);
Ted Kremenek
committed
if (B->getRHS()->getType()->isPointerType()) {
// LValue comparison.
Result = EvalBinaryOp(Op, L1, L2);
Ted Kremenek
committed
}
else {
Ted Kremenek
committed
QualType T1 = B->getLHS()->getType();
QualType T2 = B->getRHS()->getType();
Ted Kremenek
committed
// An operation between two variables of a non-lvalue type.
Result =
EvalBinaryOp(Op,
Ted Kremenek
committed
cast<NonLValue>(GetValue(N1->getState(), L1, &T1)),
cast<NonLValue>(GetValue(N2->getState(), L2, &T2)));
Ted Kremenek
committed
}
Ted Kremenek
committed
else { // Any other operation between two Non-LValues.
Ted Kremenek
committed
QualType T = B->getLHS()->getType();
const NonLValue& R1 = cast<NonLValue>(GetValue(N1->getState(),
L1, &T));
const NonLValue& R2 = cast<NonLValue>(V2);
Result = EvalBinaryOp(Op, R1, R2);
Nodify(Dst, B, N2, SetValue(SetValue(St, B, Result), L1, Result));
Ted Kremenek
committed
break;
Ted Kremenek
committed
}
}
}
}
void GRExprEngine::HandleUninitializedStore(Stmt* S, NodeTy* Pred) {
NodeTy* N = Builder->generateNode(S, Pred->getState(), Pred);
N->markAsSink();
UninitStores.insert(N);
}
void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& Dst) {
Ted Kremenek
committed
// FIXME: add metadata to the CFG so that we can disable
// this check when we KNOW that there is no block-level subexpression.
// The motivation is that this check requires a hashtable lookup.
Ted Kremenek
committed
if (S != CurrentStmt && getCFG().isBlkExpr(S)) {
Dst.Add(Pred);
return;
}
switch (S->getStmtClass()) {
default:
// Cases we intentionally have "default" handle:
// AddrLabelExpr
Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
break;
case Stmt::BinaryOperatorClass: {
BinaryOperator* B = cast<BinaryOperator>(S);
if (B->isLogicalOp()) {
VisitLogicalExpr(B, Pred, Dst);
break;
}
else if (B->getOpcode() == BinaryOperator::Comma) {
StateTy St = Pred->getState();
Nodify(Dst, B, Pred, SetValue(St, B, GetValue(St, B->getRHS())));
break;
}
Ted Kremenek
committed
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
break;
}
case Stmt::CallExprClass: {
CallExpr* C = cast<CallExpr>(S);
VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst);
break;
}
case Stmt::CastExprClass: {
CastExpr* C = cast<CastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst);
break;
}
Ted Kremenek
committed
// While explicitly creating a node+state for visiting a CharacterLiteral
// seems wasteful, it also solves a bunch of problems when handling
// the ?, &&, and ||.
case Stmt::CharacterLiteralClass: {
CharacterLiteral* C = cast<CharacterLiteral>(S);
StateTy St = Pred->getState();
NonLValue X = NonLValue::GetValue(ValMgr, C->getValue(), C->getType(),
C->getLoc());
Nodify(Dst, C, Pred, SetValue(St, C, X));
break;
}
case Stmt::ChooseExprClass: { // __builtin_choose_expr
ChooseExpr* C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
case Stmt::CompoundAssignOperatorClass:
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
break;
case Stmt::ConditionalOperatorClass: { // '?' operator
ConditionalOperator* C = cast<ConditionalOperator>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
Ted Kremenek
committed
break;
}
case Stmt::DeclRefExprClass:
VisitDeclRefExpr(cast<DeclRefExpr>(S), Pred, Dst);
break;
Ted Kremenek
committed
case Stmt::DeclStmtClass:
VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst);
break;
// While explicitly creating a node+state for visiting an IntegerLiteral
// seems wasteful, it also solves a bunch of problems when handling
// the ?, &&, and ||.
case Stmt::IntegerLiteralClass: {
StateTy St = Pred->getState();
IntegerLiteral* I = cast<IntegerLiteral>(S);
NonLValue X = NonLValue::GetValue(ValMgr, I);
Nodify(Dst, I, Pred, SetValue(St, I, X));
break;
}
case Stmt::ImplicitCastExprClass: {
ImplicitCastExpr* C = cast<ImplicitCastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst);
break;
}
case Stmt::ParenExprClass:
Visit(cast<ParenExpr>(S)->getSubExpr(), Pred, Dst);
case Stmt::SizeOfAlignOfTypeExprClass:
VisitSizeOfAlignOfTypeExpr(cast<SizeOfAlignOfTypeExpr>(S), Pred, Dst);