Start partitioning ExprEngine.cpp into separate .cpp files that handle different parts

of the analysis (e.g., analysis of C expressions, analysis of Objective-C expressions, and so on).

llvm-svn: 138194
This commit is contained in:
Ted Kremenek 2011-08-20 06:00:03 +00:00
parent 10e2503177
commit a9ce612fa2
5 changed files with 1202 additions and 1187 deletions

View File

@ -20,7 +20,10 @@ add_clang_library(clangStaticAnalyzerCore
Environment.cpp
ExplodedGraph.cpp
ExprEngine.cpp
ExprEngineC.cpp
ExprEngineCXX.cpp
ExprEngineCallAndReturn.cpp
ExprEngineObjC.cpp
HTMLDiagnostics.cpp
MemRegion.cpp
ObjCMessage.cpp

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,781 @@
//=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- 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 ExprEngine's support for C expressions.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
using llvm::APSInt;
void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ExplodedNodeSet Tmp1;
Expr *LHS = B->getLHS()->IgnoreParens();
Expr *RHS = B->getRHS()->IgnoreParens();
Visit(LHS, Pred, Tmp1);
ExplodedNodeSet Tmp3;
for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) {
SVal LeftV = (*I1)->getState()->getSVal(LHS);
ExplodedNodeSet Tmp2;
Visit(RHS, *I1, Tmp2);
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForPreStmt(CheckedSet, Tmp2, B, *this);
// With both the LHS and RHS evaluated, process the operation itself.
for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end();
I2 != E2; ++I2) {
const ProgramState *state = (*I2)->getState();
SVal RightV = state->getSVal(RHS);
BinaryOperator::Opcode Op = B->getOpcode();
if (Op == BO_Assign) {
// EXPERIMENTAL: "Conjured" symbols.
// FIXME: Handle structs.
if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV))
{
unsigned Count = Builder->getCurrentBlockCount();
RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count);
}
SVal ExprVal = B->isLValue() ? LeftV : RightV;
// Simulate the effects of a "store": bind the value of the RHS
// to the L-Value represented by the LHS.
evalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV);
continue;
}
if (!B->isAssignmentOp()) {
// Process non-assignments except commas or short-circuited
// logical expressions (LAnd and LOr).
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
if (Result.isUnknown()) {
MakeNode(Tmp3, B, *I2, state);
continue;
}
state = state->BindExpr(B, Result);
MakeNode(Tmp3, B, *I2, state);
continue;
}
assert (B->isCompoundAssignmentOp());
switch (Op) {
default:
assert(0 && "Invalid opcode for compound assignment.");
case BO_MulAssign: Op = BO_Mul; break;
case BO_DivAssign: Op = BO_Div; break;
case BO_RemAssign: Op = BO_Rem; break;
case BO_AddAssign: Op = BO_Add; break;
case BO_SubAssign: Op = BO_Sub; break;
case BO_ShlAssign: Op = BO_Shl; break;
case BO_ShrAssign: Op = BO_Shr; break;
case BO_AndAssign: Op = BO_And; break;
case BO_XorAssign: Op = BO_Xor; break;
case BO_OrAssign: Op = BO_Or; break;
}
// Perform a load (the LHS). This performs the checks for
// null dereferences, and so on.
ExplodedNodeSet Tmp4;
SVal location = state->getSVal(LHS);
evalLoad(Tmp4, LHS, *I2, state, location);
for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4;
++I4) {
state = (*I4)->getState();
SVal V = state->getSVal(LHS);
// Get the computation type.
QualType CTy =
cast<CompoundAssignOperator>(B)->getComputationResultType();
CTy = getContext().getCanonicalType(CTy);
QualType CLHSTy =
cast<CompoundAssignOperator>(B)->getComputationLHSType();
CLHSTy = getContext().getCanonicalType(CLHSTy);
QualType LTy = getContext().getCanonicalType(LHS->getType());
// Promote LHS.
V = svalBuilder.evalCast(V, CLHSTy, LTy);
// Compute the result of the operation.
SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
B->getType(), CTy);
// EXPERIMENTAL: "Conjured" symbols.
// FIXME: Handle structs.
SVal LHSVal;
if (Result.isUnknown() ||
!getConstraintManager().canReasonAbout(Result)) {
unsigned Count = Builder->getCurrentBlockCount();
// The symbolic value is actually for the type of the left-hand side
// expression, not the computation type, as this is the value the
// LValue on the LHS will bind to.
LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count);
// However, we need to convert the symbol to the computation type.
Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
}
else {
// The left-hand side may bind to a different value then the
// computation type.
LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
}
// In C++, assignment and compound assignment operators return an
// lvalue.
if (B->isLValue())
state = state->BindExpr(B, location);
else
state = state->BindExpr(B, Result);
evalStore(Tmp3, B, LHS, *I4, state, location, LHSVal);
}
}
}
getCheckerManager().runCheckersForPostStmt(Dst, Tmp3, B, *this);
}
void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
CanQualType T = getContext().getCanonicalType(BE->getType());
SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
Pred->getLocationContext());
ExplodedNodeSet Tmp;
MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V),
ProgramPoint::PostLValueKind);
// FIXME: Move all post/pre visits to ::Visit().
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
}
void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
ExplodedNodeSet dstPreStmt;
getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
if (CastE->getCastKind() == CK_LValueToRValue ||
CastE->getCastKind() == CK_GetObjCProperty) {
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
I!=E; ++I) {
ExplodedNode *subExprNode = *I;
const ProgramState *state = subExprNode->getState();
evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex));
}
return;
}
// All other casts.
QualType T = CastE->getType();
QualType ExTy = Ex->getType();
if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
T = ExCast->getTypeAsWritten();
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
I != E; ++I) {
Pred = *I;
switch (CastE->getCastKind()) {
case CK_LValueToRValue:
assert(false && "LValueToRValue casts handled earlier.");
case CK_GetObjCProperty:
assert(false && "GetObjCProperty casts handled earlier.");
case CK_ToVoid:
Dst.Add(Pred);
continue;
// The analyzer doesn't do anything special with these casts,
// since it understands retain/release semantics already.
case CK_ObjCProduceObject:
case CK_ObjCConsumeObject:
case CK_ObjCReclaimReturnedObject: // Fall-through.
// True no-ops.
case CK_NoOp:
case CK_FunctionToPointerDecay: {
// Copy the SVal of Ex to CastE.
const ProgramState *state = Pred->getState();
SVal V = state->getSVal(Ex);
state = state->BindExpr(CastE, V);
MakeNode(Dst, CastE, Pred, state);
continue;
}
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
case CK_LValueBitCast:
case CK_IntegralCast:
case CK_NullToPointer:
case CK_IntegralToPointer:
case CK_PointerToIntegral:
case CK_PointerToBoolean:
case CK_IntegralToBoolean:
case CK_IntegralToFloating:
case CK_FloatingToIntegral:
case CK_FloatingToBoolean:
case CK_FloatingCast:
case CK_FloatingRealToComplex:
case CK_FloatingComplexToReal:
case CK_FloatingComplexToBoolean:
case CK_FloatingComplexCast:
case CK_FloatingComplexToIntegralComplex:
case CK_IntegralRealToComplex:
case CK_IntegralComplexToReal:
case CK_IntegralComplexToBoolean:
case CK_IntegralComplexCast:
case CK_IntegralComplexToFloatingComplex:
case CK_AnyPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_ObjCObjectLValueCast: {
// Delegate to SValBuilder to process.
const ProgramState *state = Pred->getState();
SVal V = state->getSVal(Ex);
V = svalBuilder.evalCast(V, T, ExTy);
state = state->BindExpr(CastE, V);
MakeNode(Dst, CastE, Pred, state);
continue;
}
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase: {
// For DerivedToBase cast, delegate to the store manager.
const ProgramState *state = Pred->getState();
SVal val = state->getSVal(Ex);
val = getStoreManager().evalDerivedToBase(val, T);
state = state->BindExpr(CastE, val);
MakeNode(Dst, CastE, Pred, state);
continue;
}
// Various C++ casts that are not handled yet.
case CK_Dynamic:
case CK_ToUnion:
case CK_BaseToDerived:
case CK_NullToMemberPointer:
case CK_BaseToDerivedMemberPointer:
case CK_DerivedToBaseMemberPointer:
case CK_UserDefinedConversion:
case CK_ConstructorConversion:
case CK_VectorSplat:
case CK_MemberPointerToBoolean: {
// Recover some path-sensitivty by conjuring a new value.
QualType resultType = CastE->getType();
if (CastE->isLValue())
resultType = getContext().getPointerType(resultType);
SVal result =
svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType,
Builder->getCurrentBlockCount());
const ProgramState *state = Pred->getState()->BindExpr(CastE, result);
MakeNode(Dst, CastE, Pred, state);
continue;
}
}
}
}
void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const InitListExpr *ILE
= cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
const ProgramState *state = Pred->getState();
SVal ILV = state->getSVal(ILE);
const LocationContext *LC = Pred->getLocationContext();
state = state->bindCompoundLiteral(CL, LC, ILV);
if (CL->isLValue())
MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC)));
else
MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV));
}
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// FIXME: static variables may have an initializer, but the second
// time a function is called those values may not be current.
// This may need to be reflected in the CFG.
// Assumption: The CFG has one DeclStmt per Decl.
const Decl *D = *DS->decl_begin();
if (!D || !isa<VarDecl>(D))
return;
// FIXME: all pre/post visits should eventually be handled by ::Visit().
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
const VarDecl *VD = dyn_cast<VarDecl>(D);
for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
I!=E; ++I) {
ExplodedNode *N = *I;
const ProgramState *state = N->getState();
// Decls without InitExpr are not initialized explicitly.
const LocationContext *LC = N->getLocationContext();
if (const Expr *InitEx = VD->getInit()) {
SVal InitVal = state->getSVal(InitEx);
// We bound the temp obj region to the CXXConstructExpr. Now recover
// the lazy compound value when the variable is not a reference.
if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() &&
!VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
assert(isa<nonloc::LazyCompoundVal>(InitVal));
}
// Recover some path-sensitivity if a scalar value evaluated to
// UnknownVal.
if ((InitVal.isUnknown() ||
!getConstraintManager().canReasonAbout(InitVal)) &&
!VD->getType()->isReferenceType()) {
InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx,
Builder->getCurrentBlockCount());
}
evalBind(Dst, DS, N, state,
loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true);
}
else {
MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC)));
}
}
}
void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
assert(B->getOpcode() == BO_LAnd ||
B->getOpcode() == BO_LOr);
const ProgramState *state = Pred->getState();
SVal X = state->getSVal(B);
assert(X.isUndef());
const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
assert(Ex);
if (Ex == B->getRHS()) {
X = state->getSVal(Ex);
// Handle undefined values.
if (X.isUndef()) {
MakeNode(Dst, B, Pred, state->BindExpr(B, X));
return;
}
DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
// We took the RHS. Because the value of the '&&' or '||' expression must
// evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
// or 1. Alternatively, we could take a lazy approach, and calculate this
// value later when necessary. We don't have the machinery in place for
// this right now, and since most logical expressions are used for branches,
// the payoff is not likely to be large. Instead, we do eager evaluation.
if (const ProgramState *newState = state->assume(XD, true))
MakeNode(Dst, B, Pred,
newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType())));
if (const ProgramState *newState = state->assume(XD, false))
MakeNode(Dst, B, Pred,
newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType())));
}
else {
// We took the LHS expression. Depending on whether we are '&&' or
// '||' we know what the value of the expression is via properties of
// the short-circuiting.
X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
B->getType());
MakeNode(Dst, B, Pred, state->BindExpr(B, X));
}
}
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const ProgramState *state = Pred->getState();
QualType T = getContext().getCanonicalType(IE->getType());
unsigned NumInitElements = IE->getNumInits();
if (T->isArrayType() || T->isRecordType() || T->isVectorType()) {
llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
// Handle base case where the initializer has no elements.
// e.g: static int* myArray[] = {};
if (NumInitElements == 0) {
SVal V = svalBuilder.makeCompoundVal(T, vals);
MakeNode(Dst, IE, Pred, state->BindExpr(IE, V));
return;
}
for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
ei = IE->rend(); it != ei; ++it) {
vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals);
}
MakeNode(Dst, IE, Pred,
state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals)));
return;
}
if (Loc::isLocType(T) || T->isIntegerType()) {
assert(IE->getNumInits() == 1);
const Expr *initEx = IE->getInit(0);
MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx)));
return;
}
llvm_unreachable("unprocessed InitListExpr type");
}
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
const Expr *L,
const Expr *R,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const ProgramState *state = Pred->getState();
SVal X = state->getSVal(Ex);
assert (X.isUndef());
const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
assert(SE);
X = state->getSVal(SE);
// Make sure that we invalidate the previous binding.
MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true));
}
void ExprEngine::
VisitOffsetOfExpr(const OffsetOfExpr *OOE,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
Expr::EvalResult Res;
if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) {
const APSInt &IV = Res.Val.getInt();
assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
assert(OOE->getType()->isIntegerType());
assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType());
SVal X = svalBuilder.makeIntVal(IV);
MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X));
return;
}
// FIXME: Handle the case where __builtin_offsetof is not a constant.
Dst.Add(Pred);
}
void ExprEngine::
VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
QualType T = Ex->getTypeOfArgument();
if (Ex->getKind() == UETT_SizeOf) {
if (!T->isIncompleteType() && !T->isConstantSizeType()) {
assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
// FIXME: Add support for VLA type arguments, not just VLA expressions.
// When that happens, we should probably refactor VLASizeChecker's code.
if (Ex->isArgumentType()) {
Dst.Add(Pred);
return;
}
// Get the size by getting the extent of the sub-expression.
// First, visit the sub-expression to find its region.
const Expr *Arg = Ex->getArgumentExpr();
const ProgramState *state = Pred->getState();
const MemRegion *MR = state->getSVal(Arg).getAsRegion();
// If the subexpression can't be resolved to a region, we don't know
// anything about its size. Just leave the state as is and continue.
if (!MR) {
Dst.Add(Pred);
return;
}
// The result is the extent of the VLA.
SVal Extent = cast<SubRegion>(MR)->getExtent(svalBuilder);
MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, Extent));
return;
}
else if (T->getAs<ObjCObjectType>()) {
// Some code tries to take the sizeof an ObjCObjectType, relying that
// the compiler has laid out its representation. Just report Unknown
// for these.
Dst.Add(Pred);
return;
}
}
Expr::EvalResult Result;
Ex->Evaluate(Result, getContext());
CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue());
const ProgramState *state = Pred->getState();
state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(),
Ex->getType()));
MakeNode(Dst, Ex, Pred, state);
}
void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
switch (U->getOpcode()) {
default:
break;
case UO_Real: {
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
// FIXME: We don't have complex SValues yet.
if (Ex->getType()->isAnyComplexType()) {
// Just report "Unknown."
Dst.Add(*I);
continue;
}
// For all other types, UO_Real is an identity operation.
assert (U->getType() == Ex->getType());
const ProgramState *state = (*I)->getState();
MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
}
return;
}
case UO_Imag: {
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
// FIXME: We don't have complex SValues yet.
if (Ex->getType()->isAnyComplexType()) {
// Just report "Unknown."
Dst.Add(*I);
continue;
}
// For all other types, UO_Imag returns 0.
const ProgramState *state = (*I)->getState();
SVal X = svalBuilder.makeZeroVal(Ex->getType());
MakeNode(Dst, U, *I, state->BindExpr(U, X));
}
return;
}
case UO_Plus:
assert(!U->isLValue());
// FALL-THROUGH.
case UO_Deref:
case UO_AddrOf:
case UO_Extension: {
// Unary "+" is a no-op, similar to a parentheses. We still have places
// where it may be a block-level expression, so we need to
// generate an extra node that just propagates the value of the
// subexpression.
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
const ProgramState *state = (*I)->getState();
MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
}
return;
}
case UO_LNot:
case UO_Minus:
case UO_Not: {
assert (!U->isLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
const ProgramState *state = (*I)->getState();
// Get the value of the subexpression.
SVal V = state->getSVal(Ex);
if (V.isUnknownOrUndef()) {
MakeNode(Dst, U, *I, state->BindExpr(U, V));
continue;
}
switch (U->getOpcode()) {
default:
assert(false && "Invalid Opcode.");
break;
case UO_Not:
// FIXME: Do we need to handle promotions?
state = state->BindExpr(U, evalComplement(cast<NonLoc>(V)));
break;
case UO_Minus:
// FIXME: Do we need to handle promotions?
state = state->BindExpr(U, evalMinus(cast<NonLoc>(V)));
break;
case UO_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".
SVal Result;
if (isa<Loc>(V)) {
Loc X = svalBuilder.makeNull();
Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X,
U->getType());
}
else {
nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
U->getType());
}
state = state->BindExpr(U, Result);
break;
}
MakeNode(Dst, U, *I, state);
}
return;
}
}
// Handle ++ and -- (both pre- and post-increment).
assert (U->isIncrementDecrementOp());
ExplodedNodeSet Tmp;
const Expr *Ex = U->getSubExpr()->IgnoreParens();
Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
const ProgramState *state = (*I)->getState();
SVal loc = state->getSVal(Ex);
// Perform a load.
ExplodedNodeSet Tmp2;
evalLoad(Tmp2, Ex, *I, state, loc);
for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) {
state = (*I2)->getState();
SVal V2_untested = state->getSVal(Ex);
// Propagate unknown and undefined values.
if (V2_untested.isUnknownOrUndef()) {
MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested));
continue;
}
DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
// Handle all other values.
BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add
: BO_Sub;
// If the UnaryOperator has non-location type, use its type to create the
// constant value. If the UnaryOperator has location type, create the
// constant with int type and pointer width.
SVal RHS;
if (U->getType()->isAnyPointerType())
RHS = svalBuilder.makeArrayIndex(1);
else
RHS = svalBuilder.makeIntVal(1, U->getType());
SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
// Conjure a new symbol if necessary to recover precision.
if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){
DefinedOrUnknownSVal SymVal =
svalBuilder.getConjuredSymbolVal(NULL, Ex,
Builder->getCurrentBlockCount());
Result = SymVal;
// If the value is a location, ++/-- should always preserve
// non-nullness. Check if the original value was non-null, and if so
// propagate that constraint.
if (Loc::isLocType(U->getType())) {
DefinedOrUnknownSVal Constraint =
svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
if (!state->assume(Constraint, true)) {
// It isn't feasible for the original value to be null.
// Propagate this constraint.
Constraint = svalBuilder.evalEQ(state, SymVal,
svalBuilder.makeZeroVal(U->getType()));
state = state->assume(Constraint, false);
assert(state);
}
}
}
// Since the lvalue-to-rvalue conversion is explicit in the AST,
// we bind an l-value if the operator is prefix and an lvalue (in C++).
if (U->isLValue())
state = state->BindExpr(U, loc);
else
state = state->BindExpr(U, U->isPostfix() ? V2 : Result);
// Perform the store.
evalStore(Dst, NULL, U, *I2, state, loc, Result);
}
}
}

