summaryrefslogtreecommitdiffstats
path: root/lib/Checker
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2010-09-17 15:54:40 +0000
committerdim <dim@FreeBSD.org>2010-09-17 15:54:40 +0000
commit36c49e3f258dced101949edabd72e9bc3f1dedc4 (patch)
tree0bbe07708f7571f8b5291f6d7b96c102b7c99dee /lib/Checker
parentfc84956ac8b7cd244ef30e7a4d4d38a58dec5904 (diff)
downloadFreeBSD-src-36c49e3f258dced101949edabd72e9bc3f1dedc4.zip
FreeBSD-src-36c49e3f258dced101949edabd72e9bc3f1dedc4.tar.gz
Vendor import of clang r114020 (from the release_28 branch):
http://llvm.org/svn/llvm-project/cfe/branches/release_28@114020 Approved by: rpaulo (mentor)
Diffstat (limited to 'lib/Checker')
-rw-r--r--lib/Checker/AdjustedReturnValueChecker.cpp3
-rw-r--r--lib/Checker/AggExprVisitor.cpp11
-rw-r--r--lib/Checker/AnalysisConsumer.cpp12
-rw-r--r--lib/Checker/AnalysisManager.cpp31
-rw-r--r--lib/Checker/ArrayBoundChecker.cpp2
-rw-r--r--lib/Checker/BasicObjCFoundationChecks.cpp5
-rw-r--r--lib/Checker/BasicStore.cpp63
-rw-r--r--lib/Checker/BasicValueFactory.cpp32
-rw-r--r--lib/Checker/BugReporter.cpp51
-rw-r--r--lib/Checker/BugReporterVisitors.cpp49
-rw-r--r--lib/Checker/CFRefCount.cpp202
-rw-r--r--lib/Checker/CMakeLists.txt4
-rw-r--r--lib/Checker/CStringChecker.cpp594
-rw-r--r--lib/Checker/CallAndMessageChecker.cpp2
-rw-r--r--lib/Checker/CallInliner.cpp54
-rw-r--r--lib/Checker/CastSizeChecker.cpp4
-rw-r--r--lib/Checker/CheckDeadStores.cpp4
-rw-r--r--lib/Checker/CheckSecuritySyntaxOnly.cpp16
-rw-r--r--lib/Checker/CheckerHelpers.cpp80
-rw-r--r--lib/Checker/CocoaConventions.cpp5
-rw-r--r--lib/Checker/DivZeroChecker.cpp8
-rw-r--r--lib/Checker/Environment.cpp41
-rw-r--r--lib/Checker/FixedAddressChecker.cpp2
-rw-r--r--lib/Checker/FlatStore.cpp82
-rw-r--r--lib/Checker/GRCXXExprEngine.cpp34
-rw-r--r--lib/Checker/GRCoreEngine.cpp154
-rw-r--r--lib/Checker/GRExprEngine.cpp678
-rw-r--r--lib/Checker/GRExprEngineExperimentalChecks.cpp16
-rw-r--r--lib/Checker/GRExprEngineExperimentalChecks.h5
-rw-r--r--lib/Checker/GRState.cpp224
-rw-r--r--lib/Checker/IdempotentOperationChecker.cpp673
-rw-r--r--lib/Checker/LLVMConventionsChecker.cpp1
-rw-r--r--lib/Checker/Makefile1
-rw-r--r--lib/Checker/MallocChecker.cpp251
-rw-r--r--lib/Checker/MemRegion.cpp69
-rw-r--r--lib/Checker/OSAtomicChecker.cpp8
-rw-r--r--lib/Checker/PointerArithChecker.cpp3
-rw-r--r--lib/Checker/PointerSubChecker.cpp2
-rw-r--r--lib/Checker/RangeConstraintManager.cpp1
-rw-r--r--lib/Checker/RegionStore.cpp356
-rw-r--r--lib/Checker/ReturnPointerRangeChecker.cpp2
-rw-r--r--lib/Checker/ReturnUndefChecker.cpp1
-rw-r--r--lib/Checker/SVals.cpp7
-rw-r--r--lib/Checker/SValuator.cpp4
-rw-r--r--lib/Checker/SimpleConstraintManager.cpp70
-rw-r--r--lib/Checker/SimpleConstraintManager.h4
-rw-r--r--lib/Checker/SimpleSValuator.cpp284
-rw-r--r--lib/Checker/StackAddrLeakChecker.cpp2
-rw-r--r--lib/Checker/Store.cpp41
-rw-r--r--lib/Checker/StreamChecker.cpp197
-rw-r--r--lib/Checker/SymbolManager.cpp139
-rw-r--r--lib/Checker/UndefBranchChecker.cpp17
-rw-r--r--lib/Checker/UndefinedAssignmentChecker.cpp12
-rw-r--r--lib/Checker/UnixAPIChecker.cpp2
-rw-r--r--lib/Checker/UnreachableCodeChecker.cpp226
-rw-r--r--lib/Checker/VLASizeChecker.cpp2
-rw-r--r--lib/Checker/ValueManager.cpp17
57 files changed, 3348 insertions, 1512 deletions
diff --git a/lib/Checker/AdjustedReturnValueChecker.cpp b/lib/Checker/AdjustedReturnValueChecker.cpp
index b92f2e7..0ed04fb 100644
--- a/lib/Checker/AdjustedReturnValueChecker.cpp
+++ b/lib/Checker/AdjustedReturnValueChecker.cpp
@@ -70,8 +70,7 @@ void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C,
}
else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) {
const BlockTextRegion *BR = BD->getCodeRegion();
- const BlockPointerType *BT =
- BR->getLocationType(C.getASTContext())->getAs<BlockPointerType>();
+ const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>();
const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>();
actualResultTy = FT->getResultType();
}
diff --git a/lib/Checker/AggExprVisitor.cpp b/lib/Checker/AggExprVisitor.cpp
index 343afec..6d472f4 100644
--- a/lib/Checker/AggExprVisitor.cpp
+++ b/lib/Checker/AggExprVisitor.cpp
@@ -18,6 +18,13 @@
using namespace clang;
namespace {
+/// AggExprVisitor is designed after AggExprEmitter of the CodeGen module. It
+/// is used for evaluating exprs of C++ object type. Evaluating such exprs
+/// requires a destination pointer pointing to the object being evaluated
+/// into. Passing such a pointer around would pollute the Visit* interface of
+/// GRExprEngine. AggExprVisitor encapsulates code that goes through various
+/// cast and construct exprs (and others), and at the final point, dispatches
+/// back to the GRExprEngine to let the real evaluation logic happen.
class AggExprVisitor : public StmtVisitor<AggExprVisitor> {
SVal DestPtr;
ExplodedNode *Pred;
@@ -38,8 +45,8 @@ void AggExprVisitor::VisitCastExpr(CastExpr *E) {
switch (E->getCastKind()) {
default:
assert(0 && "Unhandled cast kind");
- case CastExpr::CK_NoOp:
- case CastExpr::CK_ConstructorConversion:
+ case CK_NoOp:
+ case CK_ConstructorConversion:
Visit(E->getSubExpr());
break;
}
diff --git a/lib/Checker/AnalysisConsumer.cpp b/lib/Checker/AnalysisConsumer.cpp
index 524f37e..ad5ccb50 100644
--- a/lib/Checker/AnalysisConsumer.cpp
+++ b/lib/Checker/AnalysisConsumer.cpp
@@ -29,6 +29,7 @@
#include "clang/Checker/PathSensitive/GRTransferFuncs.h"
#include "clang/Checker/PathDiagnosticClients.h"
#include "GRExprEngineExperimentalChecks.h"
+#include "GRExprEngineInternalChecks.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/AnalyzerOptions.h"
@@ -173,10 +174,12 @@ public:
Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
PP.getLangOptions(), PD,
CreateStoreMgr, CreateConstraintMgr,
+ /* Indexer */ 0,
Opts.MaxNodes, Opts.MaxLoop,
Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
Opts.PurgeDead, Opts.EagerlyAssume,
- Opts.TrimGraph, Opts.InlineCall));
+ Opts.TrimGraph, Opts.InlineCall,
+ Opts.UnoptimizedCFG));
}
virtual void HandleTranslationUnit(ASTContext &C);
@@ -341,7 +344,10 @@ static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
if (C.Opts.EnableExperimentalChecks)
RegisterExperimentalChecks(Eng);
- if (C.Opts.EnableIdempotentOperationChecker)
+ // Enable idempotent operation checking if it was explicitly turned on, or if
+ // we are running experimental checks (i.e. everything)
+ if (C.Opts.IdempotentOps || C.Opts.EnableExperimentalChecks
+ || C.Opts.EnableExperimentalInternalChecks)
RegisterIdempotentOperationChecker(Eng);
// Set the graph auditor.
@@ -352,7 +358,7 @@ static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
}
// Execute the worklist algorithm.
- Eng.ExecuteWorkList(mgr.getStackFrame(D), mgr.getMaxNodes());
+ Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes());
// Release the auditor (if any) so that it doesn't monitor the graph
// created BugReporter.
diff --git a/lib/Checker/AnalysisManager.cpp b/lib/Checker/AnalysisManager.cpp
new file mode 100644
index 0000000..339cdab
--- /dev/null
+++ b/lib/Checker/AnalysisManager.cpp
@@ -0,0 +1,31 @@
+//===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/PathSensitive/AnalysisManager.h"
+#include "clang/Index/Entity.h"
+#include "clang/Index/Indexer.h"
+
+using namespace clang;
+
+const AnalysisContext *
+AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) {
+ idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D),
+ Idxer->getProgram());
+ FunctionDecl *FuncDef;
+ idx::TranslationUnit *TU;
+ llvm::tie(FuncDef, TU) = Idxer->getDefinitionFor(Ent);
+
+ if (FuncDef == 0)
+ return 0;
+
+ // This AnalysisContext wraps function definition in another translation unit.
+ // But it is still owned by the AnalysisManager associated with the current
+ // translation unit.
+ return AnaCtxMgr.getContext(FuncDef, TU);
+}
diff --git a/lib/Checker/ArrayBoundChecker.cpp b/lib/Checker/ArrayBoundChecker.cpp
index 746b3f9..98345bd 100644
--- a/lib/Checker/ArrayBoundChecker.cpp
+++ b/lib/Checker/ArrayBoundChecker.cpp
@@ -58,7 +58,7 @@ void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){
// Get the size of the array.
DefinedOrUnknownSVal NumElements
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
- ER->getValueType(C.getASTContext()));
+ ER->getValueType());
const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true);
const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false);
diff --git a/lib/Checker/BasicObjCFoundationChecks.cpp b/lib/Checker/BasicObjCFoundationChecks.cpp
index ecb2d1c..3c1a6d1 100644
--- a/lib/Checker/BasicObjCFoundationChecks.cpp
+++ b/lib/Checker/BasicObjCFoundationChecks.cpp
@@ -73,9 +73,6 @@ class BasicObjCFoundationChecks : public GRSimpleAPICheck {
bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
- void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
- void WarnNilArg(ExplodedNode* N, const Expr* E);
-
bool CheckNilArg(ExplodedNode* N, unsigned Arg);
public:
@@ -358,7 +355,7 @@ bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
if (!R)
return false;
- QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
+ QualType T = Ctx.getCanonicalType(R->getValueType());
// FIXME: If the pointee isn't an integer type, should we flag a warning?
// People can do weird stuff with pointers.
diff --git a/lib/Checker/BasicStore.cpp b/lib/Checker/BasicStore.cpp
index 62c8d9c..f82e1b2 100644
--- a/lib/Checker/BasicStore.cpp
+++ b/lib/Checker/BasicStore.cpp
@@ -52,7 +52,7 @@ public:
Store InvalidateRegions(Store store, const MemRegion * const *Begin,
const MemRegion * const *End, const Expr *E,
unsigned Count, InvalidatedSymbols *IS,
- bool invalidateGlobals);
+ bool invalidateGlobals, InvalidatedRegions *Regions);
Store scanForIvars(Stmt *B, const Decl* SelfDecl,
const MemRegion *SelfRegion, Store St);
@@ -61,11 +61,6 @@ public:
Store Remove(Store St, Loc loc);
Store getInitialStore(const LocationContext *InitLoc);
- // FIXME: Investigate what is using this. This method should be removed.
- virtual Loc getLoc(const VarDecl* VD, const LocationContext *LC) {
- return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC));
- }
-
Store BindCompoundLiteral(Store store, const CompoundLiteralExpr*,
const LocationContext*, SVal val) {
return store;
@@ -77,9 +72,8 @@ public:
/// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values.
/// It updatees the GRState object in place with the values removed.
- const GRState *RemoveDeadBindings(GRState &state,
- const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
+ Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx,
+ SymbolReaper& SymReaper,
llvm::SmallVectorImpl<const MemRegion*>& RegionRoots);
void iterBindings(Store store, BindingsHandler& f);
@@ -103,8 +97,6 @@ public:
private:
SVal LazyRetrieve(Store store, const TypedRegion *R);
-
- ASTContext& getContext() { return StateMgr.getContext(); }
};
} // end anonymous namespace
@@ -228,17 +220,15 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) {
return VBFactory.Add(B, R, V).getRoot();
}
- ASTContext &C = StateMgr.getContext();
-
// Special case: handle store of pointer values (Loc) to pointers via
// a cast to intXX_t*, void*, etc. This is needed to handle
// OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier.
if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V))
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
// FIXME: Should check for index 0.
- QualType T = ER->getLocationType(C);
+ QualType T = ER->getLocationType();
- if (isHigherOrderRawPtr(T, C))
+ if (isHigherOrderRawPtr(T, Ctx))
R = ER->getSuperRegion();
}
@@ -249,7 +239,7 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) {
// Do not bind to arrays. We need to explicitly check for this so that
// we do not encounter any weirdness of trying to load/store from arrays.
- if (TyR->isBoundable() && TyR->getValueType(C)->isArrayType())
+ if (TyR->isBoundable() && TyR->getValueType()->isArrayType())
return store;
if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) {
@@ -259,7 +249,7 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) {
// a pointer. We may wish to flag a type error here if the types
// are incompatible. This may also cause lots of breakage
// elsewhere. Food for thought.
- if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType(C)))
+ if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType()))
V = X->getLoc();
}
@@ -285,12 +275,11 @@ Store BasicStoreManager::Remove(Store store, Loc loc) {
}
}
-const GRState *BasicStoreManager::RemoveDeadBindings(GRState &state,
+Store BasicStoreManager::RemoveDeadBindings(Store store,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper,
llvm::SmallVectorImpl<const MemRegion*>& RegionRoots)
{
- Store store = state.getStore();
BindingsTy B = GetBindings(store);
typedef SVal::symbol_iterator symbol_iterator;
@@ -365,8 +354,7 @@ const GRState *BasicStoreManager::RemoveDeadBindings(GRState &state,
}
}
- state.setStore(store);
- return StateMgr.getPersistentState(state);
+ return store;
}
Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl,
@@ -406,10 +394,10 @@ Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) {
Store St = VBFactory.GetEmptyMap().getRoot();
for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) {
- NamedDecl* ND = const_cast<NamedDecl*>(I->first);
+ const NamedDecl* ND = I->first;
// Handle implicit parameters.
- if (ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) {
+ if (const ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) {
const Decl& CD = *InitLoc->getDecl();
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) {
if (MD->getSelfDecl() == PD) {
@@ -449,11 +437,11 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR,
// will not be called more than once.
// Static global variables should not be visited here.
- assert(!(VD->getStorageClass() == VarDecl::Static &&
+ assert(!(VD->getStorageClass() == SC_Static &&
VD->isFileVarDecl()));
// Process static variables.
- if (VD->getStorageClass() == VarDecl::Static) {
+ if (VD->getStorageClass() == SC_Static) {
// C99: 6.7.8 Initialization
// If an object that has static storage duration is not initialized
// explicitly, then:
@@ -465,12 +453,9 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR,
if (Loc::IsLocType(T))
store = Bind(store, loc::MemRegionVal(VR),
loc::ConcreteInt(BasicVals.getValue(0, T)));
- else if (T->isIntegerType())
+ else if (T->isIntegerType() && T->isScalarType())
store = Bind(store, loc::MemRegionVal(VR),
nonloc::ConcreteInt(BasicVals.getValue(0, T)));
- else {
- // assert(0 && "ignore other types of variables");
- }
} else {
store = Bind(store, loc::MemRegionVal(VR), *InitVal);
}
@@ -478,7 +463,8 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR,
} else {
// Process local scalar variables.
QualType T = VD->getType();
- if (ValMgr.getSymbolManager().canSymbolicate(T)) {
+ // BasicStore only supports scalars.
+ if (T->isScalarType() && ValMgr.getSymbolManager().canSymbolicate(T)) {
SVal V = InitVal ? *InitVal : UndefinedVal();
store = Bind(store, loc::MemRegionVal(VR), V);
}
@@ -523,11 +509,12 @@ StoreManager::BindingsHandler::~BindingsHandler() {}
Store BasicStoreManager::InvalidateRegions(Store store,
- const MemRegion * const *I,
- const MemRegion * const *End,
- const Expr *E, unsigned Count,
- InvalidatedSymbols *IS,
- bool invalidateGlobals) {
+ const MemRegion * const *I,
+ const MemRegion * const *End,
+ const Expr *E, unsigned Count,
+ InvalidatedSymbols *IS,
+ bool invalidateGlobals,
+ InvalidatedRegions *Regions) {
if (invalidateGlobals) {
BindingsTy B = GetBindings(store);
for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) {
@@ -545,6 +532,8 @@ Store BasicStoreManager::InvalidateRegions(Store store,
continue;
}
store = InvalidateRegion(store, *I, E, Count, IS);
+ if (Regions)
+ Regions->push_back(R);
}
// FIXME: This is copy-and-paste from RegionStore.cpp.
@@ -558,6 +547,8 @@ Store BasicStoreManager::InvalidateRegions(Store store,
Count);
store = Bind(store, loc::MemRegionVal(GS), V);
+ if (Regions)
+ Regions->push_back(GS);
}
return store;
@@ -582,7 +573,7 @@ Store BasicStoreManager::InvalidateRegion(Store store,
}
}
- QualType T = cast<TypedRegion>(R)->getValueType(R->getContext());
+ QualType T = cast<TypedRegion>(R)->getValueType();
SVal V = ValMgr.getConjuredSymbolVal(R, E, T, Count);
return Bind(store, loc::MemRegionVal(R), V);
}
diff --git a/lib/Checker/BasicValueFactory.cpp b/lib/Checker/BasicValueFactory.cpp
index 246beea..4c9b109 100644
--- a/lib/Checker/BasicValueFactory.cpp
+++ b/lib/Checker/BasicValueFactory.cpp
@@ -149,22 +149,22 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op,
default:
assert (false && "Invalid Opcode.");
- case BinaryOperator::Mul:
+ case BO_Mul:
return &getValue( V1 * V2 );
- case BinaryOperator::Div:
+ case BO_Div:
return &getValue( V1 / V2 );
- case BinaryOperator::Rem:
+ case BO_Rem:
return &getValue( V1 % V2 );
- case BinaryOperator::Add:
+ case BO_Add:
return &getValue( V1 + V2 );
- case BinaryOperator::Sub:
+ case BO_Sub:
return &getValue( V1 - V2 );
- case BinaryOperator::Shl: {
+ case BO_Shl: {
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
@@ -182,7 +182,7 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op,
return &getValue( V1.operator<<( (unsigned) Amt ));
}
- case BinaryOperator::Shr: {
+ case BO_Shr: {
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
@@ -200,33 +200,33 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op,
return &getValue( V1.operator>>( (unsigned) Amt ));
}
- case BinaryOperator::LT:
+ case BO_LT:
return &getTruthValue( V1 < V2 );
- case BinaryOperator::GT:
+ case BO_GT:
return &getTruthValue( V1 > V2 );
- case BinaryOperator::LE:
+ case BO_LE:
return &getTruthValue( V1 <= V2 );
- case BinaryOperator::GE:
+ case BO_GE:
return &getTruthValue( V1 >= V2 );
- case BinaryOperator::EQ:
+ case BO_EQ:
return &getTruthValue( V1 == V2 );
- case BinaryOperator::NE:
+ case BO_NE:
return &getTruthValue( V1 != V2 );
// Note: LAnd, LOr, Comma are handled specially by higher-level logic.
- case BinaryOperator::And:
+ case BO_And:
return &getValue( V1 & V2 );
- case BinaryOperator::Or:
+ case BO_Or:
return &getValue( V1 | V2 );
- case BinaryOperator::Xor:
+ case BO_Xor:
return &getValue( V1 ^ V2 );
}
}
diff --git a/lib/Checker/BugReporter.cpp b/lib/Checker/BugReporter.cpp
index 0422d80..bffbd52 100644
--- a/lib/Checker/BugReporter.cpp
+++ b/lib/Checker/BugReporter.cpp
@@ -94,8 +94,8 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) {
case Stmt::ChooseExprClass:
case Stmt::ConditionalOperatorClass: continue;
case Stmt::BinaryOperatorClass: {
- BinaryOperator::Opcode Op = cast<BinaryOperator>(S)->getOpcode();
- if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr)
+ BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
+ if (Op == BO_LAnd || Op == BO_LOr)
continue;
break;
}
@@ -177,18 +177,9 @@ public:
}
virtual NodeMapClosure& getNodeResolver() { return NMC; }
- BugReport& getReport() { return *R; }
PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
- PathDiagnosticLocation
- getEnclosingStmtLocation(const PathDiagnosticLocation &L) {
- if (const Stmt *S = L.asStmt())
- return getEnclosingStmtLocation(S);
-
- return L;
- }
-
PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const {
return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive;
}
@@ -541,9 +532,9 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
- CFGBlock* Src = BE->getSrc();
- CFGBlock* Dst = BE->getDst();
- Stmt* T = Src->getTerminator();
+ const CFGBlock* Src = BE->getSrc();
+ const CFGBlock* Dst = BE->getDst();
+ const Stmt* T = Src->getTerminator();
if (!T)
continue;
@@ -577,7 +568,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- if (Stmt* S = Dst->getLabel()) {
+ if (const Stmt* S = Dst->getLabel()) {
PathDiagnosticLocation End(S, SMgr);
switch (S->getStmtClass()) {
@@ -593,17 +584,17 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
case Stmt::CaseStmtClass: {
os << "Control jumps to 'case ";
- CaseStmt* Case = cast<CaseStmt>(S);
- Expr* LHS = Case->getLHS()->IgnoreParenCasts();
+ const CaseStmt* Case = cast<CaseStmt>(S);
+ const Expr* LHS = Case->getLHS()->IgnoreParenCasts();
// Determine if it is an enum.
bool GetRawInt = true;
- if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) {
+ if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) {
// FIXME: Maybe this should be an assertion. Are there cases
// were it is not an EnumConstantDecl?
- EnumConstantDecl* D =
- dyn_cast<EnumConstantDecl>(DR->getDecl());
+ const EnumConstantDecl* D =
+ dyn_cast<EnumConstantDecl>(DR->getDecl());
if (D) {
GetRawInt = false;
@@ -668,12 +659,12 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (!PDB.supportsLogicalOpControlFlow())
break;
- BinaryOperator *B = cast<BinaryOperator>(T);
+ const BinaryOperator *B = cast<BinaryOperator>(T);
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "Left side of '";
- if (B->getOpcode() == BinaryOperator::LAnd) {
+ if (B->getOpcode() == BO_LAnd) {
os << "&&" << "' is ";
if (*(Src->succ_begin()+1) == Dst) {
@@ -692,7 +683,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
}
}
else {
- assert(B->getOpcode() == BinaryOperator::LOr);
+ assert(B->getOpcode() == BO_LOr);
os << "||" << "' is ";
if (*(Src->succ_begin()+1) == Dst) {
@@ -902,8 +893,6 @@ class EdgeBuilder {
CLocs.pop_back();
}
- PathDiagnosticLocation IgnoreParens(const PathDiagnosticLocation &L);
-
public:
EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb)
: PD(pd), PDB(pdb) {
@@ -935,10 +924,6 @@ public:
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false);
- void addEdge(const Stmt *S, bool alwaysAdd = false) {
- addEdge(PathDiagnosticLocation(S, PDB.getSourceManager()), alwaysAdd);
- }
-
void rawAddEdge(PathDiagnosticLocation NewLoc);
void addContext(const Stmt *S);
@@ -1006,14 +991,6 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
SM.getInstantiationColumnNumber(ContainerREnd)));
}
-PathDiagnosticLocation
-EdgeBuilder::IgnoreParens(const PathDiagnosticLocation &L) {
- if (const Expr* E = dyn_cast_or_null<Expr>(L.asStmt()))
- return PathDiagnosticLocation(E->IgnoreParenCasts(),
- PDB.getSourceManager());
- return L;
-}
-
void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
if (!PrevLoc.isValid()) {
PrevLoc = NewLoc;
diff --git a/lib/Checker/BugReporterVisitors.cpp b/lib/Checker/BugReporterVisitors.cpp
index 776e12b..91cf349 100644
--- a/lib/Checker/BugReporterVisitors.cpp
+++ b/lib/Checker/BugReporterVisitors.cpp
@@ -31,7 +31,7 @@ const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) {
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
- if (U->getOpcode() == UnaryOperator::Deref)
+ if (U->getOpcode() == UO_Deref)
return U->getSubExpr()->IgnoreParenCasts();
}
else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
@@ -143,10 +143,9 @@ public:
if (isa<loc::ConcreteInt>(V)) {
bool b = false;
- ASTContext &C = BRC.getASTContext();
if (R->isBoundable()) {
if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
- if (TR->getValueType(C)->isObjCObjectPointerType()) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
os << "initialized to nil";
b = true;
}
@@ -174,10 +173,9 @@ public:
if (os.str().empty()) {
if (isa<loc::ConcreteInt>(V)) {
bool b = false;
- ASTContext &C = BRC.getASTContext();
if (R->isBoundable()) {
if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
- if (TR->getValueType(C)->isObjCObjectPointerType()) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
os << "nil object reference stored to ";
b = true;
}
@@ -209,7 +207,7 @@ public:
ProgramPoint P = N->getLocation();
if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
- CFGBlock *BSrc = BE->getSrc();
+ const CFGBlock *BSrc = BE->getSrc();
S = BSrc->getTerminatorCondition();
}
else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
@@ -282,7 +280,7 @@ public:
ProgramPoint P = N->getLocation();
if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
- CFGBlock *BSrc = BE->getSrc();
+ const CFGBlock *BSrc = BE->getSrc();
S = BSrc->getTerminatorCondition();
}
else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
@@ -421,3 +419,40 @@ public:
void clang::bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) {
BRC.addVisitor(new NilReceiverVisitor());
}
+
+// Registers every VarDecl inside a Stmt with a last store vistor.
+void clang::bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC,
+ const void *stmt,
+ const ExplodedNode *N) {
+ const Stmt *S = static_cast<const Stmt *>(stmt);
+
+ std::deque<const Stmt *> WorkList;
+
+ WorkList.push_back(S);
+
+ while (!WorkList.empty()) {
+ const Stmt *Head = WorkList.front();
+ WorkList.pop_front();
+
+ GRStateManager &StateMgr = BRC.getStateManager();
+ const GRState *state = N->getState();
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const VarRegion *R =
+ StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
+
+ // What did we load?
+ SVal V = state->getSVal(S);
+
+ if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) {
+ ::registerFindLastStore(BRC, R, V);
+ }
+ }
+ }
+
+ for (Stmt::const_child_iterator I = Head->child_begin();
+ I != Head->child_end(); ++I)
+ WorkList.push_back(*I);
+ }
+}
diff --git a/lib/Checker/CFRefCount.cpp b/lib/Checker/CFRefCount.cpp
index 3c74cd8..6fa48b2 100644
--- a/lib/Checker/CFRefCount.cpp
+++ b/lib/Checker/CFRefCount.cpp
@@ -82,8 +82,7 @@ public:
static const ObjCMethodDecl*
ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) {
- ObjCInterfaceDecl *ID =
- const_cast<ObjCInterfaceDecl*>(MD->getClassInterface());
+ const ObjCInterfaceDecl *ID = MD->getClassInterface();
return MD->isInstanceMethod()
? ID->lookupInstanceMethod(MD->getSelector())
@@ -93,11 +92,11 @@ ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) {
namespace {
class GenericNodeBuilder {
GRStmtNodeBuilder *SNB;
- Stmt *S;
+ const Stmt *S;
const void *tag;
GREndPathNodeBuilder *ENB;
public:
- GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s,
+ GenericNodeBuilder(GRStmtNodeBuilder &snb, const Stmt *s,
const void *t)
: SNB(&snb), S(s), tag(t), ENB(0) {}
@@ -195,12 +194,6 @@ public:
static RetEffect MakeNoRet() {
return RetEffect(NoRet);
}
-
- void Profile(llvm::FoldingSetNodeID& ID) const {
- ID.AddInteger((unsigned)K);
- ID.AddInteger((unsigned)O);
- ID.AddInteger(index);
- }
};
//===----------------------------------------------------------------------===//
@@ -239,9 +232,6 @@ private:
RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t)
: kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {}
- RefVal(Kind k, unsigned cnt = 0)
- : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {}
-
public:
Kind getKind() const { return kind; }
@@ -256,12 +246,6 @@ public:
QualType getType() const { return T; }
- // Useful predicates.
-
- static bool isError(Kind k) { return k >= ERROR_START; }
-
- static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; }
-
bool isOwned() const {
return getKind() == Owned;
}
@@ -278,11 +262,6 @@ public:
return getKind() == ReturnedNotOwned;
}
- bool isNonLeakError() const {
- Kind k = getKind();
- return isError(k) && !isLeak(k);
- }
-
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
unsigned Count = 1) {
return RefVal(Owned, o, Count, 0, t);
@@ -474,11 +453,6 @@ public:
DefaultArgEffect = E;
}
- /// setArg - Set the argument effect on the argument specified by idx.
- void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) {
- Args = AF.Add(Args, idx, E);
- }
-
/// getRetEffect - Returns the effect on the return value of the call.
RetEffect getRetEffect() const { return Ret; }
@@ -492,28 +466,6 @@ public:
/// getReceiverEffect - Returns the effect on the receiver of the call.
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
ArgEffect getReceiverEffect() const { return Receiver; }
-
- /// setReceiverEffect - Set the effect on the receiver of the call.
- void setReceiverEffect(ArgEffect E) { Receiver = E; }
-
- typedef ArgEffects::iterator ExprIterator;
-
- ExprIterator begin_args() const { return Args.begin(); }
- ExprIterator end_args() const { return Args.end(); }
-
- static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A,
- RetEffect RetEff, ArgEffect DefaultEff,
- ArgEffect ReceiverEff, bool EndPath) {
- ID.Add(A);
- ID.Add(RetEff);
- ID.AddInteger((unsigned) DefaultEff);
- ID.AddInteger((unsigned) ReceiverEff);
- ID.AddInteger((unsigned) EndPath);
- }
-
- void Profile(llvm::FoldingSetNodeID& ID) const {
- Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath);
- }
};
} // end anonymous namespace
@@ -618,11 +570,6 @@ public:
return Summ;
}
-
- RetainSummary* find(Expr* Receiver, Selector S) {
- return find(getReceiverDecl(Receiver), S);
- }
-
RetainSummary* find(IdentifierInfo* II, Selector S) {
// FIXME: Class method lookup. Right now we dont' have a good way
// of going between IdentifierInfo* and the class hierarchy.
@@ -634,47 +581,6 @@ public:
return I == M.end() ? NULL : I->second;
}
- const ObjCInterfaceDecl* getReceiverDecl(Expr* E) {
- if (const ObjCObjectPointerType* PT =
- E->getType()->getAs<ObjCObjectPointerType>())
- return PT->getInterfaceDecl();
-
- return NULL;
- }
-
- RetainSummary*& operator[](ObjCMessageExpr* ME) {
-
- Selector S = ME->getSelector();
-
- const ObjCInterfaceDecl* OD = 0;
- bool IsInstanceMessage = false;
- switch (ME->getReceiverKind()) {
- case ObjCMessageExpr::Instance:
- OD = getReceiverDecl(ME->getInstanceReceiver());
- IsInstanceMessage = true;
- break;
-
- case ObjCMessageExpr::SuperInstance:
- IsInstanceMessage = true;
- OD = ME->getSuperType()->getAs<ObjCObjectPointerType>()
- ->getInterfaceDecl();
- break;
-
- case ObjCMessageExpr::Class:
- OD = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
- break;
-
- case ObjCMessageExpr::SuperClass:
- OD = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
- break;
- }
-
- if (IsInstanceMessage)
- return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S];
-
- return M[ObjCSummaryKey(OD->getIdentifier(), S)];
- }
-
RetainSummary*& operator[](ObjCSummaryKey K) {
return M[K];
}
@@ -696,7 +602,7 @@ class RetainSummaryManager {
// Typedefs.
//==-----------------------------------------------------------------==//
- typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
+ typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*>
FuncSummariesTy;
typedef ObjCSummaryCache ObjCMethodSummariesTy;
@@ -766,9 +672,10 @@ public:
RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func);
- RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
- RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
- RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, StringRef FName);
+ RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD);
+ RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD);
+ RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD,
+ StringRef FName);
RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff,
ArgEffect ReceiverEff = DoNothing,
@@ -796,12 +703,6 @@ public:
void InitializeClassMethodSummaries();
void InitializeMethodSummaries();
private:
-
- void addClsMethSummary(IdentifierInfo* ClsII, Selector S,
- RetainSummary* Summ) {
- ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
- }
-
void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
ObjCClassMethodSummaries[S] = Summ;
}
@@ -892,7 +793,7 @@ public:
~RetainSummaryManager();
- RetainSummary* getSummary(FunctionDecl* FD);
+ RetainSummary* getSummary(const FunctionDecl* FD);
RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME,
const GRState *state,
@@ -999,15 +900,15 @@ RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff,
// Summary creation for functions (largely uses of Core Foundation).
//===----------------------------------------------------------------------===//
-static bool isRetain(FunctionDecl* FD, StringRef FName) {
+static bool isRetain(const FunctionDecl* FD, StringRef FName) {
return FName.endswith("Retain");
}
-static bool isRelease(FunctionDecl* FD, StringRef FName) {
+static bool isRelease(const FunctionDecl* FD, StringRef FName) {
return FName.endswith("Release");
}
-RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
+RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
if (I != FuncSummaries.end())
@@ -1201,7 +1102,7 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) {
}
RetainSummary*
-RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD,
+RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD,
StringRef FName) {
if (FName.find("Create") != StringRef::npos ||
@@ -1250,7 +1151,8 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
}
}
-RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
+RetainSummary*
+RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl* FD) {
assert (ScratchArgs.isEmpty());
if (FD->getIdentifier() == CFDictionaryCreateII) {
@@ -1261,7 +1163,8 @@ RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
}
-RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
+RetainSummary*
+RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) {
assert (ScratchArgs.isEmpty());
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
DoNothing, DoNothing);
@@ -1767,7 +1670,7 @@ private:
void ProcessNonLeakError(ExplodedNodeSet& Dst,
GRStmtNodeBuilder& Builder,
- Expr* NodeExpr, SourceRange ErrorRange,
+ const Expr* NodeExpr, SourceRange ErrorRange,
ExplodedNode* Pred,
const GRState* St,
RefVal::Kind hasErr, SymbolRef Sym);
@@ -1810,33 +1713,26 @@ public:
void EvalSummary(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- Expr* Ex,
+ const Expr* Ex,
InstanceReceiver Receiver,
const RetainSummary& Summ,
const MemRegion *Callee,
- ExprIterator arg_beg, ExprIterator arg_end,
+ ConstExprIterator arg_beg, ConstExprIterator arg_end,
ExplodedNode* Pred, const GRState *state);
virtual void EvalCall(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- CallExpr* CE, SVal L,
+ const CallExpr* CE, SVal L,
ExplodedNode* Pred);
virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder& Builder,
- ObjCMessageExpr* ME,
+ const ObjCMessageExpr* ME,
ExplodedNode* Pred,
const GRState *state);
-
- bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst,
- GRExprEngine& Engine,
- GRStmtNodeBuilder& Builder,
- ObjCMessageExpr* ME,
- ExplodedNode* Pred);
-
// Stores.
virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val);
@@ -1861,7 +1757,7 @@ public:
virtual void EvalReturn(ExplodedNodeSet& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder& Builder,
- ReturnStmt* S,
+ const ReturnStmt* S,
ExplodedNode* Pred);
// Assumptions.
@@ -1934,7 +1830,6 @@ namespace {
public:
CFRefCount& getTF() { return TF; }
- const CFRefCount& getTF() const { return TF; }
// FIXME: Eventually remove.
virtual const char* getDescription() const = 0;
@@ -2049,9 +1944,6 @@ namespace {
CFRefBug& getBugType() {
return (CFRefBug&) RangedBugReport::getBugType();
}
- const CFRefBug& getBugType() const {
- return (const CFRefBug&) RangedBugReport::getBugType();
- }
virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) {
if (!getBugType().isLeak())
@@ -2605,11 +2497,12 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) {
void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- Expr* Ex,
+ const Expr* Ex,
InstanceReceiver Receiver,
const RetainSummary& Summ,
const MemRegion *Callee,
- ExprIterator arg_beg, ExprIterator arg_end,
+ ConstExprIterator arg_beg,
+ ConstExprIterator arg_end,
ExplodedNode* Pred, const GRState *state) {
// Evaluate the effect of the arguments.
@@ -2620,19 +2513,25 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate;
- for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
+ // HACK: Symbols that have ref-count state that are referenced directly
+ // (not as structure or array elements, or via bindings) by an argument
+ // should not have their ref-count state stripped after we have
+ // done an invalidation pass.
+ llvm::DenseSet<SymbolRef> WhitelistedSymbols;
+
+ for (ConstExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
SVal V = state->getSValAsScalarOrLoc(*I);
SymbolRef Sym = V.getAsLocSymbol();
if (Sym)
if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) {
+ WhitelistedSymbols.insert(Sym);
state = Update(state, Sym, *T, Summ.getArg(idx), hasErr);
if (hasErr) {
ErrorRange = (*I)->getSourceRange();
ErrorSym = Sym;
break;
}
- continue;
}
tryAgain:
@@ -2703,22 +2602,22 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
// expression (the context) and the expression itself. This should
// disambiguate conjured symbols.
unsigned Count = Builder.getCurrentBlockCount();
- StoreManager& StoreMgr = Eng.getStateManager().getStoreManager();
StoreManager::InvalidatedSymbols IS;
- Store store = state->getStore();
// NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
// global variables.
- store = StoreMgr.InvalidateRegions(store, RegionsToInvalidate.data(),
- RegionsToInvalidate.data() +
- RegionsToInvalidate.size(),
- Ex, Count, &IS,
- /* invalidateGlobals = */ true);
+ state = state->InvalidateRegions(RegionsToInvalidate.data(),
+ RegionsToInvalidate.data() +
+ RegionsToInvalidate.size(),
+ Ex, Count, &IS,
+ /* invalidateGlobals = */ true);
- state = state->makeWithStore(store);
for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(),
E = IS.end(); I!=E; ++I) {
- // Remove any existing reference-count binding.
+ SymbolRef sym = *I;
+ if (WhitelistedSymbols.count(sym))
+ continue;
+ // Remove any existing reference-count binding.
state = state->remove<RefBindings>(*I);
}
@@ -2860,7 +2759,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- CallExpr* CE, SVal L,
+ const CallExpr* CE, SVal L,
ExplodedNode* Pred) {
RetainSummary *Summ = 0;
@@ -2874,7 +2773,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
else {
const FunctionDecl* FD = L.getAsFunctionDecl();
Summ = !FD ? Summaries.getDefaultSummary() :
- Summaries.getSummary(const_cast<FunctionDecl*>(FD));
+ Summaries.getSummary(FD);
}
assert(Summ);
@@ -2885,7 +2784,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst,
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- ObjCMessageExpr* ME,
+ const ObjCMessageExpr* ME,
ExplodedNode* Pred,
const GRState *state) {
RetainSummary *Summ =
@@ -2956,10 +2855,10 @@ void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) {
void CFRefCount::EvalReturn(ExplodedNodeSet& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder& Builder,
- ReturnStmt* S,
+ const ReturnStmt* S,
ExplodedNode* Pred) {
- Expr* RetE = S->getRetValue();
+ const Expr* RetE = S->getRetValue();
if (!RetE)
return;
@@ -3404,7 +3303,7 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst,
ExplodedNode* Pred,
const GRState* state,
SymbolReaper& SymReaper) {
- Stmt *S = Builder.getStmt();
+ const Stmt *S = Builder.getStmt();
RefBindings B = state->get<RefBindings>();
// Update counts from autorelease pools
@@ -3454,7 +3353,8 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst,
void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
GRStmtNodeBuilder& Builder,
- Expr* NodeExpr, SourceRange ErrorRange,
+ const Expr* NodeExpr,
+ SourceRange ErrorRange,
ExplodedNode* Pred,
const GRState* St,
RefVal::Kind hasErr, SymbolRef Sym) {
diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt
index 259346a..5b54f0d 100644
--- a/lib/Checker/CMakeLists.txt
+++ b/lib/Checker/CMakeLists.txt
@@ -4,6 +4,7 @@ add_clang_library(clangChecker
AdjustedReturnValueChecker.cpp
AggExprVisitor.cpp
AnalysisConsumer.cpp
+ AnalysisManager.cpp
ArrayBoundChecker.cpp
AttrNonNullChecker.cpp
BasicConstraintManager.cpp
@@ -15,7 +16,6 @@ add_clang_library(clangChecker
BuiltinFunctionChecker.cpp
CFRefCount.cpp
CallAndMessageChecker.cpp
- CallInliner.cpp
CastSizeChecker.cpp
CastToStructChecker.cpp
CheckDeadStores.cpp
@@ -24,6 +24,7 @@ add_clang_library(clangChecker
CheckSecuritySyntaxOnly.cpp
CheckSizeofPointer.cpp
Checker.cpp
+ CheckerHelpers.cpp
CocoaConventions.cpp
CStringChecker.cpp
DereferenceChecker.cpp
@@ -74,6 +75,7 @@ add_clang_library(clangChecker
UndefinedArraySubscriptChecker.cpp
UndefinedAssignmentChecker.cpp
UnixAPIChecker.cpp
+ UnreachableCodeChecker.cpp
VLASizeChecker.cpp
ValueManager.cpp
)
diff --git a/lib/Checker/CStringChecker.cpp b/lib/Checker/CStringChecker.cpp
index a92d409..9ea572f 100644
--- a/lib/Checker/CStringChecker.cpp
+++ b/lib/Checker/CStringChecker.cpp
@@ -15,19 +15,30 @@
#include "GRExprEngineExperimentalChecks.h"
#include "clang/Checker/BugReporter/BugType.h"
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Checker/PathSensitive/GRStateTrait.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
namespace {
class CStringChecker : public CheckerVisitor<CStringChecker> {
- BugType *BT_Null, *BT_Bounds, *BT_Overlap;
+ BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString;
public:
CStringChecker()
- : BT_Null(0), BT_Bounds(0), BT_Overlap(0) {}
+ : BT_Null(0), BT_Bounds(0), BT_BoundsWrite(0), BT_Overlap(0), BT_NotCString(0)
+ {}
static void *getTag() { static int tag; return &tag; }
bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
+ void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS);
+ void MarkLiveSymbols(const GRState *state, SymbolReaper &SR);
+ void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SR);
+ bool WantsRegionChangeUpdate(const GRState *state);
+
+ const GRState *EvalRegionChanges(const GRState *state,
+ const MemRegion * const *Begin,
+ const MemRegion * const *End,
+ bool*);
typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *);
@@ -40,26 +51,61 @@ public:
void EvalMemcmp(CheckerContext &C, const CallExpr *CE);
+ void EvalStrlen(CheckerContext &C, const CallExpr *CE);
+
+ void EvalStrcpy(CheckerContext &C, const CallExpr *CE);
+ void EvalStpcpy(CheckerContext &C, const CallExpr *CE);
+ void EvalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd);
+
// Utility methods
std::pair<const GRState*, const GRState*>
AssumeZero(CheckerContext &C, const GRState *state, SVal V, QualType Ty);
+ const GRState *SetCStringLength(const GRState *state, const MemRegion *MR,
+ SVal StrLen);
+ SVal GetCStringLengthForRegion(CheckerContext &C, const GRState *&state,
+ const Expr *Ex, const MemRegion *MR);
+ SVal GetCStringLength(CheckerContext &C, const GRState *&state,
+ const Expr *Ex, SVal Buf);
+
+ const GRState *InvalidateBuffer(CheckerContext &C, const GRState *state,
+ const Expr *Ex, SVal V);
+
+ bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
+ const MemRegion *MR);
+
+ // Re-usable checks
const GRState *CheckNonNull(CheckerContext &C, const GRState *state,
const Expr *S, SVal l);
const GRState *CheckLocation(CheckerContext &C, const GRState *state,
- const Expr *S, SVal l);
+ const Expr *S, SVal l,
+ bool IsDestination = false);
const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state,
const Expr *Size,
const Expr *FirstBuf,
- const Expr *SecondBuf = NULL);
+ const Expr *SecondBuf = NULL,
+ bool FirstIsDestination = false);
const GRState *CheckOverlap(CheckerContext &C, const GRState *state,
const Expr *Size, const Expr *First,
const Expr *Second);
void EmitOverlapBug(CheckerContext &C, const GRState *state,
const Stmt *First, const Stmt *Second);
};
+
+class CStringLength {
+public:
+ typedef llvm::ImmutableMap<const MemRegion *, SVal> EntryMap;
+};
} //end anonymous namespace
+namespace clang {
+ template <>
+ struct GRStateTrait<CStringLength>
+ : public GRStatePartialTrait<CStringLength::EntryMap> {
+ static void *GDMIndex() { return CStringChecker::getTag(); }
+ };
+}
+
void clang::RegisterCStringChecker(GRExprEngine &Eng) {
Eng.registerCheck(new CStringChecker());
}
@@ -122,7 +168,8 @@ const GRState *CStringChecker::CheckNonNull(CheckerContext &C,
// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
const GRState *CStringChecker::CheckLocation(CheckerContext &C,
const GRState *state,
- const Expr *S, SVal l) {
+ const Expr *S, SVal l,
+ bool IsDestination) {
// If a previous check has failed, propagate the failure.
if (!state)
return NULL;
@@ -136,7 +183,7 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
if (!ER)
return state;
- assert(ER->getValueType(C.getASTContext()) == C.getASTContext().CharTy &&
+ assert(ER->getValueType() == C.getASTContext().CharTy &&
"CheckLocation should only be called with char* ElementRegions");
// Get the size of the array.
@@ -155,17 +202,26 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
if (!N)
return NULL;
- if (!BT_Bounds)
- BT_Bounds = new BuiltinBug("Out-of-bound array access",
- "Byte string function accesses out-of-bound array element "
- "(buffer overflow)");
+ BuiltinBug *BT;
+ if (IsDestination) {
+ if (!BT_BoundsWrite) {
+ BT_BoundsWrite = new BuiltinBug("Out-of-bound array access",
+ "Byte string function overflows destination buffer");
+ }
+ BT = static_cast<BuiltinBug*>(BT_BoundsWrite);
+ } else {
+ if (!BT_Bounds) {
+ BT_Bounds = new BuiltinBug("Out-of-bound array access",
+ "Byte string function accesses out-of-bound array element");
+ }
+ BT = static_cast<BuiltinBug*>(BT_Bounds);
+ }
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
// Generate a report for this bug.
- BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds);
RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N);
report->addRange(S->getSourceRange());
@@ -182,7 +238,8 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
const GRState *state,
const Expr *Size,
const Expr *FirstBuf,
- const Expr *SecondBuf) {
+ const Expr *SecondBuf,
+ bool FirstIsDestination) {
// If a previous check has failed, propagate the failure.
if (!state)
return NULL;
@@ -191,7 +248,7 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
SValuator &SV = VM.getSValuator();
ASTContext &Ctx = C.getASTContext();
- QualType SizeTy = Ctx.getSizeType();
+ QualType SizeTy = Size->getType();
QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
// Check that the first buffer is non-null.
@@ -208,18 +265,20 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
// Compute the offset of the last element to be accessed: size-1.
NonLoc One = cast<NonLoc>(VM.makeIntVal(1, SizeTy));
- NonLoc LastOffset = cast<NonLoc>(SV.EvalBinOpNN(state, BinaryOperator::Sub,
+ NonLoc LastOffset = cast<NonLoc>(SV.EvalBinOpNN(state, BO_Sub,
*Length, One, SizeTy));
// Check that the first buffer is sufficently long.
- Loc BufStart = cast<Loc>(SV.EvalCast(BufVal, PtrTy, FirstBuf->getType()));
- SVal BufEnd
- = SV.EvalBinOpLN(state, BinaryOperator::Add, BufStart, LastOffset, PtrTy);
- state = CheckLocation(C, state, FirstBuf, BufEnd);
+ SVal BufStart = SV.EvalCast(BufVal, PtrTy, FirstBuf->getType());
+ if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) {
+ SVal BufEnd = SV.EvalBinOpLN(state, BO_Add, *BufLoc,
+ LastOffset, PtrTy);
+ state = CheckLocation(C, state, FirstBuf, BufEnd, FirstIsDestination);
- // If the buffer isn't large enough, abort.
- if (!state)
- return NULL;
+ // If the buffer isn't large enough, abort.
+ if (!state)
+ return NULL;
+ }
// If there's a second buffer, check it as well.
if (SecondBuf) {
@@ -228,10 +287,12 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
if (!state)
return NULL;
- BufStart = cast<Loc>(SV.EvalCast(BufVal, PtrTy, SecondBuf->getType()));
- BufEnd
- = SV.EvalBinOpLN(state, BinaryOperator::Add, BufStart, LastOffset, PtrTy);
- state = CheckLocation(C, state, SecondBuf, BufEnd);
+ BufStart = SV.EvalCast(BufVal, PtrTy, SecondBuf->getType());
+ if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) {
+ SVal BufEnd = SV.EvalBinOpLN(state, BO_Add, *BufLoc,
+ LastOffset, PtrTy);
+ state = CheckLocation(C, state, SecondBuf, BufEnd);
+ }
}
// Large enough or not, return this state!
@@ -284,7 +345,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C,
// Which value comes first?
QualType CmpTy = Ctx.IntTy;
- SVal Reverse = SV.EvalBinOpLL(state, BinaryOperator::GT,
+ SVal Reverse = SV.EvalBinOpLL(state, BO_GT,
*FirstLoc, *SecondLoc, CmpTy);
DefinedOrUnknownSVal *ReverseTest = dyn_cast<DefinedOrUnknownSVal>(&Reverse);
if (!ReverseTest)
@@ -324,14 +385,14 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C,
return state;
// Compute the end of the first buffer. Bail out if THAT fails.
- SVal FirstEnd = SV.EvalBinOpLN(state, BinaryOperator::Add,
+ SVal FirstEnd = SV.EvalBinOpLN(state, BO_Add,
*FirstStartLoc, *Length, CharPtrTy);
Loc *FirstEndLoc = dyn_cast<Loc>(&FirstEnd);
if (!FirstEndLoc)
return state;
// Is the end of the first buffer past the start of the second buffer?
- SVal Overlap = SV.EvalBinOpLL(state, BinaryOperator::GT,
+ SVal Overlap = SV.EvalBinOpLL(state, BO_GT,
*FirstEndLoc, *SecondLoc, CmpTy);
DefinedOrUnknownSVal *OverlapTest = dyn_cast<DefinedOrUnknownSVal>(&Overlap);
if (!OverlapTest)
@@ -369,6 +430,222 @@ void CStringChecker::EmitOverlapBug(CheckerContext &C, const GRState *state,
C.EmitReport(report);
}
+const GRState *CStringChecker::SetCStringLength(const GRState *state,
+ const MemRegion *MR,
+ SVal StrLen) {
+ assert(!StrLen.isUndef() && "Attempt to set an undefined string length");
+ if (StrLen.isUnknown())
+ return state;
+
+ MR = MR->StripCasts();
+
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind:
+ // FIXME: This can happen if we strcpy() into a string region. This is
+ // undefined [C99 6.4.5p6], but we should still warn about it.
+ return state;
+
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ return state->set<CStringLength>(MR, StrLen);
+
+ case MemRegion::ElementRegionKind:
+ // FIXME: Handle element regions by upper-bounding the parent region's
+ // string length.
+ return state;
+
+ default:
+ // Other regions (mostly non-data) can't have a reliable C string length.
+ // For now, just ignore the change.
+ // FIXME: These are rare but not impossible. We should output some kind of
+ // warning for things like strcpy((char[]){'a', 0}, "b");
+ return state;
+ }
+}
+
+SVal CStringChecker::GetCStringLengthForRegion(CheckerContext &C,
+ const GRState *&state,
+ const Expr *Ex,
+ const MemRegion *MR) {
+ // If there's a recorded length, go ahead and return it.
+ const SVal *Recorded = state->get<CStringLength>(MR);
+ if (Recorded)
+ return *Recorded;
+
+ // Otherwise, get a new symbol and update the state.
+ unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ ValueManager &ValMgr = C.getValueManager();
+ QualType SizeTy = ValMgr.getContext().getSizeType();
+ SVal Strlen = ValMgr.getMetadataSymbolVal(getTag(), MR, Ex, SizeTy, Count);
+
+ state = state->set<CStringLength>(MR, Strlen);
+ return Strlen;
+}
+
+SVal CStringChecker::GetCStringLength(CheckerContext &C, const GRState *&state,
+ const Expr *Ex, SVal Buf) {
+ const MemRegion *MR = Buf.getAsRegion();
+ if (!MR) {
+ // If we can't get a region, see if it's something we /know/ isn't a
+ // C string. In the context of locations, the only time we can issue such
+ // a warning is for labels.
+ if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&Buf)) {
+ if (ExplodedNode *N = C.GenerateNode(state)) {
+ if (!BT_NotCString)
+ BT_NotCString = new BuiltinBug("API",
+ "Argument is not a null-terminated string.");
+
+ llvm::SmallString<120> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << "Argument to byte string function is the address of the label '"
+ << Label->getLabel()->getID()->getName()
+ << "', which is not a null-terminated string";
+
+ // Generate a report for this bug.
+ EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString,
+ os.str(), N);
+
+ report->addRange(Ex->getSourceRange());
+ C.EmitReport(report);
+ }
+
+ return UndefinedVal();
+ }
+
+ // If it's not a region and not a label, give up.
+ return UnknownVal();
+ }
+
+ // If we have a region, strip casts from it and see if we can figure out
+ // its length. For anything we can't figure out, just return UnknownVal.
+ MR = MR->StripCasts();
+
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind: {
+ // Modifying the contents of string regions is undefined [C99 6.4.5p6],
+ // so we can assume that the byte length is the correct C string length.
+ ValueManager &ValMgr = C.getValueManager();
+ QualType SizeTy = ValMgr.getContext().getSizeType();
+ const StringLiteral *Str = cast<StringRegion>(MR)->getStringLiteral();
+ return ValMgr.makeIntVal(Str->getByteLength(), SizeTy);
+ }
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ return GetCStringLengthForRegion(C, state, Ex, MR);
+ case MemRegion::CompoundLiteralRegionKind:
+ // FIXME: Can we track this? Is it necessary?
+ return UnknownVal();
+ case MemRegion::ElementRegionKind:
+ // FIXME: How can we handle this? It's not good enough to subtract the
+ // offset from the base string length; consider "123\x00567" and &a[5].
+ return UnknownVal();
+ default:
+ // Other regions (mostly non-data) can't have a reliable C string length.
+ // In this case, an error is emitted and UndefinedVal is returned.
+ // The caller should always be prepared to handle this case.
+ if (ExplodedNode *N = C.GenerateNode(state)) {
+ if (!BT_NotCString)
+ BT_NotCString = new BuiltinBug("API",
+ "Argument is not a null-terminated string.");
+
+ llvm::SmallString<120> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "Argument to byte string function is ";
+
+ if (SummarizeRegion(os, C.getASTContext(), MR))
+ os << ", which is not a null-terminated string";
+ else
+ os << "not a null-terminated string";
+
+ // Generate a report for this bug.
+ EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString,
+ os.str(), N);
+
+ report->addRange(Ex->getSourceRange());
+ C.EmitReport(report);
+ }
+
+ return UndefinedVal();
+ }
+}
+
+const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
+ const GRState *state,
+ const Expr *E, SVal V) {
+ Loc *L = dyn_cast<Loc>(&V);
+ if (!L)
+ return state;
+
+ // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
+ // some assumptions about the value that CFRefCount can't. Even so, it should
+ // probably be refactored.
+ if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(L)) {
+ const MemRegion *R = MR->getRegion()->StripCasts();
+
+ // Are we dealing with an ElementRegion? If so, we should be invalidating
+ // the super-region.
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ R = ER->getSuperRegion();
+ // FIXME: What about layers of ElementRegions?
+ }
+
+ // Invalidate this region.
+ unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ return state->InvalidateRegion(R, E, Count, NULL);
+ }
+
+ // If we have a non-region value by chance, just remove the binding.
+ // FIXME: is this necessary or correct? This handles the non-Region
+ // cases. Is it ever valid to store to these?
+ return state->unbindLoc(*L);
+}
+
+bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
+ const MemRegion *MR) {
+ const TypedRegion *TR = dyn_cast<TypedRegion>(MR);
+ if (!TR)
+ return false;
+
+ switch (TR->getKind()) {
+ case MemRegion::FunctionTextRegionKind: {
+ const FunctionDecl *FD = cast<FunctionTextRegion>(TR)->getDecl();
+ if (FD)
+ os << "the address of the function '" << FD << "'";
+ else
+ os << "the address of a function";
+ return true;
+ }
+ case MemRegion::BlockTextRegionKind:
+ os << "block text";
+ return true;
+ case MemRegion::BlockDataRegionKind:
+ os << "a block";
+ return true;
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::CXXObjectRegionKind:
+ os << "a C++ object of type " << TR->getValueType().getAsString();
+ return true;
+ case MemRegion::VarRegionKind:
+ os << "a variable of type" << TR->getValueType().getAsString();
+ return true;
+ case MemRegion::FieldRegionKind:
+ os << "a field of type " << TR->getValueType().getAsString();
+ return true;
+ case MemRegion::ObjCIvarRegionKind:
+ os << "an instance variable of type " << TR->getValueType().getAsString();
+ return true;
+ default:
+ return false;
+ }
+}
+
//===----------------------------------------------------------------------===//
// Evaluation of individual function calls.
//===----------------------------------------------------------------------===//
@@ -390,11 +667,20 @@ void CStringChecker::EvalCopyCommon(CheckerContext &C, const GRState *state,
// If the size can be nonzero, we have to check the other arguments.
if (StNonZeroSize) {
state = StNonZeroSize;
- state = CheckBufferAccess(C, state, Size, Dest, Source);
+ state = CheckBufferAccess(C, state, Size, Dest, Source,
+ /* FirstIsDst = */ true);
if (Restricted)
state = CheckOverlap(C, state, Size, Dest, Source);
- if (state)
+
+ if (state) {
+ // Invalidate the destination.
+ // FIXME: Even if we can't perfectly model the copy, we should see if we
+ // can use LazyCompoundVals to copy the source values into the destination.
+ // This would probably remove any existing bindings past the end of the
+ // copied region, but that's still an improvement over blank invalidation.
+ state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest));
C.addTransition(state);
+ }
}
}
@@ -481,7 +767,7 @@ void CStringChecker::EvalMemcmp(CheckerContext &C, const CallExpr *CE) {
if (state) {
// The return value is the comparison result, which we don't know.
unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
- SVal CmpV = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count);
+ SVal CmpV = ValMgr.getConjuredSymbolVal(NULL, CE, Count);
state = state->BindExpr(CE, CmpV);
C.addTransition(state);
}
@@ -489,8 +775,123 @@ void CStringChecker::EvalMemcmp(CheckerContext &C, const CallExpr *CE) {
}
}
+void CStringChecker::EvalStrlen(CheckerContext &C, const CallExpr *CE) {
+ // size_t strlen(const char *s);
+ const GRState *state = C.getState();
+ const Expr *Arg = CE->getArg(0);
+ SVal ArgVal = state->getSVal(Arg);
+
+ // Check that the argument is non-null.
+ state = CheckNonNull(C, state, Arg, ArgVal);
+
+ if (state) {
+ SVal StrLen = GetCStringLength(C, state, Arg, ArgVal);
+
+ // If the argument isn't a valid C string, there's no valid state to
+ // transition to.
+ if (StrLen.isUndef())
+ return;
+
+ // If GetCStringLength couldn't figure out the length, conjure a return
+ // value, so it can be used in constraints, at least.
+ if (StrLen.isUnknown()) {
+ ValueManager &ValMgr = C.getValueManager();
+ unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ StrLen = ValMgr.getConjuredSymbolVal(NULL, CE, Count);
+ }
+
+ // Bind the return value.
+ state = state->BindExpr(CE, StrLen);
+ C.addTransition(state);
+ }
+}
+
+void CStringChecker::EvalStrcpy(CheckerContext &C, const CallExpr *CE) {
+ // char *strcpy(char *restrict dst, const char *restrict src);
+ EvalStrcpyCommon(C, CE, /* ReturnEnd = */ false);
+}
+
+void CStringChecker::EvalStpcpy(CheckerContext &C, const CallExpr *CE) {
+ // char *stpcpy(char *restrict dst, const char *restrict src);
+ EvalStrcpyCommon(C, CE, /* ReturnEnd = */ true);
+}
+
+void CStringChecker::EvalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
+ bool ReturnEnd) {
+ const GRState *state = C.getState();
+
+ // Check that the destination is non-null
+ const Expr *Dst = CE->getArg(0);
+ SVal DstVal = state->getSVal(Dst);
+
+ state = CheckNonNull(C, state, Dst, DstVal);
+ if (!state)
+ return;
+
+ // Check that the source is non-null.
+ const Expr *Src = CE->getArg(1);
+ SVal SrcVal = state->getSVal(Src);
+
+ state = CheckNonNull(C, state, Src, SrcVal);
+ if (!state)
+ return;
+
+ // Get the string length of the source.
+ SVal StrLen = GetCStringLength(C, state, Src, SrcVal);
+
+ // If the source isn't a valid C string, give up.
+ if (StrLen.isUndef())
+ return;
+
+ SVal Result = (ReturnEnd ? UnknownVal() : DstVal);
+
+ // If the destination is a MemRegion, try to check for a buffer overflow and
+ // record the new string length.
+ if (loc::MemRegionVal *DstRegVal = dyn_cast<loc::MemRegionVal>(&DstVal)) {
+ // If the length is known, we can check for an overflow.
+ if (NonLoc *KnownStrLen = dyn_cast<NonLoc>(&StrLen)) {
+ SValuator &SV = C.getSValuator();
+
+ SVal LastElement = SV.EvalBinOpLN(state, BO_Add,
+ *DstRegVal, *KnownStrLen,
+ Dst->getType());
+
+ state = CheckLocation(C, state, Dst, LastElement, /* IsDst = */ true);
+ if (!state)
+ return;
+
+ // If this is a stpcpy-style copy, the last element is the return value.
+ if (ReturnEnd)
+ Result = LastElement;
+ }
+
+ // Invalidate the destination. This must happen before we set the C string
+ // length because invalidation will clear the length.
+ // FIXME: Even if we can't perfectly model the copy, we should see if we
+ // can use LazyCompoundVals to copy the source values into the destination.
+ // This would probably remove any existing bindings past the end of the
+ // string, but that's still an improvement over blank invalidation.
+ state = InvalidateBuffer(C, state, Dst, *DstRegVal);
+
+ // Set the C string length of the destination.
+ state = SetCStringLength(state, DstRegVal->getRegion(), StrLen);
+ }
+
+ // If this is a stpcpy-style copy, but we were unable to check for a buffer
+ // overflow, we still need a result. Conjure a return value.
+ if (ReturnEnd && Result.isUnknown()) {
+ ValueManager &ValMgr = C.getValueManager();
+ unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ StrLen = ValMgr.getConjuredSymbolVal(NULL, CE, Count);
+ }
+
+ // Set the return value.
+ state = state->BindExpr(CE, Result);
+ C.addTransition(state);
+}
+
//===----------------------------------------------------------------------===//
-// The driver method.
+// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
@@ -512,6 +913,9 @@ bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
.Cases("memcpy", "__memcpy_chk", &CStringChecker::EvalMemcpy)
.Cases("memcmp", "bcmp", &CStringChecker::EvalMemcmp)
.Cases("memmove", "__memmove_chk", &CStringChecker::EvalMemmove)
+ .Cases("strcpy", "__strcpy_chk", &CStringChecker::EvalStrcpy)
+ .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::EvalStpcpy)
+ .Case("strlen", &CStringChecker::EvalStrlen)
.Case("bcopy", &CStringChecker::EvalBcopy)
.Default(NULL);
@@ -523,3 +927,129 @@ bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
(this->*EvalFunction)(C, CE);
return true;
}
+
+void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) {
+ // Record string length for char a[] = "abc";
+ const GRState *state = C.getState();
+
+ for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
+ I != E; ++I) {
+ const VarDecl *D = dyn_cast<VarDecl>(*I);
+ if (!D)
+ continue;
+
+ // FIXME: Handle array fields of structs.
+ if (!D->getType()->isArrayType())
+ continue;
+
+ const Expr *Init = D->getInit();
+ if (!Init)
+ continue;
+ if (!isa<StringLiteral>(Init))
+ continue;
+
+ Loc VarLoc = state->getLValue(D, C.getPredecessor()->getLocationContext());
+ const MemRegion *MR = VarLoc.getAsRegion();
+ if (!MR)
+ continue;
+
+ SVal StrVal = state->getSVal(Init);
+ assert(StrVal.isValid() && "Initializer string is unknown or undefined");
+ DefinedOrUnknownSVal StrLen
+ = cast<DefinedOrUnknownSVal>(GetCStringLength(C, state, Init, StrVal));
+
+ state = state->set<CStringLength>(MR, StrLen);
+ }
+
+ C.addTransition(state);
+}
+
+bool CStringChecker::WantsRegionChangeUpdate(const GRState *state) {
+ CStringLength::EntryMap Entries = state->get<CStringLength>();
+ return !Entries.isEmpty();
+}
+
+const GRState *CStringChecker::EvalRegionChanges(const GRState *state,
+ const MemRegion * const *Begin,
+ const MemRegion * const *End,
+ bool *) {
+ CStringLength::EntryMap Entries = state->get<CStringLength>();
+ if (Entries.isEmpty())
+ return state;
+
+ llvm::SmallPtrSet<const MemRegion *, 8> Invalidated;
+ llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
+
+ // First build sets for the changed regions and their super-regions.
+ for ( ; Begin != End; ++Begin) {
+ const MemRegion *MR = *Begin;
+ Invalidated.insert(MR);
+
+ SuperRegions.insert(MR);
+ while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) {
+ MR = SR->getSuperRegion();
+ SuperRegions.insert(MR);
+ }
+ }
+
+ CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>();
+
+ // Then loop over the entries in the current state.
+ for (CStringLength::EntryMap::iterator I = Entries.begin(),
+ E = Entries.end(); I != E; ++I) {
+ const MemRegion *MR = I.getKey();
+
+ // Is this entry for a super-region of a changed region?
+ if (SuperRegions.count(MR)) {
+ Entries = F.Remove(Entries, MR);
+ continue;
+ }
+
+ // Is this entry for a sub-region of a changed region?
+ const MemRegion *Super = MR;
+ while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) {
+ Super = SR->getSuperRegion();
+ if (Invalidated.count(Super)) {
+ Entries = F.Remove(Entries, MR);
+ break;
+ }
+ }
+ }
+
+ return state->set<CStringLength>(Entries);
+}
+
+void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) {
+ // Mark all symbols in our string length map as valid.
+ CStringLength::EntryMap Entries = state->get<CStringLength>();
+
+ for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end();
+ I != E; ++I) {
+ SVal Len = I.getData();
+ if (SymbolRef Sym = Len.getAsSymbol())
+ SR.markInUse(Sym);
+ }
+}
+
+void CStringChecker::EvalDeadSymbols(CheckerContext &C, SymbolReaper &SR) {
+ if (!SR.hasDeadSymbols())
+ return;
+
+ const GRState *state = C.getState();
+ CStringLength::EntryMap Entries = state->get<CStringLength>();
+ if (Entries.isEmpty())
+ return;
+
+ CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>();
+ for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end();
+ I != E; ++I) {
+ SVal Len = I.getData();
+ if (SymbolRef Sym = Len.getAsSymbol()) {
+ if (SR.isDead(Sym))
+ Entries = F.Remove(Entries, I.getKey());
+ }
+ }
+
+ state = state->set<CStringLength>(Entries);
+ C.GenerateNode(state);
+}
diff --git a/lib/Checker/CallAndMessageChecker.cpp b/lib/Checker/CallAndMessageChecker.cpp
index c619d75..3c9ddce 100644
--- a/lib/Checker/CallAndMessageChecker.cpp
+++ b/lib/Checker/CallAndMessageChecker.cpp
@@ -114,7 +114,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
: C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
bool Find(const TypedRegion *R) {
- QualType T = R->getValueType(C);
+ QualType T = R->getValueType();
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
diff --git a/lib/Checker/CallInliner.cpp b/lib/Checker/CallInliner.cpp
deleted file mode 100644
index c47e06c..0000000
--- a/lib/Checker/CallInliner.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===--- CallInliner.cpp - Transfer function that inlines callee ----------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the callee inlining transfer function.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Checker/PathSensitive/CheckerVisitor.h"
-#include "clang/Checker/PathSensitive/GRState.h"
-#include "clang/Checker/Checkers/LocalCheckers.h"
-
-using namespace clang;
-
-namespace {
-class CallInliner : public Checker {
-public:
- static void *getTag() {
- static int x;
- return &x;
- }
-
- virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
-};
-}
-
-void clang::RegisterCallInliner(GRExprEngine &Eng) {
- Eng.registerCheck(new CallInliner());
-}
-
-bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
- const GRState *state = C.getState();
- const Expr *Callee = CE->getCallee();
- SVal L = state->getSVal(Callee);
-
- const FunctionDecl *FD = L.getAsFunctionDecl();
- if (!FD)
- return false;
-
- if (!FD->hasBody(FD))
- return false;
-
- // Now we have the definition of the callee, create a CallEnter node.
- CallEnter Loc(CE, FD, C.getPredecessor()->getLocationContext());
- C.addTransition(state, Loc);
-
- return true;
-}
-
diff --git a/lib/Checker/CastSizeChecker.cpp b/lib/Checker/CastSizeChecker.cpp
index a502c10..6676fe5 100644
--- a/lib/Checker/CastSizeChecker.cpp
+++ b/lib/Checker/CastSizeChecker.cpp
@@ -44,6 +44,10 @@ void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) {
QualType ToPointeeTy = ToPTy->getPointeeType();
+ // Only perform the check if 'ToPointeeTy' is a complete type.
+ if (ToPointeeTy->isIncompleteType())
+ return;
+
const GRState *state = C.getState();
const MemRegion *R = state->getSVal(E).getAsRegion();
if (R == 0)
diff --git a/lib/Checker/CheckDeadStores.cpp b/lib/Checker/CheckDeadStores.cpp
index d6ea187..3896100 100644
--- a/lib/Checker/CheckDeadStores.cpp
+++ b/lib/Checker/CheckDeadStores.cpp
@@ -217,7 +217,7 @@ public:
// If x is EVER assigned a new value later, don't issue
// a warning. This is because such initialization can be
// due to defensive programming.
- if (E->isConstantInitializer(Ctx))
+ if (E->isConstantInitializer(Ctx, false))
return;
if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
@@ -268,7 +268,7 @@ public:
// Check for '&'. Any VarDecl whose value has its address-taken we
// treat as escaped.
Expr* E = U->getSubExpr()->IgnoreParenCasts();
- if (U->getOpcode() == UnaryOperator::AddrOf)
+ if (U->getOpcode() == UO_AddrOf)
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E))
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) {
Escaped.insert(VD);
diff --git a/lib/Checker/CheckSecuritySyntaxOnly.cpp b/lib/Checker/CheckSecuritySyntaxOnly.cpp
index af85c2f..9a2ac45 100644
--- a/lib/Checker/CheckSecuritySyntaxOnly.cpp
+++ b/lib/Checker/CheckSecuritySyntaxOnly.cpp
@@ -41,8 +41,8 @@ class WalkAST : public StmtVisitor<WalkAST> {
public:
WalkAST(BugReporter &br) : BR(br),
- II_gets(0), II_getpw(0), II_mktemp(0),
- II_rand(), II_random(0), II_setid(),
+ II_gets(0), II_getpw(0), II_mktemp(0),
+ II_rand(), II_random(0), II_setid(),
CheckRand(isArc4RandomAvailable(BR.getContext())) {}
// Statement visitor methods.
@@ -131,7 +131,7 @@ GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
- B->getOpcode() == BinaryOperator::Comma))
+ B->getOpcode() == BO_Comma))
return NULL;
if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y))
@@ -217,7 +217,7 @@ void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) {
llvm::SmallString<256> sbuf;
llvm::raw_svector_ostream os(sbuf);
- os << "Variable '" << drCond->getDecl()->getNameAsCString()
+ os << "Variable '" << drCond->getDecl()->getName()
<< "' with floating point type '" << drCond->getType().getAsString()
<< "' should not be used as a loop counter";
@@ -332,10 +332,10 @@ void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a waring.
SourceRange R = CE->getCallee()->getSourceRange();
BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'",
- "Security",
- "Call to function 'mktemp' is insecure as it always "
- "creates or uses insecure temporary file. Use 'mkstemp' instead",
- CE->getLocStart(), &R, 1);
+ "Security",
+ "Call to function 'mktemp' is insecure as it always "
+ "creates or uses insecure temporary file. Use 'mkstemp' instead",
+ CE->getLocStart(), &R, 1);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Checker/CheckerHelpers.cpp b/lib/Checker/CheckerHelpers.cpp
new file mode 100644
index 0000000..ece6943
--- /dev/null
+++ b/lib/Checker/CheckerHelpers.cpp
@@ -0,0 +1,80 @@
+//===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines several static functions for use in checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/PathSensitive/CheckerHelpers.h"
+#include "clang/AST/Expr.h"
+
+// Recursively find any substatements containing macros
+bool clang::containsMacro(const Stmt *S) {
+ if (S->getLocStart().isMacroID())
+ return true;
+
+ if (S->getLocEnd().isMacroID())
+ return true;
+
+ for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+ ++I)
+ if (const Stmt *child = *I)
+ if (containsMacro(child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing enum constants
+bool clang::containsEnum(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+ if (DR && isa<EnumConstantDecl>(DR->getDecl()))
+ return true;
+
+ for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+ ++I)
+ if (const Stmt *child = *I)
+ if (containsEnum(child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing static vars
+bool clang::containsStaticLocal(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+ if (DR)
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ if (VD->isStaticLocal())
+ return true;
+
+ for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+ ++I)
+ if (const Stmt *child = *I)
+ if (containsStaticLocal(child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing __builtin_offsetof
+bool clang::containsBuiltinOffsetOf(const Stmt *S) {
+ if (isa<OffsetOfExpr>(S))
+ return true;
+
+ for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+ ++I)
+ if (const Stmt *child = *I)
+ if (containsBuiltinOffsetOf(child))
+ return true;
+
+ return false;
+}
diff --git a/lib/Checker/CocoaConventions.cpp b/lib/Checker/CocoaConventions.cpp
index 3ba887c..b446a04 100644
--- a/lib/Checker/CocoaConventions.cpp
+++ b/lib/Checker/CocoaConventions.cpp
@@ -173,9 +173,10 @@ bool cocoa::isCocoaObjectRef(QualType Ty) {
if (!PT)
return true;
- // We assume that id<..>, id, and "Class" all represent tracked objects.
+ // We assume that id<..>, id, Class, and Class<..> all represent tracked
+ // objects.
if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() ||
- PT->isObjCClassType())
+ PT->isObjCClassType() || PT->isObjCQualifiedClassType())
return true;
// Does the interface subclass NSObject?
diff --git a/lib/Checker/DivZeroChecker.cpp b/lib/Checker/DivZeroChecker.cpp
index e09a871..32e2a17 100644
--- a/lib/Checker/DivZeroChecker.cpp
+++ b/lib/Checker/DivZeroChecker.cpp
@@ -40,10 +40,10 @@ void *DivZeroChecker::getTag() {
void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C,
const BinaryOperator *B) {
BinaryOperator::Opcode Op = B->getOpcode();
- if (Op != BinaryOperator::Div &&
- Op != BinaryOperator::Rem &&
- Op != BinaryOperator::DivAssign &&
- Op != BinaryOperator::RemAssign)
+ if (Op != BO_Div &&
+ Op != BO_Rem &&
+ Op != BO_DivAssign &&
+ Op != BO_RemAssign)
return;
if (!B->getRHS()->getType()->isIntegerType() ||
diff --git a/lib/Checker/Environment.cpp b/lib/Checker/Environment.cpp
index 48152ce..02291f4 100644
--- a/lib/Checker/Environment.cpp
+++ b/lib/Checker/Environment.cpp
@@ -80,7 +80,7 @@ SVal Environment::GetSVal(const Stmt *E, ValueManager& ValMgr) const {
return LookupExpr(E);
}
-Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S,
+Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S,
SVal V, bool Invalidate) {
assert(S);
@@ -94,6 +94,16 @@ Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S,
return Environment(F.Add(Env.ExprBindings, S, V));
}
+static inline const Stmt *MakeLocation(const Stmt *S) {
+ return (const Stmt*) (((uintptr_t) S) | 0x1);
+}
+
+Environment EnvironmentManager::bindExprAndLocation(Environment Env,
+ const Stmt *S,
+ SVal location, SVal V) {
+ return Environment(F.Add(F.Add(Env.ExprBindings, MakeLocation(S), V), S, V));
+}
+
namespace {
class MarkLiveCallback : public SymbolVisitor {
SymbolReaper &SymReaper;
@@ -115,6 +125,12 @@ static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) {
return false;
}
+// In addition to mapping from Stmt * - > SVals in the Environment, we also
+// maintain a mapping from Stmt * -> SVals (locations) that were used during
+// a load and store.
+static inline bool IsLocation(const Stmt *S) {
+ return (bool) (((uintptr_t) S) & 0x1);
+}
// RemoveDeadBindings:
// - Remove subexpression bindings.
@@ -123,7 +139,6 @@ static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) {
// - Mark their reachable symbols live in SymbolReaper,
// see ScanReachableSymbols.
// - Mark the region in DRoots if the binding is a loc::MemRegionVal.
-
Environment
EnvironmentManager::RemoveDeadBindings(Environment Env,
SymbolReaper &SymReaper,
@@ -136,12 +151,25 @@ EnvironmentManager::RemoveDeadBindings(Environment Env,
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
Environment NewEnv = getInitialEnvironment();
+
+ llvm::SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations;
// Iterate over the block-expr bindings.
for (Environment::iterator I = Env.begin(), E = Env.end();
I != E; ++I) {
const Stmt *BlkExpr = I.getKey();
+
+ // For recorded locations (used when evaluating loads and stores), we
+ // consider them live only when their associated normal expression is
+ // also live.
+ // NOTE: This assumes that loads/stores that evaluated to UnknownVal
+ // still have an entry in the map.
+ if (IsLocation(BlkExpr)) {
+ deferredLocations.push_back(std::make_pair(BlkExpr, I.getData()));
+ continue;
+ }
+
const SVal &X = I.getData();
// Block-level expressions in callers are assumed always live.
@@ -186,6 +214,15 @@ EnvironmentManager::RemoveDeadBindings(Environment Env,
if (X.isUndef() && cast<UndefinedVal>(X).getData())
NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X);
}
+
+ // Go through he deferred locations and add them to the new environment if
+ // the correspond Stmt* is in the map as well.
+ for (llvm::SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator
+ I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) {
+ const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1);
+ if (NewEnv.ExprBindings.lookup(S))
+ NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, I->first, I->second);
+ }
return NewEnv;
}
diff --git a/lib/Checker/FixedAddressChecker.cpp b/lib/Checker/FixedAddressChecker.cpp
index 4fce45b..29a3c3a 100644
--- a/lib/Checker/FixedAddressChecker.cpp
+++ b/lib/Checker/FixedAddressChecker.cpp
@@ -40,7 +40,7 @@ void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C,
// Using a fixed address is not portable because that address will probably
// not be valid in all environments or platforms.
- if (B->getOpcode() != BinaryOperator::Assign)
+ if (B->getOpcode() != BO_Assign)
return;
QualType T = B->getType();
diff --git a/lib/Checker/FlatStore.cpp b/lib/Checker/FlatStore.cpp
index 64575b3c9..21fa422 100644
--- a/lib/Checker/FlatStore.cpp
+++ b/lib/Checker/FlatStore.cpp
@@ -44,11 +44,10 @@ public:
}
SVal ArrayToPointer(Loc Array);
- const GRState *RemoveDeadBindings(GRState &state,
- const StackFrameContext *LCtx,
+ Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx,
SymbolReaper& SymReaper,
llvm::SmallVectorImpl<const MemRegion*>& RegionRoots){
- return StateMgr.getPersistentState(state);
+ return store;
}
Store BindDecl(Store store, const VarRegion *VR, SVal initVal);
@@ -57,13 +56,10 @@ public:
typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols;
- Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E,
- unsigned Count, InvalidatedSymbols *IS);
-
Store InvalidateRegions(Store store, const MemRegion * const *I,
const MemRegion * const *E, const Expr *Ex,
unsigned Count, InvalidatedSymbols *IS,
- bool invalidateGlobals);
+ bool invalidateGlobals, InvalidatedRegions *Regions);
void print(Store store, llvm::raw_ostream& Out, const char* nl,
const char *sep);
@@ -74,7 +70,14 @@ private:
return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store));
}
- Interval RegionToInterval(const MemRegion *R);
+ class RegionInterval {
+ public:
+ const MemRegion *R;
+ Interval I;
+ RegionInterval(const MemRegion *r, int64_t s, int64_t e) : R(r), I(s, e){}
+ };
+
+ RegionInterval RegionToInterval(const MemRegion *R);
SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T);
};
@@ -86,11 +89,15 @@ StoreManager *clang::CreateFlatStoreManager(GRStateManager &StMgr) {
SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) {
const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
- Interval I = RegionToInterval(R);
+ RegionInterval RI = RegionToInterval(R);
+ // FIXME: FlatStore should handle regions with unknown intervals.
+ if (!RI.R)
+ return UnknownVal();
+
RegionBindings B = getRegionBindings(store);
- const BindingVal *BV = B.lookup(R);
+ const BindingVal *BV = B.lookup(RI.R);
if (BV) {
- const SVal *V = BVFactory.Lookup(*BV, I);
+ const SVal *V = BVFactory.Lookup(*BV, RI.I);
if (V)
return *V;
else
@@ -116,9 +123,12 @@ Store FlatStoreManager::Bind(Store store, Loc L, SVal val) {
if (V)
BV = *V;
- Interval I = RegionToInterval(R);
- BV = BVFactory.Add(BV, I, val);
- B = RBFactory.Add(B, R, BV);
+ RegionInterval RI = RegionToInterval(R);
+ // FIXME: FlatStore should handle regions with unknown intervals.
+ if (!RI.R)
+ return B.getRoot();
+ BV = BVFactory.Add(BV, RI.I, val);
+ B = RBFactory.Add(B, RI.R, BV);
return B.getRoot();
}
@@ -139,7 +149,7 @@ SVal FlatStoreManager::ArrayToPointer(Loc Array) {
Store FlatStoreManager::BindDecl(Store store, const VarRegion *VR,
SVal initVal) {
- return store;
+ return Bind(store, ValMgr.makeLoc(VR), initVal);
}
Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) {
@@ -147,18 +157,12 @@ Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) {
}
Store FlatStoreManager::InvalidateRegions(Store store,
- const MemRegion * const *I,
- const MemRegion * const *E,
- const Expr *Ex, unsigned Count,
- InvalidatedSymbols *IS,
- bool invalidateGlobals) {
- assert(false && "Not implemented");
- return store;
-}
-
-Store FlatStoreManager::InvalidateRegion(Store store, const MemRegion *R,
- const Expr *E, unsigned Count,
- InvalidatedSymbols *IS) {
+ const MemRegion * const *I,
+ const MemRegion * const *E,
+ const Expr *Ex, unsigned Count,
+ InvalidatedSymbols *IS,
+ bool invalidateGlobals,
+ InvalidatedRegions *Regions) {
assert(false && "Not implemented");
return store;
}
@@ -170,15 +174,29 @@ void FlatStoreManager::print(Store store, llvm::raw_ostream& Out,
void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) {
}
-Interval FlatStoreManager::RegionToInterval(const MemRegion *R) {
+FlatStoreManager::RegionInterval
+FlatStoreManager::RegionToInterval(const MemRegion *R) {
switch (R->getKind()) {
case MemRegion::VarRegionKind: {
- QualType T = cast<VarRegion>(R)->getValueType(Ctx);
- uint64_t Size = Ctx.getTypeSize(T);
- return Interval(0, Size-1);
+ QualType T = cast<VarRegion>(R)->getValueType();
+ int64_t Size = Ctx.getTypeSize(T);
+ return RegionInterval(R, 0, Size-1);
}
+
+ case MemRegion::ElementRegionKind:
+ case MemRegion::FieldRegionKind: {
+ RegionOffset Offset = R->getAsOffset();
+ // We cannot compute offset for all regions, for example, elements
+ // with symbolic offsets.
+ if (!Offset.getRegion())
+ return RegionInterval(0, 0, 0);
+ int64_t Start = Offset.getOffset();
+ int64_t Size = Ctx.getTypeSize(cast<TypedRegion>(R)->getValueType());
+ return RegionInterval(Offset.getRegion(), Start, Start+Size);
+ }
+
default:
llvm_unreachable("Region kind unhandled.");
- return Interval(0, 0);
+ return RegionInterval(0, 0, 0);
}
}
diff --git a/lib/Checker/GRCXXExprEngine.cpp b/lib/Checker/GRCXXExprEngine.cpp
index 18e112c..a49989b 100644
--- a/lib/Checker/GRCXXExprEngine.cpp
+++ b/lib/Checker/GRCXXExprEngine.cpp
@@ -17,7 +17,7 @@
using namespace clang;
-void GRExprEngine::EvalArguments(ExprIterator AI, ExprIterator AE,
+void GRExprEngine::EvalArguments(ConstExprIterator AI, ConstExprIterator AE,
const FunctionProtoType *FnType,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
llvm::SmallVector<CallExprWLItem, 20> WorkList;
@@ -55,7 +55,7 @@ const CXXThisRegion *GRExprEngine::getCXXThisRegion(const CXXMethodDecl *D,
return ValMgr.getRegionManager().getCXXThisRegion(PT, SFC);
}
-void GRExprEngine::CreateCXXTemporaryObject(Expr *Ex, ExplodedNode *Pred,
+void GRExprEngine::CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
@@ -94,9 +94,7 @@ void GRExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, SVal Dest,
// Evaluate other arguments.
ExplodedNodeSet ArgsEvaluated;
const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>();
- EvalArguments(const_cast<CXXConstructExpr*>(E)->arg_begin(),
- const_cast<CXXConstructExpr*>(E)->arg_end(),
- FnType, Pred, ArgsEvaluated);
+ EvalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, ArgsEvaluated);
// The callee stack frame context used to create the 'this' parameter region.
const StackFrameContext *SFC = AMgr.getStackFrame(CD,
Pred->getLocationContext(),
@@ -104,11 +102,12 @@ void GRExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, SVal Dest,
const CXXThisRegion *ThisR = getCXXThisRegion(E->getConstructor(), SFC);
- CallEnter Loc(E, CD, Pred->getLocationContext());
+ CallEnter Loc(E, SFC->getAnalysisContext(), Pred->getLocationContext());
for (ExplodedNodeSet::iterator NI = ArgsEvaluated.begin(),
NE = ArgsEvaluated.end(); NI != NE; ++NI) {
const GRState *state = GetState(*NI);
- // Setup 'this' region.
+ // Setup 'this' region, so that the ctor is evaluated on the object pointed
+ // by 'Dest'.
state = state->bindLoc(loc::MemRegionVal(ThisR), Dest);
ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
if (N)
@@ -126,9 +125,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE,
// Evaluate explicit arguments with a worklist.
ExplodedNodeSet ArgsEvaluated;
- EvalArguments(const_cast<CXXMemberCallExpr*>(MCE)->arg_begin(),
- const_cast<CXXMemberCallExpr*>(MCE)->arg_end(),
- FnType, Pred, ArgsEvaluated);
+ EvalArguments(MCE->arg_begin(), MCE->arg_end(), FnType, Pred, ArgsEvaluated);
// Evaluate the implicit object argument.
ExplodedNodeSet AllArgsEvaluated;
@@ -157,7 +154,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE,
Builder->getBlock(),
Builder->getIndex());
const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC);
- CallEnter Loc(MCE, MD, Pred->getLocationContext());
+ CallEnter Loc(MCE, SFC->getAnalysisContext(), Pred->getLocationContext());
for (ExplodedNodeSet::iterator I = AllArgsEvaluated.begin(),
E = AllArgsEvaluated.end(); I != E; ++I) {
// Set up 'this' region.
@@ -169,7 +166,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE,
}
}
-void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred,
+void GRExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
if (CNE->isArray()) {
// FIXME: allocating an array has not been handled.
@@ -177,7 +174,7 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred,
}
unsigned Count = Builder->getCurrentBlockCount();
- DefinedOrUnknownSVal SymVal = getValueManager().getConjuredSymbolVal(NULL,CNE,
+ DefinedOrUnknownSVal SymVal = getValueManager().getConjuredSymbolVal(NULL,CNE,
CNE->getType(), Count);
const MemRegion *NewReg = cast<loc::MemRegionVal>(SymVal).getRegion();
@@ -201,10 +198,7 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred,
const GRState *state = GetState(*I);
if (ObjTy->isRecordType()) {
- Store store = state->getStore();
- StoreManager::InvalidatedSymbols IS;
- store = getStoreManager().InvalidateRegion(store, EleReg, CNE, Count, &IS);
- state = state->makeWithStore(store);
+ state = state->InvalidateRegion(EleReg, CNE, Count);
} else {
if (CNE->hasInitializer()) {
SVal V = state->getSVal(*CNE->constructor_arg_begin());
@@ -220,8 +214,8 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred,
}
}
-void GRExprEngine::VisitCXXDeleteExpr(CXXDeleteExpr *CDE, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
+void GRExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
+ ExplodedNode *Pred,ExplodedNodeSet &Dst) {
// Should do more checking.
ExplodedNodeSet ArgEvaluated;
Visit(CDE->getArgument(), Pred, ArgEvaluated);
@@ -232,7 +226,7 @@ void GRExprEngine::VisitCXXDeleteExpr(CXXDeleteExpr *CDE, ExplodedNode *Pred,
}
}
-void GRExprEngine::VisitCXXThisExpr(CXXThisExpr *TE, ExplodedNode *Pred,
+void GRExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// Get the this object region from StoreManager.
const MemRegion *R =
diff --git a/lib/Checker/GRCoreEngine.cpp b/lib/Checker/GRCoreEngine.cpp
index a816186..5125f36 100644
--- a/lib/Checker/GRCoreEngine.cpp
+++ b/lib/Checker/GRCoreEngine.cpp
@@ -12,8 +12,10 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Checker/PathSensitive/AnalysisManager.h"
#include "clang/Checker/PathSensitive/GRCoreEngine.h"
#include "clang/Checker/PathSensitive/GRExprEngine.h"
+#include "clang/Index/TranslationUnit.h"
#include "clang/AST/Expr.h"
#include "llvm/Support/Casting.h"
#include "llvm/ADT/DenseMap.h"
@@ -24,6 +26,12 @@ using llvm::cast;
using llvm::isa;
using namespace clang;
+// This should be removed in the future.
+namespace clang {
+GRTransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
+ const LangOptions& lopts);
+}
+
//===----------------------------------------------------------------------===//
// Worklist classes for exploration of reachable states.
//===----------------------------------------------------------------------===//
@@ -118,47 +126,15 @@ GRWorkList* GRWorkList::MakeBFSBlockDFSContents() {
//===----------------------------------------------------------------------===//
// Core analysis engine.
//===----------------------------------------------------------------------===//
-void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) {
- SubEngine.ProcessEndPath(Builder);
-}
-
-void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) {
- SubEngine.ProcessStmt(E, Builder);
-}
-
-bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const ExplodedNode *Pred,
- GRBlockCounter BC) {
- return SubEngine.ProcessBlockEntrance(Blk, Pred, BC);
-}
-
-void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator,
- GRBranchNodeBuilder& Builder) {
- SubEngine.ProcessBranch(Condition, Terminator, Builder);
-}
-
-void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) {
- SubEngine.ProcessIndirectGoto(Builder);
-}
-
-void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) {
- SubEngine.ProcessSwitch(Builder);
-}
-
-void GRCoreEngine::ProcessCallEnter(GRCallEnterNodeBuilder &Builder) {
- SubEngine.ProcessCallEnter(Builder);
-}
-
-void GRCoreEngine::ProcessCallExit(GRCallExitNodeBuilder &Builder) {
- SubEngine.ProcessCallExit(Builder);
-}
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
-bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) {
+bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
+ const GRState *InitState) {
if (G->num_roots() == 0) { // Initialize the analysis by constructing
// the root if none exists.
- CFGBlock* Entry = &(L->getCFG()->getEntry());
+ const CFGBlock* Entry = &(L->getCFG()->getEntry());
assert (Entry->empty() &&
"Entry block must be empty.");
@@ -167,7 +143,7 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) {
"Entry block must have 1 successor.");
// Get the solitary successor.
- CFGBlock* Succ = *(Entry->succ_begin());
+ const CFGBlock* Succ = *(Entry->succ_begin());
// Construct an edge representing the
// starting location in the function.
@@ -176,8 +152,11 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) {
// Set the current block counter to being empty.
WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
- // Generate the root.
- GenerateNode(StartLoc, getInitialState(L), 0);
+ if (!InitState)
+ // Generate the root.
+ GenerateNode(StartLoc, getInitialState(L), 0);
+ else
+ GenerateNode(StartLoc, InitState, 0);
}
while (Steps && WList->hasWork()) {
@@ -221,14 +200,25 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) {
}
}
- SubEngine.ProcessEndWorklist(WList->hasWork() || BlockAborted);
+ SubEngine.ProcessEndWorklist(hasWorkRemaining());
return WList->hasWork();
}
+void GRCoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
+ unsigned Steps,
+ const GRState *InitState,
+ ExplodedNodeSet &Dst) {
+ ExecuteWorkList(L, Steps, InitState);
+ for (llvm::SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(),
+ E = G->EndNodes.end(); I != E; ++I) {
+ Dst.Add(*I);
+ }
+}
+
void GRCoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block,
unsigned Index, ExplodedNode *Pred) {
- GRCallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), L.getCallee(),
- Block, Index);
+ GRCallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(),
+ L.getCalleeContext(), Block, Index);
ProcessCallEnter(Builder);
}
@@ -239,7 +229,7 @@ void GRCoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) {
void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) {
- CFGBlock* Blk = L.getDst();
+ const CFGBlock* Blk = L.getDst();
// Check if we are entering the EXIT block.
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
@@ -260,8 +250,9 @@ void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) {
if (ProcessBlockEntrance(Blk, Pred, WList->getBlockCounter()))
GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()),
Pred->State, Pred);
- else
- BlockAborted = true;
+ else {
+ blocksAborted.push_back(std::make_pair(L, Pred));
+ }
}
void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L,
@@ -284,9 +275,9 @@ void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L,
HandleBlockExit(L.getBlock(), Pred);
}
-void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) {
+void GRCoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) {
- if (Stmt* Term = B->getTerminator()) {
+ if (const Stmt* Term = B->getTerminator()) {
switch (Term->getStmtClass()) {
default:
assert(false && "Analysis for this terminator not implemented.");
@@ -372,8 +363,8 @@ void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) {
Pred->State, Pred);
}
-void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B,
- ExplodedNode* Pred) {
+void GRCoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term,
+ const CFGBlock * B, ExplodedNode* Pred) {
assert (B->succ_size() == 2);
GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1),
@@ -382,7 +373,7 @@ void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B,
ProcessBranch(Cond, Term, Builder);
}
-void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B,
+void GRCoreEngine::HandlePostStmt(const PostStmt& L, const CFGBlock* B,
unsigned StmtIdx, ExplodedNode* Pred) {
assert (!B->empty());
@@ -415,7 +406,7 @@ void GRCoreEngine::GenerateNode(const ProgramPoint& Loc,
if (IsNew) WList->Enqueue(Node);
}
-GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx,
+GRStmtNodeBuilder::GRStmtNodeBuilder(const CFGBlock* b, unsigned idx,
ExplodedNode* N, GRCoreEngine* e,
GRStateManager &mgr)
: Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), Auditor(0),
@@ -438,7 +429,7 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
if (isa<CallEnter>(N->getLocation())) {
// Still use the index of the CallExpr. It's needed to create the callee
// StackFrameContext.
- Eng.WList->Enqueue(N, B, Idx);
+ Eng.WList->Enqueue(N, &B, Idx);
return;
}
@@ -447,7 +438,7 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
if (Loc == N->getLocation()) {
// Note: 'N' should be a fresh node because otherwise it shouldn't be
// a member of Deferred.
- Eng.WList->Enqueue(N, B, Idx+1);
+ Eng.WList->Enqueue(N, &B, Idx+1);
return;
}
@@ -456,10 +447,10 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
Succ->addPredecessor(N, *Eng.G);
if (IsNew)
- Eng.WList->Enqueue(Succ, B, Idx+1);
+ Eng.WList->Enqueue(Succ, &B, Idx+1);
}
-ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, Stmt* S,
+ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
ExplodedNode* Pred, const GRState* St,
ProgramPoint::Kind K) {
const GRState* PredState = GetState(Pred);
@@ -692,6 +683,59 @@ void GREndPathNodeBuilder::GenerateCallExitNode(const GRState *state) {
void GRCallEnterNodeBuilder::GenerateNode(const GRState *state,
const LocationContext *LocCtx) {
+ // Check if the callee is in the same translation unit.
+ if (CalleeCtx->getTranslationUnit() !=
+ Pred->getLocationContext()->getTranslationUnit()) {
+ // Create a new engine. We must be careful that the new engine should not
+ // reference data structures owned by the old engine.
+
+ AnalysisManager &OldMgr = Eng.SubEngine.getAnalysisManager();
+
+ // Get the callee's translation unit.
+ idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit();
+
+ // Create a new AnalysisManager with components of the callee's
+ // TranslationUnit.
+ // The Diagnostic is actually shared when we create ASTUnits from AST files.
+ AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(),
+ OldMgr.getLangOptions(),
+ OldMgr.getPathDiagnosticClient(),
+ OldMgr.getStoreManagerCreator(),
+ OldMgr.getConstraintManagerCreator(),
+ OldMgr.getIndexer(),
+ OldMgr.getMaxNodes(), OldMgr.getMaxLoop(),
+ OldMgr.shouldVisualizeGraphviz(),
+ OldMgr.shouldVisualizeUbigraph(),
+ OldMgr.shouldPurgeDead(),
+ OldMgr.shouldEagerlyAssume(),
+ OldMgr.shouldTrimGraph(),
+ OldMgr.shouldInlineCall(),
+ OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG());
+ llvm::OwningPtr<GRTransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(),
+ /* GCEnabled */ false,
+ AMgr.getLangOptions()));
+ // Create the new engine.
+ GRExprEngine NewEng(AMgr, TF.take());
+
+ // Create the new LocationContext.
+ AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(),
+ CalleeCtx->getTranslationUnit());
+ const StackFrameContext *OldLocCtx = cast<StackFrameContext>(LocCtx);
+ const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx,
+ OldLocCtx->getParent(),
+ OldLocCtx->getCallSite(),
+ OldLocCtx->getCallSiteBlock(),
+ OldLocCtx->getIndex());
+
+ // Now create an initial state for the new engine.
+ const GRState *NewState = NewEng.getStateManager().MarshalState(state,
+ NewLocCtx);
+ ExplodedNodeSet ReturnNodes;
+ NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(),
+ NewState, ReturnNodes);
+ return;
+ }
+
// Get the callee entry block.
const CFGBlock *Entry = &(LocCtx->getCFG()->getEntry());
assert(Entry->empty());
@@ -721,6 +765,6 @@ void GRCallExitNodeBuilder::GenerateNode(const GRState *state) {
ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew);
Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G);
if (isNew)
- Eng.WList->Enqueue(Node, *const_cast<CFGBlock*>(LocCtx->getCallSiteBlock()),
+ Eng.WList->Enqueue(Node, LocCtx->getCallSiteBlock(),
LocCtx->getIndex() + 1);
}
diff --git a/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp
index 07fee9d..feb826e 100644
--- a/lib/Checker/GRExprEngine.cpp
+++ b/lib/Checker/GRExprEngine.cpp
@@ -169,17 +169,18 @@ public:
// Checker worklist routines.
//===----------------------------------------------------------------------===//
-void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
- ExplodedNodeSet &Src, bool isPrevisit) {
+void GRExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst,
+ ExplodedNodeSet &Src, CallbackKind Kind) {
// Determine if we already have a cached 'CheckersOrdered' vector
- // specifically tailored for the provided <Stmt kind, isPrevisit>. This
+ // specifically tailored for the provided <CallbackKind, Stmt kind>. This
// can reduce the number of checkers actually called.
CheckersOrdered *CO = &Checkers;
llvm::OwningPtr<CheckersOrdered> NewCO;
-
- const std::pair<unsigned, unsigned> &K =
- std::make_pair((unsigned)S->getStmtClass(), isPrevisit ? 1U : 0U);
+
+ // The cache key is made up of the and the callback kind (pre- or post-visit)
+ // and the statement kind.
+ CallbackTag K = GetCallbackTag(Kind, S->getStmtClass());
CheckersOrdered *& CO_Ref = COCache[K];
@@ -204,7 +205,10 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
ExplodedNodeSet *PrevSet = &Src;
unsigned checkersEvaluated = 0;
- for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I){
+ for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I) {
+ // If all nodes are sunk, bail out early.
+ if (PrevSet->empty())
+ break;
ExplodedNodeSet *CurrSet = 0;
if (I+1 == E)
CurrSet = &Dst;
@@ -219,8 +223,8 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
NI != NE; ++NI) {
- checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit,
- respondsToCallback);
+ checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag,
+ Kind == PreVisitStmtCallback, respondsToCallback);
}
@@ -235,7 +239,9 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
// If we built NewCO, check if we called all the checkers. This is important
// so that we know that we accurately determined the entire set of checkers
- // that responds to this callback.
+ // that responds to this callback. Note that 'checkersEvaluated' might
+ // not be the same as Checkers.size() if one of the Checkers generates
+ // a sink node.
if (NewCO.get() && checkersEvaluated == Checkers.size())
CO_Ref = NewCO.take();
@@ -300,10 +306,9 @@ bool GRExprEngine::CheckerEvalCall(const CallExpr *CE,
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
// unify.
-void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
- ExplodedNodeSet &Dst,
- ExplodedNodeSet &Src,
- SVal location, SVal val, bool isPrevisit) {
+void GRExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst,
+ ExplodedNodeSet &Src, SVal location,
+ SVal val, bool isPrevisit) {
if (Checkers.empty()) {
Dst.insert(Src);
@@ -328,7 +333,7 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
NI != NE; ++NI)
- checker->GR_VisitBind(*CurrSet, *Builder, *this, AssignE, StoreE,
+ checker->GR_VisitBind(*CurrSet, *Builder, *this, StoreE,
*NI, tag, location, val, isPrevisit);
// Update which NodeSet is the current one.
@@ -457,7 +462,7 @@ const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) {
break;
SVal V = state->getSVal(loc::MemRegionVal(R));
- SVal Constraint_untested = EvalBinOp(state, BinaryOperator::GT, V,
+ SVal Constraint_untested = EvalBinOp(state, BO_GT, V,
ValMgr.makeZeroVal(T),
getContext().IntTy);
@@ -499,29 +504,135 @@ const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) {
/// logic for handling assumptions on symbolic values.
const GRState *GRExprEngine::ProcessAssume(const GRState *state, SVal cond,
bool assumption) {
- for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
- I != E; ++I) {
+ // Determine if we already have a cached 'CheckersOrdered' vector
+ // specifically tailored for processing assumptions. This
+ // can reduce the number of checkers actually called.
+ CheckersOrdered *CO = &Checkers;
+ llvm::OwningPtr<CheckersOrdered> NewCO;
- if (!state)
- return NULL;
+ CallbackTag K = GetCallbackTag(ProcessAssumeCallback);
+ CheckersOrdered *& CO_Ref = COCache[K];
+
+ if (!CO_Ref) {
+ // If we have no previously cached CheckersOrdered vector for this
+ // statement kind, then create one.
+ NewCO.reset(new CheckersOrdered);
+ }
+ else {
+ // Use the already cached set.
+ CO = CO_Ref;
+ }
+
+ if (!CO->empty()) {
+ // Let the checkers have a crack at the assume before the transfer functions
+ // get their turn.
+ for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I!=E; ++I) {
- state = I->second->EvalAssume(state, cond, assumption);
+ // If any checker declares the state infeasible (or if it starts that
+ // way), bail out.
+ if (!state)
+ return NULL;
+
+ Checker *C = I->second;
+ bool respondsToCallback = true;
+
+ state = C->EvalAssume(state, cond, assumption, &respondsToCallback);
+
+ // Check if we're building the cache of checkers that care about Assumes.
+ if (NewCO.get() && respondsToCallback)
+ NewCO->push_back(*I);
+ }
+
+ // If we got through all the checkers, and we built a list of those that
+ // care about Assumes, save it.
+ if (NewCO.get())
+ CO_Ref = NewCO.take();
}
+ // If the state is infeasible at this point, bail out.
if (!state)
return NULL;
return TF->EvalAssume(state, cond, assumption);
}
+bool GRExprEngine::WantsRegionChangeUpdate(const GRState* state) {
+ CallbackTag K = GetCallbackTag(EvalRegionChangesCallback);
+ CheckersOrdered *CO = COCache[K];
+
+ if (!CO)
+ CO = &Checkers;
+
+ for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) {
+ Checker *C = I->second;
+ if (C->WantsRegionChangeUpdate(state))
+ return true;
+ }
+
+ return false;
+}
+
+const GRState *
+GRExprEngine::ProcessRegionChanges(const GRState *state,
+ const MemRegion * const *Begin,
+ const MemRegion * const *End) {
+ // FIXME: Most of this method is copy-pasted from ProcessAssume.
+
+ // Determine if we already have a cached 'CheckersOrdered' vector
+ // specifically tailored for processing region changes. This
+ // can reduce the number of checkers actually called.
+ CheckersOrdered *CO = &Checkers;
+ llvm::OwningPtr<CheckersOrdered> NewCO;
+
+ CallbackTag K = GetCallbackTag(EvalRegionChangesCallback);
+ CheckersOrdered *& CO_Ref = COCache[K];
+
+ if (!CO_Ref) {
+ // If we have no previously cached CheckersOrdered vector for this
+ // callback, then create one.
+ NewCO.reset(new CheckersOrdered);
+ }
+ else {
+ // Use the already cached set.
+ CO = CO_Ref;
+ }
+
+ // If there are no checkers, just return the state as is.
+ if (CO->empty())
+ return state;
+
+ for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) {
+ // If any checker declares the state infeasible (or if it starts that way),
+ // bail out.
+ if (!state)
+ return NULL;
+
+ Checker *C = I->second;
+ bool respondsToCallback = true;
+
+ state = C->EvalRegionChanges(state, Begin, End, &respondsToCallback);
+
+ // See if we're building a cache of checkers that care about region changes.
+ if (NewCO.get() && respondsToCallback)
+ NewCO->push_back(*I);
+ }
+
+ // If we got through all the checkers, and we built a list of those that
+ // care about region changes, save it.
+ if (NewCO.get())
+ CO_Ref = NewCO.take();
+
+ return state;
+}
+
void GRExprEngine::ProcessEndWorklist(bool hasWorkRemaining) {
for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
I != E; ++I) {
- I->second->VisitEndAnalysis(G, BR, hasWorkRemaining);
+ I->second->VisitEndAnalysis(G, BR, *this);
}
}
-void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) {
+void GRExprEngine::ProcessStmt(const CFGElement CE,GRStmtNodeBuilder& builder) {
CurrentStmt = CE.getStmt();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
CurrentStmt->getLocStart(),
@@ -535,15 +646,23 @@ void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) {
Builder->setAuditor(BatchAuditor.get());
// Create the cleaned state.
- const ExplodedNode *BasePred = Builder->getBasePredecessor();
+ const LocationContext *LC = EntryNode->getLocationContext();
+ SymbolReaper SymReaper(LC, CurrentStmt, SymMgr);
+
+ if (AMgr.shouldPurgeDead()) {
+ const GRState *St = EntryNode->getState();
- SymbolReaper SymReaper(BasePred->getLocationContext(), CurrentStmt, SymMgr);
+ for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end();
+ I != E; ++I) {
+ Checker *checker = I->second;
+ checker->MarkLiveSymbols(St, SymReaper);
+ }
- CleanedState = AMgr.shouldPurgeDead()
- ? StateMgr.RemoveDeadBindings(EntryNode->getState(),
- BasePred->getLocationContext()->getCurrentStackFrame(),
- SymReaper)
- : EntryNode->getState();
+ const StackFrameContext *SFC = LC->getCurrentStackFrame();
+ CleanedState = StateMgr.RemoveDeadBindings(St, SFC, SymReaper);
+ } else {
+ CleanedState = EntryNode->getState();
+ }
// Process any special transfer function for dead symbols.
ExplodedNodeSet Tmp;
@@ -625,7 +744,8 @@ void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) {
Builder = NULL;
}
-void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
+void GRExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
+ ExplodedNodeSet& Dst) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
S->getLocStart(),
"Error evaluating statement");
@@ -641,7 +761,6 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
switch (S->getStmtClass()) {
// C++ stuff we don't support yet.
- case Stmt::CXXBindReferenceExprClass:
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXCatchStmtClass:
case Stmt::CXXConstructExprClass:
@@ -740,13 +859,13 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
break;
case Stmt::BinaryOperatorClass: {
- BinaryOperator* B = cast<BinaryOperator>(S);
+ const BinaryOperator* B = cast<BinaryOperator>(S);
if (B->isLogicalOp()) {
VisitLogicalExpr(B, Pred, Dst);
break;
}
- else if (B->getOpcode() == BinaryOperator::Comma) {
+ else if (B->getOpcode() == BO_Comma) {
const GRState* state = GetState(Pred);
MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS())));
break;
@@ -766,25 +885,25 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
case Stmt::CallExprClass:
case Stmt::CXXOperatorCallExprClass: {
- CallExpr* C = cast<CallExpr>(S);
+ const CallExpr* C = cast<CallExpr>(S);
VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, false);
break;
}
case Stmt::CXXMemberCallExprClass: {
- CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S);
+ const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S);
VisitCXXMemberCallExpr(MCE, Pred, Dst);
break;
}
case Stmt::CXXNewExprClass: {
- CXXNewExpr *NE = cast<CXXNewExpr>(S);
+ const CXXNewExpr *NE = cast<CXXNewExpr>(S);
VisitCXXNewExpr(NE, Pred, Dst);
break;
}
case Stmt::CXXDeleteExprClass: {
- CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
+ const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
VisitCXXDeleteExpr(CDE, Pred, Dst);
break;
}
@@ -792,7 +911,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
// the CFG do not model them as explicit control-flow.
case Stmt::ChooseExprClass: { // __builtin_choose_expr
- ChooseExpr* C = cast<ChooseExpr>(S);
+ const ChooseExpr* C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
@@ -806,7 +925,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
break;
case Stmt::ConditionalOperatorClass: { // '?' operator
- ConditionalOperator* C = cast<ConditionalOperator>(S);
+ const ConditionalOperator* C = cast<ConditionalOperator>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
@@ -836,7 +955,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass: {
- CastExpr* C = cast<CastExpr>(S);
+ const CastExpr* C = cast<CastExpr>(S);
VisitCast(C, C->getSubExpr(), Pred, Dst, false);
break;
}
@@ -893,7 +1012,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
break;
case Stmt::StmtExprClass: {
- StmtExpr* SE = cast<StmtExpr>(S);
+ const StmtExpr* SE = cast<StmtExpr>(S);
if (SE->getSubStmt()->body_empty()) {
// Empty statement expression.
@@ -924,8 +1043,8 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
break;
case Stmt::UnaryOperatorClass: {
- UnaryOperator *U = cast<UnaryOperator>(S);
- if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UnaryOperator::LNot)) {
+ const UnaryOperator *U = cast<UnaryOperator>(S);
+ if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) {
ExplodedNodeSet Tmp;
VisitUnaryOperator(U, Pred, Tmp, false);
EvalEagerlyAssume(Dst, Tmp, U);
@@ -943,7 +1062,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) {
}
}
-void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
+void GRExprEngine::VisitLValue(const Expr* Ex, ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
@@ -984,7 +1103,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
case Stmt::CallExprClass:
case Stmt::CXXOperatorCallExprClass: {
- CallExpr *C = cast<CallExpr>(Ex);
+ const CallExpr *C = cast<CallExpr>(Ex);
assert(CalleeReturnsReferenceOrRecord(C));
VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, true);
break;
@@ -1000,7 +1119,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
case Stmt::ImplicitCastExprClass:
case Stmt::CStyleCastExprClass: {
- CastExpr *C = cast<CastExpr>(Ex);
+ const CastExpr *C = cast<CastExpr>(Ex);
QualType T = Ex->getType();
VisitCast(C, C->getSubExpr(), Pred, Dst, true);
break;
@@ -1015,7 +1134,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
return;
case Stmt::ObjCMessageExprClass: {
- ObjCMessageExpr *ME = cast<ObjCMessageExpr>(Ex);
+ const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(Ex);
assert(ReceiverReturnsReferenceOrRecord(ME));
VisitObjCMessageExpr(ME, Pred, Dst, true);
return;
@@ -1056,6 +1175,9 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
// In C++, binding an rvalue to a reference requires to create an object.
case Stmt::CXXBoolLiteralExprClass:
case Stmt::IntegerLiteralClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::ImaginaryLiteralClass:
CreateCXXTemporaryObject(Ex, Pred, Dst);
return;
@@ -1081,7 +1203,8 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred,
// Block entrance. (Update counters).
//===----------------------------------------------------------------------===//
-bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const ExplodedNode *Pred,
+bool GRExprEngine::ProcessBlockEntrance(const CFGBlock* B,
+ const ExplodedNode *Pred,
GRBlockCounter BC) {
return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(),
B->getBlockID()) < AMgr.getMaxLoop();
@@ -1091,7 +1214,7 @@ bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const ExplodedNode *Pred,
// Generic node creation.
//===----------------------------------------------------------------------===//
-ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S,
+ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
ExplodedNode* Pred, const GRState* St,
ProgramPoint::Kind K, const void *tag) {
assert (Builder && "GRStmtNodeBuilder not present.");
@@ -1105,8 +1228,8 @@ ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S,
//===----------------------------------------------------------------------===//
const GRState* GRExprEngine::MarkBranch(const GRState* state,
- Stmt* Terminator,
- bool branchTaken) {
+ const Stmt* Terminator,
+ bool branchTaken) {
switch (Terminator->getStmtClass()) {
default:
@@ -1114,10 +1237,10 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state,
case Stmt::BinaryOperatorClass: { // '&&' and '||'
- BinaryOperator* B = cast<BinaryOperator>(Terminator);
+ const BinaryOperator* B = cast<BinaryOperator>(Terminator);
BinaryOperator::Opcode Op = B->getOpcode();
- assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr);
+ assert (Op == BO_LAnd || Op == BO_LOr);
// For &&, if we take the true branch, then the value of the whole
// expression is that of the RHS expression.
@@ -1125,21 +1248,21 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state,
// For ||, if we take the false branch, then the value of the whole
// expression is that of the RHS expression.
- Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) ||
- (Op == BinaryOperator::LOr && !branchTaken)
- ? B->getRHS() : B->getLHS();
+ const Expr* Ex = (Op == BO_LAnd && branchTaken) ||
+ (Op == BO_LOr && !branchTaken)
+ ? B->getRHS() : B->getLHS();
return state->BindExpr(B, UndefinedVal(Ex));
}
case Stmt::ConditionalOperatorClass: { // ?:
- ConditionalOperator* C = cast<ConditionalOperator>(Terminator);
+ const ConditionalOperator* C = cast<ConditionalOperator>(Terminator);
// For ?, if branchTaken == true then the value is either the LHS or
// the condition itself. (GNU extension).
- Expr* Ex;
+ const Expr* Ex;
if (branchTaken)
Ex = C->getLHS() ? C->getLHS() : C->getCond();
@@ -1151,9 +1274,9 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state,
case Stmt::ChooseExprClass: { // ?:
- ChooseExpr* C = cast<ChooseExpr>(Terminator);
+ const ChooseExpr* C = cast<ChooseExpr>(Terminator);
- Expr* Ex = branchTaken ? C->getLHS() : C->getRHS();
+ const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS();
return state->BindExpr(C, UndefinedVal(Ex));
}
}
@@ -1165,16 +1288,16 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state,
/// This function returns the SVal bound to Condition->IgnoreCasts if all the
// cast(s) did was sign-extend the original value.
static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state,
- Stmt* Condition, ASTContext& Ctx) {
+ const Stmt* Condition, ASTContext& Ctx) {
- Expr *Ex = dyn_cast<Expr>(Condition);
+ const Expr *Ex = dyn_cast<Expr>(Condition);
if (!Ex)
return UnknownVal();
uint64_t bits = 0;
bool bitsInit = false;
- while (CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
+ while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
QualType T = CE->getType();
if (!T->isIntegerType())
@@ -1198,7 +1321,7 @@ static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state,
return state->getSVal(Ex);
}
-void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term,
+void GRExprEngine::ProcessBranch(const Stmt* Condition, const Stmt* Term,
GRBranchNodeBuilder& builder) {
// Check for NULL conditions; e.g. "for(;;)"
@@ -1285,7 +1408,7 @@ void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) {
typedef GRIndirectGotoNodeBuilder::iterator iterator;
if (isa<loc::GotoLabel>(V)) {
- LabelStmt* L = cast<loc::GotoLabel>(V).getLabel();
+ const LabelStmt* L = cast<loc::GotoLabel>(V).getLabel();
for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) {
if (I.getLabel() == L) {
@@ -1314,7 +1437,8 @@ void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) {
}
-void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R,
+void GRExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L,
+ const Expr* R,
ExplodedNode* Pred, ExplodedNodeSet& Dst) {
assert(Ex == CurrentStmt &&
@@ -1325,7 +1449,7 @@ void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R,
assert (X.isUndef());
- Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
+ const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
assert(SE);
X = state->getSVal(SE);
@@ -1350,7 +1474,7 @@ void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) {
void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) {
typedef GRSwitchNodeBuilder::iterator iterator;
const GRState* state = builder.getState();
- Expr* CondE = builder.getCondition();
+ const Expr* CondE = builder.getCondition();
SVal CondV_untested = state->getSVal(CondE);
if (CondV_untested.isUndef()) {
@@ -1363,10 +1487,12 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) {
DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested);
const GRState *DefaultSt = state;
- bool defaultIsFeasible = false;
+
+ iterator I = builder.begin(), EI = builder.end();
+ bool defaultIsFeasible = I == EI;
- for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) {
- CaseStmt* Case = cast<CaseStmt>(I.getCase());
+ for ( ; I != EI; ++I) {
+ const CaseStmt* Case = I.getCase();
// Evaluate the LHS of the case value.
Expr::EvalResult V1;
@@ -1382,7 +1508,7 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) {
// Get the RHS of the case, if it exists.
Expr::EvalResult V2;
- if (Expr* E = Case->getRHS()) {
+ if (const Expr* E = Case->getRHS()) {
b = E->Evaluate(V2, getContext());
assert(b && V2.Val.isInt() && !V2.HasSideEffects
&& "Case condition must evaluate to an integer constant.");
@@ -1440,15 +1566,14 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) {
}
void GRExprEngine::ProcessCallEnter(GRCallEnterNodeBuilder &B) {
- const FunctionDecl *FD = B.getCallee();
- const StackFrameContext *LocCtx = AMgr.getStackFrame(FD,
- B.getLocationContext(),
- B.getCallExpr(),
- B.getBlock(),
- B.getIndex());
+ const StackFrameContext *LocCtx
+ = AMgr.getStackFrame(B.getCalleeContext(),
+ B.getLocationContext(),
+ B.getCallExpr(),
+ B.getBlock(),
+ B.getIndex());
- const GRState *state = B.getState();
- state = getStoreManager().EnterStackFrame(state, LocCtx);
+ const GRState *state = B.getState()->EnterStackFrame(LocCtx);
B.GenerateNode(state, LocCtx);
}
@@ -1490,11 +1615,11 @@ void GRExprEngine::ProcessCallExit(GRCallExitNodeBuilder &B) {
// Transfer functions: logical operations ('&&', '||').
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred,
+void GRExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
- assert(B->getOpcode() == BinaryOperator::LAnd ||
- B->getOpcode() == BinaryOperator::LOr);
+ assert(B->getOpcode() == BO_LAnd ||
+ B->getOpcode() == BO_LOr);
assert(B==CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B));
@@ -1534,7 +1659,7 @@ void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred,
// 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 = ValMgr.makeIntVal(B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U,
+ X = ValMgr.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
B->getType());
MakeNode(Dst, B, Pred, state->BindExpr(B, X));
}
@@ -1544,7 +1669,7 @@ void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred,
// Transfer functions: Loads and stores.
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred,
+void GRExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ExplodedNodeSet Tmp;
@@ -1557,21 +1682,21 @@ void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred,
ProgramPoint::PostLValueKind);
// Post-visit the BlockExpr.
- CheckerVisit(BE, Dst, Tmp, false);
+ CheckerVisit(BE, Dst, Tmp, PostVisitStmtCallback);
}
-void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred,
+void GRExprEngine::VisitDeclRefExpr(const DeclRefExpr *Ex, ExplodedNode *Pred,
ExplodedNodeSet &Dst, bool asLValue) {
VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue);
}
-void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex,
+void GRExprEngine::VisitBlockDeclRefExpr(const BlockDeclRefExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst, bool asLValue) {
VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue);
}
-void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D,
+void GRExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst, bool asLValue) {
@@ -1618,12 +1743,12 @@ void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D,
}
/// VisitArraySubscriptExpr - Transfer function for array accesses
-void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A,
+void GRExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr* A,
ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue){
- Expr* Base = A->getBase()->IgnoreParens();
- Expr* Idx = A->getIdx()->IgnoreParens();
+ const Expr* Base = A->getBase()->IgnoreParens();
+ const Expr* Idx = A->getIdx()->IgnoreParens();
ExplodedNodeSet Tmp;
if (Base->getType()->isVectorType()) {
@@ -1641,7 +1766,7 @@ void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A,
Visit(Idx, *I1, Tmp2); // Evaluate the index.
ExplodedNodeSet Tmp3;
- CheckerVisit(A, Tmp3, Tmp2, true);
+ CheckerVisit(A, Tmp3, Tmp2, PreVisitStmtCallback);
for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) {
const GRState* state = GetState(*I2);
@@ -1658,7 +1783,7 @@ void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A,
}
/// VisitMemberExpr - Transfer function for member expressions.
-void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred,
+void GRExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue) {
Expr* Base = M->getBase()->IgnoreParens();
@@ -1689,16 +1814,15 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred,
/// EvalBind - Handle the semantics of binding a value to a specific location.
/// This method is used by EvalStore and (soon) VisitDeclStmt, and others.
-void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE,
- Stmt* StoreE, ExplodedNode* Pred,
- const GRState* state, SVal location, SVal Val,
- bool atDeclInit) {
+void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, const Stmt* StoreE,
+ ExplodedNode* Pred, const GRState* state,
+ SVal location, SVal Val, bool atDeclInit) {
// Do a previsit of the bind.
ExplodedNodeSet CheckedSet, Src;
Src.Add(Pred);
- CheckerVisitBind(AssignE, StoreE, CheckedSet, Src, location, Val, true);
+ CheckerVisitBind(StoreE, CheckedSet, Src, location, Val, true);
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I!=E; ++I) {
@@ -1731,6 +1855,10 @@ void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE,
// The next thing to do is check if the GRTransferFuncs object wants to
// update the state based on the new binding. If the GRTransferFunc object
// doesn't do anything, just auto-propagate the current state.
+
+ // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE'
+ // is non-NULL. Checkers typically care about
+
GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE,
newState != state);
@@ -1740,12 +1868,14 @@ void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE,
/// EvalStore - Handle the semantics of a store via an assignment.
/// @param Dst The node set to store generated state nodes
-/// @param Ex The expression representing the location of the store
+/// @param AssignE The assignment expression if the store happens in an
+/// assignment.
+/// @param LocatioinE The location expression that is stored to.
/// @param state The current simulation state
/// @param location The location to store the value
/// @param Val The value to be stored
-void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE,
- Expr* StoreE,
+void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, const Expr *AssignE,
+ const Expr* LocationE,
ExplodedNode* Pred,
const GRState* state, SVal location, SVal Val,
const void *tag) {
@@ -1754,7 +1884,7 @@ void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE,
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
- EvalLocation(Tmp, StoreE, Pred, state, location, tag, false);
+ EvalLocation(Tmp, LocationE, Pred, state, location, tag, false);
if (Tmp.empty())
return;
@@ -1765,12 +1895,16 @@ void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE,
ProgramPoint::PostStoreKind);
SaveAndRestore<const void*> OldTag(Builder->Tag, tag);
- // Proceed with the store.
+ // Proceed with the store. We use AssignE as the anchor for the PostStore
+ // ProgramPoint if it is non-NULL, and LocationE otherwise.
+ const Expr *StoreE = AssignE ? AssignE : LocationE;
+
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
- EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val);
+ EvalBind(Dst, StoreE, *NI, GetState(*NI), location, Val);
}
-void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred,
+void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, const Expr *Ex,
+ ExplodedNode* Pred,
const GRState* state, SVal location,
const void *tag, QualType LoadTy) {
@@ -1780,7 +1914,7 @@ void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred,
if (const TypedRegion *TR =
dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
- QualType ValTy = TR->getValueType(getContext());
+ QualType ValTy = TR->getValueType();
if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) {
static int loadReferenceTag = 0;
ExplodedNodeSet Tmp;
@@ -1800,7 +1934,7 @@ void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred,
EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy);
}
-void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex,
+void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex,
ExplodedNode* Pred,
const GRState* state, SVal location,
const void *tag, QualType LoadTy) {
@@ -1834,7 +1968,7 @@ void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex,
}
}
-void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S,
+void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, const Stmt *S,
ExplodedNode* Pred,
const GRState* state, SVal location,
const void *tag, bool isLoad) {
@@ -1885,21 +2019,34 @@ bool GRExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
if (!FD)
return false;
- if (!FD->hasBody(FD))
- return false;
+ // Check if the function definition is in the same translation unit.
+ if (FD->hasBody(FD)) {
+ // Now we have the definition of the callee, create a CallEnter node.
+ CallEnter Loc(CE, AMgr.getAnalysisContext(FD), Pred->getLocationContext());
+
+ ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
+ Dst.Add(N);
+ return true;
+ }
- // Now we have the definition of the callee, create a CallEnter node.
- CallEnter Loc(CE, FD, Pred->getLocationContext());
+ // Check if we can find the function definition in other translation units.
+ if (AMgr.hasIndexer()) {
+ const AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD);
+ if (C == 0)
+ return false;
- ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
- if (N)
+ CallEnter Loc(CE, C, Pred->getLocationContext());
+ ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
Dst.Add(N);
- return true;
+ return true;
+ }
+
+ return false;
}
-void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
- CallExpr::arg_iterator AI,
- CallExpr::arg_iterator AE,
+void GRExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred,
+ CallExpr::const_arg_iterator AI,
+ CallExpr::const_arg_iterator AE,
ExplodedNodeSet& Dst, bool asLValue) {
// Determine the type of function we're calling (if available).
@@ -1946,7 +2093,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
// Now process the call itself.
ExplodedNodeSet DstTmp;
- Expr* Callee = CE->getCallee()->IgnoreParens();
+ const Expr* Callee = CE->getCallee()->IgnoreParens();
for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(),
NE=ArgsEvaluated.end(); NI != NE; ++NI) {
@@ -1954,7 +2101,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
ExplodedNodeSet DstTmp2;
Visit(Callee, *NI, DstTmp2);
// Perform the previsit of the CallExpr, storing the results in DstTmp.
- CheckerVisit(CE, DstTmp, DstTmp2, true);
+ CheckerVisit(CE, DstTmp, DstTmp2, PreVisitStmtCallback);
}
// Finally, evaluate the function call. We try each of the checkers
@@ -2009,7 +2156,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
// If the callee returns a reference and we want an rvalue, skip this check
// and do the load.
if (!(!asLValue && CalleeReturnsReference(CE))) {
- CheckerVisit(CE, Dst, DstTmp3, false);
+ CheckerVisit(CE, Dst, DstTmp3, PostVisitStmtCallback);
return;
}
@@ -2019,7 +2166,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred,
// FIXME: This conversion doesn't actually happen unless the result
// of CallExpr is consumed by another expression.
ExplodedNodeSet DstTmp4;
- CheckerVisit(CE, DstTmp4, DstTmp3, false);
+ CheckerVisit(CE, DstTmp4, DstTmp3, PostVisitStmtCallback);
QualType LoadTy = CE->getType();
static int *ConvertToRvalueTag = 0;
@@ -2039,7 +2186,7 @@ static std::pair<const void*,const void*> EagerlyAssumeTag
= std::pair<const void*,const void*>(&EagerlyAssumeTag,static_cast<void*>(0));
void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
- Expr *Ex) {
+ const Expr *Ex) {
for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
ExplodedNode *Pred = *I;
@@ -2082,10 +2229,11 @@ void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
// Transfer function: Objective-C ivar references.
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred,
+void GRExprEngine::VisitObjCIvarRefExpr(const ObjCIvarRefExpr* Ex,
+ ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue) {
- Expr* Base = cast<Expr>(Ex->getBase());
+ const Expr* Base = cast<Expr>(Ex->getBase());
ExplodedNodeSet Tmp;
Visit(Base, Pred, Tmp);
@@ -2105,7 +2253,7 @@ void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred,
// Transfer function: Objective-C fast enumeration 'for' statements.
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S,
+void GRExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S,
ExplodedNode* Pred, ExplodedNodeSet& Dst) {
// ObjCForCollectionStmts are processed in two places. This method
@@ -2133,11 +2281,11 @@ void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S,
// container is empty. Thus this transfer function will by default
// result in state splitting.
- Stmt* elem = S->getElement();
+ const Stmt* elem = S->getElement();
SVal ElementV;
- if (DeclStmt* DS = dyn_cast<DeclStmt>(elem)) {
- VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl());
+ if (const DeclStmt* DS = dyn_cast<DeclStmt>(elem)) {
+ const VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl());
assert (ElemD->getInit() == 0);
ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext());
VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV);
@@ -2153,12 +2301,12 @@ void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S,
}
}
-void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S,
+void GRExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S,
ExplodedNode* Pred, ExplodedNodeSet& Dst,
SVal ElementV) {
// Check if the location we are writing back to is a null pointer.
- Stmt* elem = S->getElement();
+ const Stmt* elem = S->getElement();
ExplodedNodeSet Tmp;
EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false);
@@ -2182,7 +2330,7 @@ void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S,
// 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(getContext());
+ QualType T = R->getValueType();
assert(Loc::IsLocType(T));
unsigned Count = Builder->getCurrentBlockCount();
SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count);
@@ -2207,23 +2355,24 @@ void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S,
namespace {
class ObjCMsgWLItem {
public:
- ObjCMessageExpr::arg_iterator I;
+ ObjCMessageExpr::const_arg_iterator I;
ExplodedNode *N;
- ObjCMsgWLItem(const ObjCMessageExpr::arg_iterator &i, ExplodedNode *n)
+ ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n)
: I(i), N(n) {}
};
} // end anonymous namespace
-void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred,
+void GRExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME,
+ ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue){
// Create a worklist to process both the arguments.
llvm::SmallVector<ObjCMsgWLItem, 20> WL;
// But first evaluate the receiver (if any).
- ObjCMessageExpr::arg_iterator AI = ME->arg_begin(), AE = ME->arg_end();
- if (Expr *Receiver = ME->getInstanceReceiver()) {
+ ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end();
+ if (const Expr *Receiver = ME->getInstanceReceiver()) {
ExplodedNodeSet Tmp;
Visit(Receiver, Pred, Tmp);
@@ -2261,7 +2410,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred,
// Now that the arguments are processed, handle the previsits checks.
ExplodedNodeSet DstPrevisit;
- CheckerVisit(ME, DstPrevisit, ArgsEvaluated, true);
+ CheckerVisit(ME, DstPrevisit, ArgsEvaluated, PreVisitStmtCallback);
// Proceed with evaluate the message expression.
ExplodedNodeSet DstEval;
@@ -2364,7 +2513,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred,
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
if (!(!asLValue && ReceiverReturnsReference(ME))) {
- CheckerVisit(ME, Dst, DstEval, false);
+ CheckerVisit(ME, Dst, DstEval, PostVisitStmtCallback);
return;
}
@@ -2374,7 +2523,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred,
// FIXME: This conversion doesn't actually happen unless the result
// of ObjCMessageExpr is consumed by another expression.
ExplodedNodeSet DstRValueConvert;
- CheckerVisit(ME, DstRValueConvert, DstEval, false);
+ CheckerVisit(ME, DstRValueConvert, DstEval, PostVisitStmtCallback);
QualType LoadTy = ME->getType();
static int *ConvertToRvalueTag = 0;
@@ -2390,8 +2539,9 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred,
// Transfer functions: Miscellaneous statements.
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
- ExplodedNodeSet &Dst, bool asLValue) {
+void GRExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst,
+ bool asLValue) {
ExplodedNodeSet S1;
QualType T = CastE->getType();
QualType ExTy = Ex->getType();
@@ -2406,7 +2556,7 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
Visit(Ex, Pred, S1);
ExplodedNodeSet S2;
- CheckerVisit(CastE, S2, S1, true);
+ CheckerVisit(CastE, S2, S1, PreVisitStmtCallback);
// If we are evaluating the cast in an lvalue context, we implicitly want
// the cast to evaluate to a location.
@@ -2417,14 +2567,14 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
}
switch (CastE->getCastKind()) {
- case CastExpr::CK_ToVoid:
+ case CK_ToVoid:
assert(!asLValue);
for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I)
Dst.Add(*I);
return;
- case CastExpr::CK_NoOp:
- case CastExpr::CK_FunctionToPointerDecay:
+ case CK_NoOp:
+ case CK_FunctionToPointerDecay:
for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {
// Copy the SVal of Ex to CastE.
ExplodedNode *N = *I;
@@ -2435,20 +2585,21 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
}
return;
- case CastExpr::CK_Unknown:
- case CastExpr::CK_ArrayToPointerDecay:
- case CastExpr::CK_BitCast:
- case CastExpr::CK_LValueBitCast:
- case CastExpr::CK_IntegralCast:
- case CastExpr::CK_IntegralToPointer:
- case CastExpr::CK_PointerToIntegral:
- case CastExpr::CK_IntegralToFloating:
- case CastExpr::CK_FloatingToIntegral:
- case CastExpr::CK_FloatingCast:
- case CastExpr::CK_AnyPointerToObjCPointerCast:
- case CastExpr::CK_AnyPointerToBlockPointerCast:
- case CastExpr::CK_DerivedToBase:
- case CastExpr::CK_UncheckedDerivedToBase: {
+ case CK_Unknown:
+ case CK_ArrayToPointerDecay:
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_IntegralCast:
+ case CK_IntegralToPointer:
+ case CK_PointerToIntegral:
+ case CK_IntegralToFloating:
+ case CK_FloatingToIntegral:
+ case CK_FloatingCast:
+ case CK_AnyPointerToObjCPointerCast:
+ case CK_AnyPointerToBlockPointerCast:
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase:
+ case CK_ObjCObjectLValueCast: {
// Delegate to SValuator to process.
for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {
ExplodedNode* N = *I;
@@ -2462,16 +2613,16 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
}
// Various C++ casts that are not handled yet.
- case CastExpr::CK_Dynamic:
- case CastExpr::CK_ToUnion:
- case CastExpr::CK_BaseToDerived:
- case CastExpr::CK_NullToMemberPointer:
- case CastExpr::CK_BaseToDerivedMemberPointer:
- case CastExpr::CK_DerivedToBaseMemberPointer:
- case CastExpr::CK_UserDefinedConversion:
- case CastExpr::CK_ConstructorConversion:
- case CastExpr::CK_VectorSplat:
- case CastExpr::CK_MemberPointerToBoolean: {
+ 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: {
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Builder->BuildSinks = true;
MakeNode(Dst, CastE, Pred, GetState(Pred));
@@ -2480,11 +2631,12 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred,
}
}
-void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL,
+void GRExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL,
ExplodedNode* Pred,
ExplodedNodeSet& Dst,
bool asLValue) {
- InitListExpr* ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
+ const InitListExpr* ILE
+ = cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
ExplodedNodeSet Tmp;
Visit(ILE, Pred, Tmp);
@@ -2502,17 +2654,17 @@ void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL,
}
}
-void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred,
+void GRExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
ExplodedNodeSet& Dst) {
// The CFG has one DeclStmt per Decl.
- Decl* D = *DS->decl_begin();
+ const Decl* D = *DS->decl_begin();
if (!D || !isa<VarDecl>(D))
return;
const VarDecl* VD = dyn_cast<VarDecl>(D);
- Expr* InitEx = const_cast<Expr*>(VD->getInit());
+ const Expr* InitEx = VD->getInit();
// FIXME: static variables may have an initializer, but the second
// time a function is called those values may not be current.
@@ -2534,7 +2686,7 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred,
Tmp.Add(Pred);
ExplodedNodeSet Tmp2;
- CheckerVisit(DS, Tmp2, Tmp, true);
+ CheckerVisit(DS, Tmp2, Tmp, PreVisitStmtCallback);
for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) {
ExplodedNode *N = *I;
@@ -2555,7 +2707,7 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred,
Builder->getCurrentBlockCount());
}
- EvalBind(Dst, DS, DS, *I, state,
+ EvalBind(Dst, DS, *I, state,
loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true);
}
else {
@@ -2565,10 +2717,10 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred,
}
}
-void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S,
+void GRExprEngine::VisitCondInit(const VarDecl *VD, const Stmt *S,
ExplodedNode *Pred, ExplodedNodeSet& Dst) {
- Expr* InitEx = VD->getInit();
+ const Expr* InitEx = VD->getInit();
ExplodedNodeSet Tmp;
Visit(InitEx, Pred, Tmp);
@@ -2587,7 +2739,7 @@ void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S,
Builder->getCurrentBlockCount());
}
- EvalBind(Dst, S, S, N, state,
+ EvalBind(Dst, S, N, state,
loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true);
}
}
@@ -2599,16 +2751,16 @@ class InitListWLItem {
public:
llvm::ImmutableList<SVal> Vals;
ExplodedNode* N;
- InitListExpr::reverse_iterator Itr;
+ InitListExpr::const_reverse_iterator Itr;
InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals,
- InitListExpr::reverse_iterator itr)
+ InitListExpr::const_reverse_iterator itr)
: Vals(vals), N(n), Itr(itr) {}
};
}
-void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred,
+void GRExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
const GRState* state = GetState(Pred);
@@ -2630,7 +2782,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred,
llvm::SmallVector<InitListWLItem, 10> WorkList;
WorkList.reserve(NumInitElements);
WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin()));
- InitListExpr::reverse_iterator ItrEnd = E->rend();
+ InitListExpr::const_reverse_iterator ItrEnd = E->rend();
assert(!(E->rbegin() == E->rend()));
// Process the worklist until it is empty.
@@ -2641,7 +2793,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred,
ExplodedNodeSet Tmp;
Visit(*X.Itr, X.N, Tmp);
- InitListExpr::reverse_iterator NewItr = X.Itr + 1;
+ InitListExpr::const_reverse_iterator NewItr = X.Itr + 1;
for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) {
// Get the last initializer value.
@@ -2673,7 +2825,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred,
if (Loc::IsLocType(T) || T->isIntegerType()) {
assert (E->getNumInits() == 1);
ExplodedNodeSet Tmp;
- Expr* Init = E->getInit(0);
+ const Expr* Init = E->getInit(0);
Visit(Init, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) {
state = GetState(*I);
@@ -2686,7 +2838,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred,
}
/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type).
-void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex,
+void GRExprEngine::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex,
ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
QualType T = Ex->getTypeOfArgument();
@@ -2709,7 +2861,7 @@ void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex,
// Get the size by getting the extent of the sub-expression.
// First, visit the sub-expression to find its region.
- Expr *Arg = Ex->getArgumentExpr();
+ const Expr *Arg = Ex->getArgumentExpr();
ExplodedNodeSet Tmp;
VisitLValue(Arg, Pred, Tmp);
@@ -2751,8 +2903,8 @@ void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex,
ValMgr.makeIntVal(amt.getQuantity(), Ex->getType())));
}
-void GRExprEngine::VisitOffsetOfExpr(OffsetOfExpr* OOE, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
+void GRExprEngine::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();
@@ -2767,7 +2919,8 @@ void GRExprEngine::VisitOffsetOfExpr(OffsetOfExpr* OOE, ExplodedNode* Pred,
Dst.Add(Pred);
}
-void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
+void GRExprEngine::VisitUnaryOperator(const UnaryOperator* U,
+ ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue) {
switch (U->getOpcode()) {
@@ -2775,9 +2928,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
default:
break;
- case UnaryOperator::Deref: {
+ case UO_Deref: {
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
@@ -2796,9 +2949,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
return;
}
- case UnaryOperator::Real: {
+ case UO_Real: {
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
@@ -2811,7 +2964,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
continue;
}
- // For all other types, UnaryOperator::Real is an identity operation.
+ // For all other types, UO_Real is an identity operation.
assert (U->getType() == Ex->getType());
const GRState* state = GetState(*I);
MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
@@ -2820,9 +2973,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
return;
}
- case UnaryOperator::Imag: {
+ case UO_Imag: {
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
@@ -2834,8 +2987,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
continue;
}
- // For all other types, UnaryOperator::Float returns 0.
- assert (Ex->getType()->isIntegerType());
+ // For all other types, UO_Imag returns 0.
const GRState* state = GetState(*I);
SVal X = ValMgr.makeZeroVal(Ex->getType());
MakeNode(Dst, U, *I, state->BindExpr(U, X));
@@ -2843,38 +2995,22 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
return;
}
-
- case UnaryOperator::OffsetOf: {
- Expr::EvalResult Res;
- if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) {
- const APSInt &IV = Res.Val.getInt();
- assert(IV.getBitWidth() == getContext().getTypeSize(U->getType()));
- assert(U->getType()->isIntegerType());
- assert(IV.isSigned() == U->getType()->isSignedIntegerType());
- SVal X = ValMgr.makeIntVal(IV);
- MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X));
- return;
- }
- // FIXME: Handle the case where __builtin_offsetof is not a constant.
- Dst.Add(Pred);
- return;
- }
- case UnaryOperator::Plus: assert(!asLValue); // FALL-THROUGH.
- case UnaryOperator::Extension: {
+ case UO_Plus: assert(!asLValue); // FALL-THROUGH.
+ 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.
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
if (asLValue)
- VisitLValue(Ex, Pred, Tmp);
+ VisitLValue(Ex, Pred, Tmp);
else
- Visit(Ex, Pred, Tmp);
+ Visit(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
const GRState* state = GetState(*I);
@@ -2884,10 +3020,10 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
return;
}
- case UnaryOperator::AddrOf: {
+ case UO_AddrOf: {
assert(!asLValue);
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
VisitLValue(Ex, Pred, Tmp);
@@ -2901,12 +3037,12 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
return;
}
- case UnaryOperator::LNot:
- case UnaryOperator::Minus:
- case UnaryOperator::Not: {
+ case UO_LNot:
+ case UO_Minus:
+ case UO_Not: {
assert (!asLValue);
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
ExplodedNodeSet Tmp;
Visit(Ex, Pred, Tmp);
@@ -2937,17 +3073,17 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
assert(false && "Invalid Opcode.");
break;
- case UnaryOperator::Not:
+ case UO_Not:
// FIXME: Do we need to handle promotions?
state = state->BindExpr(U, EvalComplement(cast<NonLoc>(V)));
break;
- case UnaryOperator::Minus:
+ case UO_Minus:
// FIXME: Do we need to handle promotions?
state = state->BindExpr(U, EvalMinus(cast<NonLoc>(V)));
break;
- case UnaryOperator::LNot:
+ case UO_LNot:
// C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
//
@@ -2957,12 +3093,12 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
if (isa<Loc>(V)) {
Loc X = ValMgr.makeNull();
- Result = EvalBinOp(state, BinaryOperator::EQ, cast<Loc>(V), X,
+ Result = EvalBinOp(state, BO_EQ, cast<Loc>(V), X,
U->getType());
}
else {
nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
- Result = EvalBinOp(state, BinaryOperator::EQ, cast<NonLoc>(V), X,
+ Result = EvalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
U->getType());
}
@@ -2982,7 +3118,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
assert (U->isIncrementDecrementOp());
ExplodedNodeSet Tmp;
- Expr* Ex = U->getSubExpr()->IgnoreParens();
+ const Expr* Ex = U->getSubExpr()->IgnoreParens();
VisitLValue(Ex, Pred, Tmp);
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
@@ -3007,8 +3143,8 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
// Handle all other values.
- BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add
- : BinaryOperator::Sub;
+ 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
@@ -3057,14 +3193,14 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred,
}
}
-void GRExprEngine::VisitAsmStmt(AsmStmt* A, ExplodedNode* Pred,
+void GRExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst);
}
-void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A,
- AsmStmt::outputs_iterator I,
- AsmStmt::outputs_iterator E,
+void GRExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A,
+ AsmStmt::const_outputs_iterator I,
+ AsmStmt::const_outputs_iterator E,
ExplodedNode* Pred, ExplodedNodeSet& Dst) {
if (I == E) {
VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst);
@@ -3080,9 +3216,9 @@ void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A,
VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst);
}
-void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A,
- AsmStmt::inputs_iterator I,
- AsmStmt::inputs_iterator E,
+void GRExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A,
+ AsmStmt::const_inputs_iterator I,
+ AsmStmt::const_inputs_iterator E,
ExplodedNode* Pred,
ExplodedNodeSet& Dst) {
if (I == E) {
@@ -3096,7 +3232,7 @@ void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A,
const GRState* state = GetState(Pred);
- for (AsmStmt::outputs_iterator OI = A->begin_outputs(),
+ for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(),
OE = A->end_outputs(); OI != OE; ++OI) {
SVal X = state->getSVal(*OI);
@@ -3119,10 +3255,10 @@ void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A,
VisitAsmStmtHelperInputs(A, I, E, *NI, Dst);
}
-void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred,
+void GRExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ExplodedNodeSet Src;
- if (Expr *RetE = RS->getRetValue()) {
+ 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.
{
@@ -3141,7 +3277,7 @@ void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred,
}
ExplodedNodeSet CheckedSet;
- CheckerVisit(RS, CheckedSet, Src, true);
+ CheckerVisit(RS, CheckedSet, Src, PreVisitStmtCallback);
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I != E; ++I) {
@@ -3167,7 +3303,7 @@ void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred,
// Transfer functions: Binary operators.
//===----------------------------------------------------------------------===//
-void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
+void GRExprEngine::VisitBinaryOperator(const BinaryOperator* B,
ExplodedNode* Pred,
ExplodedNodeSet& Dst, bool asLValue) {
@@ -3194,7 +3330,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
Visit(RHS, *I1, Tmp2);
ExplodedNodeSet CheckedSet;
- CheckerVisit(B, CheckedSet, Tmp2, true);
+ CheckerVisit(B, CheckedSet, Tmp2, PreVisitStmtCallback);
// With both the LHS and RHS evaluated, process the operation itself.
@@ -3207,13 +3343,13 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
BinaryOperator::Opcode Op = B->getOpcode();
- if (Op == BinaryOperator::Assign) {
+ if (Op == BO_Assign) {
// EXPERIMENTAL: "Conjured" symbols.
// FIXME: Handle structs.
QualType T = RHS->getType();
- if ((RightV.isUnknown()||!getConstraintManager().canReasonAbout(RightV))
- && (Loc::IsLocType(T) || (T->isScalarType()&&T->isIntegerType()))) {
+ if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV))
+ {
unsigned Count = Builder->getCurrentBlockCount();
RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count);
}
@@ -3253,16 +3389,16 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
switch (Op) {
default:
assert(0 && "Invalid opcode for compound assignment.");
- case BinaryOperator::MulAssign: Op = BinaryOperator::Mul; break;
- case BinaryOperator::DivAssign: Op = BinaryOperator::Div; break;
- case BinaryOperator::RemAssign: Op = BinaryOperator::Rem; break;
- case BinaryOperator::AddAssign: Op = BinaryOperator::Add; break;
- case BinaryOperator::SubAssign: Op = BinaryOperator::Sub; break;
- case BinaryOperator::ShlAssign: Op = BinaryOperator::Shl; break;
- case BinaryOperator::ShrAssign: Op = BinaryOperator::Shr; break;
- case BinaryOperator::AndAssign: Op = BinaryOperator::And; break;
- case BinaryOperator::XorAssign: Op = BinaryOperator::Xor; break;
- case BinaryOperator::OrAssign: Op = BinaryOperator::Or; break;
+ 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
@@ -3300,10 +3436,8 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
SVal LHSVal;
- if ((Result.isUnknown() ||
- !getConstraintManager().canReasonAbout(Result))
- && (Loc::IsLocType(CTy)
- || (CTy->isScalarType() && CTy->isIntegerType()))) {
+ if (Result.isUnknown() ||
+ !getConstraintManager().canReasonAbout(Result)) {
unsigned Count = Builder->getCurrentBlockCount();
@@ -3327,7 +3461,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B,
}
}
- CheckerVisit(B, Dst, Tmp3, false);
+ CheckerVisit(B, Dst, Tmp3, PostVisitStmtCallback);
}
//===----------------------------------------------------------------------===//
@@ -3457,7 +3591,7 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B"
<< E.getDst()->getBlockID() << ')';
- if (Stmt* T = E.getSrc()->getTerminator()) {
+ if (const Stmt* T = E.getSrc()->getTerminator()) {
SourceLocation SLoc = T->getLocStart();
@@ -3473,15 +3607,15 @@ struct DOTGraphTraits<ExplodedNode*> :
}
if (isa<SwitchStmt>(T)) {
- Stmt* Label = E.getDst()->getLabel();
+ const Stmt* Label = E.getDst()->getLabel();
if (Label) {
- if (CaseStmt* C = dyn_cast<CaseStmt>(Label)) {
+ if (const CaseStmt* C = dyn_cast<CaseStmt>(Label)) {
Out << "\\lcase ";
LangOptions LO; // FIXME.
C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO));
- if (Stmt* RHS = C->getRHS()) {
+ if (const Stmt* RHS = C->getRHS()) {
Out << " .. ";
RHS->printPretty(Out, 0, PrintingPolicy(LO));
}
diff --git a/lib/Checker/GRExprEngineExperimentalChecks.cpp b/lib/Checker/GRExprEngineExperimentalChecks.cpp
index d138e81..84262b0 100644
--- a/lib/Checker/GRExprEngineExperimentalChecks.cpp
+++ b/lib/Checker/GRExprEngineExperimentalChecks.cpp
@@ -18,13 +18,14 @@
using namespace clang;
-void clang::RegisterExperimentalChecks(GRExprEngine &Eng) {
+void clang::RegisterExperimentalChecks(GRExprEngine &Eng) {
// These are checks that never belong as internal checks
// within GRExprEngine.
- RegisterPthreadLockChecker(Eng);
+ RegisterCStringChecker(Eng);
RegisterMallocChecker(Eng);
+ RegisterPthreadLockChecker(Eng);
RegisterStreamChecker(Eng);
- RegisterCStringChecker(Eng);
+ RegisterUnreachableCodeChecker(Eng);
}
void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) {
@@ -34,11 +35,10 @@ void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) {
// Note that this must be registered after ReturnStackAddresEngsChecker.
RegisterReturnPointerRangeChecker(Eng);
+ RegisterArrayBoundChecker(Eng);
+ RegisterCastSizeChecker(Eng);
+ RegisterCastToStructChecker(Eng);
RegisterFixedAddressChecker(Eng);
- RegisterPointerSubChecker(Eng);
RegisterPointerArithChecker(Eng);
- RegisterCastToStructChecker(Eng);
- RegisterCastSizeChecker(Eng);
- RegisterArrayBoundChecker(Eng);
-
+ RegisterPointerSubChecker(Eng);
}
diff --git a/lib/Checker/GRExprEngineExperimentalChecks.h b/lib/Checker/GRExprEngineExperimentalChecks.h
index 7d1eb77..7b5b0ed 100644
--- a/lib/Checker/GRExprEngineExperimentalChecks.h
+++ b/lib/Checker/GRExprEngineExperimentalChecks.h
@@ -20,10 +20,11 @@ namespace clang {
class GRExprEngine;
void RegisterCStringChecker(GRExprEngine &Eng);
-void RegisterPthreadLockChecker(GRExprEngine &Eng);
+void RegisterIdempotentOperationChecker(GRExprEngine &Eng);
void RegisterMallocChecker(GRExprEngine &Eng);
+void RegisterPthreadLockChecker(GRExprEngine &Eng);
void RegisterStreamChecker(GRExprEngine &Eng);
-void RegisterIdempotentOperationChecker(GRExprEngine &Eng);
+void RegisterUnreachableCodeChecker(GRExprEngine &Eng);
} // end clang namespace
#endif
diff --git a/lib/Checker/GRState.cpp b/lib/Checker/GRState.cpp
index 9e584b5..d38ae21 100644
--- a/lib/Checker/GRState.cpp
+++ b/lib/Checker/GRState.cpp
@@ -14,6 +14,7 @@
#include "clang/Analysis/CFG.h"
#include "clang/Checker/PathSensitive/GRStateTrait.h"
#include "clang/Checker/PathSensitive/GRState.h"
+#include "clang/Checker/PathSensitive/GRSubEngine.h"
#include "clang/Checker/PathSensitive/GRTransferFuncs.h"
#include "llvm/Support/raw_ostream.h"
@@ -51,22 +52,105 @@ GRStateManager::RemoveDeadBindings(const GRState* state,
state, RegionRoots);
// Clean up the store.
- const GRState *s = StoreMgr->RemoveDeadBindings(NewState, LCtx,
- SymReaper, RegionRoots);
+ NewState.St = StoreMgr->RemoveDeadBindings(NewState.St, LCtx,
+ SymReaper, RegionRoots);
+ state = getPersistentState(NewState);
+ return ConstraintMgr->RemoveDeadBindings(state, SymReaper);
+}
+
+const GRState *GRStateManager::MarshalState(const GRState *state,
+ const StackFrameContext *InitLoc) {
+ // make up an empty state for now.
+ GRState State(this,
+ EnvMgr.getInitialEnvironment(),
+ StoreMgr->getInitialStore(InitLoc),
+ GDMFactory.GetEmptyMap());
+
+ return getPersistentState(State);
+}
+
+const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL,
+ const LocationContext *LC,
+ SVal V) const {
+ Store new_store =
+ getStateManager().StoreMgr->BindCompoundLiteral(St, CL, LC, V);
+ return makeWithStore(new_store);
+}
+
+const GRState *GRState::bindDecl(const VarRegion* VR, SVal IVal) const {
+ Store new_store = getStateManager().StoreMgr->BindDecl(St, VR, IVal);
+ return makeWithStore(new_store);
+}
+
+const GRState *GRState::bindDeclWithNoInit(const VarRegion* VR) const {
+ Store new_store = getStateManager().StoreMgr->BindDeclWithNoInit(St, VR);
+ return makeWithStore(new_store);
+}
+
+const GRState *GRState::bindLoc(Loc LV, SVal V) const {
+ GRStateManager &Mgr = getStateManager();
+ Store new_store = Mgr.StoreMgr->Bind(St, LV, V);
+ const GRState *new_state = makeWithStore(new_store);
- return ConstraintMgr->RemoveDeadBindings(s, SymReaper);
+ const MemRegion *MR = LV.getAsRegion();
+ if (MR)
+ return Mgr.getOwningEngine().ProcessRegionChange(new_state, MR);
+
+ return new_state;
+}
+
+const GRState *GRState::bindDefault(SVal loc, SVal V) const {
+ GRStateManager &Mgr = getStateManager();
+ const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion();
+ Store new_store = Mgr.StoreMgr->BindDefault(St, R, V);
+ const GRState *new_state = makeWithStore(new_store);
+ return Mgr.getOwningEngine().ProcessRegionChange(new_state, R);
+}
+
+const GRState *GRState::InvalidateRegions(const MemRegion * const *Begin,
+ const MemRegion * const *End,
+ const Expr *E, unsigned Count,
+ StoreManager::InvalidatedSymbols *IS,
+ bool invalidateGlobals) const {
+ GRStateManager &Mgr = getStateManager();
+ GRSubEngine &Eng = Mgr.getOwningEngine();
+
+ if (Eng.WantsRegionChangeUpdate(this)) {
+ StoreManager::InvalidatedRegions Regions;
+
+ Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End,
+ E, Count, IS,
+ invalidateGlobals,
+ &Regions);
+ const GRState *new_state = makeWithStore(new_store);
+
+ return Eng.ProcessRegionChanges(new_state,
+ &Regions.front(),
+ &Regions.back()+1);
+ }
+
+ Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End,
+ E, Count, IS,
+ invalidateGlobals,
+ NULL);
+ return makeWithStore(new_store);
}
const GRState *GRState::unbindLoc(Loc LV) const {
+ assert(!isa<loc::MemRegionVal>(LV) && "Use InvalidateRegion instead.");
+
Store OldStore = getStore();
Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV);
if (NewStore == OldStore)
return this;
- GRState NewSt = *this;
- NewSt.St = NewStore;
- return getStateManager().getPersistentState(NewSt);
+ return makeWithStore(NewStore);
+}
+
+const GRState *GRState::EnterStackFrame(const StackFrameContext *frame) const {
+ Store new_store = getStateManager().StoreMgr->EnterStackFrame(this, frame);
+ return makeWithStore(new_store);
}
SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const {
@@ -77,7 +161,7 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const {
return UnknownVal();
if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
- QualType T = TR->getValueType(getStateManager().getContext());
+ QualType T = TR->getValueType();
if (Loc::IsLocType(T) || T->isIntegerType())
return getSVal(R);
}
@@ -85,9 +169,45 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const {
return UnknownVal();
}
+SVal GRState::getSimplifiedSVal(Loc location, QualType T) const {
+ SVal V = getSVal(cast<Loc>(location), T);
+
+ // If 'V' is a symbolic value that is *perfectly* constrained to
+ // be a constant value, use that value instead to lessen the burden
+ // on later analysis stages (so we have less symbolic values to reason
+ // about).
+ if (!T.isNull()) {
+ if (SymbolRef sym = V.getAsSymbol()) {
+ if (const llvm::APSInt *Int = getSymVal(sym)) {
+ // FIXME: Because we don't correctly model (yet) sign-extension
+ // and truncation of symbolic values, we need to convert
+ // the integer value to the correct signedness and bitwidth.
+ //
+ // This shows up in the following:
+ //
+ // char foo();
+ // unsigned x = foo();
+ // if (x == 54)
+ // ...
+ //
+ // The symbolic value stored to 'x' is actually the conjured
+ // symbol for the call to foo(); the type of that symbol is 'char',
+ // not unsigned.
+ const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int);
+
+ if (isa<Loc>(V))
+ return loc::ConcreteInt(NewV);
+ else
+ return nonloc::ConcreteInt(NewV);
+ }
+ }
+ }
+
+ return V;
+}
-const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{
- Environment NewEnv = getStateManager().EnvMgr.BindExpr(Env, Ex, V,
+const GRState *GRState::BindExpr(const Stmt* S, SVal V, bool Invalidate) const{
+ Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V,
Invalidate);
if (NewEnv == Env)
return this;
@@ -97,6 +217,63 @@ const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{
return getStateManager().getPersistentState(NewSt);
}
+const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location,
+ SVal V) const {
+ Environment NewEnv =
+ getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V);
+
+ if (NewEnv == Env)
+ return this;
+
+ GRState NewSt = *this;
+ NewSt.Env = NewEnv;
+ return getStateManager().getPersistentState(NewSt);
+}
+
+const GRState *GRState::AssumeInBound(DefinedOrUnknownSVal Idx,
+ DefinedOrUnknownSVal UpperBound,
+ bool Assumption) const {
+ if (Idx.isUnknown() || UpperBound.isUnknown())
+ return this;
+
+ // Build an expression for 0 <= Idx < UpperBound.
+ // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed.
+ // FIXME: This should probably be part of SValuator.
+ GRStateManager &SM = getStateManager();
+ ValueManager &VM = SM.getValueManager();
+ SValuator &SV = VM.getSValuator();
+ ASTContext &Ctx = VM.getContext();
+
+ // Get the offset: the minimum value of the array index type.
+ BasicValueFactory &BVF = VM.getBasicValueFactory();
+ // FIXME: This should be using ValueManager::ArrayIndexTy...somehow.
+ QualType IndexTy = Ctx.IntTy;
+ nonloc::ConcreteInt Min = BVF.getMinValue(IndexTy);
+
+ // Adjust the index.
+ SVal NewIdx = SV.EvalBinOpNN(this, BO_Add,
+ cast<NonLoc>(Idx), Min, IndexTy);
+ if (NewIdx.isUnknownOrUndef())
+ return this;
+
+ // Adjust the upper bound.
+ SVal NewBound = SV.EvalBinOpNN(this, BO_Add,
+ cast<NonLoc>(UpperBound), Min, IndexTy);
+ if (NewBound.isUnknownOrUndef())
+ return this;
+
+ // Build the actual comparison.
+ SVal InBound = SV.EvalBinOpNN(this, BO_LT,
+ cast<NonLoc>(NewIdx), cast<NonLoc>(NewBound),
+ Ctx.IntTy);
+ if (InBound.isUnknownOrUndef())
+ return this;
+
+ // Finally, let the constraint manager take care of it.
+ ConstraintManager &CM = SM.getConstraintManager();
+ return CM.Assume(this, cast<DefinedSVal>(InBound), Assumption);
+}
+
const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) {
GRState State(this,
EnvMgr.getInitialEnvironment(),
@@ -131,6 +308,11 @@ const GRState* GRState::makeWithStore(Store store) const {
// State pretty-printing.
//===----------------------------------------------------------------------===//
+static bool IsEnvLoc(const Stmt *S) {
+ // FIXME: This is a layering violation. Should be in environment.
+ return (bool) (((uintptr_t) S) & 0x1);
+}
+
void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
const char* sep) const {
// Print the store.
@@ -140,8 +322,9 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
// Print Subexpression bindings.
bool isFirst = true;
+ // FIXME: All environment printing should be moved inside Environment.
for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
- if (C.isBlkExpr(I.getKey()))
+ if (C.isBlkExpr(I.getKey()) || IsEnvLoc(I.getKey()))
continue;
if (isFirst) {
@@ -174,6 +357,27 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
I.getKey()->printPretty(Out, 0, PrintingPolicy(LO));
Out << " : " << I.getData();
}
+
+ // Print locations.
+ isFirst = true;
+
+ for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
+ if (!IsEnvLoc(I.getKey()))
+ continue;
+
+ if (isFirst) {
+ Out << nl << nl << "Load/store locations:" << nl;
+ isFirst = false;
+ }
+ else { Out << nl; }
+
+ const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1));
+
+ Out << " (" << (void*) S << ") ";
+ LangOptions LO; // FIXME.
+ S->printPretty(Out, 0, PrintingPolicy(LO));
+ Out << " : " << I.getData();
+ }
Mgr.getConstraintManager().print(this, Out, nl, sep);
diff --git a/lib/Checker/IdempotentOperationChecker.cpp b/lib/Checker/IdempotentOperationChecker.cpp
index 6ed1841..6411c79 100644
--- a/lib/Checker/IdempotentOperationChecker.cpp
+++ b/lib/Checker/IdempotentOperationChecker.cpp
@@ -36,25 +36,26 @@
// != | 0 | | | | | |
//===----------------------------------------------------------------------===//
//
-// Ways to reduce false positives (that need to be implemented):
-// - Don't flag downsizing casts
-// - Improved handling of static/global variables
-// - Per-block marking of incomplete analysis
-// - Handling ~0 values
-// - False positives involving silencing unused variable warnings
-//
-// Other things TODO:
+// Things TODO:
// - Improved error messages
// - Handle mixed assumptions (which assumptions can belong together?)
// - Finer grained false positive control (levels)
+// - Handling ~0 values
#include "GRExprEngineExperimentalChecks.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
+#include "clang/Checker/BugReporter/BugReporter.h"
#include "clang/Checker/BugReporter/BugType.h"
+#include "clang/Checker/PathSensitive/CheckerHelpers.h"
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Checker/PathSensitive/GRCoreEngine.h"
#include "clang/Checker/PathSensitive/SVals.h"
#include "clang/AST/Stmt.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/ErrorHandling.h"
+#include <deque>
using namespace clang;
@@ -64,38 +65,41 @@ class IdempotentOperationChecker
public:
static void *getTag();
void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B);
- void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B,
- bool hasWorkRemaining);
+ void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B);
+ void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, GRExprEngine &Eng);
private:
// Our assumption about a particular operation.
- enum Assumption { Possible, Impossible, Equal, LHSis1, RHSis1, LHSis0,
+ enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0,
RHSis0 };
void UpdateAssumption(Assumption &A, const Assumption &New);
- /// contains* - Useful recursive methods to see if a statement contains an
- /// element somewhere. Used in static analysis to reduce false positives.
- static bool containsMacro(const Stmt *S);
- static bool containsEnum(const Stmt *S);
- static bool containsBuiltinOffsetOf(const Stmt *S);
- static bool containsZeroConstant(const Stmt *S);
- static bool containsOneConstant(const Stmt *S);
- template <class T> static bool containsStmt(const Stmt *S) {
- if (isa<T>(S))
- return true;
-
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsStmt<T>(child))
- return true;
-
- return false;
- }
-
- // Hash table
- typedef llvm::DenseMap<const BinaryOperator *, Assumption> AssumptionMap;
+ // False positive reduction methods
+ static bool isSelfAssign(const Expr *LHS, const Expr *RHS);
+ static bool isUnused(const Expr *E, AnalysisContext *AC);
+ //static bool isTruncationExtensionAssignment(const Expr *LHS,
+ // const Expr *RHS);
+ static bool PathWasCompletelyAnalyzed(const CFG *C,
+ const CFGBlock *CB,
+ const GRCoreEngine &CE);
+ static bool CanVary(const Expr *Ex,
+ AnalysisContext *AC);
+ static bool isConstantOrPseudoConstant(const DeclRefExpr *DR,
+ AnalysisContext *AC);
+ static bool containsNonLocalVarDecl(const Stmt *S);
+
+ // Hash table and related data structures
+ struct BinaryOperatorData {
+ BinaryOperatorData() : assumption(Possible), analysisContext(0) {}
+
+ Assumption assumption;
+ AnalysisContext *analysisContext;
+ ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a
+ // BinaryOperator
+ };
+ typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData>
+ AssumptionMap;
AssumptionMap hash;
};
}
@@ -112,30 +116,38 @@ void clang::RegisterIdempotentOperationChecker(GRExprEngine &Eng) {
void IdempotentOperationChecker::PreVisitBinaryOperator(
CheckerContext &C,
const BinaryOperator *B) {
- // Find or create an entry in the hash for this BinaryOperator instance
- AssumptionMap::iterator i = hash.find(B);
- Assumption &A = i == hash.end() ? hash[B] : i->second;
-
- // If we had to create an entry, initialise the value to Possible
- if (i == hash.end())
- A = Possible;
+ // Find or create an entry in the hash for this BinaryOperator instance.
+ // If we haven't done a lookup before, it will get default initialized to
+ // 'Possible'. At this stage we do not store the ExplodedNode, as it has not
+ // been created yet.
+ BinaryOperatorData &Data = hash[B];
+ Assumption &A = Data.assumption;
+ AnalysisContext *AC = C.getCurrentAnalysisContext();
+ Data.analysisContext = AC;
// If we already have visited this node on a path that does not contain an
// idempotent operation, return immediately.
if (A == Impossible)
return;
- // Skip binary operators containing common false positives
- if (containsMacro(B) || containsEnum(B) || containsStmt<SizeOfAlignOfExpr>(B)
- || containsZeroConstant(B) || containsOneConstant(B)
- || containsBuiltinOffsetOf(B)) {
- A = Impossible;
- return;
- }
-
+ // Retrieve both sides of the operator and determine if they can vary (which
+ // may mean this is a false positive.
const Expr *LHS = B->getLHS();
const Expr *RHS = B->getRHS();
+ // At this stage we can calculate whether each side contains a false positive
+ // that applies to all operators. We only need to calculate this the first
+ // time.
+ bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false;
+ if (A == Possible) {
+ // An expression contains a false positive if it can't vary, or if it
+ // contains a known false positive VarDecl.
+ LHSContainsFalsePositive = !CanVary(LHS, AC)
+ || containsNonLocalVarDecl(LHS);
+ RHSContainsFalsePositive = !CanVary(RHS, AC)
+ || containsNonLocalVarDecl(RHS);
+ }
+
const GRState *state = C.getState();
SVal LHSVal = state->getSVal(LHS);
@@ -154,16 +166,16 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break;
// Fall through intentional
- case BinaryOperator::AddAssign:
- case BinaryOperator::SubAssign:
- case BinaryOperator::MulAssign:
- case BinaryOperator::DivAssign:
- case BinaryOperator::AndAssign:
- case BinaryOperator::OrAssign:
- case BinaryOperator::XorAssign:
- case BinaryOperator::ShlAssign:
- case BinaryOperator::ShrAssign:
- case BinaryOperator::Assign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_AndAssign:
+ case BO_OrAssign:
+ case BO_XorAssign:
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_Assign:
// Assign statements have one extra level of indirection
if (!isa<Loc>(LHSVal)) {
A = Impossible;
@@ -181,20 +193,37 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break; // We don't care about any other operators.
// Fall through intentional
- case BinaryOperator::SubAssign:
- case BinaryOperator::DivAssign:
- case BinaryOperator::AndAssign:
- case BinaryOperator::OrAssign:
- case BinaryOperator::XorAssign:
- case BinaryOperator::Assign:
- case BinaryOperator::Sub:
- case BinaryOperator::Div:
- case BinaryOperator::And:
- case BinaryOperator::Or:
- case BinaryOperator::Xor:
- case BinaryOperator::LOr:
- case BinaryOperator::LAnd:
- if (LHSVal != RHSVal)
+ case BO_Assign:
+ // x Assign x can be used to silence unused variable warnings intentionally.
+ // If this is a self assignment and the variable is referenced elsewhere,
+ // then it is a false positive.
+ if (isSelfAssign(LHS, RHS)) {
+ if (!isUnused(LHS, AC)) {
+ UpdateAssumption(A, Equal);
+ return;
+ }
+ else {
+ A = Impossible;
+ return;
+ }
+ }
+
+ case BO_SubAssign:
+ case BO_DivAssign:
+ case BO_AndAssign:
+ case BO_OrAssign:
+ case BO_XorAssign:
+ case BO_Sub:
+ case BO_Div:
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
+ case BO_LOr:
+ case BO_LAnd:
+ case BO_EQ:
+ case BO_NE:
+ if (LHSVal != RHSVal || LHSContainsFalsePositive
+ || RHSContainsFalsePositive)
break;
UpdateAssumption(A, Equal);
return;
@@ -206,13 +235,13 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break; // We don't care about any other operators.
// Fall through intentional
- case BinaryOperator::MulAssign:
- case BinaryOperator::DivAssign:
- case BinaryOperator::Mul:
- case BinaryOperator::Div:
- case BinaryOperator::LOr:
- case BinaryOperator::LAnd:
- if (!RHSVal.isConstant(1))
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_Mul:
+ case BO_Div:
+ case BO_LOr:
+ case BO_LAnd:
+ if (!RHSVal.isConstant(1) || RHSContainsFalsePositive)
break;
UpdateAssumption(A, RHSis1);
return;
@@ -224,11 +253,11 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break; // We don't care about any other operators.
// Fall through intentional
- case BinaryOperator::MulAssign:
- case BinaryOperator::Mul:
- case BinaryOperator::LOr:
- case BinaryOperator::LAnd:
- if (!LHSVal.isConstant(1))
+ case BO_MulAssign:
+ case BO_Mul:
+ case BO_LOr:
+ case BO_LAnd:
+ if (!LHSVal.isConstant(1) || LHSContainsFalsePositive)
break;
UpdateAssumption(A, LHSis1);
return;
@@ -240,23 +269,23 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break; // We don't care about any other operators.
// Fall through intentional
- case BinaryOperator::AddAssign:
- case BinaryOperator::SubAssign:
- case BinaryOperator::MulAssign:
- case BinaryOperator::AndAssign:
- case BinaryOperator::OrAssign:
- case BinaryOperator::XorAssign:
- case BinaryOperator::Add:
- case BinaryOperator::Sub:
- case BinaryOperator::Mul:
- case BinaryOperator::And:
- case BinaryOperator::Or:
- case BinaryOperator::Xor:
- case BinaryOperator::Shl:
- case BinaryOperator::Shr:
- case BinaryOperator::LOr:
- case BinaryOperator::LAnd:
- if (!RHSVal.isConstant(0))
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_MulAssign:
+ case BO_AndAssign:
+ case BO_OrAssign:
+ case BO_XorAssign:
+ case BO_Add:
+ case BO_Sub:
+ case BO_Mul:
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_LOr:
+ case BO_LAnd:
+ if (!RHSVal.isConstant(0) || RHSContainsFalsePositive)
break;
UpdateAssumption(A, RHSis0);
return;
@@ -268,27 +297,27 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
break; // We don't care about any other operators.
// Fall through intentional
- //case BinaryOperator::AddAssign: // Common false positive
- case BinaryOperator::SubAssign: // Check only if unsigned
- case BinaryOperator::MulAssign:
- case BinaryOperator::DivAssign:
- case BinaryOperator::AndAssign:
- //case BinaryOperator::OrAssign: // Common false positive
- //case BinaryOperator::XorAssign: // Common false positive
- case BinaryOperator::ShlAssign:
- case BinaryOperator::ShrAssign:
- case BinaryOperator::Add:
- case BinaryOperator::Sub:
- case BinaryOperator::Mul:
- case BinaryOperator::Div:
- case BinaryOperator::And:
- case BinaryOperator::Or:
- case BinaryOperator::Xor:
- case BinaryOperator::Shl:
- case BinaryOperator::Shr:
- case BinaryOperator::LOr:
- case BinaryOperator::LAnd:
- if (!LHSVal.isConstant(0))
+ //case BO_AddAssign: // Common false positive
+ case BO_SubAssign: // Check only if unsigned
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_AndAssign:
+ //case BO_OrAssign: // Common false positive
+ //case BO_XorAssign: // Common false positive
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_Add:
+ case BO_Sub:
+ case BO_Mul:
+ case BO_Div:
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_LOr:
+ case BO_LAnd:
+ if (!LHSVal.isConstant(0) || LHSContainsFalsePositive)
break;
UpdateAssumption(A, LHSis0);
return;
@@ -298,47 +327,103 @@ void IdempotentOperationChecker::PreVisitBinaryOperator(
A = Impossible;
}
-void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
- BugReporter &B,
- bool hasWorkRemaining) {
- // If there is any work remaining we cannot be 100% sure about our warnings
- if (hasWorkRemaining)
- return;
+// At the post visit stage, the predecessor ExplodedNode will be the
+// BinaryOperator that was just created. We use this hook to collect the
+// ExplodedNode.
+void IdempotentOperationChecker::PostVisitBinaryOperator(
+ CheckerContext &C,
+ const BinaryOperator *B) {
+ // Add the ExplodedNode we just visited
+ BinaryOperatorData &Data = hash[B];
+ Data.explodedNodes.Add(C.getPredecessor());
+}
+void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
+ BugReporter &BR,
+ GRExprEngine &Eng) {
+ BugType *BT = new BugType("Idempotent operation", "Dead code");
// Iterate over the hash to see if we have any paths with definite
// idempotent operations.
- for (AssumptionMap::const_iterator i =
- hash.begin(); i != hash.end(); ++i) {
- if (i->second != Impossible) {
- // Select the error message.
- const char *msg = 0;
- switch (i->second) {
- case Equal:
- msg = "idempotent operation; both operands are always equal in value";
- break;
- case LHSis1:
- msg = "idempotent operation; the left operand is always 1";
- break;
- case RHSis1:
- msg = "idempotent operation; the right operand is always 1";
- break;
- case LHSis0:
- msg = "idempotent operation; the left operand is always 0";
- break;
- case RHSis0:
- msg = "idempotent operation; the right operand is always 0";
- break;
- case Possible:
- llvm_unreachable("Operation was never marked with an assumption");
- case Impossible:
- llvm_unreachable(0);
+ for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) {
+ // Unpack the hash contents
+ const BinaryOperatorData &Data = i->second;
+ const Assumption &A = Data.assumption;
+ AnalysisContext *AC = Data.analysisContext;
+ const ExplodedNodeSet &ES = Data.explodedNodes;
+
+ const BinaryOperator *B = i->first;
+
+ if (A == Impossible)
+ continue;
+
+ // If the analyzer did not finish, check to see if we can still emit this
+ // warning
+ if (Eng.hasWorkRemaining()) {
+ const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(),
+ &AC->getParentMap());
+
+ // If we can trace back
+ if (!PathWasCompletelyAnalyzed(AC->getCFG(),
+ CBM->getBlock(B),
+ Eng.getCoreEngine()))
+ continue;
+
+ delete CBM;
+ }
+
+ // Select the error message and SourceRanges to report.
+ llvm::SmallString<128> buf;
+ llvm::raw_svector_ostream os(buf);
+ bool LHSRelevant = false, RHSRelevant = false;
+ switch (A) {
+ case Equal:
+ LHSRelevant = true;
+ RHSRelevant = true;
+ if (B->getOpcode() == BO_Assign)
+ os << "Assigned value is always the same as the existing value";
+ else
+ os << "Both operands to '" << B->getOpcodeStr()
+ << "' always have the same value";
+ break;
+ case LHSis1:
+ LHSRelevant = true;
+ os << "The left operand to '" << B->getOpcodeStr() << "' is always 1";
+ break;
+ case RHSis1:
+ RHSRelevant = true;
+ os << "The right operand to '" << B->getOpcodeStr() << "' is always 1";
+ break;
+ case LHSis0:
+ LHSRelevant = true;
+ os << "The left operand to '" << B->getOpcodeStr() << "' is always 0";
+ break;
+ case RHSis0:
+ RHSRelevant = true;
+ os << "The right operand to '" << B->getOpcodeStr() << "' is always 0";
+ break;
+ case Possible:
+ llvm_unreachable("Operation was never marked with an assumption");
+ case Impossible:
+ llvm_unreachable(0);
+ }
+
+ // Add a report for each ExplodedNode
+ for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) {
+ EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I);
+
+ // Add source ranges and visitor hooks
+ if (LHSRelevant) {
+ const Expr *LHS = i->first->getLHS();
+ report->addRange(LHS->getSourceRange());
+ report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS);
+ }
+ if (RHSRelevant) {
+ const Expr *RHS = i->first->getRHS();
+ report->addRange(i->first->getRHS()->getSourceRange());
+ report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS);
}
- // Create the SourceRange Arrays
- SourceRange S[2] = { i->first->getLHS()->getSourceRange(),
- i->first->getRHS()->getSourceRange() };
- B.EmitBasicReport("Idempotent operation", msg, i->first->getOperatorLoc(),
- S, 2);
+ BR.EmitReport(report);
}
}
}
@@ -346,6 +431,10 @@ void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
// Updates the current assumption given the new assumption
inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
const Assumption &New) {
+// If the assumption is the same, there is nothing to do
+ if (A == New)
+ return;
+
switch (A) {
// If we don't currently have an assumption, set it
case Possible:
@@ -366,89 +455,249 @@ inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
}
}
-// Recursively find any substatements containing macros
-bool IdempotentOperationChecker::containsMacro(const Stmt *S) {
- if (S->getLocStart().isMacroID())
- return true;
+// Check for a statement where a variable is self assigned to possibly avoid an
+// unused variable warning.
+bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) {
+ LHS = LHS->IgnoreParenCasts();
+ RHS = RHS->IgnoreParenCasts();
- if (S->getLocEnd().isMacroID())
- return true;
+ const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS);
+ if (!LHS_DR)
+ return false;
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsMacro(child))
- return true;
+ const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl());
+ if (!VD)
+ return false;
- return false;
+ const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS);
+ if (!RHS_DR)
+ return false;
+
+ if (VD != RHS_DR->getDecl())
+ return false;
+
+ return true;
}
-// Recursively find any substatements containing enum constants
-bool IdempotentOperationChecker::containsEnum(const Stmt *S) {
- const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+// Returns true if the Expr points to a VarDecl that is not read anywhere
+// outside of self-assignments.
+bool IdempotentOperationChecker::isUnused(const Expr *E,
+ AnalysisContext *AC) {
+ if (!E)
+ return false;
- if (DR && isa<EnumConstantDecl>(DR->getDecl()))
- return true;
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts());
+ if (!DR)
+ return false;
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsEnum(child))
- return true;
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (!VD)
+ return false;
- return false;
+ if (AC->getPseudoConstantAnalysis()->wasReferenced(VD))
+ return false;
+
+ return true;
}
-// Recursively find any substatements containing __builtin_offset_of
-bool IdempotentOperationChecker::containsBuiltinOffsetOf(const Stmt *S) {
- const UnaryOperator *UO = dyn_cast<UnaryOperator>(S);
+#if 0
+// Check for self casts truncating/extending a variable
+bool IdempotentOperationChecker::isTruncationExtensionAssignment(
+ const Expr *LHS,
+ const Expr *RHS) {
- if (UO && UO->getOpcode() == UnaryOperator::OffsetOf)
- return true;
+ const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParenCasts());
+ if (!LHS_DR)
+ return false;
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsBuiltinOffsetOf(child))
- return true;
+ const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl());
+ if (!VD)
+ return false;
- return false;
+ const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS->IgnoreParenCasts());
+ if (!RHS_DR)
+ return false;
+
+ if (VD != RHS_DR->getDecl())
+ return false;
+
+ return dyn_cast<DeclRefExpr>(RHS->IgnoreParens()) == NULL;
}
+#endif
+
+// Returns false if a path to this block was not completely analyzed, or true
+// otherwise.
+bool IdempotentOperationChecker::PathWasCompletelyAnalyzed(
+ const CFG *C,
+ const CFGBlock *CB,
+ const GRCoreEngine &CE) {
+ std::deque<const CFGBlock *> WorkList;
+ llvm::SmallSet<unsigned, 8> Aborted;
+ llvm::SmallSet<unsigned, 128> Visited;
+
+ // Create a set of all aborted blocks
+ typedef GRCoreEngine::BlocksAborted::const_iterator AbortedIterator;
+ for (AbortedIterator I = CE.blocks_aborted_begin(),
+ E = CE.blocks_aborted_end(); I != E; ++I) {
+ const BlockEdge &BE = I->first;
+
+ // The destination block on the BlockEdge is the first block that was not
+ // analyzed.
+ Aborted.insert(BE.getDst()->getBlockID());
+ }
-bool IdempotentOperationChecker::containsZeroConstant(const Stmt *S) {
- const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
- if (IL && IL->getValue() == 0)
+ // Save the entry block ID for early exiting
+ unsigned EntryBlockID = C->getEntry().getBlockID();
+
+ // Create initial node
+ WorkList.push_back(CB);
+
+ while (!WorkList.empty()) {
+ const CFGBlock *Head = WorkList.front();
+ WorkList.pop_front();
+ Visited.insert(Head->getBlockID());
+
+ // If we found the entry block, then there exists a path from the target
+ // node to the entry point of this function -> the path was completely
+ // analyzed.
+ if (Head->getBlockID() == EntryBlockID)
+ return true;
+
+ // If any of the aborted blocks are on the path to the beginning, then all
+ // paths to this block were not analyzed.
+ if (Aborted.count(Head->getBlockID()))
+ return false;
+
+ // Add the predecessors to the worklist unless we have already visited them
+ for (CFGBlock::const_pred_iterator I = Head->pred_begin();
+ I != Head->pred_end(); ++I)
+ if (!Visited.count((*I)->getBlockID()))
+ WorkList.push_back(*I);
+ }
+
+ // If we get to this point, there is no connection to the entry block or an
+ // aborted block. This path is unreachable and we can report the error.
+ return true;
+}
+
+// Recursive function that determines whether an expression contains any element
+// that varies. This could be due to a compile-time constant like sizeof. An
+// expression may also involve a variable that behaves like a constant. The
+// function returns true if the expression varies, and false otherwise.
+bool IdempotentOperationChecker::CanVary(const Expr *Ex,
+ AnalysisContext *AC) {
+ // Parentheses and casts are irrelevant here
+ Ex = Ex->IgnoreParenCasts();
+
+ if (Ex->getLocStart().isMacroID())
+ return false;
+
+ switch (Ex->getStmtClass()) {
+ // Trivially true cases
+ case Stmt::ArraySubscriptExprClass:
+ case Stmt::MemberExprClass:
+ case Stmt::StmtExprClass:
+ case Stmt::CallExprClass:
+ case Stmt::VAArgExprClass:
+ case Stmt::ShuffleVectorExprClass:
+ return true;
+ default:
return true;
- const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
- if (FL && FL->getValue().isZero())
+ // Trivially false cases
+ case Stmt::IntegerLiteralClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::PredefinedExprClass:
+ case Stmt::ImaginaryLiteralClass:
+ case Stmt::StringLiteralClass:
+ case Stmt::OffsetOfExprClass:
+ case Stmt::CompoundLiteralExprClass:
+ case Stmt::AddrLabelExprClass:
+ case Stmt::TypesCompatibleExprClass:
+ case Stmt::GNUNullExprClass:
+ case Stmt::InitListExprClass:
+ case Stmt::DesignatedInitExprClass:
+ case Stmt::BlockExprClass:
+ case Stmt::BlockDeclRefExprClass:
+ return false;
+
+ // Cases requiring custom logic
+ case Stmt::SizeOfAlignOfExprClass: {
+ const SizeOfAlignOfExpr *SE = cast<const SizeOfAlignOfExpr>(Ex);
+ if (!SE->isSizeOf())
+ return false;
+ return SE->getTypeOfArgument()->isVariableArrayType();
+ }
+ case Stmt::DeclRefExprClass:
+ // Check for constants/pseudoconstants
+ return !isConstantOrPseudoConstant(cast<DeclRefExpr>(Ex), AC);
+
+ // The next cases require recursion for subexpressions
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *B = cast<const BinaryOperator>(Ex);
+ return CanVary(B->getRHS(), AC)
+ || CanVary(B->getLHS(), AC);
+ }
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *U = cast<const UnaryOperator>(Ex);
+ // Handle trivial case first
+ switch (U->getOpcode()) {
+ case UO_Extension:
+ return false;
+ default:
+ return CanVary(U->getSubExpr(), AC);
+ }
+ }
+ case Stmt::ChooseExprClass:
+ return CanVary(cast<const ChooseExpr>(Ex)->getChosenSubExpr(
+ AC->getASTContext()), AC);
+ case Stmt::ConditionalOperatorClass:
+ return CanVary(cast<const ConditionalOperator>(Ex)->getCond(), AC);
+ }
+}
+
+// Returns true if a DeclRefExpr is or behaves like a constant.
+bool IdempotentOperationChecker::isConstantOrPseudoConstant(
+ const DeclRefExpr *DR,
+ AnalysisContext *AC) {
+ // Check if the type of the Decl is const-qualified
+ if (DR->getType().isConstQualified())
return true;
- for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
- ++I)
- if (const Stmt *child = *I)
- if (containsZeroConstant(child))
- return true;
+ // Check for an enum
+ if (isa<EnumConstantDecl>(DR->getDecl()))
+ return true;
+
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (!VD)
+ return true;
+
+ // Check if the Decl behaves like a constant. This check also takes care of
+ // static variables, which can only change between function calls if they are
+ // modified in the AST.
+ PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis();
+ if (PCA->isPseudoConstant(VD))
+ return true;
return false;
}
-bool IdempotentOperationChecker::containsOneConstant(const Stmt *S) {
- const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
- if (IL && IL->getValue() == 1)
- return true;
+// Recursively find any substatements containing VarDecl's with storage other
+// than local
+bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
- const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
- const llvm::APFloat one(1.0);
- if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual)
- return true;
+ if (DR)
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ if (!VD->hasLocalStorage())
+ return true;
for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
++I)
if (const Stmt *child = *I)
- if (containsOneConstant(child))
+ if (containsNonLocalVarDecl(child))
return true;
return false;
}
-
diff --git a/lib/Checker/LLVMConventionsChecker.cpp b/lib/Checker/LLVMConventionsChecker.cpp
index c121257..2f87da1 100644
--- a/lib/Checker/LLVMConventionsChecker.cpp
+++ b/lib/Checker/LLVMConventionsChecker.cpp
@@ -128,7 +128,6 @@ public:
void VisitDeclStmt(DeclStmt *DS);
private:
void VisitVarDecl(VarDecl *VD);
- void CheckStringRefBoundtoTemporaryString(VarDecl *VD);
};
} // end anonymous namespace
diff --git a/lib/Checker/Makefile b/lib/Checker/Makefile
index 1bc6529..4ec6f65 100644
--- a/lib/Checker/Makefile
+++ b/lib/Checker/Makefile
@@ -13,7 +13,6 @@
CLANG_LEVEL := ../..
LIBRARYNAME := clangChecker
-BUILD_ARCHIVE = 1
include $(CLANG_LEVEL)/Makefile
diff --git a/lib/Checker/MallocChecker.cpp b/lib/Checker/MallocChecker.cpp
index dcc21ca..c9b6d75 100644
--- a/lib/Checker/MallocChecker.cpp
+++ b/lib/Checker/MallocChecker.cpp
@@ -24,15 +24,18 @@ using namespace clang;
namespace {
class RefState {
- enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K;
+ enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped,
+ Relinquished } K;
const Stmt *S;
public:
RefState(Kind k, const Stmt *s) : K(k), S(s) {}
bool isAllocated() const { return K == AllocateUnchecked; }
+ //bool isFailed() const { return K == AllocateFailed; }
bool isReleased() const { return K == Released; }
- bool isEscaped() const { return K == Escaped; }
+ //bool isEscaped() const { return K == Escaped; }
+ //bool isRelinquished() const { return K == Relinquished; }
bool operator==(const RefState &X) const {
return K == X.K && S == X.S;
@@ -46,6 +49,9 @@ public:
}
static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); }
+ static RefState getRelinquished(const Stmt *s) {
+ return RefState(Relinquished, s);
+ }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
@@ -59,23 +65,30 @@ class MallocChecker : public CheckerVisitor<MallocChecker> {
BuiltinBug *BT_DoubleFree;
BuiltinBug *BT_Leak;
BuiltinBug *BT_UseFree;
+ BuiltinBug *BT_UseRelinquished;
BuiltinBug *BT_BadFree;
IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc;
public:
MallocChecker()
- : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_BadFree(0),
+ : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0),
+ BT_BadFree(0),
II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {}
static void *getTag();
bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper);
void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng);
void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
- const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption);
+ const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption,
+ bool *respondsToCallback);
void VisitLocation(CheckerContext &C, const Stmt *S, SVal l);
+ virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE,
+ SVal location, SVal val);
private:
void MallocMem(CheckerContext &C, const CallExpr *CE);
+ void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att);
const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
const GRState *state) {
@@ -86,8 +99,10 @@ private:
const GRState *state);
void FreeMem(CheckerContext &C, const CallExpr *CE);
+ void FreeMemAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att);
const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state);
+ const GRState *state, unsigned Num, bool Hold);
void ReallocMem(CheckerContext &C, const CallExpr *CE);
void CallocMem(CheckerContext &C, const CallExpr *CE);
@@ -103,7 +118,7 @@ typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy;
namespace clang {
template <>
struct GRStateTrait<RegionState>
- : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > {
+ : public GRStatePartialTrait<RegionStateTy> {
static void *GDMIndex() { return MallocChecker::getTag(); }
};
}
@@ -156,7 +171,32 @@ bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
return true;
}
- return false;
+ // Check all the attributes, if there are any.
+ // There can be multiple of these attributes.
+ bool rv = false;
+ if (FD->hasAttrs()) {
+ for (specific_attr_iterator<OwnershipAttr>
+ i = FD->specific_attr_begin<OwnershipAttr>(),
+ e = FD->specific_attr_end<OwnershipAttr>();
+ i != e; ++i) {
+ switch ((*i)->getOwnKind()) {
+ case OwnershipAttr::Returns: {
+ MallocMemReturnsAttr(C, CE, *i);
+ rv = true;
+ break;
+ }
+ case OwnershipAttr::Takes:
+ case OwnershipAttr::Holds: {
+ FreeMemAttr(C, CE, *i);
+ rv = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return rv;
}
void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
@@ -165,6 +205,23 @@ void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
C.addTransition(state);
}
+void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att) {
+ if (Att->getModule() != "malloc")
+ return;
+
+ OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
+ if (I != E) {
+ const GRState *state =
+ MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
+ C.addTransition(state);
+ return;
+ }
+ const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(),
+ C.getState());
+ C.addTransition(state);
+}
+
const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
const CallExpr *CE,
SVal Size, SVal Init,
@@ -196,25 +253,53 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
}
void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) {
- const GRState *state = FreeMemAux(C, CE, C.getState());
+ const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false);
if (state)
C.addTransition(state);
}
+void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att) {
+ if (Att->getModule() != "malloc")
+ return;
+
+ for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
+ I != E; ++I) {
+ const GRState *state = FreeMemAux(C, CE, C.getState(), *I,
+ Att->getOwnKind() == OwnershipAttr::Holds);
+ if (state)
+ C.addTransition(state);
+ }
+}
+
const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state) {
- const Expr *ArgExpr = CE->getArg(0);
+ const GRState *state, unsigned Num,
+ bool Hold) {
+ const Expr *ArgExpr = CE->getArg(Num);
SVal ArgVal = state->getSVal(ArgExpr);
- // If ptr is NULL, no operation is preformed.
- if (ArgVal.isZeroConstant())
+ DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal);
+
+ // Check for null dereferences.
+ if (!isa<Loc>(location))
return state;
-
+
+ // FIXME: Technically using 'Assume' here can result in a path
+ // bifurcation. In such cases we need to return two states, not just one.
+ const GRState *notNullState, *nullState;
+ llvm::tie(notNullState, nullState) = state->Assume(location);
+
+ // The explicit NULL case, no operation is performed.
+ if (nullState && !notNullState)
+ return nullState;
+
+ assert(notNullState);
+
// Unknown values could easily be okay
// Undefined values are handled elsewhere
if (ArgVal.isUnknownOrUndef())
- return state;
+ return notNullState;
const MemRegion *R = ArgVal.getAsRegion();
@@ -253,24 +338,23 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
// Various cases could lead to non-symbol values here.
// For now, ignore them.
if (!SR)
- return state;
+ return notNullState;
SymbolRef Sym = SR->getSymbol();
-
const RefState *RS = state->get<RegionState>(Sym);
// If the symbol has not been tracked, return. This is possible when free() is
// called on a pointer that does not get its pointee directly from malloc().
// Full support of this requires inter-procedural analysis.
if (!RS)
- return state;
+ return notNullState;
// Check double free.
if (RS->isReleased()) {
- ExplodedNode *N = C.GenerateSink();
- if (N) {
+ if (ExplodedNode *N = C.GenerateSink()) {
if (!BT_DoubleFree)
- BT_DoubleFree = new BuiltinBug("Double free",
+ BT_DoubleFree
+ = new BuiltinBug("Double free",
"Try to free a memory block that has been released");
// FIXME: should find where it's freed last time.
BugReport *R = new BugReport(*BT_DoubleFree,
@@ -281,7 +365,9 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
}
// Normal free.
- return state->set<RegionState>(Sym, RefState::getReleased(CE));
+ if (Hold)
+ return notNullState->set<RegionState>(Sym, RefState::getRelinquished(CE));
+ return notNullState->set<RegionState>(Sym, RefState::getReleased(CE));
}
bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) {
@@ -376,8 +462,7 @@ bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os,
void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
SourceRange range) {
- ExplodedNode *N = C.GenerateSink();
- if (N) {
+ if (ExplodedNode *N = C.GenerateSink()) {
if (!BT_BadFree)
BT_BadFree = new BuiltinBug("Bad free");
@@ -446,13 +531,13 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) {
ValMgr.makeIntValWithPtrWidth(0, false));
if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) {
- const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero);
+ const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false);
if (stateFree)
C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true));
}
if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) {
- const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero);
+ const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, 0, false);
if (stateFree) {
// FIXME: We should copy the content of the original buffer.
const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
@@ -471,7 +556,7 @@ void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) {
SVal Count = state->getSVal(CE->getArg(0));
SVal EleSize = state->getSVal(CE->getArg(1));
- SVal TotalSize = SVator.EvalBinOp(state, BinaryOperator::Mul, Count, EleSize,
+ SVal TotalSize = SVator.EvalBinOp(state, BO_Mul, Count, EleSize,
ValMgr.getContext().getSizeType());
SVal Zero = ValMgr.makeZeroVal(ValMgr.getContext().CharTy);
@@ -481,36 +566,42 @@ void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) {
}
void MallocChecker::EvalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) {
- for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
- E = SymReaper.dead_end(); I != E; ++I) {
- SymbolRef Sym = *I;
- const GRState *state = C.getState();
- const RefState *RS = state->get<RegionState>(Sym);
- if (!RS)
- return;
-
- if (RS->isAllocated()) {
- ExplodedNode *N = C.GenerateSink();
- if (N) {
- if (!BT_Leak)
- BT_Leak = new BuiltinBug("Memory leak",
+ if (!SymReaper.hasDeadSymbols())
+ return;
+
+ const GRState *state = C.getState();
+ RegionStateTy RS = state->get<RegionState>();
+ RegionStateTy::Factory &F = state->get_context<RegionState>();
+
+ for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first)) {
+ if (I->second.isAllocated()) {
+ if (ExplodedNode *N = C.GenerateNode()) {
+ if (!BT_Leak)
+ BT_Leak = new BuiltinBug("Memory leak",
"Allocated memory never released. Potential memory leak.");
- // FIXME: where it is allocated.
- BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
- C.EmitReport(R);
+ // FIXME: where it is allocated.
+ BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
+ C.EmitReport(R);
+ }
}
+
+ // Remove the dead symbol from the map.
+ RS = F.Remove(RS, I->first);
}
}
+
+ state = state->set<RegionState>(RS);
+ C.GenerateNode(state);
}
void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag,
GRExprEngine &Eng) {
SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode);
const GRState *state = B.getState();
- typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap;
- SymMap M = state->get<RegionState>();
+ RegionStateTy M = state->get<RegionState>();
- for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+ for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) {
RefState RS = I->second;
if (RS.isAllocated()) {
ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
@@ -549,7 +640,8 @@ void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
}
const GRState *MallocChecker::EvalAssume(const GRState *state, SVal Cond,
- bool Assumption) {
+ bool Assumption,
+ bool * /* respondsToCallback */) {
// If a symblic region is assumed to NULL, set its state to AllocateFailed.
// FIXME: should also check symbols assumed to non-null.
@@ -568,9 +660,8 @@ void MallocChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) {
SymbolRef Sym = l.getLocSymbolInBase();
if (Sym) {
const RefState *RS = C.getState()->get<RegionState>(Sym);
- if (RS)
- if (RS->isReleased()) {
- ExplodedNode *N = C.GenerateSink();
+ if (RS && RS->isReleased()) {
+ if (ExplodedNode *N = C.GenerateNode()) {
if (!BT_UseFree)
BT_UseFree = new BuiltinBug("Use dynamically allocated memory after"
" it is freed.");
@@ -579,5 +670,67 @@ void MallocChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) {
N);
C.EmitReport(R);
}
+ }
+ }
+}
+
+void MallocChecker::PreVisitBind(CheckerContext &C,
+ const Stmt *StoreE,
+ SVal location,
+ SVal val) {
+ // The PreVisitBind implements the same algorithm as already used by the
+ // Objective C ownership checker: if the pointer escaped from this scope by
+ // assignment, let it go. However, assigning to fields of a stack-storage
+ // structure does not transfer ownership.
+
+ const GRState *state = C.getState();
+ DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location);
+
+ // Check for null dereferences.
+ if (!isa<Loc>(l))
+ return;
+
+ // Before checking if the state is null, check if 'val' has a RefState.
+ // Only then should we check for null and bifurcate the state.
+ SymbolRef Sym = val.getLocSymbolInBase();
+ if (Sym) {
+ if (const RefState *RS = state->get<RegionState>(Sym)) {
+ // If ptr is NULL, no operation is performed.
+ const GRState *notNullState, *nullState;
+ llvm::tie(notNullState, nullState) = state->Assume(l);
+
+ // Generate a transition for 'nullState' to record the assumption
+ // that the state was null.
+ if (nullState)
+ C.addTransition(nullState);
+
+ if (!notNullState)
+ return;
+
+ if (RS->isAllocated()) {
+ // Something we presently own is being assigned somewhere.
+ const MemRegion *AR = location.getAsRegion();
+ if (!AR)
+ return;
+ AR = AR->StripCasts()->getBaseRegion();
+ do {
+ // If it is on the stack, we still own it.
+ if (AR->hasStackNonParametersStorage())
+ break;
+
+ // If the state can't represent this binding, we still own it.
+ if (notNullState == (notNullState->bindLoc(cast<Loc>(location),
+ UnknownVal())))
+ break;
+
+ // We no longer own this pointer.
+ notNullState =
+ notNullState->set<RegionState>(Sym,
+ RefState::getRelinquished(StoreE));
+ }
+ while (false);
+ }
+ C.addTransition(notNullState);
+ }
}
}
diff --git a/lib/Checker/MemRegion.cpp b/lib/Checker/MemRegion.cpp
index 9cfeb7a..3f706e1 100644
--- a/lib/Checker/MemRegion.cpp
+++ b/lib/Checker/MemRegion.cpp
@@ -18,6 +18,7 @@
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/Support/BumpVector.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/RecordLayout.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -177,7 +178,7 @@ const StackFrameContext *VarRegion::getStackFrame() const {
DefinedOrUnknownSVal DeclRegion::getExtent(ValueManager& ValMgr) const {
ASTContext& Ctx = ValMgr.getContext();
- QualType T = getDesugaredValueType(Ctx);
+ QualType T = getDesugaredValueType();
if (isa<VariableArrayType>(T))
return nonloc::SymbolVal(ValMgr.getSymbolManager().getExtentSymbol(this));
@@ -195,8 +196,7 @@ DefinedOrUnknownSVal FieldRegion::getExtent(ValueManager& ValMgr) const {
// A zero-length array at the end of a struct often stands for dynamically-
// allocated extra memory.
if (Extent.isZeroConstant()) {
- ASTContext& Ctx = ValMgr.getContext();
- QualType T = getDesugaredValueType(Ctx);
+ QualType T = getDesugaredValueType();
if (isa<ConstantArrayType>(T))
return UnknownVal();
@@ -785,7 +785,7 @@ static bool IsCompleteType(ASTContext &Ctx, QualType Ty) {
return true;
}
-RegionRawOffset ElementRegion::getAsRawOffset() const {
+RegionRawOffset ElementRegion::getAsArrayOffset() const {
CharUnits offset = CharUnits::Zero();
const ElementRegion *ER = this;
const MemRegion *superR = NULL;
@@ -827,6 +827,67 @@ RegionRawOffset ElementRegion::getAsRawOffset() const {
return RegionRawOffset(superR, offset.getQuantity());
}
+RegionOffset MemRegion::getAsOffset() const {
+ const MemRegion *R = this;
+ int64_t Offset = 0;
+
+ while (1) {
+ switch (R->getKind()) {
+ default:
+ return RegionOffset(0);
+ case SymbolicRegionKind:
+ case AllocaRegionKind:
+ case CompoundLiteralRegionKind:
+ case CXXThisRegionKind:
+ case StringRegionKind:
+ case VarRegionKind:
+ case CXXObjectRegionKind:
+ goto Finish;
+ case ElementRegionKind: {
+ const ElementRegion *ER = cast<ElementRegion>(R);
+ QualType EleTy = ER->getValueType();
+
+ if (!IsCompleteType(getContext(), EleTy))
+ return RegionOffset(0);
+
+ SVal Index = ER->getIndex();
+ if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) {
+ int64_t i = CI->getValue().getSExtValue();
+ CharUnits Size = getContext().getTypeSizeInChars(EleTy);
+ Offset += i * Size.getQuantity() * 8;
+ } else {
+ // We cannot compute offset for non-concrete index.
+ return RegionOffset(0);
+ }
+ R = ER->getSuperRegion();
+ break;
+ }
+ case FieldRegionKind: {
+ const FieldRegion *FR = cast<FieldRegion>(R);
+ const RecordDecl *RD = FR->getDecl()->getParent();
+ if (!RD->isDefinition())
+ // We cannot compute offset for incomplete type.
+ return RegionOffset(0);
+ // Get the field number.
+ unsigned idx = 0;
+ for (RecordDecl::field_iterator FI = RD->field_begin(),
+ FE = RD->field_end(); FI != FE; ++FI, ++idx)
+ if (FR->getDecl() == *FI)
+ break;
+
+ const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
+ // This is offset in bits.
+ Offset += Layout.getFieldOffset(idx);
+ R = FR->getSuperRegion();
+ break;
+ }
+ }
+ }
+
+ Finish:
+ return RegionOffset(R, Offset);
+}
+
//===----------------------------------------------------------------------===//
// BlockDataRegion
//===----------------------------------------------------------------------===//
diff --git a/lib/Checker/OSAtomicChecker.cpp b/lib/Checker/OSAtomicChecker.cpp
index 1ea1bd9..02de0a8 100644
--- a/lib/Checker/OSAtomicChecker.cpp
+++ b/lib/Checker/OSAtomicChecker.cpp
@@ -110,9 +110,9 @@ bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C,
QualType LoadTy;
if (const TypedRegion *TR =
dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
- LoadTy = TR->getValueType(Ctx);
+ LoadTy = TR->getValueType();
}
- Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(),
+ Engine.EvalLoad(Tmp, theValueExpr, C.getPredecessor(),
state, location, OSAtomicLoadTag, LoadTy);
if (Tmp.empty()) {
@@ -158,10 +158,10 @@ bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C,
// Handle implicit value casts.
if (const TypedRegion *R =
dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
- val = SVator.EvalCast(val,R->getValueType(Ctx),newValueExpr->getType());
+ val = SVator.EvalCast(val,R->getValueType(), newValueExpr->getType());
}
- Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N,
+ Engine.EvalStore(TmpStore, NULL, theValueExpr, N,
stateEqual, location, val, OSAtomicStoreTag);
if (TmpStore.empty()) {
diff --git a/lib/Checker/PointerArithChecker.cpp b/lib/Checker/PointerArithChecker.cpp
index ed60c42..cbac423 100644
--- a/lib/Checker/PointerArithChecker.cpp
+++ b/lib/Checker/PointerArithChecker.cpp
@@ -36,8 +36,7 @@ void *PointerArithChecker::getTag() {
void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C,
const BinaryOperator *B) {
- if (B->getOpcode() != BinaryOperator::Sub &&
- B->getOpcode() != BinaryOperator::Add)
+ if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add)
return;
const GRState *state = C.getState();
diff --git a/lib/Checker/PointerSubChecker.cpp b/lib/Checker/PointerSubChecker.cpp
index bc0fd24..d64b6ae 100644
--- a/lib/Checker/PointerSubChecker.cpp
+++ b/lib/Checker/PointerSubChecker.cpp
@@ -39,7 +39,7 @@ void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C,
const BinaryOperator *B) {
// When doing pointer subtraction, if the two pointers do not point to the
// same memory chunk, emit a warning.
- if (B->getOpcode() != BinaryOperator::Sub)
+ if (B->getOpcode() != BO_Sub)
return;
const GRState *state = C.getState();
diff --git a/lib/Checker/RangeConstraintManager.cpp b/lib/Checker/RangeConstraintManager.cpp
index 2a35d32..697694e 100644
--- a/lib/Checker/RangeConstraintManager.cpp
+++ b/lib/Checker/RangeConstraintManager.cpp
@@ -83,7 +83,6 @@ public:
typedef PrimRangeSet::iterator iterator;
RangeSet(PrimRangeSet RS) : ranges(RS) {}
- RangeSet(Factory& F) : ranges(F.GetEmptySet()) {}
iterator begin() const { return ranges.begin(); }
iterator end() const { return ranges.end(); }
diff --git a/lib/Checker/RegionStore.cpp b/lib/Checker/RegionStore.cpp
index 74a7fee..1a3eded 100644
--- a/lib/Checker/RegionStore.cpp
+++ b/lib/Checker/RegionStore.cpp
@@ -44,10 +44,9 @@ private:
uint64_t Offset;
explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
- : P(r, (unsigned) k), Offset(offset) { assert(r); }
+ : P(r, (unsigned) k), Offset(offset) {}
public:
- bool isDefault() const { return P.getInt() == Default; }
bool isDirect() const { return P.getInt() == Direct; }
const MemRegion *getRegion() const { return P.getPointer(); }
@@ -72,9 +71,26 @@ public:
return P.getOpaqueValue() == X.P.getOpaqueValue() &&
Offset == X.Offset;
}
+
+ bool isValid() const {
+ return getRegion() != NULL;
+ }
};
} // end anonymous namespace
+BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ const RegionRawOffset &O = ER->getAsArrayOffset();
+
+ // FIXME: There are some ElementRegions for which we cannot compute
+ // raw offsets yet, including regions with symbolic offsets. These will be
+ // ignored by the store.
+ return BindingKey(O.getRegion(), O.getByteOffset(), k);
+ }
+
+ return BindingKey(R, 0, k);
+}
+
namespace llvm {
static inline
llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) {
@@ -101,35 +117,20 @@ struct maximal_features_tag {};
class RegionStoreFeatures {
bool SupportsFields;
- bool SupportsRemaining;
-
public:
RegionStoreFeatures(minimal_features_tag) :
- SupportsFields(false), SupportsRemaining(false) {}
+ SupportsFields(false) {}
RegionStoreFeatures(maximal_features_tag) :
- SupportsFields(true), SupportsRemaining(false) {}
+ SupportsFields(true) {}
void enableFields(bool t) { SupportsFields = t; }
bool supportsFields() const { return SupportsFields; }
- bool supportsRemaining() const { return SupportsRemaining; }
};
}
//===----------------------------------------------------------------------===//
-// Utility functions.
-//===----------------------------------------------------------------------===//
-
-static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) {
- if (ty->isAnyPointerType())
- return true;
-
- return ty->isIntegerType() && ty->isScalarType() &&
- Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy);
-}
-
-//===----------------------------------------------------------------------===//
// Main RegionStore logic.
//===----------------------------------------------------------------------===//
@@ -180,6 +181,14 @@ public:
}
};
+void
+RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL,
+ const SubRegion *R) {
+ const MemRegion *superR = R->getSuperRegion();
+ if (add(superR, R))
+ if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
+ WL.push_back(sr);
+}
class RegionStoreManager : public StoreManager {
const RegionStoreFeatures Features;
@@ -197,7 +206,6 @@ public:
RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store);
- Optional<SVal> getBinding(RegionBindings B, const MemRegion *R);
Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R);
/// getDefaultBinding - Returns an SVal* representing an optional default
/// binding associated with a region and its subregions.
@@ -226,18 +234,13 @@ public:
// Binding values to regions.
//===-------------------------------------------------------------------===//
- Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E,
- unsigned Count, InvalidatedSymbols *IS) {
- return RegionStoreManager::InvalidateRegions(store, &R, &R+1, E, Count, IS,
- false);
- }
-
Store InvalidateRegions(Store store,
const MemRegion * const *Begin,
const MemRegion * const *End,
const Expr *E, unsigned Count,
InvalidatedSymbols *IS,
- bool invalidateGlobals);
+ bool invalidateGlobals,
+ InvalidatedRegions *Regions);
public: // Made public for helper classes.
@@ -260,8 +263,6 @@ public: // Made public for helper classes.
return Remove(Remove(B, R, BindingKey::Direct), R, BindingKey::Default);
}
- Store Remove(Store store, BindingKey K);
-
public: // Part of public interface to class.
Store Bind(Store store, Loc LV, SVal V);
@@ -289,7 +290,7 @@ public: // Part of public interface to class.
Store BindArray(Store store, const TypedRegion* R, SVal V);
/// KillStruct - Set the entire struct to unknown.
- Store KillStruct(Store store, const TypedRegion* R);
+ Store KillStruct(Store store, const TypedRegion* R, SVal DefaultVal);
Store Remove(Store store, Loc LV);
@@ -352,13 +353,11 @@ public: // Part of public interface to class.
/// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values.
/// It returns a new Store with these values removed.
- const GRState *RemoveDeadBindings(GRState &state,
- const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
+ Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx,
+ SymbolReaper& SymReaper,
llvm::SmallVectorImpl<const MemRegion*>& RegionRoots);
- const GRState *EnterStackFrame(const GRState *state,
- const StackFrameContext *frame);
+ Store EnterStackFrame(const GRState *state, const StackFrameContext *frame);
//===------------------------------------------------------------------===//
// Region "extents".
@@ -392,9 +391,6 @@ public: // Part of public interface to class.
}
}
}
-
- // FIXME: Remove.
- ASTContext& getContext() { return StateMgr.getContext(); }
};
} // end anonymous namespace
@@ -414,14 +410,6 @@ StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) {
return new RegionStoreManager(StMgr, F);
}
-void
-RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL,
- const SubRegion *R) {
- const MemRegion *superR = R->getSuperRegion();
- if (add(superR, R))
- if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
- WL.push_back(sr);
-}
RegionStoreSubRegionMap*
RegionStoreManager::getRegionStoreSubRegionMap(Store store) {
@@ -579,14 +567,16 @@ class InvalidateRegionsWorker : public ClusterAnalysis<InvalidateRegionsWorker>
const Expr *Ex;
unsigned Count;
StoreManager::InvalidatedSymbols *IS;
+ StoreManager::InvalidatedRegions *Regions;
public:
InvalidateRegionsWorker(RegionStoreManager &rm,
GRStateManager &stateMgr,
RegionBindings b,
const Expr *ex, unsigned count,
- StoreManager::InvalidatedSymbols *is)
+ StoreManager::InvalidatedSymbols *is,
+ StoreManager::InvalidatedRegions *r)
: ClusterAnalysis<InvalidateRegionsWorker>(rm, stateMgr, b),
- Ex(ex), Count(count), IS(is) {}
+ Ex(ex), Count(count), IS(is), Regions(r) {}
void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
void VisitBaseRegion(const MemRegion *baseR);
@@ -657,6 +647,10 @@ void InvalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
return;
}
+ // Otherwise, we have a normal data region. Record that we touched the region.
+ if (Regions)
+ Regions->push_back(baseR);
+
if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelavant.
@@ -670,19 +664,12 @@ void InvalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
return;
const TypedRegion *TR = cast<TypedRegion>(baseR);
- QualType T = TR->getValueType(Ctx);
+ QualType T = TR->getValueType();
// Invalidate the binding.
- if (const RecordType *RT = T->getAsStructureType()) {
- const RecordDecl *RD = RT->getDecl()->getDefinition();
- // No record definition. There is nothing we can do.
- if (!RD) {
- B = RM.Remove(B, baseR);
- return;
- }
-
- // Invalidate the region by setting its default value to
- // conjured symbol. The type of the symbol is irrelavant.
+ if (T->isStructureType()) {
+ // Invalidate the region by setting its default value to
+ // conjured symbol. The type of the symbol is irrelavant.
DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy,
Count);
B = RM.Add(B, baseR, BindingKey::Default, V);
@@ -707,10 +694,11 @@ Store RegionStoreManager::InvalidateRegions(Store store,
const MemRegion * const *E,
const Expr *Ex, unsigned Count,
InvalidatedSymbols *IS,
- bool invalidateGlobals) {
+ bool invalidateGlobals,
+ InvalidatedRegions *Regions) {
InvalidateRegionsWorker W(*this, StateMgr,
RegionStoreManager::GetRegionBindings(store),
- Ex, Count, IS);
+ Ex, Count, IS, Regions);
// Scan the bindings and generate the clusters.
W.GenerateClusters(invalidateGlobals);
@@ -733,6 +721,11 @@ Store RegionStoreManager::InvalidateRegions(Store store,
/* symbol type, doesn't matter */ Ctx.IntTy,
Count);
B = Add(B, BindingKey::Make(GS, BindingKey::Default), V);
+
+ // Even if there are no bindings in the global scope, we still need to
+ // record that we touched it.
+ if (Regions)
+ Regions->push_back(GS);
}
return B.getRoot();
@@ -752,7 +745,7 @@ DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state,
return UnknownVal();
CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue());
- CharUnits EleSize = getContext().getTypeSizeInChars(EleTy);
+ CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy);
// If a variable is reinterpreted as a type that doesn't fit into a larger
// type evenly, round it down.
@@ -781,13 +774,12 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) {
return UnknownVal();
// Strip off typedefs from the ArrayRegion's ValueType.
- QualType T = ArrayR->getValueType(getContext()).getDesugaredType();
+ QualType T = ArrayR->getValueType().getDesugaredType();
ArrayType *AT = cast<ArrayType>(T);
T = AT->getElementType();
SVal ZeroIdx = ValMgr.makeZeroArrayIndex();
- return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR,
- getContext()));
+ return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx));
}
//===----------------------------------------------------------------------===//
@@ -806,8 +798,8 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R,
default:
// Handle it normally.
break;
- case BinaryOperator::Add:
- case BinaryOperator::Sub:
+ case BO_Add:
+ case BO_Sub:
// FIXME: does this need to be casted to match resultTy?
return L;
}
@@ -820,7 +812,7 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R,
case MemRegion::SymbolicRegionKind: {
const SymbolicRegion *SR = cast<SymbolicRegion>(MR);
SymbolRef Sym = SR->getSymbol();
- QualType T = Sym->getType(getContext());
+ QualType T = Sym->getType(Ctx);
QualType EleTy;
if (const PointerType *PT = T->getAs<PointerType>())
@@ -829,14 +821,14 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R,
EleTy = T->getAs<ObjCObjectPointerType>()->getPointeeType();
SVal ZeroIdx = ValMgr.makeZeroArrayIndex();
- ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, getContext());
+ ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, Ctx);
break;
}
case MemRegion::AllocaRegionKind: {
const AllocaRegion *AR = cast<AllocaRegion>(MR);
- QualType EleTy = getContext().CharTy; // Create an ElementRegion of bytes.
+ QualType EleTy = Ctx.CharTy; // Create an ElementRegion of bytes.
SVal ZeroIdx = ValMgr.makeZeroArrayIndex();
- ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, getContext());
+ ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, Ctx);
break;
}
@@ -891,13 +883,13 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R,
cast<nonloc::ConcreteInt>(ValMgr.convertToArrayIndex(*Offset)));
const MemRegion* NewER =
MRMgr.getElementRegion(ER->getElementType(), NewIdx,
- ER->getSuperRegion(), getContext());
+ ER->getSuperRegion(), Ctx);
return ValMgr.makeLoc(NewER);
}
if (0 == Base->getValue()) {
const MemRegion* NewER =
MRMgr.getElementRegion(ER->getElementType(), R,
- ER->getSuperRegion(), getContext());
+ ER->getSuperRegion(), Ctx);
return ValMgr.makeLoc(NewER);
}
}
@@ -922,7 +914,7 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B,
const MemRegion *R) {
if (R->isBoundable())
if (const TypedRegion *TR = dyn_cast<TypedRegion>(R))
- if (TR->getValueType(getContext())->isUnionType())
+ if (TR->getValueType()->isUnionType())
return UnknownVal();
if (const SVal *V = Lookup(B, R, BindingKey::Default))
@@ -931,38 +923,6 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B,
return Optional<SVal>();
}
-Optional<SVal> RegionStoreManager::getBinding(RegionBindings B,
- const MemRegion *R) {
-
- if (const Optional<SVal> &V = getDirectBinding(B, R))
- return V;
-
- return getDefaultBinding(B, R);
-}
-
-static bool IsReinterpreted(QualType RTy, QualType UsedTy, ASTContext &Ctx) {
- RTy = Ctx.getCanonicalType(RTy);
- UsedTy = Ctx.getCanonicalType(UsedTy);
-
- if (RTy == UsedTy)
- return false;
-
-
- // Recursively check the types. We basically want to see if a pointer value
- // is ever reinterpreted as a non-pointer, e.g. void** and intptr_t*
- // represents a reinterpretation.
- if (Loc::IsLocType(RTy) && Loc::IsLocType(UsedTy)) {
- const PointerType *PRTy = RTy->getAs<PointerType>();
- const PointerType *PUsedTy = UsedTy->getAs<PointerType>();
-
- return PUsedTy && PRTy &&
- IsReinterpreted(PRTy->getPointeeType(),
- PUsedTy->getPointeeType(), Ctx);
- }
-
- return true;
-}
-
SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
assert(!isa<UnknownVal>(L) && "location unknown");
assert(!isa<UndefinedVal>(L) && "location undefined");
@@ -977,7 +937,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR)) {
if (T.isNull()) {
const SymbolicRegion *SR = cast<SymbolicRegion>(MR);
- T = SR->getSymbol()->getType(getContext());
+ T = SR->getSymbol()->getType(Ctx);
}
MR = GetElementZeroRegion(MR, T);
}
@@ -990,7 +950,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
// instead of 'Loc', and have the other Loc cases handled at a higher level.
const TypedRegion *R = cast<TypedRegion>(MR);
- QualType RTy = R->getValueType(getContext());
+ QualType RTy = R->getValueType();
// FIXME: We should eventually handle funny addressing. e.g.:
//
@@ -1001,17 +961,6 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
//
// Such funny addressing will occur due to layering of regions.
-#if 0
- ASTContext &Ctx = getContext();
- if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) {
- SVal ZeroIdx = ValMgr.makeZeroArrayIndex();
- R = MRMgr.getElementRegion(T, ZeroIdx, R, Ctx);
- RTy = T;
- assert(Ctx.getCanonicalType(RTy) ==
- Ctx.getCanonicalType(R->getValueType(Ctx)));
- }
-#endif
-
if (RTy->isStructureOrClassType())
return RetrieveStruct(store, R);
@@ -1121,8 +1070,7 @@ SVal RegionStoreManager::RetrieveElement(Store store,
if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) {
// FIXME: Handle loads from strings where the literal is treated as
// an integer, e.g., *((unsigned int*)"hello")
- ASTContext &Ctx = getContext();
- QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType();
+ QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
if (T != Ctx.getCanonicalType(R->getElementType()))
return UnknownVal();
@@ -1131,16 +1079,18 @@ SVal RegionStoreManager::RetrieveElement(Store store,
if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) {
int64_t i = CI->getValue().getSExtValue();
int64_t byteLength = Str->getByteLength();
- if (i > byteLength) {
- // Buffer overflow checking in GRExprEngine should handle this case,
- // but we shouldn't rely on it to not overflow here if that checking
- // is disabled.
- return UnknownVal();
- }
- char c = (i == byteLength) ? '\0' : Str->getStrData()[i];
+ // Technically, only i == byteLength is guaranteed to be null.
+ // However, such overflows should be caught before reaching this point;
+ // the only time such an access would be made is if a string literal was
+ // used to initialize a larger array.
+ char c = (i >= byteLength) ? '\0' : Str->getString()[i];
return ValMgr.makeIntVal(c, T);
}
}
+
+ // Check for loads from a code text region. For such loads, just give up.
+ if (isa<CodeTextRegion>(superR))
+ return UnknownVal();
// Handle the case where we are indexing into a larger scalar object.
// For example, this handles:
@@ -1148,9 +1098,9 @@ SVal RegionStoreManager::RetrieveElement(Store store,
// char *y = &x;
// return *y;
// FIXME: This is a hack, and doesn't do anything really intelligent yet.
- const RegionRawOffset &O = R->getAsRawOffset();
+ const RegionRawOffset &O = R->getAsArrayOffset();
if (const TypedRegion *baseR = dyn_cast_or_null<TypedRegion>(O.getRegion())) {
- QualType baseT = baseR->getValueType(Ctx);
+ QualType baseT = baseR->getValueType();
if (baseT->isScalarType()) {
QualType elemT = R->getElementType();
if (elemT->isScalarType()) {
@@ -1180,7 +1130,7 @@ SVal RegionStoreManager::RetrieveField(Store store,
if (const Optional<SVal> &V = getDirectBinding(B, R))
return *V;
- QualType Ty = R->getValueType(getContext());
+ QualType Ty = R->getValueType();
return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion());
}
@@ -1243,13 +1193,18 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
}
if (R->hasStackNonParametersStorage()) {
- if (isa<ElementRegion>(R)) {
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
// Currently we don't reason specially about Clang-style vectors. Check
// if superR is a vector and if so return Unknown.
if (const TypedRegion *typedSuperR = dyn_cast<TypedRegion>(superR)) {
- if (typedSuperR->getValueType(getContext())->isVectorType())
+ if (typedSuperR->getValueType()->isVectorType())
return UnknownVal();
}
+
+ // FIXME: We also need to take ElementRegions with symbolic indexes into
+ // account.
+ if (!ER->getIndex().isConstant())
+ return UnknownVal();
}
return UndefinedVal();
@@ -1332,21 +1287,18 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) {
}
SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) {
-
- QualType valTy = R->getValueType(getContext());
-
// All other values are symbolic.
return ValMgr.getRegionValueSymbolVal(R);
}
SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) {
- QualType T = R->getValueType(getContext());
+ QualType T = R->getValueType();
assert(T->isStructureOrClassType());
return ValMgr.makeLazyCompoundVal(store, R);
}
SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) {
- assert(isa<ConstantArrayType>(R->getValueType(getContext())));
+ assert(isa<ConstantArrayType>(R->getValueType()));
return ValMgr.makeLazyCompoundVal(store, R);
}
@@ -1371,38 +1323,26 @@ Store RegionStoreManager::Bind(Store store, Loc L, SVal V) {
// Check if the region is a struct region.
if (const TypedRegion* TR = dyn_cast<TypedRegion>(R))
- if (TR->getValueType(getContext())->isStructureOrClassType())
+ if (TR->getValueType()->isStructureOrClassType())
return BindStruct(store, TR, V);
- // Special case: the current region represents a cast and it and the super
- // region both have pointer types or intptr_t types. If so, perform the
- // bind to the super region.
- // This is needed to support OSAtomicCompareAndSwap and friends or other
- // loads that treat integers as pointers and vis versa.
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
if (ER->getIndex().isZeroConstant()) {
if (const TypedRegion *superR =
dyn_cast<TypedRegion>(ER->getSuperRegion())) {
- ASTContext &Ctx = getContext();
- QualType superTy = superR->getValueType(Ctx);
- QualType erTy = ER->getValueType(Ctx);
-
- if (IsAnyPointerOrIntptr(superTy, Ctx) &&
- IsAnyPointerOrIntptr(erTy, Ctx)) {
- V = ValMgr.getSValuator().EvalCast(V, superTy, erTy);
- return Bind(store, loc::MemRegionVal(superR), V);
- }
+ QualType superTy = superR->getValueType();
// For now, just invalidate the fields of the struct/union/class.
+ // This is for test rdar_test_7185607 in misc-ps-region-store.m.
// FIXME: Precisely handle the fields of the record.
- if (superTy->isRecordType())
- return InvalidateRegion(store, superR, NULL, 0, NULL);
+ if (superTy->isStructureOrClassType())
+ return KillStruct(store, superR, UnknownVal());
}
}
}
else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
// Binding directly to a symbolic region should be treated as binding
// to element 0.
- QualType T = SR->getSymbol()->getType(getContext());
+ QualType T = SR->getSymbol()->getType(Ctx);
// FIXME: Is this the right way to handle symbols that are references?
if (const PointerType *PT = T->getAs<PointerType>())
@@ -1454,7 +1394,7 @@ Store RegionStoreManager::setImplicitDefaultValue(Store store,
else if (T->isStructureOrClassType() || T->isArrayType()) {
// Set the default value to a zero constant when it is a structure
// or array. The type doesn't really matter.
- V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy);
+ V = ValMgr.makeZeroVal(Ctx.IntTy);
}
else {
return store;
@@ -1466,44 +1406,21 @@ Store RegionStoreManager::setImplicitDefaultValue(Store store,
Store RegionStoreManager::BindArray(Store store, const TypedRegion* R,
SVal Init) {
- ASTContext &Ctx = getContext();
- const ArrayType *AT =
- cast<ArrayType>(Ctx.getCanonicalType(R->getValueType(Ctx)));
+ const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType()));
QualType ElementTy = AT->getElementType();
Optional<uint64_t> Size;
if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT))
Size = CAT->getSize().getZExtValue();
- // Check if the init expr is a StringLiteral.
- if (isa<loc::MemRegionVal>(Init)) {
- const MemRegion* InitR = cast<loc::MemRegionVal>(Init).getRegion();
- const StringLiteral* S = cast<StringRegion>(InitR)->getStringLiteral();
- const char* str = S->getStrData();
- unsigned len = S->getByteLength();
- unsigned j = 0;
-
- // Copy bytes from the string literal into the target array. Trailing bytes
- // in the array that are not covered by the string literal are initialized
- // to zero.
+ // Check if the init expr is a string literal.
+ if (loc::MemRegionVal *MRV = dyn_cast<loc::MemRegionVal>(&Init)) {
+ const StringRegion *S = cast<StringRegion>(MRV->getRegion());
- // We assume that string constants are bound to
- // constant arrays.
- uint64_t size = Size.getValue();
-
- for (uint64_t i = 0; i < size; ++i, ++j) {
- if (j >= len)
- break;
-
- SVal Idx = ValMgr.makeArrayIndex(i);
- const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R,
- getContext());
-
- SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true);
- store = Bind(store, loc::MemRegionVal(ER), V);
- }
-
- return store;
+ // Treat the string as a lazy compound value.
+ nonloc::LazyCompoundVal LCV =
+ cast<nonloc::LazyCompoundVal>(ValMgr.makeLazyCompoundVal(store, S));
+ return CopyLazyBindings(LCV, store, R);
}
// Handle lazy compound values.
@@ -1525,10 +1442,12 @@ Store RegionStoreManager::BindArray(Store store, const TypedRegion* R,
break;
SVal Idx = ValMgr.makeArrayIndex(i);
- const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext());
+ const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx);
if (ElementTy->isStructureOrClassType())
store = BindStruct(store, ER, *VI);
+ else if (ElementTy->isArrayType())
+ store = BindArray(store, ER, *VI);
else
store = Bind(store, ValMgr.makeLoc(ER), *VI);
}
@@ -1547,7 +1466,7 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R,
if (!Features.supportsFields())
return store;
- QualType T = R->getValueType(getContext());
+ QualType T = R->getValueType();
assert(T->isStructureOrClassType());
const RecordType* RT = T->getAs<RecordType>();
@@ -1560,10 +1479,13 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R,
if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V))
return CopyLazyBindings(*LCV, store, R);
- // We may get non-CompoundVal accidentally due to imprecise cast logic.
- // Ignore them and kill the field values.
- if (V.isUnknown() || !isa<nonloc::CompoundVal>(V))
- return KillStruct(store, R);
+ // We may get non-CompoundVal accidentally due to imprecise cast logic or
+ // that we are binding symbolic struct value. Kill the field values, and if
+ // the value is symbolic go and bind it as a "default" binding.
+ if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) {
+ SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal();
+ return KillStruct(store, R, SV);
+ }
nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
@@ -1596,14 +1518,15 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R,
return store;
}
-Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R) {
+Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R,
+ SVal DefaultVal) {
RegionBindings B = GetRegionBindings(store);
llvm::OwningPtr<RegionStoreSubRegionMap>
SubRegions(getRegionStoreSubRegionMap(store));
RemoveSubRegionBindings(B, R, *SubRegions);
// Set the default value of the struct region to "unknown".
- return Add(B, R, BindingKey::Default, UnknownVal()).getRoot();
+ return Add(B, R, BindingKey::Default, DefaultVal).getRoot();
}
Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
@@ -1627,21 +1550,10 @@ Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
// "Raw" retrievals and bindings.
//===----------------------------------------------------------------------===//
-BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- const RegionRawOffset &O = ER->getAsRawOffset();
-
- if (O.getRegion())
- return BindingKey(O.getRegion(), O.getByteOffset(), k);
-
- // FIXME: There are some ElementRegions for which we cannot compute
- // raw offsets yet, including regions with symbolic offsets.
- }
-
- return BindingKey(R, 0, k);
-}
RegionBindings RegionStoreManager::Add(RegionBindings B, BindingKey K, SVal V) {
+ if (!K.isValid())
+ return B;
return RBFactory.Add(B, K, V);
}
@@ -1651,6 +1563,8 @@ RegionBindings RegionStoreManager::Add(RegionBindings B, const MemRegion *R,
}
const SVal *RegionStoreManager::Lookup(RegionBindings B, BindingKey K) {
+ if (!K.isValid())
+ return NULL;
return B.lookup(K);
}
@@ -1661,6 +1575,8 @@ const SVal *RegionStoreManager::Lookup(RegionBindings B,
}
RegionBindings RegionStoreManager::Remove(RegionBindings B, BindingKey K) {
+ if (!K.isValid())
+ return B;
return RBFactory.Remove(B, K);
}
@@ -1669,11 +1585,6 @@ RegionBindings RegionStoreManager::Remove(RegionBindings B, const MemRegion *R,
return Remove(B, BindingKey::Make(R, k));
}
-Store RegionStoreManager::Remove(Store store, BindingKey K) {
- RegionBindings B = GetRegionBindings(store);
- return Remove(B, K).getRoot();
-}
-
//===----------------------------------------------------------------------===//
// State pruning.
//===----------------------------------------------------------------------===//
@@ -1818,12 +1729,12 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() {
return changed;
}
-const GRState *RegionStoreManager::RemoveDeadBindings(GRState &state,
+Store RegionStoreManager::RemoveDeadBindings(Store store,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper,
llvm::SmallVectorImpl<const MemRegion*>& RegionRoots)
{
- RegionBindings B = GetRegionBindings(state.getStore());
+ RegionBindings B = GetRegionBindings(store);
RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
W.GenerateClusters();
@@ -1856,14 +1767,13 @@ const GRState *RegionStoreManager::RemoveDeadBindings(GRState &state,
for (; SI != SE; ++SI)
SymReaper.maybeDead(*SI);
}
- state.setStore(B.getRoot());
- const GRState *s = StateMgr.getPersistentState(state);
- return s;
+
+ return B.getRoot();
}
-GRState const *RegionStoreManager::EnterStackFrame(GRState const *state,
- StackFrameContext const *frame) {
+Store RegionStoreManager::EnterStackFrame(const GRState *state,
+ const StackFrameContext *frame) {
FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl());
FunctionDecl::param_const_iterator PI = FD->param_begin();
Store store = state->getStore();
@@ -1887,9 +1797,9 @@ GRState const *RegionStoreManager::EnterStackFrame(GRState const *state,
store = Bind(store, ValMgr.makeLoc(MRMgr.getVarRegion(*PI,frame)),ArgVal);
}
} else
- assert(0 && "Unhandled call expression.");
+ llvm_unreachable("Unhandled call expression.");
- return state->makeWithStore(store);
+ return store;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Checker/ReturnPointerRangeChecker.cpp b/lib/Checker/ReturnPointerRangeChecker.cpp
index 14edf56..a9eb5ce 100644
--- a/lib/Checker/ReturnPointerRangeChecker.cpp
+++ b/lib/Checker/ReturnPointerRangeChecker.cpp
@@ -66,7 +66,7 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C,
DefinedOrUnknownSVal NumElements
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
- ER->getValueType(C.getASTContext()));
+ ER->getValueType());
const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true);
const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false);
diff --git a/lib/Checker/ReturnUndefChecker.cpp b/lib/Checker/ReturnUndefChecker.cpp
index 52a0b30..73d1890 100644
--- a/lib/Checker/ReturnUndefChecker.cpp
+++ b/lib/Checker/ReturnUndefChecker.cpp
@@ -61,6 +61,7 @@ void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C,
EnhancedBugReport *report =
new EnhancedBugReport(*BT, BT->getDescription(), N);
+ report->addRange(RetE->getSourceRange());
report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE);
C.EmitReport(report);
diff --git a/lib/Checker/SVals.cpp b/lib/Checker/SVals.cpp
index 7a99e86..97ba74e 100644
--- a/lib/Checker/SVals.cpp
+++ b/lib/Checker/SVals.cpp
@@ -62,6 +62,9 @@ const FunctionDecl *SVal::getAsFunctionDecl() const {
/// wraps a symbol, return that SymbolRef. Otherwise return 0.
// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
SymbolRef SVal::getAsLocSymbol() const {
+ if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this))
+ return X->getLoc().getAsLocSymbol();
+
if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) {
const MemRegion *R = X->StripCasts();
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R))
@@ -247,8 +250,8 @@ SVal loc::ConcreteInt::EvalBinOp(BasicValueFactory& BasicVals,
BinaryOperator::Opcode Op,
const loc::ConcreteInt& R) const {
- assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub ||
- (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE));
+ assert (Op == BO_Add || Op == BO_Sub ||
+ (Op >= BO_LT && Op <= BO_NE));
const llvm::APSInt* X = BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue());
diff --git a/lib/Checker/SValuator.cpp b/lib/Checker/SValuator.cpp
index a7e15fc..273e574 100644
--- a/lib/Checker/SValuator.cpp
+++ b/lib/Checker/SValuator.cpp
@@ -37,7 +37,7 @@ SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op,
if (isa<Loc>(R)) {
// Support pointer arithmetic where the addend is on the left
// and the pointer on the right.
- assert(Op == BinaryOperator::Add);
+ assert(Op == BO_Add);
// Commute the operands.
return EvalBinOpLN(ST, Op, cast<Loc>(R), cast<NonLoc>(L), T);
@@ -49,7 +49,7 @@ SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op,
DefinedOrUnknownSVal SValuator::EvalEQ(const GRState *ST,
DefinedOrUnknownSVal L,
DefinedOrUnknownSVal R) {
- return cast<DefinedOrUnknownSVal>(EvalBinOp(ST, BinaryOperator::EQ, L, R,
+ return cast<DefinedOrUnknownSVal>(EvalBinOp(ST, BO_EQ, L, R,
ValMgr.getContext().IntTy));
}
diff --git a/lib/Checker/SimpleConstraintManager.cpp b/lib/Checker/SimpleConstraintManager.cpp
index 321381b..04496e1 100644
--- a/lib/Checker/SimpleConstraintManager.cpp
+++ b/lib/Checker/SimpleConstraintManager.cpp
@@ -31,17 +31,17 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const {
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
switch (SIE->getOpcode()) {
// We don't reason yet about bitwise-constraints on symbolic values.
- case BinaryOperator::And:
- case BinaryOperator::Or:
- case BinaryOperator::Xor:
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
return false;
// We don't reason yet about these arithmetic constraints on
// symbolic values.
- case BinaryOperator::Mul:
- case BinaryOperator::Div:
- case BinaryOperator::Rem:
- case BinaryOperator::Shl:
- case BinaryOperator::Shr:
+ case BO_Mul:
+ case BO_Div:
+ case BO_Rem:
+ case BO_Shl:
+ case BO_Shr:
return false;
// All other cases.
default:
@@ -125,12 +125,12 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
switch (op) {
default:
assert(false && "Invalid opcode.");
- case BinaryOperator::LT: return BinaryOperator::GE;
- case BinaryOperator::GT: return BinaryOperator::LE;
- case BinaryOperator::LE: return BinaryOperator::GT;
- case BinaryOperator::GE: return BinaryOperator::LT;
- case BinaryOperator::EQ: return BinaryOperator::NE;
- case BinaryOperator::NE: return BinaryOperator::EQ;
+ case BO_LT: return BO_GE;
+ case BO_GT: return BO_LE;
+ case BO_LE: return BO_GT;
+ case BO_GE: return BO_LT;
+ case BO_EQ: return BO_NE;
+ case BO_NE: return BO_EQ;
}
}
@@ -178,7 +178,7 @@ const GRState *SimpleConstraintManager::AssumeAux(const GRState *state,
if (!BinaryOperator::isComparisonOp(op)) {
QualType T = SymMgr.getType(SE);
const llvm::APSInt &zero = BasicVals.getValue(0, T);
- op = (Assumption ? BinaryOperator::NE : BinaryOperator::EQ);
+ op = (Assumption ? BO_NE : BO_EQ);
return AssumeSymRel(state, SE, op, zero);
}
@@ -238,10 +238,10 @@ const GRState *SimpleConstraintManager::AssumeSymRel(const GRState *state,
// Get the constant out of the expression "($sym+constant1)".
switch (SE->getOpcode()) {
- case BinaryOperator::Add:
+ case BO_Add:
Adjustment = SE->getRHS();
break;
- case BinaryOperator::Sub:
+ case BO_Sub:
Adjustment = -SE->getRHS();
break;
default:
@@ -276,48 +276,24 @@ const GRState *SimpleConstraintManager::AssumeSymRel(const GRState *state,
// No logic yet for other operators. Assume the constraint is feasible.
return state;
- case BinaryOperator::EQ:
+ case BO_EQ:
return AssumeSymEQ(state, Sym, ConvertedInt, Adjustment);
- case BinaryOperator::NE:
+ case BO_NE:
return AssumeSymNE(state, Sym, ConvertedInt, Adjustment);
- case BinaryOperator::GT:
+ case BO_GT:
return AssumeSymGT(state, Sym, ConvertedInt, Adjustment);
- case BinaryOperator::GE:
+ case BO_GE:
return AssumeSymGE(state, Sym, ConvertedInt, Adjustment);
- case BinaryOperator::LT:
+ case BO_LT:
return AssumeSymLT(state, Sym, ConvertedInt, Adjustment);
- case BinaryOperator::LE:
+ case BO_LE:
return AssumeSymLE(state, Sym, ConvertedInt, Adjustment);
} // end switch
}
-const GRState *SimpleConstraintManager::AssumeInBound(const GRState *state,
- DefinedSVal Idx,
- DefinedSVal UpperBound,
- bool Assumption) {
-
- // Only support ConcreteInt for now.
- if (!(isa<nonloc::ConcreteInt>(Idx) && isa<nonloc::ConcreteInt>(UpperBound)))
- return state;
-
- const llvm::APSInt& Zero = state->getBasicVals().getZeroWithPtrWidth(false);
- llvm::APSInt IdxV = cast<nonloc::ConcreteInt>(Idx).getValue();
- // IdxV might be too narrow.
- if (IdxV.getBitWidth() < Zero.getBitWidth())
- IdxV.extend(Zero.getBitWidth());
- // UBV might be too narrow, too.
- llvm::APSInt UBV = cast<nonloc::ConcreteInt>(UpperBound).getValue();
- if (UBV.getBitWidth() < Zero.getBitWidth())
- UBV.extend(Zero.getBitWidth());
-
- bool InBound = (Zero <= IdxV) && (IdxV < UBV);
- bool isFeasible = Assumption ? InBound : !InBound;
- return isFeasible ? state : NULL;
-}
-
} // end of namespace clang
diff --git a/lib/Checker/SimpleConstraintManager.h b/lib/Checker/SimpleConstraintManager.h
index 45057e6..96811b3 100644
--- a/lib/Checker/SimpleConstraintManager.h
+++ b/lib/Checker/SimpleConstraintManager.h
@@ -43,10 +43,6 @@ public:
BinaryOperator::Opcode op,
const llvm::APSInt& Int);
- const GRState *AssumeInBound(const GRState *state, DefinedSVal Idx,
- DefinedSVal UpperBound,
- bool Assumption);
-
protected:
//===------------------------------------------------------------------===//
diff --git a/lib/Checker/SimpleSValuator.cpp b/lib/Checker/SimpleSValuator.cpp
index 3bc4ee7..782cd4f 100644
--- a/lib/Checker/SimpleSValuator.cpp
+++ b/lib/Checker/SimpleSValuator.cpp
@@ -168,12 +168,12 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
switch (op) {
default:
assert(false && "Invalid opcode.");
- case BinaryOperator::LT: return BinaryOperator::GE;
- case BinaryOperator::GT: return BinaryOperator::LE;
- case BinaryOperator::LE: return BinaryOperator::GT;
- case BinaryOperator::GE: return BinaryOperator::LT;
- case BinaryOperator::EQ: return BinaryOperator::NE;
- case BinaryOperator::NE: return BinaryOperator::EQ;
+ case BO_LT: return BO_GE;
+ case BO_GT: return BO_LE;
+ case BO_LE: return BO_GT;
+ case BO_GE: return BO_LT;
+ case BO_EQ: return BO_NE;
+ case BO_NE: return BO_EQ;
}
}
@@ -181,12 +181,12 @@ static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) {
switch (op) {
default:
assert(false && "Invalid opcode.");
- case BinaryOperator::LT: return BinaryOperator::GT;
- case BinaryOperator::GT: return BinaryOperator::LT;
- case BinaryOperator::LE: return BinaryOperator::GE;
- case BinaryOperator::GE: return BinaryOperator::LE;
- case BinaryOperator::EQ:
- case BinaryOperator::NE:
+ case BO_LT: return BO_GT;
+ case BO_GT: return BO_LT;
+ case BO_LE: return BO_GE;
+ case BO_GE: return BO_LE;
+ case BO_EQ:
+ case BO_NE:
return op;
}
}
@@ -202,14 +202,14 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS,
default:
// We can't reduce this case; just treat it normally.
break;
- case BinaryOperator::Mul:
+ case BO_Mul:
// a*0 and a*1
if (RHS == 0)
return ValMgr.makeIntVal(0, resultTy);
else if (RHS == 1)
isIdempotent = true;
break;
- case BinaryOperator::Div:
+ case BO_Div:
// a/0 and a/1
if (RHS == 0)
// This is also handled elsewhere.
@@ -217,7 +217,7 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS,
else if (RHS == 1)
isIdempotent = true;
break;
- case BinaryOperator::Rem:
+ case BO_Rem:
// a%0 and a%1
if (RHS == 0)
// This is also handled elsewhere.
@@ -225,23 +225,23 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS,
else if (RHS == 1)
return ValMgr.makeIntVal(0, resultTy);
break;
- case BinaryOperator::Add:
- case BinaryOperator::Sub:
- case BinaryOperator::Shl:
- case BinaryOperator::Shr:
- case BinaryOperator::Xor:
+ case BO_Add:
+ case BO_Sub:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_Xor:
// a+0, a-0, a<<0, a>>0, a^0
if (RHS == 0)
isIdempotent = true;
break;
- case BinaryOperator::And:
+ case BO_And:
// a&0 and a&(~0)
if (RHS == 0)
return ValMgr.makeIntVal(0, resultTy);
else if (RHS.isAllOnesValue())
isIdempotent = true;
break;
- case BinaryOperator::Or:
+ case BO_Or:
// a|0 and a|(~0)
if (RHS == 0)
isIdempotent = true;
@@ -275,19 +275,19 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
switch (op) {
default:
break;
- case BinaryOperator::EQ:
- case BinaryOperator::LE:
- case BinaryOperator::GE:
+ case BO_EQ:
+ case BO_LE:
+ case BO_GE:
return ValMgr.makeTruthVal(true, resultTy);
- case BinaryOperator::LT:
- case BinaryOperator::GT:
- case BinaryOperator::NE:
+ case BO_LT:
+ case BO_GT:
+ case BO_NE:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::Xor:
- case BinaryOperator::Sub:
+ case BO_Xor:
+ case BO_Sub:
return ValMgr.makeIntVal(0, resultTy);
- case BinaryOperator::Or:
- case BinaryOperator::And:
+ case BO_Or:
+ case BO_And:
return EvalCastNL(lhs, resultTy);
}
@@ -312,9 +312,9 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
}
default:
switch (op) {
- case BinaryOperator::EQ:
+ case BO_EQ:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::NE:
+ case BO_NE:
return ValMgr.makeTruthVal(true, resultTy);
default:
// This case also handles pointer arithmetic.
@@ -333,7 +333,7 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
return UnknownVal();
// Is this a logical not? (!x is represented as x == 0.)
- if (op == BinaryOperator::EQ && rhs.isZeroConstant()) {
+ if (op == BO_EQ && rhs.isZeroConstant()) {
// We know how to negate certain expressions. Simplify them here.
BinaryOperator::Opcode opc = symIntExpr->getOpcode();
@@ -342,34 +342,34 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
// We don't know how to negate this operation.
// Just handle it as if it were a normal comparison to 0.
break;
- case BinaryOperator::LAnd:
- case BinaryOperator::LOr:
+ case BO_LAnd:
+ case BO_LOr:
assert(false && "Logical operators handled by branching logic.");
return UnknownVal();
- case BinaryOperator::Assign:
- case BinaryOperator::MulAssign:
- case BinaryOperator::DivAssign:
- case BinaryOperator::RemAssign:
- case BinaryOperator::AddAssign:
- case BinaryOperator::SubAssign:
- case BinaryOperator::ShlAssign:
- case BinaryOperator::ShrAssign:
- case BinaryOperator::AndAssign:
- case BinaryOperator::XorAssign:
- case BinaryOperator::OrAssign:
- case BinaryOperator::Comma:
+ case BO_Assign:
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_AndAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ case BO_Comma:
assert(false && "'=' and ',' operators handled by GRExprEngine.");
return UnknownVal();
- case BinaryOperator::PtrMemD:
- case BinaryOperator::PtrMemI:
+ case BO_PtrMemD:
+ case BO_PtrMemI:
assert(false && "Pointer arithmetic not handled here.");
return UnknownVal();
- case BinaryOperator::LT:
- case BinaryOperator::GT:
- case BinaryOperator::LE:
- case BinaryOperator::GE:
- case BinaryOperator::EQ:
- case BinaryOperator::NE:
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
// Negate the comparison and make a value.
opc = NegateComparison(opc);
assert(symIntExpr->getType(ValMgr.getContext()) == resultTy);
@@ -402,9 +402,9 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
const llvm::APSInt *newRHS;
if (lop == op)
- newRHS = BVF.EvaluateAPSInt(BinaryOperator::Add, first, second);
+ newRHS = BVF.EvaluateAPSInt(BO_Add, first, second);
else
- newRHS = BVF.EvaluateAPSInt(BinaryOperator::Sub, first, second);
+ newRHS = BVF.EvaluateAPSInt(BO_Sub, first, second);
return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy);
}
}
@@ -429,26 +429,26 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
lhs = tmp;
switch (op) {
- case BinaryOperator::LT:
- case BinaryOperator::GT:
- case BinaryOperator::LE:
- case BinaryOperator::GE:
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
op = ReverseComparison(op);
continue;
- case BinaryOperator::EQ:
- case BinaryOperator::NE:
- case BinaryOperator::Add:
- case BinaryOperator::Mul:
- case BinaryOperator::And:
- case BinaryOperator::Xor:
- case BinaryOperator::Or:
+ case BO_EQ:
+ case BO_NE:
+ case BO_Add:
+ case BO_Mul:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
continue;
- case BinaryOperator::Shr:
+ case BO_Shr:
if (lhsValue.isAllOnesValue() && lhsValue.isSigned())
// At this point lhs and rhs have been swapped.
return rhs;
// FALL-THROUGH
- case BinaryOperator::Shl:
+ case BO_Shl:
if (lhsValue == 0)
// At this point lhs and rhs have been swapped.
return rhs;
@@ -461,10 +461,12 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
case nonloc::SymbolValKind: {
nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);
SymbolRef Sym = slhs->getSymbol();
-
+
+ ASTContext& Ctx = ValMgr.getContext();
+
// Does the symbol simplify to a constant? If so, "fold" the constant
// by setting 'lhs' to a ConcreteInt and try again.
- if (Sym->getType(ValMgr.getContext())->isIntegerType())
+ if (Sym->getType(Ctx)->isIntegerType())
if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {
// The symbol evaluates to a constant. If necessary, promote the
// folded constant (LHS) to the result type.
@@ -474,7 +476,7 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
// Also promote the RHS (if necessary).
- // For shifts, it necessary promote the RHS to the result type.
+ // For shifts, it is not necessary to promote the RHS.
if (BinaryOperator::isShiftOp(op))
continue;
@@ -486,7 +488,20 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
continue;
}
-
+
+ // Is the RHS a symbol we can simplify?
+ if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) {
+ SymbolRef RSym = srhs->getSymbol();
+ if (RSym->getType(Ctx)->isIntegerType()) {
+ if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
+ // The symbol evaluates to a constant.
+ BasicValueFactory &BVF = ValMgr.getBasicValueFactory();
+ const llvm::APSInt &rhs_I = BVF.Convert(resultTy, *Constant);
+ rhs = nonloc::ConcreteInt(rhs_I);
+ }
+ }
+ }
+
if (isa<nonloc::ConcreteInt>(rhs)) {
return MakeSymIntVal(slhs->getSymbol(), op,
cast<nonloc::ConcreteInt>(rhs).getValue(),
@@ -510,7 +525,7 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
// calling this function with another operation (PR7527). We don't attempt to
// model this for now, but it could be useful, particularly when the
// "location" is actually an integer value that's been passed through a void*.
- if (!(BinaryOperator::isComparisonOp(op) || op == BinaryOperator::Sub))
+ if (!(BinaryOperator::isComparisonOp(op) || op == BO_Sub))
return UnknownVal();
// Special cases for when both sides are identical.
@@ -519,15 +534,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
default:
assert(false && "Unimplemented operation for two identical values");
return UnknownVal();
- case BinaryOperator::Sub:
+ case BO_Sub:
return ValMgr.makeZeroVal(resultTy);
- case BinaryOperator::EQ:
- case BinaryOperator::LE:
- case BinaryOperator::GE:
+ case BO_EQ:
+ case BO_LE:
+ case BO_GE:
return ValMgr.makeTruthVal(true, resultTy);
- case BinaryOperator::NE:
- case BinaryOperator::LT:
- case BinaryOperator::GT:
+ case BO_NE:
+ case BO_LT:
+ case BO_GT:
return ValMgr.makeTruthVal(false, resultTy);
}
}
@@ -543,15 +558,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
switch (op) {
default:
break;
- case BinaryOperator::Sub:
+ case BO_Sub:
return EvalCastL(lhs, resultTy);
- case BinaryOperator::EQ:
- case BinaryOperator::LE:
- case BinaryOperator::LT:
+ case BO_EQ:
+ case BO_LE:
+ case BO_LT:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::NE:
- case BinaryOperator::GT:
- case BinaryOperator::GE:
+ case BO_NE:
+ case BO_GT:
+ case BO_GE:
return ValMgr.makeTruthVal(true, resultTy);
}
}
@@ -594,13 +609,13 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
switch (op) {
default:
break;
- case BinaryOperator::EQ:
- case BinaryOperator::GT:
- case BinaryOperator::GE:
+ case BO_EQ:
+ case BO_GT:
+ case BO_GE:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::NE:
- case BinaryOperator::LT:
- case BinaryOperator::LE:
+ case BO_NE:
+ case BO_LT:
+ case BO_LE:
return ValMgr.makeTruthVal(true, resultTy);
}
}
@@ -624,15 +639,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
switch (op) {
default:
break;
- case BinaryOperator::Sub:
+ case BO_Sub:
return EvalCastL(lhs, resultTy);
- case BinaryOperator::EQ:
- case BinaryOperator::LT:
- case BinaryOperator::LE:
+ case BO_EQ:
+ case BO_LT:
+ case BO_LE:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::NE:
- case BinaryOperator::GT:
- case BinaryOperator::GE:
+ case BO_NE:
+ case BO_GT:
+ case BO_GE:
return ValMgr.makeTruthVal(true, resultTy);
}
}
@@ -660,9 +675,9 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
switch (op) {
default:
return UnknownVal();
- case BinaryOperator::EQ:
+ case BO_EQ:
return ValMgr.makeTruthVal(false, resultTy);
- case BinaryOperator::NE:
+ case BO_NE:
return ValMgr.makeTruthVal(true, resultTy);
}
}
@@ -711,8 +726,8 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
}
// If the element indexes aren't comparable, see if the raw offsets are.
- RegionRawOffset LeftOffset = LeftER->getAsRawOffset();
- RegionRawOffset RightOffset = RightER->getAsRawOffset();
+ RegionRawOffset LeftOffset = LeftER->getAsArrayOffset();
+ RegionRawOffset RightOffset = RightER->getAsArrayOffset();
if (LeftOffset.getRegion() != NULL &&
LeftOffset.getRegion() == RightOffset.getRegion()) {
@@ -722,17 +737,17 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
switch (op) {
default:
return UnknownVal();
- case BinaryOperator::LT:
+ case BO_LT:
return ValMgr.makeTruthVal(left < right, resultTy);
- case BinaryOperator::GT:
+ case BO_GT:
return ValMgr.makeTruthVal(left > right, resultTy);
- case BinaryOperator::LE:
+ case BO_LE:
return ValMgr.makeTruthVal(left <= right, resultTy);
- case BinaryOperator::GE:
+ case BO_GE:
return ValMgr.makeTruthVal(left >= right, resultTy);
- case BinaryOperator::EQ:
+ case BO_EQ:
return ValMgr.makeTruthVal(left == right, resultTy);
- case BinaryOperator::NE:
+ case BO_NE:
return ValMgr.makeTruthVal(left != right, resultTy);
}
}
@@ -770,16 +785,16 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state,
// We know for sure that the two fields are not the same, since that
// would have given us the same SVal.
- if (op == BinaryOperator::EQ)
+ if (op == BO_EQ)
return ValMgr.makeTruthVal(false, resultTy);
- if (op == BinaryOperator::NE)
+ if (op == BO_NE)
return ValMgr.makeTruthVal(true, resultTy);
// Iterate through the fields and see which one comes first.
// [C99 6.7.2.1.13] "Within a structure object, the non-bit-field
// members and the units in which bit-fields reside have addresses that
// increase in the order in which they are declared."
- bool leftFirst = (op == BinaryOperator::LT || op == BinaryOperator::LE);
+ bool leftFirst = (op == BO_LT || op == BO_LE);
for (RecordDecl::field_iterator I = RD->field_begin(),
E = RD->field_end(); I!=E; ++I) {
if (*I == LeftFD)
@@ -818,8 +833,41 @@ SVal SimpleSValuator::EvalBinOpLN(const GRState *state,
}
}
}
+
+ // We are dealing with pointer arithmetic.
+
+ // Handle pointer arithmetic on constant values.
+ if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) {
+ if (loc::ConcreteInt *lhsInt = dyn_cast<loc::ConcreteInt>(&lhs)) {
+ const llvm::APSInt &leftI = lhsInt->getValue();
+ assert(leftI.isUnsigned());
+ llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true);
+
+ // Convert the bitwidth of rightI. This should deal with overflow
+ // since we are dealing with concrete values.
+ rightI.extOrTrunc(leftI.getBitWidth());
+
+ // Offset the increment by the pointer size.
+ llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true);
+ rightI *= Multiplicand;
+
+ // Compute the adjusted pointer.
+ switch (op) {
+ case BO_Add:
+ rightI = leftI + rightI;
+ break;
+ case BO_Sub:
+ rightI = leftI - rightI;
+ break;
+ default:
+ llvm_unreachable("Invalid pointer arithmetic operation");
+ }
+ return loc::ConcreteInt(ValMgr.getBasicValueFactory().getValue(rightI));
+ }
+ }
+
- // Delegate pointer arithmetic to the StoreManager.
+ // Delegate remaining pointer arithmetic to the StoreManager.
return state->getStateManager().getStoreManager().EvalBinOp(op, lhs,
rhs, resultTy);
}
diff --git a/lib/Checker/StackAddrLeakChecker.cpp b/lib/Checker/StackAddrLeakChecker.cpp
index f4a9db6..c67a81d 100644
--- a/lib/Checker/StackAddrLeakChecker.cpp
+++ b/lib/Checker/StackAddrLeakChecker.cpp
@@ -108,7 +108,7 @@ void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
report->addRange(range);
C.EmitReport(report);
-}
+}
void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C,
const ReturnStmt *RS) {
diff --git a/lib/Checker/Store.cpp b/lib/Checker/Store.cpp
index b128331..1cb5cd7 100644
--- a/lib/Checker/Store.cpp
+++ b/lib/Checker/Store.cpp
@@ -21,6 +21,11 @@ StoreManager::StoreManager(GRStateManager &stateMgr)
: ValMgr(stateMgr.getValueManager()), StateMgr(stateMgr),
MRMgr(ValMgr.getRegionManager()), Ctx(stateMgr.getContext()) {}
+Store StoreManager::EnterStackFrame(const GRState *state,
+ const StackFrameContext *frame) {
+ return state->getStore();
+}
+
const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base,
QualType EleTy, uint64_t index) {
SVal idx = ValMgr.makeArrayIndex(index);
@@ -78,7 +83,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy)
// Handle casts from compatible types.
if (R->isBoundable())
if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx));
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
if (CanonPointeeTy == ObjTy)
return R;
}
@@ -96,17 +101,10 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy)
assert(0 && "Invalid region cast");
break;
}
-
+
case MemRegion::FunctionTextRegionKind:
case MemRegion::BlockTextRegionKind:
- case MemRegion::BlockDataRegionKind: {
- // CodeTextRegion should be cast to only a function or block pointer type,
- // although they can in practice be casted to anything, e.g, void*, char*,
- // etc.
- // Just return the region.
- return R;
- }
-
+ case MemRegion::BlockDataRegionKind:
case MemRegion::StringRegionKind:
// FIXME: Need to handle arbitrary downcasts.
case MemRegion::SymbolicRegionKind:
@@ -139,7 +137,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy)
// FIXME: Handle symbolic raw offsets.
const ElementRegion *elementR = cast<ElementRegion>(R);
- const RegionRawOffset &rawOff = elementR->getAsRawOffset();
+ const RegionRawOffset &rawOff = elementR->getAsArrayOffset();
const MemRegion *baseR = rawOff.getRegion();
// If we cannot compute a raw offset, throw up our hands and return
@@ -154,7 +152,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy)
// check to see if type we are casting to is the same as the base
// region. If so, just return the base region.
if (const TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx));
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
if (CanonPointeeTy == ObjTy)
return baseR;
@@ -217,7 +215,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R,
if (performTestOnly) {
// Automatically translate references to pointers.
- QualType T = R->getValueType(Ctx);
+ QualType T = R->getValueType();
if (const ReferenceType *RT = T->getAs<ReferenceType>())
T = Ctx.getPointerType(RT->getPointeeType());
@@ -279,10 +277,6 @@ SVal StoreManager::getLValueElement(QualType elementType, SVal Offset,
if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base))
return Base;
- // Only handle integer offsets... for now.
- if (!isa<nonloc::ConcreteInt>(Offset))
- return UnknownVal();
-
const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion();
// Pointer of any type can be cast and used as array base.
@@ -311,6 +305,19 @@ SVal StoreManager::getLValueElement(QualType elementType, SVal Offset,
return UnknownVal();
const llvm::APSInt& BaseIdxI = cast<nonloc::ConcreteInt>(BaseIdx).getValue();
+
+ // Only allow non-integer offsets if the base region has no offset itself.
+ // FIXME: This is a somewhat arbitrary restriction. We should be using
+ // SValuator here to add the two offsets without checking their types.
+ if (!isa<nonloc::ConcreteInt>(Offset)) {
+ if (isa<ElementRegion>(BaseRegion->StripCasts()))
+ return UnknownVal();
+
+ return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset,
+ ElemR->getSuperRegion(),
+ Ctx));
+ }
+
const llvm::APSInt& OffI = cast<nonloc::ConcreteInt>(Offset).getValue();
assert(BaseIdxI.isSigned());
diff --git a/lib/Checker/StreamChecker.cpp b/lib/Checker/StreamChecker.cpp
index c527ca2..8553875 100644
--- a/lib/Checker/StreamChecker.cpp
+++ b/lib/Checker/StreamChecker.cpp
@@ -23,18 +23,49 @@ using namespace clang;
namespace {
+struct StreamState {
+ enum Kind { Opened, Closed, OpenFailed, Escaped } K;
+ const Stmt *S;
+
+ StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
+
+ bool isOpened() const { return K == Opened; }
+ bool isClosed() const { return K == Closed; }
+ //bool isOpenFailed() const { return K == OpenFailed; }
+ //bool isEscaped() const { return K == Escaped; }
+
+ bool operator==(const StreamState &X) const {
+ return K == X.K && S == X.S;
+ }
+
+ static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
+ static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
+ static StreamState getOpenFailed(const Stmt *s) {
+ return StreamState(OpenFailed, s);
+ }
+ static StreamState getEscaped(const Stmt *s) {
+ return StreamState(Escaped, s);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ ID.AddPointer(S);
+ }
+};
+
class StreamChecker : public CheckerVisitor<StreamChecker> {
- IdentifierInfo *II_fopen, *II_fread, *II_fwrite,
+ IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite,
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
- BuiltinBug *BT_nullfp, *BT_illegalwhence;
+ BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak;
public:
StreamChecker()
- : II_fopen(0), II_fread(0), II_fwrite(0),
+ : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0),
II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0),
II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0),
- BT_nullfp(0), BT_illegalwhence(0) {}
+ BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0),
+ BT_ResourceLeak(0) {}
static void *getTag() {
static int x;
@@ -42,9 +73,14 @@ public:
}
virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
+ void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper);
+ void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng);
+ void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
private:
void Fopen(CheckerContext &C, const CallExpr *CE);
+ void Tmpfile(CheckerContext &C, const CallExpr *CE);
+ void Fclose(CheckerContext &C, const CallExpr *CE);
void Fread(CheckerContext &C, const CallExpr *CE);
void Fwrite(CheckerContext &C, const CallExpr *CE);
void Fseek(CheckerContext &C, const CallExpr *CE);
@@ -56,14 +92,25 @@ private:
void Feof(CheckerContext &C, const CallExpr *CE);
void Ferror(CheckerContext &C, const CallExpr *CE);
void Fileno(CheckerContext &C, const CallExpr *CE);
+
+ void OpenFileAux(CheckerContext &C, const CallExpr *CE);
- // Return true indicates the stream pointer is NULL.
const GRState *CheckNullStream(SVal SV, const GRState *state,
CheckerContext &C);
+ const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state,
+ CheckerContext &C);
};
} // end anonymous namespace
+namespace clang {
+ template <>
+ struct GRStateTrait<StreamState>
+ : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
+ static void *GDMIndex() { return StreamChecker::getTag(); }
+ };
+}
+
void clang::RegisterStreamChecker(GRExprEngine &Eng) {
Eng.registerCheck(new StreamChecker());
}
@@ -79,6 +126,10 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
ASTContext &Ctx = C.getASTContext();
if (!II_fopen)
II_fopen = &Ctx.Idents.get("fopen");
+ if (!II_tmpfile)
+ II_tmpfile = &Ctx.Idents.get("tmpfile");
+ if (!II_fclose)
+ II_fclose = &Ctx.Idents.get("fclose");
if (!II_fread)
II_fread = &Ctx.Idents.get("fread");
if (!II_fwrite)
@@ -106,6 +157,14 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
Fopen(C, CE);
return true;
}
+ if (FD->getIdentifier() == II_tmpfile) {
+ Tmpfile(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fclose) {
+ Fclose(C, CE);
+ return true;
+ }
if (FD->getIdentifier() == II_fread) {
Fread(C, CE);
return true;
@@ -155,21 +214,43 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
}
void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) {
+ OpenFileAux(C, CE);
+}
+
+void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) {
+ OpenFileAux(C, CE);
+}
+
+void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) {
const GRState *state = C.getState();
unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
ValueManager &ValMgr = C.getValueManager();
DefinedSVal RetVal = cast<DefinedSVal>(ValMgr.getConjuredSymbolVal(0, CE,
Count));
state = state->BindExpr(CE, RetVal);
-
+
ConstraintManager &CM = C.getConstraintManager();
// Bifurcate the state into two: one with a valid FILE* pointer, the other
// with a NULL.
const GRState *stateNotNull, *stateNull;
llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, RetVal);
+
+ if (SymbolRef Sym = RetVal.getAsSymbol()) {
+ // if RetVal is not NULL, set the symbol's state to Opened.
+ stateNotNull =
+ stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE));
+ stateNull =
+ stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE));
+
+ C.addTransition(stateNotNull);
+ C.addTransition(stateNull);
+ }
+}
- C.addTransition(stateNotNull);
- C.addTransition(stateNull);
+void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) {
+ const GRState *state = CheckDoubleClose(CE, C.getState(), C);
+ if (state)
+ C.addTransition(state);
}
void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) {
@@ -285,3 +366,103 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state,
}
return stateNotNull;
}
+
+const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
+ const GRState *state,
+ CheckerContext &C) {
+ SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol();
+ if (!Sym)
+ return state;
+
+ const StreamState *SS = state->get<StreamState>(Sym);
+
+ // If the file stream is not tracked, return.
+ if (!SS)
+ return state;
+
+ // Check: Double close a File Descriptor could cause undefined behaviour.
+ // Conforming to man-pages
+ if (SS->isClosed()) {
+ ExplodedNode *N = C.GenerateSink();
+ if (N) {
+ if (!BT_doubleclose)
+ BT_doubleclose = new BuiltinBug("Double fclose",
+ "Try to close a file Descriptor already"
+ " closed. Cause undefined behaviour.");
+ BugReport *R = new BugReport(*BT_doubleclose,
+ BT_doubleclose->getDescription(), N);
+ C.EmitReport(R);
+ }
+ return NULL;
+ }
+
+ // Close the File Descriptor.
+ return state->set<StreamState>(Sym, StreamState::getClosed(CE));
+}
+
+void StreamChecker::EvalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) {
+ for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
+ E = SymReaper.dead_end(); I != E; ++I) {
+ SymbolRef Sym = *I;
+ const GRState *state = C.getState();
+ const StreamState *SS = state->get<StreamState>(Sym);
+ if (!SS)
+ return;
+
+ if (SS->isOpened()) {
+ ExplodedNode *N = C.GenerateSink();
+ if (N) {
+ if (!BT_ResourceLeak)
+ BT_ResourceLeak = new BuiltinBug("Resource Leak",
+ "Opened File never closed. Potential Resource leak.");
+ BugReport *R = new BugReport(*BT_ResourceLeak,
+ BT_ResourceLeak->getDescription(), N);
+ C.EmitReport(R);
+ }
+ }
+ }
+}
+
+void StreamChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag,
+ GRExprEngine &Eng) {
+ SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode);
+ const GRState *state = B.getState();
+ typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
+ SymMap M = state->get<StreamState>();
+
+ for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+ StreamState SS = I->second;
+ if (SS.isOpened()) {
+ ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
+ if (N) {
+ if (!BT_ResourceLeak)
+ BT_ResourceLeak = new BuiltinBug("Resource Leak",
+ "Opened File never closed. Potential Resource leak.");
+ BugReport *R = new BugReport(*BT_ResourceLeak,
+ BT_ResourceLeak->getDescription(), N);
+ Eng.getBugReporter().EmitReport(R);
+ }
+ }
+ }
+}
+
+void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
+ const Expr *RetE = S->getRetValue();
+ if (!RetE)
+ return;
+
+ const GRState *state = C.getState();
+ SymbolRef Sym = state->getSVal(RetE).getAsSymbol();
+
+ if (!Sym)
+ return;
+
+ const StreamState *SS = state->get<StreamState>(Sym);
+ if(!SS)
+ return;
+
+ if (SS->isOpened())
+ state = state->set<StreamState>(Sym, StreamState::getEscaped(S));
+
+ C.addTransition(state);
+}
diff --git a/lib/Checker/SymbolManager.cpp b/lib/Checker/SymbolManager.cpp
index c2b557e..3b1bb6d 100644
--- a/lib/Checker/SymbolManager.cpp
+++ b/lib/Checker/SymbolManager.cpp
@@ -28,22 +28,22 @@ static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) {
default:
assert(false && "operator printing not implemented");
break;
- case BinaryOperator::Mul: os << '*' ; break;
- case BinaryOperator::Div: os << '/' ; break;
- case BinaryOperator::Rem: os << '%' ; break;
- case BinaryOperator::Add: os << '+' ; break;
- case BinaryOperator::Sub: os << '-' ; break;
- case BinaryOperator::Shl: os << "<<" ; break;
- case BinaryOperator::Shr: os << ">>" ; break;
- case BinaryOperator::LT: os << "<" ; break;
- case BinaryOperator::GT: os << '>' ; break;
- case BinaryOperator::LE: os << "<=" ; break;
- case BinaryOperator::GE: os << ">=" ; break;
- case BinaryOperator::EQ: os << "==" ; break;
- case BinaryOperator::NE: os << "!=" ; break;
- case BinaryOperator::And: os << '&' ; break;
- case BinaryOperator::Xor: os << '^' ; break;
- case BinaryOperator::Or: os << '|' ; break;
+ case BO_Mul: os << '*' ; break;
+ case BO_Div: os << '/' ; break;
+ case BO_Rem: os << '%' ; break;
+ case BO_Add: os << '+' ; break;
+ case BO_Sub: os << '-' ; break;
+ case BO_Shl: os << "<<" ; break;
+ case BO_Shr: os << ">>" ; break;
+ case BO_LT: os << "<" ; break;
+ case BO_GT: os << '>' ; break;
+ case BO_LE: os << "<=" ; break;
+ case BO_GE: os << ">=" ; break;
+ case BO_EQ: os << "==" ; break;
+ case BO_NE: os << "!=" ; break;
+ case BO_And: os << '&' ; break;
+ case BO_Xor: os << '^' ; break;
+ case BO_Or: os << '|' ; break;
}
}
@@ -78,6 +78,11 @@ void SymbolExtent::dumpToStream(llvm::raw_ostream& os) const {
os << "extent_$" << getSymbolID() << '{' << getRegion() << '}';
}
+void SymbolMetadata::dumpToStream(llvm::raw_ostream& os) const {
+ os << "meta_$" << getSymbolID() << '{'
+ << getRegion() << ',' << T.getAsString() << '}';
+}
+
void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const {
os << "reg_$" << getSymbolID() << "<" << R << ">";
}
@@ -150,6 +155,24 @@ SymbolManager::getExtentSymbol(const SubRegion *R) {
return cast<SymbolExtent>(SD);
}
+const SymbolMetadata*
+SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt* S, QualType T,
+ unsigned Count, const void* SymbolTag) {
+
+ llvm::FoldingSetNodeID profile;
+ SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag);
+ void* InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>();
+ new (SD) SymbolMetadata(SymbolCounter, R, S, T, Count, SymbolTag);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolMetadata>(SD);
+}
+
const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs,
BinaryOperator::Opcode op,
const llvm::APSInt& v,
@@ -191,21 +214,34 @@ QualType SymbolConjured::getType(ASTContext&) const {
}
QualType SymbolDerived::getType(ASTContext& Ctx) const {
- return R->getValueType(Ctx);
+ return R->getValueType();
}
QualType SymbolExtent::getType(ASTContext& Ctx) const {
return Ctx.getSizeType();
}
+QualType SymbolMetadata::getType(ASTContext&) const {
+ return T;
+}
+
QualType SymbolRegionValue::getType(ASTContext& C) const {
- return R->getValueType(C);
+ return R->getValueType();
}
SymbolManager::~SymbolManager() {}
bool SymbolManager::canSymbolicate(QualType T) {
- return Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType());
+ if (Loc::IsLocType(T))
+ return true;
+
+ if (T->isIntegerType())
+ return T->isScalarType();
+
+ if (T->isRecordType())
+ return true;
+
+ return false;
}
void SymbolReaper::markLive(SymbolRef sym) {
@@ -213,6 +249,11 @@ void SymbolReaper::markLive(SymbolRef sym) {
TheDead.erase(sym);
}
+void SymbolReaper::markInUse(SymbolRef sym) {
+ if (isa<SymbolMetadata>(sym))
+ MetadataInUse.insert(sym);
+}
+
bool SymbolReaper::maybeDead(SymbolRef sym) {
if (isLive(sym))
return false;
@@ -221,6 +262,31 @@ bool SymbolReaper::maybeDead(SymbolRef sym) {
return true;
}
+static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) {
+ MR = MR->getBaseRegion();
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ return Reaper.isLive(SR->getSymbol());
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(MR))
+ return Reaper.isLive(VR);
+
+ // FIXME: This is a gross over-approximation. What we really need is a way to
+ // tell if anything still refers to this region. Unlike SymbolicRegions,
+ // AllocaRegions don't have associated symbols, though, so we don't actually
+ // have a way to track their liveness.
+ if (isa<AllocaRegion>(MR))
+ return true;
+
+ if (isa<CXXThisRegion>(MR))
+ return true;
+
+ if (isa<MemSpaceRegion>(MR))
+ return true;
+
+ return false;
+}
+
bool SymbolReaper::isLive(SymbolRef sym) {
if (TheLiving.count(sym))
return true;
@@ -234,11 +300,21 @@ bool SymbolReaper::isLive(SymbolRef sym) {
}
if (const SymbolExtent *extent = dyn_cast<SymbolExtent>(sym)) {
- const MemRegion *Base = extent->getRegion()->getBaseRegion();
- if (const VarRegion *VR = dyn_cast<VarRegion>(Base))
- return isLive(VR);
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Base))
- return isLive(SR->getSymbol());
+ if (IsLiveRegion(*this, extent->getRegion())) {
+ markLive(sym);
+ return true;
+ }
+ return false;
+ }
+
+ if (const SymbolMetadata *metadata = dyn_cast<SymbolMetadata>(sym)) {
+ if (MetadataInUse.count(sym)) {
+ if (IsLiveRegion(*this, metadata->getRegion())) {
+ markLive(sym);
+ MetadataInUse.erase(sym);
+ return true;
+ }
+ }
return false;
}
@@ -248,16 +324,19 @@ bool SymbolReaper::isLive(SymbolRef sym) {
}
bool SymbolReaper::isLive(const Stmt* ExprVal) const {
- return LCtx->getLiveVariables()->isLive(Loc, ExprVal);
+ return LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
+ isLive(Loc, ExprVal);
}
bool SymbolReaper::isLive(const VarRegion *VR) const {
- const StackFrameContext *SFC = VR->getStackFrame();
+ const StackFrameContext *VarContext = VR->getStackFrame();
+ const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
+
+ if (VarContext == CurrentContext)
+ return LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
+ isLive(Loc, VR->getDecl());
- if (SFC == LCtx->getCurrentStackFrame())
- return LCtx->getLiveVariables()->isLive(Loc, VR->getDecl());
- else
- return SFC->isParentOf(LCtx->getCurrentStackFrame());
+ return VarContext->isParentOf(CurrentContext);
}
SymbolVisitor::~SymbolVisitor() {}
diff --git a/lib/Checker/UndefBranchChecker.cpp b/lib/Checker/UndefBranchChecker.cpp
index 9088345..1ff0641 100644
--- a/lib/Checker/UndefBranchChecker.cpp
+++ b/lib/Checker/UndefBranchChecker.cpp
@@ -29,27 +29,28 @@ class UndefBranchChecker : public Checker {
FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {}
- Expr* FindExpr(Expr* Ex) {
+ const Expr* FindExpr(const Expr* Ex) {
if (!MatchesCriteria(Ex))
return 0;
- for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I)
- if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) {
- Expr* E2 = FindExpr(ExI);
+ for (Stmt::const_child_iterator I = Ex->child_begin(),
+ E = Ex->child_end();I!=E;++I)
+ if (const Expr* ExI = dyn_cast_or_null<Expr>(*I)) {
+ const Expr* E2 = FindExpr(ExI);
if (E2) return E2;
}
return Ex;
}
- bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); }
+ bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); }
};
public:
UndefBranchChecker() : BT(0) {}
static void *getTag();
void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng,
- Stmt *Condition, void *tag);
+ const Stmt *Condition, void *tag);
};
}
@@ -65,7 +66,7 @@ void *UndefBranchChecker::getTag() {
void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder,
GRExprEngine &Eng,
- Stmt *Condition, void *tag) {
+ const Stmt *Condition, void *tag){
const GRState *state = Builder.getState();
SVal X = state->getSVal(Condition);
if (X.isUndef()) {
@@ -81,7 +82,7 @@ void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder,
// subexpressions and roughly look for the most nested subexpression
// that binds to Undefined. We then highlight that expression's range.
BlockEdge B = cast<BlockEdge>(N->getLocation());
- Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition());
+ const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition());
assert (Ex && "Block must have a terminator.");
// Get the predecessor node and check if is a PostStmt with the Stmt
diff --git a/lib/Checker/UndefinedAssignmentChecker.cpp b/lib/Checker/UndefinedAssignmentChecker.cpp
index 6cef60e..ccc9748 100644
--- a/lib/Checker/UndefinedAssignmentChecker.cpp
+++ b/lib/Checker/UndefinedAssignmentChecker.cpp
@@ -25,9 +25,8 @@ class UndefinedAssignmentChecker
public:
UndefinedAssignmentChecker() : BT(0) {}
static void *getTag();
- virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE,
- const Stmt *StoreE, SVal location,
- SVal val);
+ virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE,
+ SVal location, SVal val);
};
}
@@ -41,7 +40,6 @@ void *UndefinedAssignmentChecker::getTag() {
}
void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C,
- const Stmt *AssignE,
const Stmt *StoreE,
SVal location,
SVal val) {
@@ -61,8 +59,8 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C,
// Generate a report for this bug.
const Expr *ex = 0;
- while (AssignE) {
- if (const BinaryOperator *B = dyn_cast<BinaryOperator>(AssignE)) {
+ while (StoreE) {
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
if (B->isCompoundAssignmentOp()) {
const GRState *state = C.getState();
if (state->getSVal(B->getLHS()).isUndef()) {
@@ -77,7 +75,7 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C,
break;
}
- if (const DeclStmt *DS = dyn_cast<DeclStmt>(AssignE)) {
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl());
ex = VD->getInit();
}
diff --git a/lib/Checker/UnixAPIChecker.cpp b/lib/Checker/UnixAPIChecker.cpp
index e9b8f09..de7346d 100644
--- a/lib/Checker/UnixAPIChecker.cpp
+++ b/lib/Checker/UnixAPIChecker.cpp
@@ -100,7 +100,7 @@ static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC,
NonLoc ocreateFlag =
cast<NonLoc>(C.getValueManager().makeIntVal(UC.Val_O_CREAT.getValue(),
oflagsEx->getType()));
- SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BinaryOperator::And,
+ SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BO_And,
oflags, ocreateFlag,
oflagsEx->getType());
if (maskedFlagsUC.isUnknownOrUndef())
diff --git a/lib/Checker/UnreachableCodeChecker.cpp b/lib/Checker/UnreachableCodeChecker.cpp
new file mode 100644
index 0000000..7a56c7f
--- /dev/null
+++ b/lib/Checker/UnreachableCodeChecker.cpp
@@ -0,0 +1,226 @@
+//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file implements a generalized unreachable code checker using a
+// path-sensitive analysis. We mark any path visited, and then walk the CFG as a
+// post-analysis to determine what was never visited.
+//
+// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ParentMap.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Checker/PathSensitive/ExplodedGraph.h"
+#include "clang/Checker/PathSensitive/SVals.h"
+#include "clang/Checker/PathSensitive/CheckerHelpers.h"
+#include "clang/Checker/BugReporter/BugReporter.h"
+#include "GRExprEngineExperimentalChecks.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+// The number of CFGBlock pointers we want to reserve memory for. This is used
+// once for each function we analyze.
+#define DEFAULT_CFGBLOCKS 256
+
+using namespace clang;
+
+namespace {
+class UnreachableCodeChecker : public CheckerVisitor<UnreachableCodeChecker> {
+public:
+ static void *getTag();
+ void VisitEndAnalysis(ExplodedGraph &G,
+ BugReporter &B,
+ GRExprEngine &Eng);
+private:
+ static inline const Stmt *getUnreachableStmt(const CFGBlock *CB);
+ void FindUnreachableEntryPoints(const CFGBlock *CB);
+ static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM);
+ static inline bool isEmptyCFGBlock(const CFGBlock *CB);
+
+ llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> reachable;
+ llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> visited;
+};
+}
+
+void *UnreachableCodeChecker::getTag() {
+ static int x = 0;
+ return &x;
+}
+
+void clang::RegisterUnreachableCodeChecker(GRExprEngine &Eng) {
+ Eng.registerCheck(new UnreachableCodeChecker());
+}
+
+void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G,
+ BugReporter &B,
+ GRExprEngine &Eng) {
+ // Bail out if we didn't cover all paths
+ if (Eng.hasWorkRemaining())
+ return;
+
+ CFG *C = 0;
+ ParentMap *PM = 0;
+ // Iterate over ExplodedGraph
+ for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
+ I != E; ++I) {
+ const ProgramPoint &P = I->getLocation();
+ const LocationContext *LC = P.getLocationContext();
+
+ // Save the CFG if we don't have it already
+ if (!C)
+ C = LC->getAnalysisContext()->getUnoptimizedCFG();
+ if (!PM)
+ PM = &LC->getParentMap();
+
+ if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) {
+ const CFGBlock *CB = BE->getBlock();
+ reachable.insert(CB->getBlockID());
+ }
+ }
+
+ // Bail out if we didn't get the CFG or the ParentMap.
+ if (!C || !PM)
+ return;
+
+ ASTContext &Ctx = B.getContext();
+
+ // Find CFGBlocks that were not covered by any node
+ for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
+ const CFGBlock *CB = *I;
+ // Check if the block is unreachable
+ if (reachable.count(CB->getBlockID()))
+ continue;
+
+ // Check if the block is empty (an artificial block)
+ if (isEmptyCFGBlock(CB))
+ continue;
+
+ // Find the entry points for this block
+ FindUnreachableEntryPoints(CB);
+
+ // This block may have been pruned; check if we still want to report it
+ if (reachable.count(CB->getBlockID()))
+ continue;
+
+ // Check for false positives
+ if (CB->size() > 0 && isInvalidPath(CB, *PM))
+ continue;
+
+ // Special case for __builtin_unreachable.
+ // FIXME: This should be extended to include other unreachable markers,
+ // such as llvm_unreachable.
+ if (!CB->empty()) {
+ const Stmt *First = CB->front();
+ if (const CallExpr *CE = dyn_cast<CallExpr>(First)) {
+ if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)
+ continue;
+ }
+ }
+
+ // We found a block that wasn't covered - find the statement to report
+ SourceRange SR;
+ SourceLocation SL;
+ if (const Stmt *S = getUnreachableStmt(CB)) {
+ SR = S->getSourceRange();
+ SL = S->getLocStart();
+ if (SR.isInvalid() || SL.isInvalid())
+ continue;
+ }
+ else
+ continue;
+
+ // Check if the SourceLocation is in a system header
+ const SourceManager &SM = B.getSourceManager();
+ if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL))
+ continue;
+
+ B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never"
+ " executed", SL, SR);
+ }
+}
+
+// Recursively finds the entry point(s) for this dead CFGBlock.
+void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) {
+ bool allPredecessorsReachable = true;
+
+ visited.insert(CB->getBlockID());
+
+ for (CFGBlock::const_pred_iterator I = CB->pred_begin(); I != CB->pred_end();
+ ++I) {
+ // Recurse over all unreachable blocks
+ if (!reachable.count((*I)->getBlockID())) {
+ // At least one predeccessor was unreachable
+ allPredecessorsReachable = false;
+
+ // Only visit the block once
+ if (!visited.count((*I)->getBlockID()))
+ FindUnreachableEntryPoints(*I);
+ }
+ }
+
+ // If at least one predecessor is unreachable, mark this block as reachable
+ // so we don't report this block.
+ if (!allPredecessorsReachable) {
+ reachable.insert(CB->getBlockID());
+ }
+}
+
+// Find the Stmt* in a CFGBlock for reporting a warning
+const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {
+ if (CB->size() > 0)
+ return CB->front().getStmt();
+ else if (const Stmt *S = CB->getTerminator())
+ return S;
+ else
+ return 0;
+}
+
+// Determines if the path to this CFGBlock contained an element that infers this
+// block is a false positive. We assume that FindUnreachableEntryPoints has
+// already marked only the entry points to any dead code, so we need only to
+// find the condition that led to this block (the predecessor of this block.)
+// There will never be more than one predecessor.
+bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB,
+ const ParentMap &PM) {
+ // We only expect a predecessor size of 0 or 1. If it is >1, then an external
+ // condition has broken our assumption (for example, a sink being placed by
+ // another check). In these cases, we choose not to report.
+ if (CB->pred_size() > 1)
+ return true;
+
+ // If there are no predecessors, then this block is trivially unreachable
+ if (CB->pred_size() == 0)
+ return false;
+
+ const CFGBlock *pred = *CB->pred_begin();
+
+ // Get the predecessor block's terminator conditon
+ const Stmt *cond = pred->getTerminatorCondition();
+
+ //assert(cond && "CFGBlock's predecessor has a terminator condition");
+ // The previous assertion is invalid in some cases (eg do/while). Leaving
+ // reporting of these situations on at the moment to help triage these cases.
+ if (!cond)
+ return false;
+
+ // Run each of the checks on the conditions
+ if (containsMacro(cond) || containsEnum(cond)
+ || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond)
+ || containsStmt<SizeOfAlignOfExpr>(cond))
+ return true;
+
+ return false;
+}
+
+// Returns true if the given CFGBlock is empty
+bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) {
+ return CB->getLabel() == 0 // No labels
+ && CB->size() == 0 // No statements
+ && CB->getTerminator() == 0; // No terminator
+}
diff --git a/lib/Checker/VLASizeChecker.cpp b/lib/Checker/VLASizeChecker.cpp
index 936991d..0800b8b 100644
--- a/lib/Checker/VLASizeChecker.cpp
+++ b/lib/Checker/VLASizeChecker.cpp
@@ -117,7 +117,7 @@ void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) {
SVal EleSizeVal = ValMgr.makeIntVal(EleSize.getQuantity(), SizeTy);
// Multiply the array length by the element size.
- SVal ArraySizeVal = SV.EvalBinOpNN(state, BinaryOperator::Mul, ArrayLength,
+ SVal ArraySizeVal = SV.EvalBinOpNN(state, BO_Mul, ArrayLength,
cast<NonLoc>(EleSizeVal), SizeTy);
// Finally, Assume that the array's extent matches the given size.
diff --git a/lib/Checker/ValueManager.cpp b/lib/Checker/ValueManager.cpp
index aa0c3c8..8b7cd7b 100644
--- a/lib/Checker/ValueManager.cpp
+++ b/lib/Checker/ValueManager.cpp
@@ -72,7 +72,7 @@ SVal ValueManager::convertToArrayIndex(SVal V) {
DefinedOrUnknownSVal
ValueManager::getRegionValueSymbolVal(const TypedRegion* R) {
- QualType T = R->getValueType(SymMgr.getContext());
+ QualType T = R->getValueType();
if (!SymbolManager::canSymbolicate(T))
return UnknownVal();
@@ -117,11 +117,24 @@ DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag,
return nonloc::SymbolVal(sym);
}
+DefinedSVal ValueManager::getMetadataSymbolVal(const void *SymbolTag,
+ const MemRegion *MR,
+ const Expr *E, QualType T,
+ unsigned Count) {
+ assert(SymbolManager::canSymbolicate(T) && "Invalid metadata symbol type");
+
+ SymbolRef sym = SymMgr.getMetadataSymbol(MR, E, T, Count, SymbolTag);
+
+ if (Loc::IsLocType(T))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
DefinedOrUnknownSVal
ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
const TypedRegion *R) {
- QualType T = R->getValueType(R->getContext());
+ QualType T = R->getValueType();
if (!SymbolManager::canSymbolicate(T))
return UnknownVal();
OpenPOWER on IntegriCloud