View File

@ -0,0 +1,163 @@
//=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- 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 ExprEngine's support for calls and returns.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
namespace {
// Trait class for recording returned expression in the state.
struct ReturnExpr {
static int TagInt;
typedef const Stmt *data_type;
};
int ReturnExpr::TagInt;
}
void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) {
const ProgramState *state =
B.getState()->enterStackFrame(B.getCalleeContext());
B.generateNode(state);
}
void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
const ProgramState *state = B.getState();
const ExplodedNode *Pred = B.getPredecessor();
const StackFrameContext *calleeCtx =
cast<StackFrameContext>(Pred->getLocationContext());
const Stmt *CE = calleeCtx->getCallSite();
// If the callee returns an expression, bind its value to CallExpr.
const Stmt *ReturnedExpr = state->get<ReturnExpr>();
if (ReturnedExpr) {
SVal RetVal = state->getSVal(ReturnedExpr);
state = state->BindExpr(CE, RetVal);
// Clear the return expr GDM.
state = state->remove<ReturnExpr>();
}
// Bind the constructed object value to CXXConstructExpr.
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
const CXXThisRegion *ThisR =
getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
SVal ThisV = state->getSVal(ThisR);
// Always bind the region to the CXXConstructExpr.
state = state->BindExpr(CCE, ThisV);
}
B.generateNode(state);
}
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
ExplodedNodeSet &dst) {
// Perform the previsit of the CallExpr.
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
// Now evaluate the call itself.
class DefaultEval : public GraphExpander {
ExprEngine &Eng;
const CallExpr *CE;
public:
DefaultEval(ExprEngine &eng, const CallExpr *ce)
: Eng(eng), CE(ce) {}
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
// Should we inline the call?
if (Eng.getAnalysisManager().shouldInlineCall() &&
Eng.InlineCall(Dst, CE, Pred)) {
return;
}
StmtNodeBuilder &Builder = Eng.getBuilder();
assert(&Builder && "StmtNodeBuilder must be defined.");
// Dispatch to the plug-in transfer function.
unsigned oldSize = Dst.size();
SaveOr OldHasGen(Builder.hasGeneratedNode);
// Dispatch to transfer function logic to handle the call itself.
const Expr *Callee = CE->getCallee()->IgnoreParens();
const ProgramState *state = Pred->getState();
SVal L = state->getSVal(Callee);
Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
// Handle the case where no nodes where generated. Auto-generate that
// contains the updated state if we aren't generating sinks.
if (!Builder.BuildSinks && Dst.size() == oldSize &&
!Builder.hasGeneratedNode)
Eng.MakeNode(Dst, CE, Pred, state);
}
};
// Finally, evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call.
ExplodedNodeSet dstCallEvaluated;
DefaultEval defEval(*this, CE);
getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
dstPreVisit,
CE, *this, &defEval);
// Finally, perform the post-condition check of the CallExpr and store
// the created nodes in 'Dst'.
getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
*this);
}
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ExplodedNodeSet Src;
if (const Expr *RetE = RS->getRetValue()) {
// Record the returned expression in the state. It will be used in
// processCallExit to bind the return value to the call expr.
{
static SimpleProgramPointTag tag("ExprEngine: ReturnStmt");
const ProgramState *state = Pred->getState();
state = state->set<ReturnExpr>(RetE);
Pred = Builder->generateNode(RetE, state, Pred, &tag);
}
// We may get a NULL Pred because we generated a cached node.
if (Pred)
Visit(RetE, Pred, Src);
}
else {
Src.Add(Pred);
}
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForPreStmt(CheckedSet, Src, RS, *this);
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I != E; ++I) {
assert(Builder && "StmtNodeBuilder must be defined.");
Pred = *I;
unsigned size = Dst.size();
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
SaveOr OldHasGen(Builder->hasGeneratedNode);
getTF().evalReturn(Dst, *this, *Builder, RS, Pred);
// Handle the case where no nodes where generated.
if (!Builder->BuildSinks && Dst.size() == size &&
!Builder->hasGeneratedNode)
MakeNode(Dst, RS, Pred, Pred->getState());
}
}

View File

@ -0,0 +1,245 @@
//=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- 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 ExprEngine's support for Objective-C expressions.
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const ProgramState *state = Pred->getState();
SVal baseVal = state->getSVal(Ex->getBase());
SVal location = state->getLValue(Ex->getDecl(), baseVal);
ExplodedNodeSet dstIvar;
MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location));
// Perform the post-condition check of the ObjCIvarRefExpr and store
// the created nodes in 'Dst'.
getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
}
void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
}
void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// ObjCForCollectionStmts are processed in two places. This method
// handles the case where an ObjCForCollectionStmt* occurs as one of the
// statements within a basic block. This transfer function does two things:
//
// (1) binds the next container value to 'element'. This creates a new
// node in the ExplodedGraph.
//
// (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
// whether or not the container has any more elements. This value
// will be tested in ProcessBranch. We need to explicitly bind
// this value because a container can contain nil elements.
//
// FIXME: Eventually this logic should actually do dispatches to
// 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
// This will require simulating a temporary NSFastEnumerationState, either
// through an SVal or through the use of MemRegions. This value can
// be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
// terminates we reclaim the temporary (it goes out of scope) and we
// we can test if the SVal is 0 or if the MemRegion is null (depending
// on what approach we take).
//
// For now: simulate (1) by assigning either a symbol or nil if the
// container is empty. Thus this transfer function will by default
// result in state splitting.
const Stmt *elem = S->getElement();
const ProgramState *state = Pred->getState();
SVal elementV;
if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
assert(elemD->getInit() == 0);
elementV = state->getLValue(elemD, Pred->getLocationContext());
}
else {
elementV = state->getSVal(elem);
}
ExplodedNodeSet dstLocation;
evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false);
if (dstLocation.empty())
return;
for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
NE = dstLocation.end(); NI!=NE; ++NI) {
Pred = *NI;
const ProgramState *state = Pred->getState();
// Handle the case where the container still has elements.
SVal TrueV = svalBuilder.makeTruthVal(1);
const ProgramState *hasElems = state->BindExpr(S, TrueV);
// Handle the case where the container has no elements.
SVal FalseV = svalBuilder.makeTruthVal(0);
const ProgramState *noElems = state->BindExpr(S, FalseV);
if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV))
if (const TypedValueRegion *R =
dyn_cast<TypedValueRegion>(MV->getRegion())) {
// FIXME: The proper thing to do is to really iterate over the
// container. We will do this with dispatch logic to the store.
// For now, just 'conjure' up a symbolic value.
QualType T = R->getValueType();
assert(Loc::isLocType(T));
unsigned Count = Builder->getCurrentBlockCount();
SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count);
SVal V = svalBuilder.makeLoc(Sym);
hasElems = hasElems->bindLoc(elementV, V);
// Bind the location to 'nil' on the false branch.
SVal nilV = svalBuilder.makeIntVal(0, T);
noElems = noElems->bindLoc(elementV, nilV);
}
// Create the new nodes.
MakeNode(Dst, S, Pred, hasElems);
MakeNode(Dst, S, Pred, noElems);
}
}
void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// Handle the previsits checks.
ExplodedNodeSet dstPrevisit;
getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
msg, *this);
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
DE = dstPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
bool RaisesException = false;
unsigned oldSize = dstEval.size();
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
SaveOr OldHasGen(Builder->hasGeneratedNode);
if (const Expr *Receiver = msg.getInstanceReceiver()) {
const ProgramState *state = Pred->getState();
SVal recVal = state->getSVal(Receiver);
if (!recVal.isUndef()) {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
const ProgramState *notNilState, *nilState;
llvm::tie(notNilState, nilState) = state->assume(receiverVal);
// There are three cases: can be nil or non-nil, must be nil, must be
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
if (nilState && !notNilState) {
dstEval.insert(Pred);
continue;
}
// Check if the "raise" message was sent.
assert(notNilState);
if (msg.getSelector() == RaiseSel)
RaisesException = true;
// Check if we raise an exception. For now treat these as sinks.
// Eventually we will want to handle exceptions properly.
if (RaisesException)
Builder->BuildSinks = true;
// Dispatch to plug-in transfer function.
evalObjCMessage(dstEval, msg, Pred, notNilState);
}
}
else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
IdentifierInfo* ClsName = Iface->getIdentifier();
Selector S = msg.getSelector();
// Check for special instance methods.
if (!NSExceptionII) {
ASTContext &Ctx = getContext();
NSExceptionII = &Ctx.Idents.get("NSException");
}
if (ClsName == NSExceptionII) {
enum { NUM_RAISE_SELECTORS = 2 };
// Lazily create a cache of the selectors.
if (!NSExceptionInstanceRaiseSelectors) {
ASTContext &Ctx = getContext();
NSExceptionInstanceRaiseSelectors =
new Selector[NUM_RAISE_SELECTORS];
SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
unsigned idx = 0;
// raise:format:
II.push_back(&Ctx.Idents.get("raise"));
II.push_back(&Ctx.Idents.get("format"));
NSExceptionInstanceRaiseSelectors[idx++] =
Ctx.Selectors.getSelector(II.size(), &II[0]);
// raise:format::arguments:
II.push_back(&Ctx.Idents.get("arguments"));
NSExceptionInstanceRaiseSelectors[idx++] =
Ctx.Selectors.getSelector(II.size(), &II[0]);
}
for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
if (S == NSExceptionInstanceRaiseSelectors[i]) {
RaisesException = true;
break;
}
}
// Check if we raise an exception. For now treat these as sinks.
// Eventually we will want to handle exceptions properly.
if (RaisesException)
Builder->BuildSinks = true;
// Dispatch to plug-in transfer function.
evalObjCMessage(dstEval, msg, Pred, Pred->getState());
}
// Handle the case where no nodes where generated. Auto-generate that
// contains the updated state if we aren't generating sinks.
if (!Builder->BuildSinks && dstEval.size() == oldSize &&
!Builder->hasGeneratedNode)
MakeNode(dstEval, msg.getOriginExpr(), Pred, Pred->getState());
}
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
}
void ExprEngine::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
MakeNode(Dst, Ex, Pred, Pred->getState()->BindExpr(Ex, loc::ObjCPropRef(Ex)));
}