summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r--lib/StaticAnalyzer/Core/AggExprVisitor.cpp69
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/BasicConstraintManager.cpp66
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp750
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp237
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt3
-rw-r--r--lib/StaticAnalyzer/Core/Checker.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/CheckerContext.cpp76
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp174
-rw-r--r--lib/StaticAnalyzer/Core/CheckerRegistry.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp592
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp147
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp174
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp1215
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp611
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp237
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp357
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp92
-rw-r--r--lib/StaticAnalyzer/Core/FunctionSummary.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp127
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp170
-rw-r--r--lib/StaticAnalyzer/Core/ObjCMessage.cpp118
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp466
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp298
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp350
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp64
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp388
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp152
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp89
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp178
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.h36
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp341
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp46
-rw-r--r--lib/StaticAnalyzer/Core/SubEngine.cpp14
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp128
-rw-r--r--lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp31
36 files changed, 4746 insertions, 3140 deletions
diff --git a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp
deleted file mode 100644
index 0936d61..0000000
--- a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//=-- AggExprVisitor.cpp - evaluating expressions of C++ class type -*- 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 AggExprVisitor class, which contains lots of boiler
-// plate code for evaluating expressions of C++ class type.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/AST/StmtVisitor.h"
-
-using namespace clang;
-using namespace ento;
-
-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
-/// ExprEngine. AggExprVisitor encapsulates code that goes through various
-/// cast and construct exprs (and others), and at the final point, dispatches
-/// back to the ExprEngine to let the real evaluation logic happen.
-class AggExprVisitor : public StmtVisitor<AggExprVisitor> {
- const MemRegion *Dest;
- ExplodedNode *Pred;
- ExplodedNodeSet &DstSet;
- ExprEngine &Eng;
-
-public:
- AggExprVisitor(const MemRegion *dest, ExplodedNode *N, ExplodedNodeSet &dst,
- ExprEngine &eng)
- : Dest(dest), Pred(N), DstSet(dst), Eng(eng) {}
-
- void VisitCastExpr(CastExpr *E);
- void VisitCXXConstructExpr(CXXConstructExpr *E);
- void VisitCXXMemberCallExpr(CXXMemberCallExpr *E);
-};
-}
-
-void AggExprVisitor::VisitCastExpr(CastExpr *E) {
- switch (E->getCastKind()) {
- default:
- llvm_unreachable("Unhandled cast kind");
- case CK_NoOp:
- case CK_ConstructorConversion:
- case CK_UserDefinedConversion:
- Visit(E->getSubExpr());
- break;
- }
-}
-
-void AggExprVisitor::VisitCXXConstructExpr(CXXConstructExpr *E) {
- Eng.VisitCXXConstructExpr(E, Dest, Pred, DstSet);
-}
-
-void AggExprVisitor::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
- Eng.Visit(E, Pred, DstSet);
-}
-
-void ExprEngine::VisitAggExpr(const Expr *E, const MemRegion *Dest,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {
- AggExprVisitor(Dest, Pred, Dst, *this).Visit(const_cast<Expr *>(E));
-}
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 17ec70d..82ac8bd 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -14,6 +14,8 @@
using namespace clang;
using namespace ento;
+void AnalysisManager::anchor() { }
+
AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
const LangOptions &lang,
PathDiagnosticConsumer *pd,
@@ -25,17 +27,27 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
bool vizdot, bool vizubi,
AnalysisPurgeMode purge,
bool eager, bool trim,
- bool inlinecall, bool useUnoptimizedCFG,
+ bool useUnoptimizedCFG,
bool addImplicitDtors, bool addInitializers,
- bool eagerlyTrimEGraph)
+ bool eagerlyTrimEGraph,
+ AnalysisIPAMode ipa,
+ unsigned inlineMaxStack,
+ unsigned inlineMaxFunctionSize,
+ AnalysisInliningMode IMode,
+ bool NoRetry)
: AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
- Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd),
+ Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd),
CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
CheckerMgr(checkerMgr), Idxer(idxer),
AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
- EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
- EagerlyTrimEGraph(eagerlyTrimEGraph)
+ EagerlyAssume(eager), TrimGraph(trim),
+ EagerlyTrimEGraph(eagerlyTrimEGraph),
+ IPAMode(ipa),
+ InlineMaxStackDepth(inlineMaxStack),
+ InlineMaxFunctionSize(inlineMaxFunctionSize),
+ InliningMode(IMode),
+ NoRetryExhausted(NoRetry)
{
AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
}
@@ -46,7 +58,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors,
ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers),
Ctx(ctx), Diags(diags),
- LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()),
+ LangOpts(ParentAM.LangOpts), PD(ParentAM.getPathDiagnosticConsumer()),
CreateStoreMgr(ParentAM.CreateStoreMgr),
CreateConstraintMgr(ParentAM.CreateConstraintMgr),
CheckerMgr(ParentAM.CheckerMgr),
@@ -59,15 +71,19 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
PurgeDead(ParentAM.PurgeDead),
EagerlyAssume(ParentAM.EagerlyAssume),
TrimGraph(ParentAM.TrimGraph),
- InlineCall(ParentAM.InlineCall),
- EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph)
+ EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph),
+ IPAMode(ParentAM.IPAMode),
+ InlineMaxStackDepth(ParentAM.InlineMaxStackDepth),
+ InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize),
+ InliningMode(ParentAM.InliningMode),
+ NoRetryExhausted(ParentAM.NoRetryExhausted)
{
AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
}
-AnalysisContext *
-AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) {
+AnalysisDeclContext *
+AnalysisManager::getAnalysisDeclContextInAnotherTU(const Decl *D) {
idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D),
Idxer->getProgram());
FunctionDecl *FuncDef;
@@ -77,7 +93,7 @@ AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) {
if (FuncDef == 0)
return 0;
- // This AnalysisContext wraps function definition in another translation unit.
+ // This AnalysisDeclContext 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/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
index 6c748b6..2d9addd 100644
--- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
@@ -56,59 +56,59 @@ public:
: SimpleConstraintManager(subengine),
ISetFactory(statemgr.getAllocator()) {}
- const ProgramState *assumeSymNE(const ProgramState *state,
+ ProgramStateRef assumeSymNE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymEQ(const ProgramState *state,
+ ProgramStateRef assumeSymEQ(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymLT(const ProgramState *state,
+ ProgramStateRef assumeSymLT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymGT(const ProgramState *state,
+ ProgramStateRef assumeSymGT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymGE(const ProgramState *state,
+ ProgramStateRef assumeSymGE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymLE(const ProgramState *state,
+ ProgramStateRef assumeSymLE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment);
- const ProgramState *AddEQ(const ProgramState *state,
+ ProgramStateRef AddEQ(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V);
- const ProgramState *AddNE(const ProgramState *state,
+ ProgramStateRef AddNE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V);
- const llvm::APSInt* getSymVal(const ProgramState *state,
+ const llvm::APSInt* getSymVal(ProgramStateRef state,
SymbolRef sym) const;
- bool isNotEqual(const ProgramState *state,
+ bool isNotEqual(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) const;
- bool isEqual(const ProgramState *state,
+ bool isEqual(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) const;
- const ProgramState *removeDeadBindings(const ProgramState *state,
+ ProgramStateRef removeDeadBindings(ProgramStateRef state,
SymbolReaper& SymReaper);
- void print(const ProgramState *state,
+ void print(ProgramStateRef state,
raw_ostream &Out,
const char* nl,
const char *sep);
@@ -122,8 +122,8 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr,
return new BasicConstraintManager(statemgr, subengine);
}
-const ProgramState*
-BasicConstraintManager::assumeSymNE(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymNE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -143,8 +143,8 @@ BasicConstraintManager::assumeSymNE(const ProgramState *state,
return AddNE(state, sym, Adjusted);
}
-const ProgramState*
-BasicConstraintManager::assumeSymEQ(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymEQ(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -165,8 +165,8 @@ BasicConstraintManager::assumeSymEQ(const ProgramState *state,
}
// The logic for these will be handled in another ConstraintManager.
-const ProgramState*
-BasicConstraintManager::assumeSymLT(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymLT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -180,8 +180,8 @@ BasicConstraintManager::assumeSymLT(const ProgramState *state,
return assumeSymNE(state, sym, V, Adjustment);
}
-const ProgramState*
-BasicConstraintManager::assumeSymGT(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymGT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -195,8 +195,8 @@ BasicConstraintManager::assumeSymGT(const ProgramState *state,
return assumeSymNE(state, sym, V, Adjustment);
}
-const ProgramState*
-BasicConstraintManager::assumeSymGE(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymGE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -224,8 +224,8 @@ BasicConstraintManager::assumeSymGE(const ProgramState *state,
return state;
}
-const ProgramState*
-BasicConstraintManager::assumeSymLE(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::assumeSymLE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
@@ -253,14 +253,14 @@ BasicConstraintManager::assumeSymLE(const ProgramState *state,
return state;
}
-const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state,
+ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) {
// Create a new state with the old binding replaced.
return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V));
}
-const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state,
+ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) {
@@ -275,13 +275,13 @@ const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state,
return state->set<ConstNotEq>(sym, S);
}
-const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state,
+const llvm::APSInt* BasicConstraintManager::getSymVal(ProgramStateRef state,
SymbolRef sym) const {
const ConstEqTy::data_type* T = state->get<ConstEq>(sym);
return T ? *T : NULL;
}
-bool BasicConstraintManager::isNotEqual(const ProgramState *state,
+bool BasicConstraintManager::isNotEqual(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) const {
@@ -292,7 +292,7 @@ bool BasicConstraintManager::isNotEqual(const ProgramState *state,
return T ? T->contains(&state->getBasicVals().getValue(V)) : false;
}
-bool BasicConstraintManager::isEqual(const ProgramState *state,
+bool BasicConstraintManager::isEqual(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt& V) const {
// Retrieve the EQ-set associated with the given symbol.
@@ -303,8 +303,8 @@ bool BasicConstraintManager::isEqual(const ProgramState *state,
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
-const ProgramState*
-BasicConstraintManager::removeDeadBindings(const ProgramState *state,
+ProgramStateRef
+BasicConstraintManager::removeDeadBindings(ProgramStateRef state,
SymbolReaper& SymReaper) {
ConstEqTy CE = state->get<ConstEq>();
@@ -329,7 +329,7 @@ BasicConstraintManager::removeDeadBindings(const ProgramState *state,
return state->set<ConstNotEq>(CNE);
}
-void BasicConstraintManager::print(const ProgramState *state,
+void BasicConstraintManager::print(ProgramStateRef state,
raw_ostream &Out,
const char* nl, const char *sep) {
// Print equality constraints.
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index fbbdb04..a264212 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/AST/ASTContext.h"
#include "clang/Analysis/CFG.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtObjC.h"
@@ -25,8 +26,10 @@
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include <queue>
using namespace clang;
@@ -34,6 +37,8 @@ using namespace ento;
BugReporterVisitor::~BugReporterVisitor() {}
+void BugReporterContext::anchor() {}
+
//===----------------------------------------------------------------------===//
// Helper routines for walking the ExplodedGraph and fetching statements.
//===----------------------------------------------------------------------===//
@@ -106,6 +111,59 @@ GetCurrentOrNextStmt(const ExplodedNode *N) {
}
//===----------------------------------------------------------------------===//
+// Diagnostic cleanup.
+//===----------------------------------------------------------------------===//
+
+/// Recursively scan through a path and prune out calls and macros pieces
+/// that aren't needed. Return true if afterwards the path contains
+/// "interesting stuff" which means it should be pruned from the parent path.
+static bool RemoveUneededCalls(PathPieces &pieces) {
+ bool containsSomethingInteresting = false;
+ const unsigned N = pieces.size();
+
+ for (unsigned i = 0 ; i < N ; ++i) {
+ // Remove the front piece from the path. If it is still something we
+ // want to keep once we are done, we will push it back on the end.
+ IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front());
+ pieces.pop_front();
+
+ switch (piece->getKind()) {
+ case PathDiagnosticPiece::Call: {
+ PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece);
+ // Recursively clean out the subclass. Keep this call around if
+ // it contains any informative diagnostics.
+ if (!RemoveUneededCalls(call->path))
+ continue;
+ containsSomethingInteresting = true;
+ break;
+ }
+ case PathDiagnosticPiece::Macro: {
+ PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece);
+ if (!RemoveUneededCalls(macro->subPieces))
+ continue;
+ containsSomethingInteresting = true;
+ break;
+ }
+ case PathDiagnosticPiece::Event: {
+ PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece);
+ // We never throw away an event, but we do throw it away wholesale
+ // as part of a path if we throw the entire path away.
+ if (event->isPrunable())
+ continue;
+ containsSomethingInteresting = true;
+ break;
+ }
+ case PathDiagnosticPiece::ControlFlow:
+ break;
+ }
+
+ pieces.push_back(piece);
+ }
+
+ return containsSomethingInteresting;
+}
+
+//===----------------------------------------------------------------------===//
// PathDiagnosticBuilder and its associated routines and helper objects.
//===----------------------------------------------------------------------===//
@@ -128,14 +186,17 @@ public:
class PathDiagnosticBuilder : public BugReporterContext {
BugReport *R;
PathDiagnosticConsumer *PDC;
- llvm::OwningPtr<ParentMap> PM;
+ OwningPtr<ParentMap> PM;
NodeMapClosure NMC;
public:
+ const LocationContext *LC;
+
PathDiagnosticBuilder(GRBugReporter &br,
BugReport *r, NodeBackMap *Backmap,
PathDiagnosticConsumer *pdc)
: BugReporterContext(br),
- R(r), PDC(pdc), NMC(Backmap) {}
+ R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext())
+ {}
PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
@@ -145,12 +206,8 @@ public:
BugReport *getBugReport() { return R; }
Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
-
- const LocationContext* getLocationContext() {
- return R->getErrorNode()->getLocationContext();
- }
-
- ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); }
+
+ ParentMap& getParentMap() { return LC->getParentMap(); }
const Stmt *getParent(const Stmt *S) {
return getParentMap().getParent(S);
@@ -173,7 +230,7 @@ public:
PathDiagnosticLocation
PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
if (const Stmt *S = GetNextStmt(N))
- return PathDiagnosticLocation(S, getSourceManager(), getLocationContext());
+ return PathDiagnosticLocation(S, getSourceManager(), LC);
return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(),
getSourceManager());
@@ -234,7 +291,6 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
assert(S && "Null Stmt *passed to getEnclosingStmtLocation");
ParentMap &P = getParentMap();
SourceManager &SMgr = getSourceManager();
- const LocationContext *LC = getLocationContext();
while (IsNested(S, P)) {
const Stmt *Parent = P.getParentIgnoreParens(S);
@@ -322,208 +378,87 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
}
//===----------------------------------------------------------------------===//
-// ScanNotableSymbols: closure-like callback for scanning Store bindings.
+// "Minimal" path diagnostic generation algorithm.
//===----------------------------------------------------------------------===//
-
-static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N,
- ProgramStateManager& VMgr,
- SVal X) {
-
- for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) {
-
- ProgramPoint P = N->getLocation();
-
- if (!isa<PostStmt>(P))
- continue;
-
- const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt());
-
- if (!DR)
- continue;
-
- SVal Y = N->getState()->getSVal(DR);
-
- if (X != Y)
- continue;
-
- const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
-
- if (!VD)
- continue;
-
- return VD;
- }
-
- return 0;
-}
-
-namespace {
-class NotableSymbolHandler
-: public StoreManager::BindingsHandler {
-
- SymbolRef Sym;
- const ProgramState *PrevSt;
- const Stmt *S;
- ProgramStateManager& VMgr;
- const ExplodedNode *Pred;
- PathDiagnostic& PD;
- BugReporter& BR;
-
-public:
-
- NotableSymbolHandler(SymbolRef sym,
- const ProgramState *prevst,
- const Stmt *s,
- ProgramStateManager& vmgr,
- const ExplodedNode *pred,
- PathDiagnostic& pd,
- BugReporter& br)
- : Sym(sym),
- PrevSt(prevst),
- S(s),
- VMgr(vmgr),
- Pred(pred),
- PD(pd),
- BR(br) {}
-
- bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R,
- SVal V) {
-
- SymbolRef ScanSym = V.getAsSymbol();
-
- if (ScanSym != Sym)
- return true;
-
- // Check if the previous state has this binding.
- SVal X = PrevSt->getSVal(loc::MemRegionVal(R));
-
- if (X == V) // Same binding?
- return true;
-
- // Different binding. Only handle assignments for now. We don't pull
- // this check out of the loop because we will eventually handle other
- // cases.
-
- VarDecl *VD = 0;
-
- if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
- if (!B->isAssignmentOp())
- return true;
-
- // What variable did we assign to?
- DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts());
-
- if (!DR)
- return true;
-
- VD = dyn_cast<VarDecl>(DR->getDecl());
- }
- else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
- // FIXME: Eventually CFGs won't have DeclStmts. Right now we
- // assume that each DeclStmt has a single Decl. This invariant
- // holds by construction in the CFG.
- VD = dyn_cast<VarDecl>(*DS->decl_begin());
- }
-
- if (!VD)
- return true;
-
- // What is the most recently referenced variable with this binding?
- const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V);
-
- if (!MostRecent)
- return true;
-
- // Create the diagnostic.
- if (Loc::isLocType(VD->getType())) {
- llvm::SmallString<64> buf;
- llvm::raw_svector_ostream os(buf);
- os << '\'' << *VD << "' now aliases '" << *MostRecent << '\'';
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createBegin(S, BR.getSourceManager(),
- Pred->getLocationContext());
- PD.push_front(new PathDiagnosticEventPiece(L, os.str()));
- }
-
- return true;
+typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair;
+typedef SmallVector<StackDiagPair, 6> StackDiagVector;
+
+static void updateStackPiecesWithMessage(PathDiagnosticPiece *P,
+ StackDiagVector &CallStack) {
+ // If the piece contains a special message, add it to all the call
+ // pieces on the active stack.
+ if (PathDiagnosticEventPiece *ep =
+ dyn_cast<PathDiagnosticEventPiece>(P)) {
+
+ if (ep->hasCallStackHint())
+ for (StackDiagVector::iterator I = CallStack.begin(),
+ E = CallStack.end(); I != E; ++I) {
+ PathDiagnosticCallPiece *CP = I->first;
+ const ExplodedNode *N = I->second;
+ std::string stackMsg = ep->getCallStackMessage(N);
+
+ // The last message on the path to final bug is the most important
+ // one. Since we traverse the path backwards, do not add the message
+ // if one has been previously added.
+ if (!CP->hasCallStackMessage())
+ CP->setCallStackMessage(stackMsg);
+ }
}
-};
-}
-
-static void HandleNotableSymbol(const ExplodedNode *N,
- const Stmt *S,
- SymbolRef Sym, BugReporter& BR,
- PathDiagnostic& PD) {
-
- const ExplodedNode *Pred = N->pred_empty() ? 0 : *N->pred_begin();
- const ProgramState *PrevSt = Pred ? Pred->getState() : 0;
-
- if (!PrevSt)
- return;
-
- // Look at the region bindings of the current state that map to the
- // specified symbol. Are any of them not in the previous state?
- ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager();
- NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR);
- cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H);
}
-namespace {
-class ScanNotableSymbols
-: public StoreManager::BindingsHandler {
-
- llvm::SmallSet<SymbolRef, 10> AlreadyProcessed;
- const ExplodedNode *N;
- const Stmt *S;
- GRBugReporter& BR;
- PathDiagnostic& PD;
-
-public:
- ScanNotableSymbols(const ExplodedNode *n, const Stmt *s,
- GRBugReporter& br, PathDiagnostic& pd)
- : N(n), S(s), BR(br), PD(pd) {}
-
- bool HandleBinding(StoreManager& SMgr, Store store,
- const MemRegion* R, SVal V) {
-
- SymbolRef ScanSym = V.getAsSymbol();
-
- if (!ScanSym)
- return true;
-
- if (!BR.isNotable(ScanSym))
- return true;
-
- if (AlreadyProcessed.count(ScanSym))
- return true;
-
- AlreadyProcessed.insert(ScanSym);
-
- HandleNotableSymbol(N, S, ScanSym, BR, PD);
- return true;
- }
-};
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
-// "Minimal" path diagnostic generation algorithm.
-//===----------------------------------------------------------------------===//
-
-static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM);
+static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM);
static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PathDiagnosticBuilder &PDB,
- const ExplodedNode *N) {
+ const ExplodedNode *N,
+ ArrayRef<BugReporterVisitor *> visitors) {
SourceManager& SMgr = PDB.getSourceManager();
- const LocationContext *LC = PDB.getLocationContext();
+ const LocationContext *LC = PDB.LC;
const ExplodedNode *NextNode = N->pred_empty()
? NULL : *(N->pred_begin());
+
+ StackDiagVector CallStack;
+
while (NextNode) {
N = NextNode;
+ PDB.LC = N->getLocationContext();
NextNode = GetPredecessorNode(N);
ProgramPoint P = N->getLocation();
+
+ if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ PathDiagnosticCallPiece *C =
+ PathDiagnosticCallPiece::construct(N, *CE, SMgr);
+ PD.getActivePath().push_front(C);
+ PD.pushActivePath(&C->path);
+ CallStack.push_back(StackDiagPair(C, N));
+ continue;
+ }
+
+ if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ PD.popActivePath();
+ // The current active path should never be empty. Either we
+ // just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExit. If the front of the active
+ // path is not a PathDiagnosticCallPiece, it means that the
+ // path terminated within a function call. We must then take the
+ // current contents of the active path and place it within
+ // a new PathDiagnosticCallPiece.
+ assert(!PD.getActivePath().empty());
+ PathDiagnosticCallPiece *C =
+ dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ if (!C) {
+ const Decl *Caller = CE->getLocationContext()->getDecl();
+ C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
+ }
+ C->setCallee(*CE, SMgr);
+ if (!CallStack.empty()) {
+ assert(CallStack.back().first == C);
+ CallStack.pop_back();
+ }
+ continue;
+ }
if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
const CFGBlock *Src = BE->getSrc();
@@ -554,8 +489,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
os << "Control jumps to line "
<< End.asLocation().getExpansionLineNumber();
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
- os.str()));
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ os.str()));
break;
}
@@ -606,13 +541,13 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
break;
}
}
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
os << "'Default' branch taken. ";
const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
@@ -624,7 +559,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
break;
}
@@ -646,7 +581,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (const Stmt *S = End.asStmt())
End = PDB.getEnclosingStmtLocation(S);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
break;
}
@@ -669,14 +604,14 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
PathDiagnosticLocation Start =
PathDiagnosticLocation::createOperatorLoc(B, SMgr);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
os << "true";
PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
}
@@ -688,7 +623,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
os << "false";
PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
@@ -696,7 +631,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
PathDiagnosticLocation Start =
PathDiagnosticLocation::createOperatorLoc(B, SMgr);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
}
@@ -715,7 +650,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (const Stmt *S = End.asStmt())
End = PDB.getEnclosingStmtLocation(S);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
@@ -724,7 +659,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (const Stmt *S = End.asStmt())
End = PDB.getEnclosingStmtLocation(S);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
"Loop condition is false. Exiting loop"));
}
@@ -742,7 +677,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (const Stmt *S = End.asStmt())
End = PDB.getEnclosingStmtLocation(S);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
@@ -750,7 +685,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (const Stmt *S = End.asStmt())
End = PDB.getEnclosingStmtLocation(S);
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
"Loop condition is true. Entering loop body"));
}
@@ -764,10 +699,10 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
End = PDB.getEnclosingStmtLocation(S);
if (*(Src->succ_begin()+1) == Dst)
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
"Taking false branch"));
else
- PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End,
"Taking true branch"));
break;
@@ -778,24 +713,20 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (NextNode) {
// Add diagnostic pieces from custom visitors.
BugReport *R = PDB.getBugReport();
- for (BugReport::visitor_iterator I = R->visitor_begin(),
- E = R->visitor_end(); I!=E; ++I) {
- if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R))
- PD.push_front(p);
+ for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(),
+ E = visitors.end();
+ I != E; ++I) {
+ if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) {
+ PD.getActivePath().push_front(p);
+ updateStackPiecesWithMessage(p, CallStack);
+ }
}
}
-
- if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
- // Scan the region bindings, and see if a "notable" symbol has a new
- // lval binding.
- ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD);
- PDB.getStateManager().iterBindings(N->getState(), SNS);
- }
}
// After constructing the full PathDiagnostic, do a pass over it to compact
// PathDiagnosticPieces that occur within a macro.
- CompactPathDiagnostic(PD, PDB.getSourceManager());
+ CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager());
}
//===----------------------------------------------------------------------===//
@@ -879,7 +810,7 @@ class EdgeBuilder {
}
if (S != Original)
- L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext());
+ L = PathDiagnosticLocation(S, L.getManager(), PDB.LC);
}
if (firstCharOnly)
@@ -902,8 +833,8 @@ public:
// If the PathDiagnostic already has pieces, add the enclosing statement
// of the first piece as a context as well.
- if (!PD.empty()) {
- PrevLoc = PD.begin()->getLocation();
+ if (!PD.path.empty()) {
+ PrevLoc = (*PD.path.begin())->getLocation();
if (const Stmt *S = PrevLoc.asStmt())
addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
@@ -916,12 +847,18 @@ public:
// Finally, add an initial edge from the start location of the first
// statement (if it doesn't already exist).
PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
- PDB.getLocationContext(),
+ PDB.LC,
PDB.getSourceManager());
if (L.isValid())
rawAddEdge(L);
}
+ void flushLocations() {
+ while (!CLocs.empty())
+ popLocation();
+ PrevLoc = PathDiagnosticLocation();
+ }
+
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false);
void rawAddEdge(PathDiagnosticLocation NewLoc);
@@ -988,7 +925,7 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
SM.getExpansionColumnNumber(ContaineeRBeg)) &&
(ContainerEndLine != ContaineeEndLine ||
SM.getExpansionColumnNumber(ContainerREnd) >=
- SM.getExpansionColumnNumber(ContainerREnd)));
+ SM.getExpansionColumnNumber(ContaineeREnd)));
}
void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
@@ -1008,7 +945,7 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
PrevLocClean.asLocation().getExpansionLoc())
return;
- PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean));
+ PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean));
PrevLoc = NewLoc;
}
@@ -1093,7 +1030,7 @@ void EdgeBuilder::addContext(const Stmt *S) {
if (!S)
return;
- PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext());
+ PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC);
while (!CLocs.empty()) {
const PathDiagnosticLocation &TopContextLoc = CLocs.back();
@@ -1116,9 +1053,11 @@ void EdgeBuilder::addContext(const Stmt *S) {
static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticBuilder &PDB,
- const ExplodedNode *N) {
+ const ExplodedNode *N,
+ ArrayRef<BugReporterVisitor *> visitors) {
EdgeBuilder EB(PD, PDB);
const SourceManager& SM = PDB.getSourceManager();
+ StackDiagVector CallStack;
const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
while (NextNode) {
@@ -1127,14 +1066,74 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
do {
+ if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ const StackFrameContext *LCtx =
+ CE->getLocationContext()->getCurrentStackFrame();
+ PathDiagnosticLocation Loc(LCtx->getCallSite(),
+ PDB.getSourceManager(),
+ LCtx);
+ EB.addEdge(Loc, true);
+ EB.flushLocations();
+ PathDiagnosticCallPiece *C =
+ PathDiagnosticCallPiece::construct(N, *CE, SM);
+ PD.getActivePath().push_front(C);
+ PD.pushActivePath(&C->path);
+ CallStack.push_back(StackDiagPair(C, N));
+ break;
+ }
+
+ // Pop the call hierarchy if we are done walking the contents
+ // of a function call.
+ if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ // Add an edge to the start of the function.
+ const Decl *D = CE->getCalleeContext()->getDecl();
+ PathDiagnosticLocation pos =
+ PathDiagnosticLocation::createBegin(D, SM);
+ EB.addEdge(pos);
+
+ // Flush all locations, and pop the active path.
+ EB.flushLocations();
+ PD.popActivePath();
+ assert(!PD.getActivePath().empty());
+ PDB.LC = N->getLocationContext();
+
+ // The current active path should never be empty. Either we
+ // just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExit. If the front of the active
+ // path is not a PathDiagnosticCallPiece, it means that the
+ // path terminated within a function call. We must then take the
+ // current contents of the active path and place it within
+ // a new PathDiagnosticCallPiece.
+ PathDiagnosticCallPiece *C =
+ dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ if (!C) {
+ const Decl * Caller = CE->getLocationContext()->getDecl();
+ C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
+ }
+ C->setCallee(*CE, SM);
+ EB.addContext(CE->getCallExpr());
+
+ if (!CallStack.empty()) {
+ assert(CallStack.back().first == C);
+ CallStack.pop_back();
+ }
+ break;
+ }
+
+ // Note that is important that we update the LocationContext
+ // after looking at CallExits. CallExit basically adds an
+ // edge in the *caller*, so we don't want to update the LocationContext
+ // too soon.
+ PDB.LC = N->getLocationContext();
+
// Block edges.
- if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
const CFGBlock &Blk = *BE->getSrc();
const Stmt *Term = Blk.getTerminator();
// Are we jumping to the head of a loop? Add a special diagnostic.
if (const Stmt *Loop = BE->getDst()->getLoopTarget()) {
- PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext());
+ PathDiagnosticLocation L(Loop, SM, PDB.LC);
const CompoundStmt *CS = NULL;
if (!Term) {
@@ -1147,9 +1146,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticEventPiece *p =
new PathDiagnosticEventPiece(L,
"Looping back to the head of the loop");
+ p->setPrunable(true);
EB.addEdge(p->getLocation(), true);
- PD.push_front(p);
+ PD.getActivePath().push_front(p);
if (CS) {
PathDiagnosticLocation BL =
@@ -1177,6 +1177,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
break;
}
+
+
} while (0);
if (!NextNode)
@@ -1184,12 +1186,15 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
// Add pieces from custom visitors.
BugReport *R = PDB.getBugReport();
- for (BugReport::visitor_iterator I = R->visitor_begin(),
- E = R->visitor_end(); I!=E; ++I) {
+ for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(),
+ E = visitors.end();
+ I != E; ++I) {
if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) {
const PathDiagnosticLocation &Loc = p->getLocation();
EB.addEdge(Loc, true);
- PD.push_front(p);
+ PD.getActivePath().push_front(p);
+ updateStackPiecesWithMessage(p, CallStack);
+
if (const Stmt *S = Loc.asStmt())
EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
}
@@ -1204,10 +1209,14 @@ BugType::~BugType() { }
void BugType::FlushReports(BugReporter &BR) {}
+void BuiltinBug::anchor() {}
+
//===----------------------------------------------------------------------===//
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
+void BugReport::NodeResolver::anchor() {}
+
void BugReport::addVisitor(BugReporterVisitor* visitor) {
if (!visitor)
return;
@@ -1222,7 +1231,8 @@ void BugReport::addVisitor(BugReporterVisitor* visitor) {
}
CallbacksSet.InsertNode(visitor, InsertPos);
- Callbacks = F.add(visitor, Callbacks);
+ Callbacks.push_back(visitor);
+ ++ConfigurationChangeToken;
}
BugReport::~BugReport() {
@@ -1231,10 +1241,24 @@ BugReport::~BugReport() {
}
}
+const Decl *BugReport::getDeclWithIssue() const {
+ if (DeclWithIssue)
+ return DeclWithIssue;
+
+ const ExplodedNode *N = getErrorNode();
+ if (!N)
+ return 0;
+
+ const LocationContext *LC = N->getLocationContext();
+ return LC->getCurrentStackFrame()->getDecl();
+}
+
void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
hash.AddPointer(&BT);
hash.AddString(Description);
- if (Location.isValid()) {
+ if (UniqueingLocation.isValid()) {
+ UniqueingLocation.Profile(hash);
+ } else if (Location.isValid()) {
Location.Profile(hash);
} else {
assert(ErrorNode);
@@ -1251,6 +1275,61 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
}
}
+void BugReport::markInteresting(SymbolRef sym) {
+ if (!sym)
+ return;
+
+ // If the symbol wasn't already in our set, note a configuration change.
+ if (interestingSymbols.insert(sym).second)
+ ++ConfigurationChangeToken;
+
+ if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym))
+ interestingRegions.insert(meta->getRegion());
+}
+
+void BugReport::markInteresting(const MemRegion *R) {
+ if (!R)
+ return;
+
+ // If the base region wasn't already in our set, note a configuration change.
+ R = R->getBaseRegion();
+ if (interestingRegions.insert(R).second)
+ ++ConfigurationChangeToken;
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ interestingSymbols.insert(SR->getSymbol());
+}
+
+void BugReport::markInteresting(SVal V) {
+ markInteresting(V.getAsRegion());
+ markInteresting(V.getAsSymbol());
+}
+
+bool BugReport::isInteresting(SVal V) const {
+ return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol());
+}
+
+bool BugReport::isInteresting(SymbolRef sym) const {
+ if (!sym)
+ return false;
+ // We don't currently consider metadata symbols to be interesting
+ // even if we know their region is interesting. Is that correct behavior?
+ return interestingSymbols.count(sym);
+}
+
+bool BugReport::isInteresting(const MemRegion *R) const {
+ if (!R)
+ return false;
+ R = R->getBaseRegion();
+ bool b = interestingRegions.count(R);
+ if (b)
+ return true;
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ return interestingSymbols.count(SR->getSymbol());
+ return false;
+}
+
+
const Stmt *BugReport::getStmt() const {
if (!ErrorNode)
return 0;
@@ -1316,10 +1395,7 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
// Methods for BugReporter and subclasses.
//===----------------------------------------------------------------------===//
-BugReportEquivClass::~BugReportEquivClass() {
- for (iterator I=begin(), E=end(); I!=E; ++I) delete *I;
-}
-
+BugReportEquivClass::~BugReportEquivClass() { }
GRBugReporter::~GRBugReporter() { }
BugReporterData::~BugReporterData() {}
@@ -1394,8 +1470,8 @@ MakeReportGraph(const ExplodedGraph* G,
// Create owning pointers for GTrim and NMap just to ensure that they are
// released when this function exists.
- llvm::OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim);
- llvm::OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap);
+ OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim);
+ OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap);
// Find the (first) error node in the trimmed graph. We just need to consult
// the node map (NMap) which maps from nodes in the original graph to nodes
@@ -1513,19 +1589,28 @@ MakeReportGraph(const ExplodedGraph* G,
/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
/// and collapses PathDiagosticPieces that are expanded by macros.
-static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
- typedef std::vector<std::pair<PathDiagnosticMacroPiece*, SourceLocation> >
- MacroStackTy;
+static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
+ typedef std::vector<std::pair<IntrusiveRefCntPtr<PathDiagnosticMacroPiece>,
+ SourceLocation> > MacroStackTy;
- typedef std::vector<PathDiagnosticPiece*>
+ typedef std::vector<IntrusiveRefCntPtr<PathDiagnosticPiece> >
PiecesTy;
MacroStackTy MacroStack;
PiecesTy Pieces;
- for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) {
+ for (PathPieces::const_iterator I = path.begin(), E = path.end();
+ I!=E; ++I) {
+
+ PathDiagnosticPiece *piece = I->getPtr();
+
+ // Recursively compact calls.
+ if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){
+ CompactPathDiagnostic(call->path, SM);
+ }
+
// Get the location of the PathDiagnosticPiece.
- const FullSourceLoc Loc = I->getLocation().asLocation();
+ const FullSourceLoc Loc = piece->getLocation().asLocation();
// Determine the instantiation location, which is the location we group
// related PathDiagnosticPieces.
@@ -1535,7 +1620,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
if (Loc.isFileID()) {
MacroStack.clear();
- Pieces.push_back(&*I);
+ Pieces.push_back(piece);
continue;
}
@@ -1543,13 +1628,13 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
// Is the PathDiagnosticPiece within the same macro group?
if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
- MacroStack.back().first->push_back(&*I);
+ MacroStack.back().first->subPieces.push_back(piece);
continue;
}
// We aren't in the same group. Are we descending into a new macro
// or are part of an old one?
- PathDiagnosticMacroPiece *MacroGroup = 0;
+ IntrusiveRefCntPtr<PathDiagnosticMacroPiece> MacroGroup;
SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
SM.getExpansionLoc(Loc) :
@@ -1574,10 +1659,10 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
// Create a new macro group and add it to the stack.
PathDiagnosticMacroPiece *NewGroup =
new PathDiagnosticMacroPiece(
- PathDiagnosticLocation::createSingleLocation(I->getLocation()));
+ PathDiagnosticLocation::createSingleLocation(piece->getLocation()));
if (MacroGroup)
- MacroGroup->push_back(NewGroup);
+ MacroGroup->subPieces.push_back(NewGroup);
else {
assert(InstantiationLoc.isFileID());
Pieces.push_back(NewGroup);
@@ -1588,21 +1673,14 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
}
// Finally, add the PathDiagnosticPiece to the group.
- MacroGroup->push_back(&*I);
+ MacroGroup->subPieces.push_back(piece);
}
// Now take the pieces and construct a new PathDiagnostic.
- PD.resetPath(false);
+ path.clear();
- for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) {
- if (PathDiagnosticMacroPiece *MP=dyn_cast<PathDiagnosticMacroPiece>(*I))
- if (!MP->containsEvent()) {
- delete MP;
- continue;
- }
-
- PD.push_back(*I);
- }
+ for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I)
+ path.push_back(*I);
}
void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
@@ -1626,8 +1704,8 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
BugReport *R = bugReports[GPair.second.second];
assert(R && "No original report found for sliced graph.");
- llvm::OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first);
- llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second);
+ OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first);
+ OwningPtr<NodeBackMap> BackMap(GPair.first.second);
const ExplodedNode *N = GPair.second.first;
// Start building the path diagnostic...
@@ -1638,32 +1716,61 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
R->addVisitor(new NilReceiverBRVisitor());
R->addVisitor(new ConditionBRVisitor());
- // Generate the very last diagnostic piece - the piece is visible before
- // the trace is expanded.
- PathDiagnosticPiece *LastPiece = 0;
- for (BugReport::visitor_iterator I = R->visitor_begin(),
- E = R->visitor_end(); I!=E; ++I) {
- if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) {
- assert (!LastPiece &&
- "There can only be one final piece in a diagnostic.");
- LastPiece = Piece;
+ BugReport::VisitorList visitors;
+ unsigned originalReportConfigToken, finalReportConfigToken;
+
+ // While generating diagnostics, it's possible the visitors will decide
+ // new symbols and regions are interesting, or add other visitors based on
+ // the information they find. If they do, we need to regenerate the path
+ // based on our new report configuration.
+ do {
+ // Get a clean copy of all the visitors.
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end(); I != E; ++I)
+ visitors.push_back((*I)->clone());
+
+ // Clear out the active path from any previous work.
+ PD.getActivePath().clear();
+ originalReportConfigToken = R->getConfigurationChangeToken();
+
+ // Generate the very last diagnostic piece - the piece is visible before
+ // the trace is expanded.
+ PathDiagnosticPiece *LastPiece = 0;
+ for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end();
+ I != E; ++I) {
+ if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) {
+ assert (!LastPiece &&
+ "There can only be one final piece in a diagnostic.");
+ LastPiece = Piece;
+ }
}
- }
- if (!LastPiece)
- LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R);
- if (LastPiece)
- PD.push_back(LastPiece);
- else
- return;
+ if (!LastPiece)
+ LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R);
+ if (LastPiece)
+ PD.getActivePath().push_back(LastPiece);
+ else
+ return;
- switch (PDB.getGenerationScheme()) {
+ switch (PDB.getGenerationScheme()) {
case PathDiagnosticConsumer::Extensive:
- GenerateExtensivePathDiagnostic(PD, PDB, N);
+ GenerateExtensivePathDiagnostic(PD, PDB, N, visitors);
break;
case PathDiagnosticConsumer::Minimal:
- GenerateMinimalPathDiagnostic(PD, PDB, N);
+ GenerateMinimalPathDiagnostic(PD, PDB, N, visitors);
break;
- }
+ }
+
+ // Clean up the visitors we used.
+ llvm::DeleteContainerPointers(visitors);
+
+ // Did anything change while generating this path?
+ finalReportConfigToken = R->getConfigurationChangeToken();
+ } while(finalReportConfigToken != originalReportConfigToken);
+
+ // Finally, prune the diagnostic path of uninteresting stuff.
+ bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
+ assert(hasSomethingInteresting);
+ (void) hasSomethingInteresting;
}
void BugReporter::Register(BugType *BT) {
@@ -1711,17 +1818,17 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
assert(I != E);
- BugReport *R = *I;
- BugType& BT = R->getBugType();
+ BugType& BT = I->getBugType();
// If we don't need to suppress any of the nodes because they are
// post-dominated by a sink, simply add all the nodes in the equivalence class
// to 'Nodes'. Any of the reports will serve as a "representative" report.
if (!BT.isSuppressOnSink()) {
+ BugReport *R = I;
for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) {
const ExplodedNode *N = I->getErrorNode();
if (N) {
- R = *I;
+ R = I;
bugReports.push_back(R);
}
}
@@ -1737,8 +1844,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
BugReport *exampleReport = 0;
for (; I != E; ++I) {
- R = *I;
- const ExplodedNode *errorNode = R->getErrorNode();
+ const ExplodedNode *errorNode = I->getErrorNode();
if (!errorNode)
continue;
@@ -1748,9 +1854,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
}
// No successors? By definition this nodes isn't post-dominated by a sink.
if (errorNode->succ_empty()) {
- bugReports.push_back(R);
+ bugReports.push_back(I);
if (!exampleReport)
- exampleReport = R;
+ exampleReport = I;
continue;
}
@@ -1774,9 +1880,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
if (Succ->succ_empty()) {
// If we found an end-of-path node that is not a sink.
if (!Succ->isSink()) {
- bugReports.push_back(R);
+ bugReports.push_back(I);
if (!exampleReport)
- exampleReport = R;
+ exampleReport = I;
WL.clear();
break;
}
@@ -1857,8 +1963,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
// Probably doesn't make a difference in practice.
BugType& BT = exampleReport->getBugType();
- llvm::OwningPtr<PathDiagnostic>
- D(new PathDiagnostic(exampleReport->getBugType().getName(),
+ OwningPtr<PathDiagnostic>
+ D(new PathDiagnostic(exampleReport->getDeclWithIssue(),
+ exampleReport->getBugType().getName(),
!PD || PD->useVerboseDescription()
? exampleReport->getDescription()
: exampleReport->getShortDescription(),
@@ -1866,9 +1973,6 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
if (!bugReports.empty())
GeneratePathDiagnostic(*D.get(), bugReports);
-
- if (IsCachedDiagnostic(exampleReport, D.get()))
- return;
// Get the meta data.
const BugReport::ExtraTextList &Meta =
@@ -1883,24 +1987,23 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
llvm::tie(Beg, End) = exampleReport->getRanges();
DiagnosticsEngine &Diag = getDiagnostic();
- // Search the description for '%', as that will be interpretted as a
- // format character by FormatDiagnostics.
- StringRef desc = exampleReport->getShortDescription();
- unsigned ErrorDiag;
- {
- llvm::SmallString<512> TmpStr;
+ if (!IsCachedDiagnostic(exampleReport, D.get())) {
+ // Search the description for '%', as that will be interpretted as a
+ // format character by FormatDiagnostics.
+ StringRef desc = exampleReport->getShortDescription();
+
+ SmallString<512> TmpStr;
llvm::raw_svector_ostream Out(TmpStr);
- for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I)
+ for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) {
if (*I == '%')
Out << "%%";
else
Out << *I;
+ }
Out.flush();
- ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr);
- }
+ unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr);
- {
DiagnosticBuilder diagBuilder = Diag.Report(
exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag);
for (BugReport::ranges_iterator I = Beg; I != End; ++I)
@@ -1911,25 +2014,21 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
if (!PD)
return;
- if (D->empty()) {
+ if (D->path.empty()) {
PathDiagnosticPiece *piece = new PathDiagnosticEventPiece(
exampleReport->getLocation(getSourceManager()),
exampleReport->getDescription());
+ for ( ; Beg != End; ++Beg)
+ piece->addRange(*Beg);
- for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
- D->push_back(piece);
+ D->getActivePath().push_back(piece);
}
PD->HandlePathDiagnostic(D.take());
}
-void BugReporter::EmitBasicReport(StringRef name, StringRef str,
- PathDiagnosticLocation Loc,
- SourceRange* RBeg, unsigned NumRanges) {
- EmitBasicReport(name, "", str, Loc, RBeg, NumRanges);
-}
-
-void BugReporter::EmitBasicReport(StringRef name,
+void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
+ StringRef name,
StringRef category,
StringRef str, PathDiagnosticLocation Loc,
SourceRange* RBeg, unsigned NumRanges) {
@@ -1937,13 +2036,14 @@ void BugReporter::EmitBasicReport(StringRef name,
// 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(name, category);
BugReport *R = new BugReport(*BT, str, Loc);
+ R->setDeclWithIssue(DeclWithIssue);
for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg);
EmitReport(R);
}
BugType *BugReporter::getBugTypeForName(StringRef name,
StringRef category) {
- llvm::SmallString<136> fullDesc;
+ SmallString<136> fullDesc;
llvm::raw_svector_ostream(fullDesc) << name << ":" << category;
llvm::StringMapEntry<BugType *> &
entry = StrBugTypes.GetOrCreateValue(fullDesc);
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 1abd8ba..6532486 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/SmallString.h"
using namespace clang;
using namespace ento;
@@ -84,26 +85,8 @@ PathDiagnosticPiece*
BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
BugReport &BR) {
- const ProgramPoint &PP = EndPathNode->getLocation();
- PathDiagnosticLocation L;
-
- if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) {
- const CFGBlock *block = BE->getBlock();
- if (block->getBlockID() == 0) {
- L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(),
- BRC.getSourceManager());
- }
- }
-
- if (!L.isValid()) {
- const Stmt *S = BR.getStmt();
-
- if (!S)
- return NULL;
-
- L = PathDiagnosticLocation(S, BRC.getSourceManager(),
- PP.getLocationContext());
- }
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
BugReport::ranges_iterator Beg, End;
llvm::tie(Beg, End) = BR.getRanges();
@@ -138,17 +121,20 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
if (!StoreSite) {
const ExplodedNode *Node = N, *Last = NULL;
- for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
+ for ( ; Node ; Node = Node->getFirstPred()) {
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
if (const PostStmt *P = Node->getLocationAs<PostStmt>())
if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
if (DS->getSingleDecl() == VR->getDecl()) {
+ // Record the last seen initialization point.
Last = Node;
break;
}
}
+ // Does the region still bind to value V? If not, we are done
+ // looking for store sites.
if (Node->getState()->getSVal(R) != V)
break;
}
@@ -165,7 +151,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
satisfied = true;
- llvm::SmallString<256> sbuf;
+ SmallString<256> sbuf;
llvm::raw_svector_ostream os(sbuf);
if (const PostStmt *PS = N->getLocationAs<PostStmt>()) {
@@ -301,7 +287,8 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
BugReporterVisitor *
bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
- const Stmt *S) {
+ const Stmt *S,
+ BugReport *report) {
if (!S || !N)
return 0;
@@ -321,25 +308,27 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
if (!N)
return 0;
- const ProgramState *state = N->getState();
-
- // Walk through lvalue-to-rvalue conversions.
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- const VarRegion *R =
- StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
+ ProgramStateRef state = N->getState();
- // What did we load?
- SVal V = state->getSVal(loc::MemRegionVal(R));
+ // Walk through lvalue-to-rvalue conversions.
+ const Expr *Ex = dyn_cast<Expr>(S);
+ if (Ex) {
+ Ex = Ex->IgnoreParenLValueCasts();
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const VarRegion *R =
+ StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
- if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)
- || V.isUndef()) {
+ // What did we load?
+ SVal V = state->getSVal(loc::MemRegionVal(R));
+ report->markInteresting(R);
+ report->markInteresting(V);
return new FindLastStoreBRVisitor(V, R);
}
}
}
- SVal V = state->getSValAsScalarOrLoc(S);
+ SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext());
// Uncomment this to find cases where we aren't properly getting the
// base value that was dereferenced.
@@ -353,7 +342,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
}
if (R) {
- assert(isa<SymbolicRegion>(R));
+ report->markInteresting(R);
return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false);
}
}
@@ -366,7 +355,7 @@ FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N,
const MemRegion *R) {
assert(R && "The memory region is null.");
- const ProgramState *state = N->getState();
+ ProgramStateRef state = N->getState();
SVal V = state->getSVal(R);
if (V.isUnknown())
return 0;
@@ -388,8 +377,8 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
const Expr *Receiver = ME->getInstanceReceiver();
if (!Receiver)
return 0;
- const ProgramState *state = N->getState();
- const SVal &V = state->getSVal(Receiver);
+ ProgramStateRef state = N->getState();
+ const SVal &V = state->getSVal(Receiver, N->getLocationContext());
const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V);
if (!DV)
return 0;
@@ -400,11 +389,11 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
// The receiver was nil, and hence the method was skipped.
// Register a BugReporterVisitor to issue a message telling us how
// the receiver was null.
- BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver));
+ BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR));
// Issue a message saying that the method was skipped.
PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
N->getLocationContext());
- return new PathDiagnosticEventPiece(L, "No method actually called "
+ return new PathDiagnosticEventPiece(L, "No method is called "
"because the receiver is nil");
}
@@ -419,7 +408,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
const Stmt *Head = WorkList.front();
WorkList.pop_front();
- const ProgramState *state = N->getState();
+ ProgramStateRef state = N->getState();
ProgramStateManager &StateMgr = state->getStateManager();
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) {
@@ -428,7 +417,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
// What did we load?
- SVal V = state->getSVal(S);
+ SVal V = state->getSVal(S, N->getLocationContext());
if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) {
// Register a new visitor with the BugReport.
@@ -450,11 +439,22 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *Prev,
BugReporterContext &BRC,
BugReport &BR) {
+ PathDiagnosticPiece *piece = VisitNodeImpl(N, Prev, BRC, BR);
+ if (PathDiagnosticEventPiece *ev =
+ dyn_cast_or_null<PathDiagnosticEventPiece>(piece))
+ ev->setPrunable(true, /* override */ false);
+ return piece;
+}
+
+PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
+ const ExplodedNode *Prev,
+ BugReporterContext &BRC,
+ BugReport &BR) {
const ProgramPoint &progPoint = N->getLocation();
- const ProgramState *CurrentState = N->getState();
- const ProgramState *PrevState = Prev->getState();
+ ProgramStateRef CurrentState = N->getState();
+ ProgramStateRef PrevState = Prev->getState();
// Compare the GDMs of the state, because that is where constraints
// are managed. Note that ensure that we only look at nodes that
@@ -468,7 +468,7 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N,
if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) {
const CFGBlock *srcBlk = BE->getSrc();
if (const Stmt *term = srcBlk->getTerminator())
- return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC);
+ return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC);
return 0;
}
@@ -482,10 +482,10 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N,
const ProgramPointTag *tag = PS->getTag();
if (tag == tags.first)
return VisitTrueTest(cast<Expr>(PS->getStmt()), true,
- BRC, N->getLocationContext());
+ BRC, BR, N);
if (tag == tags.second)
return VisitTrueTest(cast<Expr>(PS->getStmt()), false,
- BRC, N->getLocationContext());
+ BRC, BR, N);
return 0;
}
@@ -498,6 +498,7 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term,
const ExplodedNode *N,
const CFGBlock *srcBlk,
const CFGBlock *dstBlk,
+ BugReport &R,
BugReporterContext &BRC) {
const Expr *Cond = 0;
@@ -516,14 +517,15 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term,
assert(srcBlk->succ_size() == 2);
const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk;
return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()),
- tookTrue, BRC, N->getLocationContext());
+ tookTrue, BRC, R, N);
}
PathDiagnosticPiece *
ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
bool tookTrue,
BugReporterContext &BRC,
- const LocationContext *LC) {
+ BugReport &R,
+ const ExplodedNode *N) {
const Expr *Ex = Cond;
@@ -533,9 +535,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
default:
return 0;
case Stmt::BinaryOperatorClass:
- return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC);
+ return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC,
+ R, N);
case Stmt::DeclRefExprClass:
- return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC);
+ return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC,
+ R, N);
case Stmt::UnaryOperatorClass: {
const UnaryOperator *UO = cast<UnaryOperator>(Ex);
if (UO->getOpcode() == UO_LNot) {
@@ -550,14 +554,31 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
}
bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out,
- BugReporterContext &BRC) {
+ BugReporterContext &BRC,
+ BugReport &report,
+ const ExplodedNode *N,
+ llvm::Optional<bool> &prunable) {
const Expr *OriginalExpr = Ex;
Ex = Ex->IgnoreParenCasts();
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) {
const bool quotes = isa<VarDecl>(DR->getDecl());
- if (quotes)
+ if (quotes) {
Out << '\'';
+ const LocationContext *LCtx = N->getLocationContext();
+ const ProgramState *state = N->getState().getPtr();
+ if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()),
+ LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ prunable = false;
+ else {
+ const ProgramState *state = N->getState().getPtr();
+ SVal V = state->getSVal(R);
+ if (report.isInteresting(V))
+ prunable = false;
+ }
+ }
+ }
Out << DR->getDecl()->getDeclName().getAsString();
if (quotes)
Out << '\'';
@@ -591,31 +612,43 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
const BinaryOperator *BExpr,
const bool tookTrue,
BugReporterContext &BRC,
- const LocationContext *LC) {
+ BugReport &R,
+ const ExplodedNode *N) {
bool shouldInvert = false;
+ llvm::Optional<bool> shouldPrune;
- llvm::SmallString<128> LhsString, RhsString;
+ SmallString<128> LhsString, RhsString;
{
- llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
- const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC);
- const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC);
+ llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
+ const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC, R, N,
+ shouldPrune);
+ const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N,
+ shouldPrune);
shouldInvert = !isVarLHS && isVarRHS;
}
+ BinaryOperator::Opcode Op = BExpr->getOpcode();
+
+ if (BinaryOperator::isAssignmentOp(Op)) {
+ // For assignment operators, all that we care about is that the LHS
+ // evaluates to "true" or "false".
+ return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue,
+ BRC, R, N);
+ }
+
+ // For non-assignment operations, we require that we can understand
+ // both the LHS and RHS.
if (LhsString.empty() || RhsString.empty())
return 0;
-
- // Should we invert the strings if the LHS is not a variable name?
- llvm::SmallString<256> buf;
+ // Should we invert the strings if the LHS is not a variable name?
+ SmallString<256> buf;
llvm::raw_svector_ostream Out(buf);
Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is ";
// Do we need to invert the opcode?
- BinaryOperator::Opcode Op = BExpr->getOpcode();
-
if (shouldInvert)
switch (Op) {
default: break;
@@ -637,7 +670,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
return 0;
}
- switch (BExpr->getOpcode()) {
+ switch (Op) {
case BO_EQ:
Out << "equal to ";
break;
@@ -650,9 +683,55 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
}
Out << (shouldInvert ? LhsString : RhsString);
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ PathDiagnosticEventPiece *event =
+ new PathDiagnosticEventPiece(Loc, Out.str());
+ if (shouldPrune.hasValue())
+ event->setPrunable(shouldPrune.getValue());
+ return event;
+}
- PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC);
- return new PathDiagnosticEventPiece(Loc, Out.str());
+PathDiagnosticPiece *
+ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
+ const Expr *CondVarExpr,
+ const bool tookTrue,
+ BugReporterContext &BRC,
+ BugReport &report,
+ const ExplodedNode *N) {
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ Out << "Assuming " << LhsString << " is ";
+
+ QualType Ty = CondVarExpr->getType();
+
+ if (Ty->isPointerType())
+ Out << (tookTrue ? "not null" : "null");
+ else if (Ty->isObjCObjectPointerType())
+ Out << (tookTrue ? "not nil" : "nil");
+ else if (Ty->isBooleanType())
+ Out << (tookTrue ? "true" : "false");
+ else if (Ty->isIntegerType())
+ Out << (tookTrue ? "non-zero" : "zero");
+ else
+ return 0;
+
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx);
+ PathDiagnosticEventPiece *event =
+ new PathDiagnosticEventPiece(Loc, Out.str());
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const ProgramState *state = N->getState().getPtr();
+ if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ event->setPrunable(false);
+ }
+ }
+ }
+
+ return event;
}
PathDiagnosticPiece *
@@ -660,13 +739,14 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
const DeclRefExpr *DR,
const bool tookTrue,
BugReporterContext &BRC,
- const LocationContext *LC) {
+ BugReport &report,
+ const ExplodedNode *N) {
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (!VD)
return 0;
- llvm::SmallString<256> Buf;
+ SmallString<256> Buf;
llvm::raw_svector_ostream Out(Buf);
Out << "Assuming '";
@@ -684,6 +764,21 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
else
return 0;
- PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC);
- return new PathDiagnosticEventPiece(Loc, Out.str());
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ PathDiagnosticEventPiece *event =
+ new PathDiagnosticEventPiece(Loc, Out.str());
+
+ const ProgramState *state = N->getState().getPtr();
+ if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ event->setPrunable(false);
+ else {
+ SVal V = state->getSVal(R);
+ if (report.isInteresting(V))
+ event->setPrunable(false);
+ }
+ }
+ return event;
}
+
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index 391a781..1ea25bd 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support)
set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite)
add_clang_library(clangStaticAnalyzerCore
- AggExprVisitor.cpp
AnalysisManager.cpp
BasicConstraintManager.cpp
BasicValueFactory.cpp
@@ -23,6 +22,7 @@ add_clang_library(clangStaticAnalyzerCore
ExprEngineCXX.cpp
ExprEngineCallAndReturn.cpp
ExprEngineObjC.cpp
+ FunctionSummary.cpp
HTMLDiagnostics.cpp
MemRegion.cpp
ObjCMessage.cpp
@@ -36,6 +36,7 @@ add_clang_library(clangStaticAnalyzerCore
SimpleConstraintManager.cpp
SimpleSValBuilder.cpp
Store.cpp
+ SubEngine.cpp
SymbolManager.cpp
TextPathDiagnostics.cpp
)
diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp
index a3bf2c2..07e0aac 100644
--- a/lib/StaticAnalyzer/Core/Checker.cpp
+++ b/lib/StaticAnalyzer/Core/Checker.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
using namespace clang;
@@ -20,3 +21,11 @@ StringRef CheckerBase::getTagDescription() const {
// FIXME: We want to return the package + name of the checker here.
return "A Checker";
}
+
+void Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
+ check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
+ check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
+ check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
+ check::_VoidCheck, check::_VoidCheck, check::_VoidCheck,
+ check::_VoidCheck, check::_VoidCheck, check::_VoidCheck
+ >::anchor() { }
diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 5356edc..0a047d9 100644
--- a/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -13,21 +13,71 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Lex/Lexer.h"
+
using namespace clang;
using namespace ento;
-CheckerContext::~CheckerContext() {
- // Do we need to autotransition? 'Dst' can get populated in a variety of
- // ways, including 'addTransition()' adding the predecessor node to Dst
- // without actually generated a new node. We also shouldn't autotransition
- // if we are building sinks or we generated a node and decided to not
- // add it as a transition.
- if (Dst.size() == size && !B.BuildSinks && !B.hasGeneratedNode) {
- if (ST && ST != Pred->getState()) {
- static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto");
- addTransition(ST, &autoTransitionTag);
- }
- else
- Dst.Add(Pred);
+const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const {
+ ProgramStateRef State = getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = State->getSVal(Callee, Pred->getLocationContext());
+ return L.getAsFunctionDecl();
+}
+
+StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const {
+ if (!FunDecl)
+ return StringRef();
+ IdentifierInfo *funI = FunDecl->getIdentifier();
+ if (!funI)
+ return StringRef();
+ return funI->getName();
+}
+
+
+bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
+ StringRef Name) {
+ return isCLibraryFunction(FD, Name, getASTContext());
+}
+
+bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
+ StringRef Name, ASTContext &Context) {
+ // To avoid false positives (Ex: finding user defined functions with
+ // similar names), only perform fuzzy name matching when it's a builtin.
+ // Using a string compare is slow, we might want to switch on BuiltinID here.
+ unsigned BId = FD->getBuiltinID();
+ if (BId != 0) {
+ StringRef BName = Context.BuiltinInfo.GetName(BId);
+ if (BName.find(Name) != StringRef::npos)
+ return true;
}
+
+ const IdentifierInfo *II = FD->getIdentifier();
+ // If this is a special C++ name without IdentifierInfo, it can't be a
+ // C library function.
+ if (!II)
+ return false;
+
+ StringRef FName = II->getName();
+ if (FName.equals(Name))
+ return true;
+
+ if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos))
+ return true;
+
+ if (FName.startswith("__") && FName.endswith("_chk") &&
+ FName.find(Name) != StringRef::npos)
+ return true;
+
+ return false;
+}
+
+StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) {
+ if (Loc.isMacroID())
+ return Lexer::getImmediateMacroName(Loc, getSourceManager(),
+ getLangOpts());
+ SmallVector<char, 16> buf;
+ return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts());
}
+
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index acacfb0..0bcc343 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -93,6 +93,9 @@ template <typename CHECK_CTX>
static void expandGraphWithCheckers(CHECK_CTX checkCtx,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src) {
+ const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext();
+ if (Src.empty())
+ return;
typename CHECK_CTX::CheckersTy::const_iterator
I = checkCtx.checkers_begin(), E = checkCtx.checkers_end();
@@ -113,9 +116,15 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx,
CurrSet->clear();
}
+ NodeBuilder B(*PrevSet, *CurrSet, BldrCtx);
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
- NI != NE; ++NI)
- checkCtx.runChecker(*I, *CurrSet, *NI);
+ NI != NE; ++NI) {
+ checkCtx.runChecker(*I, B, *NI);
+ }
+
+ // If all the produced transitions are sinks, stop.
+ if (CurrSet->empty())
+ return;
// Update which NodeSet is the current one.
PrevSet = CurrSet;
@@ -129,23 +138,24 @@ namespace {
const CheckersTy &Checkers;
const Stmt *S;
ExprEngine &Eng;
+ bool wasInlined;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
- const Stmt *s, ExprEngine &eng)
- : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { }
+ const Stmt *s, ExprEngine &eng, bool wasInlined = false)
+ : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
+ wasInlined(wasInlined) {}
void runChecker(CheckerManager::CheckStmtFunc checkFn,
- ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
// FIXME: Remove respondsToCallback from CheckerContext;
ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
ProgramPoint::PostStmtKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
-
+ CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
checkFn(S, C);
}
};
@@ -156,9 +166,10 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const Stmt *S,
- ExprEngine &Eng) {
+ ExprEngine &Eng,
+ bool wasInlined) {
CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
- S, Eng);
+ S, Eng, wasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -178,12 +189,14 @@ namespace {
: IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
- ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
ProgramPoint::PostStmtKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(Msg.getOriginExpr(),
- K, Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(Msg.getMessageExpr(),
+ K, Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
checkFn(Msg, C);
}
@@ -209,35 +222,44 @@ namespace {
const CheckersTy &Checkers;
SVal Loc;
bool IsLoad;
- const Stmt *S;
+ const Stmt *NodeEx; /* Will become a CFGStmt */
+ const Stmt *BoundEx;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckLocationContext(const CheckersTy &checkers,
- SVal loc, bool isLoad, const Stmt *s, ExprEngine &eng)
- : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), Eng(eng) { }
+ SVal loc, bool isLoad, const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &eng)
+ : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx),
+ BoundEx(BoundEx), Eng(eng) {}
void runChecker(CheckerManager::CheckLocationFunc checkFn,
- ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind :
ProgramPoint::PreStoreKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
- Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
-
- checkFn(Loc, IsLoad, S, C);
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(NodeEx, K,
+ Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Loc, IsLoad, BoundEx, C);
}
};
}
/// \brief Run checkers for load/store of a location.
+
void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SVal location, bool isLoad,
- const Stmt *S, ExprEngine &Eng) {
- CheckLocationContext C(LocationCheckers, location, isLoad, S, Eng);
+ const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &Eng) {
+ CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx,
+ BoundEx, Eng);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -249,20 +271,21 @@ namespace {
SVal Val;
const Stmt *S;
ExprEngine &Eng;
+ ProgramPoint::Kind PointKind;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckBindContext(const CheckersTy &checkers,
- SVal loc, SVal val, const Stmt *s, ExprEngine &eng)
- : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { }
+ SVal loc, SVal val, const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind PK)
+ : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PointKind(PK) {}
void runChecker(CheckerManager::CheckBindFunc checkFn,
- ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- ProgramPoint::Kind K = ProgramPoint::PreStmtKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, PointKind,
Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+ CheckerContext C(Bldr, Eng, Pred, L);
checkFn(Loc, Val, S, C);
}
@@ -273,8 +296,9 @@ namespace {
void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SVal location, SVal val,
- const Stmt *S, ExprEngine &Eng) {
- CheckBindContext C(BindCheckers, location, val, S, Eng);
+ const Stmt *S, ExprEngine &Eng,
+ ProgramPoint::Kind PointKind) {
+ CheckBindContext C(BindCheckers, location, val, S, Eng, PointKind);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -286,27 +310,65 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
}
/// \brief Run checkers for end of path.
-void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B,
+// Note, We do not chain the checker output (like in expandGraphWithCheckers)
+// for this callback since end of path nodes are expected to be final.
+void CheckerManager::runCheckersForEndPath(NodeBuilderContext &BC,
+ ExplodedNodeSet &Dst,
ExprEngine &Eng) {
+ ExplodedNode *Pred = BC.Pred;
+
+ // We define the builder outside of the loop bacause if at least one checkers
+ // creates a sucsessor for Pred, we do not need to generate an
+ // autotransition for it.
+ NodeBuilder Bldr(Pred, Dst, BC);
for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) {
- CheckEndPathFunc fn = EndPathCheckers[i];
- EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker);
- fn(specialB, Eng);
+ CheckEndPathFunc checkFn = EndPathCheckers[i];
+
+ const ProgramPoint &L = BlockEntrance(BC.Block,
+ Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(C);
}
}
+namespace {
+ struct CheckBranchConditionContext {
+ typedef std::vector<CheckerManager::CheckBranchConditionFunc> CheckersTy;
+ const CheckersTy &Checkers;
+ const Stmt *Condition;
+ ExprEngine &Eng;
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ CheckBranchConditionContext(const CheckersTy &checkers,
+ const Stmt *Cond, ExprEngine &eng)
+ : Checkers(checkers), Condition(Cond), Eng(eng) {}
+
+ void runChecker(CheckerManager::CheckBranchConditionFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Condition, C);
+ }
+ };
+}
+
/// \brief Run checkers for branch condition.
-void CheckerManager::runCheckersForBranchCondition(const Stmt *condition,
- BranchNodeBuilder &B,
+void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
ExprEngine &Eng) {
- for (unsigned i = 0, e = BranchConditionCheckers.size(); i != e; ++i) {
- CheckBranchConditionFunc fn = BranchConditionCheckers[i];
- fn(condition, B, Eng);
- }
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
}
/// \brief Run checkers for live symbols.
-void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state,
+void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
SymbolReaper &SymReaper) {
for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i)
LiveSymbolsCheckers[i](state, SymReaper);
@@ -328,11 +390,11 @@ namespace {
: Checkers(checkers), SR(sr), S(s), Eng(eng) { }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
- ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+ CheckerContext C(Bldr, Eng, Pred, L);
checkFn(SR, C);
}
@@ -350,7 +412,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
}
/// \brief True if at least one checker wants to check region changes.
-bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) {
+bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i)
if (RegionChangesCheckers[i].WantUpdateFn(state))
return true;
@@ -359,25 +421,26 @@ bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) {
}
/// \brief Run checkers for region changes.
-const ProgramState *
-CheckerManager::runCheckersForRegionChanges(const ProgramState *state,
+ProgramStateRef
+CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions) {
+ ArrayRef<const MemRegion *> Regions,
+ const CallOrObjCMessage *Call) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return NULL;
state = RegionChangesCheckers[i].CheckFn(state, invalidated,
- ExplicitRegions, Regions);
+ ExplicitRegions, Regions, Call);
}
return state;
}
/// \brief Run checkers for handling assumptions on symbolic values.
-const ProgramState *
-CheckerManager::runCheckersForEvalAssume(const ProgramState *state,
+ProgramStateRef
+CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
SVal Cond, bool Assumption) {
for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
@@ -437,11 +500,12 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
}
#endif
+ ExplodedNodeSet checkDst;
+ NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
// Next, check if any of the EvalCall callbacks can evaluate the call.
for (std::vector<EvalCallFunc>::iterator
EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end();
EI != EE; ++EI) {
- ExplodedNodeSet checkDst;
ProgramPoint::Kind K = ProgramPoint::PostStmtKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K,
Pred->getLocationContext(), EI->Checker);
@@ -449,7 +513,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
{ // CheckerContext generates transitions(populates checkDest) on
// destruction, so introduce the scope to make sure it gets properly
// populated.
- CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, L, 0);
+ CheckerContext C(B, Eng, Pred, L);
evaluated = (*EI)(CE, C);
}
assert(!(evaluated && anyEvaluated)
@@ -483,7 +547,7 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
}
void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
- const ProgramState *State,
+ ProgramStateRef State,
const char *NL, const char *Sep) {
for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator
I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I)
diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
index 13401ac..9791e2ec 100644
--- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
@@ -9,12 +9,13 @@
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
+#include "llvm/ADT/SetVector.h"
using namespace clang;
using namespace ento;
static const char PackageSeparator = '.';
-typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
+typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
@@ -72,7 +73,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers,
if (opt.isEnabled())
collected.insert(&*i);
else
- collected.erase(&*i);
+ collected.remove(&*i);
}
}
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 5252198..eb986af 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -12,6 +12,8 @@
//
//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "CoreEngine"
+
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
@@ -20,9 +22,16 @@
#include "clang/AST/StmtCXX.h"
#include "llvm/Support/Casting.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Statistic.h"
+
using namespace clang;
using namespace ento;
+STATISTIC(NumReachedMaxSteps,
+ "The # of times we reached the max number of steps.");
+STATISTIC(NumPathsExplored,
+ "The # of paths explored by the analyzer.");
+
//===----------------------------------------------------------------------===//
// Worklist classes for exploration of reachable states.
//===----------------------------------------------------------------------===//
@@ -152,7 +161,7 @@ WorkList* WorkList::makeBFSBlockDFSContents() {
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
- const ProgramState *InitState) {
+ ProgramStateRef InitState) {
if (G->num_roots() == 0) { // Initialize the analysis by constructing
// the root if none exists.
@@ -165,6 +174,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
assert (Entry->succ_size() == 1 &&
"Entry block must have 1 successor.");
+ // Mark the entry block as visited.
+ FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(),
+ L->getDecl(),
+ L->getCFG()->getNumBlockIDs());
+
// Get the solitary successor.
const CFGBlock *Succ = *(Entry->succ_begin());
@@ -187,8 +201,10 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
while (WList->hasWork()) {
if (!UnlimitedSteps) {
- if (Steps == 0)
+ if (Steps == 0) {
+ NumReachedMaxSteps++;
break;
+ }
--Steps;
}
@@ -200,67 +216,80 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
// Retrieve the node.
ExplodedNode *Node = WU.getNode();
- // Dispatch on the location type.
- switch (Node->getLocation().getKind()) {
- case ProgramPoint::BlockEdgeKind:
- HandleBlockEdge(cast<BlockEdge>(Node->getLocation()), Node);
- break;
-
- case ProgramPoint::BlockEntranceKind:
- HandleBlockEntrance(cast<BlockEntrance>(Node->getLocation()), Node);
- break;
-
- case ProgramPoint::BlockExitKind:
- assert (false && "BlockExit location never occur in forward analysis.");
- break;
+ dispatchWorkItem(Node, Node->getLocation(), WU);
+ }
+ SubEng.processEndWorklist(hasWorkRemaining());
+ return WList->hasWork();
+}
- case ProgramPoint::CallEnterKind:
- HandleCallEnter(cast<CallEnter>(Node->getLocation()), WU.getBlock(),
- WU.getIndex(), Node);
- break;
+void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
+ const WorkListUnit& WU) {
+ // Dispatch on the location type.
+ switch (Loc.getKind()) {
+ case ProgramPoint::BlockEdgeKind:
+ HandleBlockEdge(cast<BlockEdge>(Loc), Pred);
+ break;
+
+ case ProgramPoint::BlockEntranceKind:
+ HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred);
+ break;
+
+ case ProgramPoint::BlockExitKind:
+ assert (false && "BlockExit location never occur in forward analysis.");
+ break;
+
+ case ProgramPoint::CallEnterKind: {
+ CallEnter CEnter = cast<CallEnter>(Loc);
+ if (AnalyzedCallees)
+ if (const CallExpr* CE =
+ dyn_cast_or_null<CallExpr>(CEnter.getCallExpr()))
+ if (const Decl *CD = CE->getCalleeDecl())
+ AnalyzedCallees->insert(CD);
+ SubEng.processCallEnter(CEnter, Pred);
+ break;
+ }
- case ProgramPoint::CallExitKind:
- HandleCallExit(cast<CallExit>(Node->getLocation()), Node);
- break;
+ case ProgramPoint::CallExitKind:
+ SubEng.processCallExit(Pred);
+ break;
- default:
- assert(isa<PostStmt>(Node->getLocation()) ||
- isa<PostInitializer>(Node->getLocation()));
- HandlePostStmt(WU.getBlock(), WU.getIndex(), Node);
- break;
+ case ProgramPoint::EpsilonKind: {
+ assert(Pred->hasSinglePred() &&
+ "Assume epsilon has exactly one predecessor by construction");
+ ExplodedNode *PNode = Pred->getFirstPred();
+ dispatchWorkItem(Pred, PNode->getLocation(), WU);
+ break;
}
+ default:
+ assert(isa<PostStmt>(Loc) ||
+ isa<PostInitializer>(Loc));
+ HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred);
+ break;
}
-
- SubEng.processEndWorklist(hasWorkRemaining());
- return WList->hasWork();
}
-void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
+bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
unsigned Steps,
- const ProgramState *InitState,
+ ProgramStateRef InitState,
ExplodedNodeSet &Dst) {
- ExecuteWorkList(L, Steps, InitState);
- for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(),
- E = G->EndNodes.end(); I != E; ++I) {
+ bool DidNotFinish = ExecuteWorkList(L, Steps, InitState);
+ for (ExplodedGraph::eop_iterator I = G->eop_begin(),
+ E = G->eop_end(); I != E; ++I) {
Dst.Add(*I);
}
-}
-
-void CoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block,
- unsigned Index, ExplodedNode *Pred) {
- CallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(),
- L.getCalleeContext(), Block, Index);
- SubEng.processCallEnter(Builder);
-}
-
-void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) {
- CallExitNodeBuilder Builder(*this, Pred);
- SubEng.processCallExit(Builder);
+ return DidNotFinish;
}
void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
const CFGBlock *Blk = L.getDst();
+ NodeBuilderContext BuilderCtx(*this, Blk, Pred);
+
+ // Mark this block as visited.
+ const LocationContext *LC = Pred->getLocationContext();
+ FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(),
+ LC->getDecl(),
+ LC->getCFG()->getNumBlockIDs());
// Check if we are entering the EXIT block.
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
@@ -269,53 +298,42 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
&& "EXIT block cannot contain Stmts.");
// Process the final state transition.
- EndOfFunctionNodeBuilder Builder(Blk, Pred, this);
- SubEng.processEndOfFunction(Builder);
+ SubEng.processEndOfFunction(BuilderCtx);
// This path is done. Don't enqueue any more nodes.
return;
}
- // Call into the subengine to process entering the CFGBlock.
+ // Call into the SubEngine to process entering the CFGBlock.
ExplodedNodeSet dstNodes;
BlockEntrance BE(Blk, Pred->getLocationContext());
- GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE);
- SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder);
+ NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
+ SubEng.processCFGBlockEntrance(L, nodeBuilder);
- if (dstNodes.empty()) {
- if (!nodeBuilder.hasGeneratedNode) {
- // Auto-generate a node and enqueue it to the worklist.
- generateNode(BE, Pred->State, Pred);
- }
- }
- else {
- for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end();
- I != E; ++I) {
- WList->enqueue(*I);
- }
+ // Auto-generate a node.
+ if (!nodeBuilder.hasGeneratedNodes()) {
+ nodeBuilder.generateNode(Pred->State, Pred);
}
- for (SmallVectorImpl<ExplodedNode*>::const_iterator
- I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end();
- I != E; ++I) {
- blocksExhausted.push_back(std::make_pair(L, *I));
- }
+ // Enqueue nodes onto the worklist.
+ enqueue(dstNodes);
}
void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
ExplodedNode *Pred) {
// Increment the block counter.
+ const LocationContext *LC = Pred->getLocationContext();
+ unsigned BlockId = L.getBlock()->getBlockID();
BlockCounter Counter = WList->getBlockCounter();
- Counter = BCounterFactory.IncrementCount(Counter,
- Pred->getLocationContext()->getCurrentStackFrame(),
- L.getBlock()->getBlockID());
+ Counter = BCounterFactory.IncrementCount(Counter, LC->getCurrentStackFrame(),
+ BlockId);
WList->setBlockCounter(Counter);
// Process the entrance of the block.
if (CFGElement E = L.getFirstElement()) {
- StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this);
- SubEng.processCFGElement(E, Builder);
+ NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
+ SubEng.processCFGElement(E, Pred, 0, &Ctx);
}
else
HandleBlockExit(L.getBlock(), Pred);
@@ -345,6 +363,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred);
return;
+ case Stmt::CXXTryStmtClass: {
+ // Generate a node for each of the successors.
+ // Our logic for EH analysis can certainly be improved.
+ for (CFGBlock::const_succ_iterator it = B->succ_begin(),
+ et = B->succ_end(); it != et; ++it) {
+ if (const CFGBlock *succ = *it) {
+ generateNode(BlockEdge(B, succ, Pred->getLocationContext()),
+ Pred->State, Pred);
+ }
+ }
+ return;
+ }
+
case Stmt::DoStmtClass:
HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
return;
@@ -417,31 +448,35 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
const CFGBlock * B, ExplodedNode *Pred) {
assert(B->succ_size() == 2);
- BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1),
- Pred, this);
- SubEng.processBranch(Cond, Term, Builder);
+ NodeBuilderContext Ctx(*this, B, Pred);
+ ExplodedNodeSet Dst;
+ SubEng.processBranch(Cond, Term, Ctx, Pred, Dst,
+ *(B->succ_begin()), *(B->succ_begin()+1));
+ // Enqueue the new frontier onto the worklist.
+ enqueue(Dst);
}
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
ExplodedNode *Pred) {
- assert (!B->empty());
+ assert(B);
+ assert(!B->empty());
if (StmtIdx == B->size())
HandleBlockExit(B, Pred);
else {
- StmtNodeBuilder Builder(B, StmtIdx, Pred, this);
- SubEng.processCFGElement((*B)[StmtIdx], Builder);
+ NodeBuilderContext Ctx(*this, B, Pred);
+ SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
}
}
/// generateNode - Utility method to generate nodes, hook up successors,
/// and add nodes to the worklist.
void CoreEngine::generateNode(const ProgramPoint &Loc,
- const ProgramState *State,
+ ProgramStateRef State,
ExplodedNode *Pred) {
bool IsNew;
- ExplodedNode *Node = G->getNode(Loc, State, &IsNew);
+ ExplodedNode *Node = G->getNode(Loc, State, false, &IsNew);
if (Pred)
Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor.
@@ -454,225 +489,181 @@ void CoreEngine::generateNode(const ProgramPoint &Loc,
if (IsNew) WList->enqueue(Node);
}
-ExplodedNode *
-GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state,
- ExplodedNode *pred,
- ProgramPoint programPoint,
- bool asSink) {
-
- hasGeneratedNode = true;
- bool isNew;
- ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew);
- if (pred)
- node->addPredecessor(pred, engine.getGraph());
- if (isNew) {
- if (asSink) {
- node->markAsSink();
- sinksGenerated.push_back(node);
- }
- return node;
- }
- return 0;
-}
-
-StmtNodeBuilder::StmtNodeBuilder(const CFGBlock *b,
- unsigned idx,
- ExplodedNode *N,
- CoreEngine* e)
- : Eng(*e), B(*b), Idx(idx), Pred(N),
- PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false),
- PointKind(ProgramPoint::PostStmtKind), Tag(0) {
- Deferred.insert(N);
-}
-
-StmtNodeBuilder::~StmtNodeBuilder() {
- for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I)
- if (!(*I)->isSink())
- GenerateAutoTransition(*I);
-}
-
-void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) {
+void CoreEngine::enqueueStmtNode(ExplodedNode *N,
+ const CFGBlock *Block, unsigned Idx) {
+ assert(Block);
assert (!N->isSink());
// Check if this node entered a callee.
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);
+ WList->enqueue(N, Block, Idx);
return;
}
// Do not create extra nodes. Move to the next CFG element.
if (isa<PostInitializer>(N->getLocation())) {
- Eng.WList->enqueue(N, &B, Idx+1);
+ WList->enqueue(N, Block, Idx+1);
return;
}
- PostStmt Loc(getStmt(), N->getLocationContext());
+ if (isa<EpsilonPoint>(N->getLocation())) {
+ WList->enqueue(N, Block, Idx);
+ return;
+ }
+
+ const CFGStmt *CS = (*Block)[Idx].getAs<CFGStmt>();
+ const Stmt *St = CS ? CS->getStmt() : 0;
+ PostStmt Loc(St, N->getLocationContext());
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);
+ WList->enqueue(N, Block, Idx+1);
return;
}
bool IsNew;
- ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew);
- Succ->addPredecessor(N, *Eng.G);
+ ExplodedNode *Succ = G->getNode(Loc, N->getState(), false, &IsNew);
+ Succ->addPredecessor(N, *G);
if (IsNew)
- Eng.WList->enqueue(Succ, &B, Idx+1);
+ WList->enqueue(Succ, Block, Idx+1);
}
-ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst,
- const Stmt *S,
- ExplodedNode *Pred,
- const ProgramState *St,
- ProgramPoint::Kind K) {
-
- ExplodedNode *N = generateNode(S, St, Pred, K);
+ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) {
+ // Create a CallExit node and enqueue it.
+ const StackFrameContext *LocCtx
+ = cast<StackFrameContext>(N->getLocationContext());
+ const Stmt *CE = LocCtx->getCallSite();
- if (N) {
- if (BuildSinks)
- N->markAsSink();
- else
- Dst.Add(N);
- }
-
- return N;
-}
+ // Use the the callee location context.
+ CallExit Loc(CE, LocCtx);
-ExplodedNode*
-StmtNodeBuilder::generateNodeInternal(const Stmt *S,
- const ProgramState *state,
- ExplodedNode *Pred,
- ProgramPoint::Kind K,
- const ProgramPointTag *tag) {
-
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
- Pred->getLocationContext(), tag);
- return generateNodeInternal(L, state, Pred);
+ bool isNew;
+ ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew);
+ Node->addPredecessor(N, *G);
+ return isNew ? Node : 0;
}
-ExplodedNode*
-StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc,
- const ProgramState *State,
- ExplodedNode *Pred) {
- bool IsNew;
- ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew);
- N->addPredecessor(Pred, *Eng.G);
- Deferred.erase(Pred);
- if (IsNew) {
- Deferred.insert(N);
- return N;
+void CoreEngine::enqueue(ExplodedNodeSet &Set) {
+ for (ExplodedNodeSet::iterator I = Set.begin(),
+ E = Set.end(); I != E; ++I) {
+ WList->enqueue(*I);
}
+}
- return NULL;
+void CoreEngine::enqueue(ExplodedNodeSet &Set,
+ const CFGBlock *Block, unsigned Idx) {
+ for (ExplodedNodeSet::iterator I = Set.begin(),
+ E = Set.end(); I != E; ++I) {
+ enqueueStmtNode(*I, Block, Idx);
+ }
}
-// This function generate a new ExplodedNode but not a new branch(block edge).
-ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition,
- const ProgramState *State) {
- bool IsNew;
-
- ExplodedNode *Succ
- = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State,
- &IsNew);
-
- Succ->addPredecessor(Pred, *Eng.G);
-
- Pred = Succ;
-
- if (IsNew)
- return Succ;
-
- return NULL;
+void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) {
+ for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) {
+ ExplodedNode *N = *I;
+ // If we are in an inlined call, generate CallExit node.
+ if (N->getLocationContext()->getParent()) {
+ N = generateCallExitNode(N);
+ if (N)
+ WList->enqueue(N);
+ } else {
+ G->addEndOfPath(N);
+ NumPathsExplored++;
+ }
+ }
}
-ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State,
- bool branch) {
- // If the branch has been marked infeasible we should not generate a node.
- if (!isFeasible(branch))
- return NULL;
+void NodeBuilder::anchor() { }
+ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc,
+ ProgramStateRef State,
+ ExplodedNode *FromN,
+ bool MarkAsSink) {
+ HasGeneratedNodes = true;
bool IsNew;
+ ExplodedNode *N = C.Eng.G->getNode(Loc, State, MarkAsSink, &IsNew);
+ N->addPredecessor(FromN, *C.Eng.G);
+ Frontier.erase(FromN);
- ExplodedNode *Succ =
- Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()),
- State, &IsNew);
+ if (!IsNew)
+ return 0;
- Succ->addPredecessor(Pred, *Eng.G);
+ if (!MarkAsSink)
+ Frontier.Add(N);
- if (branch)
- GeneratedTrue = true;
- else
- GeneratedFalse = true;
+ return N;
+}
- if (IsNew) {
- Deferred.push_back(Succ);
- return Succ;
- }
+void NodeBuilderWithSinks::anchor() { }
- return NULL;
+StmtNodeBuilder::~StmtNodeBuilder() {
+ if (EnclosingBldr)
+ for (ExplodedNodeSet::iterator I = Frontier.begin(),
+ E = Frontier.end(); I != E; ++I )
+ EnclosingBldr->addNodes(*I);
}
-BranchNodeBuilder::~BranchNodeBuilder() {
- if (!GeneratedTrue) generateNode(Pred->State, true);
- if (!GeneratedFalse) generateNode(Pred->State, false);
+void BranchNodeBuilder::anchor() { }
- for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I)
- if (!(*I)->isSink()) Eng.WList->enqueue(*I);
-}
+ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State,
+ bool branch,
+ ExplodedNode *NodePred) {
+ // If the branch has been marked infeasible we should not generate a node.
+ if (!isFeasible(branch))
+ return NULL;
+ ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF,
+ NodePred->getLocationContext());
+ ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred);
+ return Succ;
+}
ExplodedNode*
IndirectGotoNodeBuilder::generateNode(const iterator &I,
- const ProgramState *St,
- bool isSink) {
+ ProgramStateRef St,
+ bool IsSink) {
bool IsNew;
-
ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
- Pred->getLocationContext()), St, &IsNew);
-
+ Pred->getLocationContext()), St,
+ IsSink, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
- if (IsNew) {
+ if (!IsNew)
+ return 0;
- if (isSink)
- Succ->markAsSink();
- else
- Eng.WList->enqueue(Succ);
-
- return Succ;
- }
+ if (!IsSink)
+ Eng.WList->enqueue(Succ);
- return NULL;
+ return Succ;
}
ExplodedNode*
SwitchNodeBuilder::generateCaseStmtNode(const iterator &I,
- const ProgramState *St) {
+ ProgramStateRef St) {
bool IsNew;
ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
- Pred->getLocationContext()),
- St, &IsNew);
+ Pred->getLocationContext()), St,
+ false, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
- if (IsNew) {
- Eng.WList->enqueue(Succ);
- return Succ;
- }
- return NULL;
+ if (!IsNew)
+ return 0;
+
+ Eng.WList->enqueue(Succ);
+ return Succ;
}
ExplodedNode*
-SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St,
- bool isSink) {
+SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St,
+ bool IsSink) {
// Get the block for the default case.
assert(Src->succ_rbegin() != Src->succ_rend());
CFGBlock *DefaultBlock = *Src->succ_rbegin();
@@ -683,145 +674,16 @@ SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St,
return NULL;
bool IsNew;
-
ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock,
- Pred->getLocationContext()), St, &IsNew);
+ Pred->getLocationContext()), St,
+ IsSink, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
- if (IsNew) {
- if (isSink)
- Succ->markAsSink();
- else
- Eng.WList->enqueue(Succ);
-
- return Succ;
- }
-
- return NULL;
-}
-
-EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() {
- // Auto-generate an EOP node if one has not been generated.
- if (!hasGeneratedNode) {
- // If we are in an inlined call, generate CallExit node.
- if (Pred->getLocationContext()->getParent())
- GenerateCallExitNode(Pred->State);
- else
- generateNode(Pred->State);
- }
-}
-
-ExplodedNode*
-EndOfFunctionNodeBuilder::generateNode(const ProgramState *State,
- ExplodedNode *P,
- const ProgramPointTag *tag) {
- hasGeneratedNode = true;
- bool IsNew;
-
- ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B,
- Pred->getLocationContext(), tag ? tag : Tag),
- State, &IsNew);
-
- Node->addPredecessor(P ? P : Pred, *Eng.G);
-
- if (IsNew) {
- Eng.G->addEndOfPath(Node);
- return Node;
- }
-
- return NULL;
-}
-
-void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) {
- hasGeneratedNode = true;
- // Create a CallExit node and enqueue it.
- const StackFrameContext *LocCtx
- = cast<StackFrameContext>(Pred->getLocationContext());
- const Stmt *CE = LocCtx->getCallSite();
+ if (!IsNew)
+ return 0;
- // Use the the callee location context.
- CallExit Loc(CE, LocCtx);
-
- bool isNew;
- ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew);
- Node->addPredecessor(Pred, *Eng.G);
-
- if (isNew)
- Eng.WList->enqueue(Node);
-}
-
-
-void CallEnterNodeBuilder::generateNode(const ProgramState *state) {
- // 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.SubEng.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);
-
- // Create the new engine.
- // FIXME: This cast isn't really safe.
- bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled();
- ExprEngine NewEng(AMgr, GCEnabled);
-
- // Create the new LocationContext.
- AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(),
- CalleeCtx->getTranslationUnit());
- const StackFrameContext *OldLocCtx = CalleeCtx;
- const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx,
- OldLocCtx->getParent(),
- OldLocCtx->getCallSite(),
- OldLocCtx->getCallSiteBlock(),
- OldLocCtx->getIndex());
-
- // Now create an initial state for the new engine.
- const ProgramState *NewState =
- NewEng.getStateManager().MarshalState(state, NewLocCtx);
- ExplodedNodeSet ReturnNodes;
- NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(),
- NewState, ReturnNodes);
- return;
- }
-
- // Get the callee entry block.
- const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry());
- assert(Entry->empty());
- assert(Entry->succ_size() == 1);
-
- // Get the solitary successor.
- const CFGBlock *SuccB = *(Entry->succ_begin());
-
- // Construct an edge representing the starting location in the callee.
- BlockEdge Loc(Entry, SuccB, CalleeCtx);
-
- bool isNew;
- ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew);
- Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G);
-
- if (isNew)
- Eng.WList->enqueue(Node);
-}
+ if (!IsSink)
+ Eng.WList->enqueue(Succ);
-void CallExitNodeBuilder::generateNode(const ProgramState *state) {
- // Get the callee's location context.
- const StackFrameContext *LocCtx
- = cast<StackFrameContext>(Pred->getLocationContext());
- // When exiting an implicit automatic obj dtor call, the callsite is the Stmt
- // that triggers the dtor.
- PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent());
- bool isNew;
- ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew);
- Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G);
- if (isNew)
- Eng.WList->enqueue(Node, LocCtx->getCallSiteBlock(),
- LocCtx->getIndex() + 1);
+ return Succ;
}
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index e1b982c..b5ea3db 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
@@ -19,7 +20,7 @@
using namespace clang;
using namespace ento;
-SVal Environment::lookupExpr(const Stmt *E) const {
+SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
const SVal* X = ExprBindings.lookup(E);
if (X) {
SVal V = *X;
@@ -28,17 +29,21 @@ SVal Environment::lookupExpr(const Stmt *E) const {
return UnknownVal();
}
-SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
- bool useOnlyDirectBindings) const {
+SVal Environment::getSVal(const EnvironmentEntry &Entry,
+ SValBuilder& svalBuilder,
+ bool useOnlyDirectBindings) const {
if (useOnlyDirectBindings) {
// This branch is rarely taken, but can be exercised by
// checkers that explicitly bind values to arbitrary
// expressions. It is crucial that we do not ignore any
// expression here, and do a direct lookup.
- return lookupExpr(E);
+ return lookupExpr(Entry);
}
+ const Stmt *E = Entry.getStmt();
+ const LocationContext *LCtx = Entry.getLocationContext();
+
for (;;) {
if (const Expr *Ex = dyn_cast<Expr>(E))
E = Ex->IgnoreParens();
@@ -55,13 +60,12 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
case Stmt::GenericSelectionExprClass:
llvm_unreachable("ParenExprs and GenericSelectionExprs should "
"have been handled by IgnoreParens()");
- return UnknownVal();
case Stmt::CharacterLiteralClass: {
const CharacterLiteral* C = cast<CharacterLiteral>(E);
return svalBuilder.makeIntVal(C->getValue(), C->getType());
}
case Stmt::CXXBoolLiteralExprClass: {
- const SVal *X = ExprBindings.lookup(E);
+ const SVal *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx));
if (X)
return *X;
else
@@ -69,12 +73,15 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
}
case Stmt::IntegerLiteralClass: {
// In C++, this expression may have been bound to a temporary object.
- SVal const *X = ExprBindings.lookup(E);
+ SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx));
if (X)
return *X;
else
return svalBuilder.makeIntVal(cast<IntegerLiteral>(E));
}
+ case Stmt::ObjCBoolLiteralExprClass:
+ return svalBuilder.makeBoolVal(cast<ObjCBoolLiteralExpr>(E));
+
// For special C0xx nullptr case, make a null pointer SVal.
case Stmt::CXXNullPtrLiteralExprClass:
return svalBuilder.makeNull();
@@ -86,6 +93,24 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
continue;
case Stmt::ObjCPropertyRefExprClass:
return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E));
+ case Stmt::ObjCStringLiteralClass: {
+ MemRegionManager &MRMgr = svalBuilder.getRegionManager();
+ const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E);
+ return svalBuilder.makeLoc(MRMgr.getObjCStringRegion(SL));
+ }
+ case Stmt::StringLiteralClass: {
+ MemRegionManager &MRMgr = svalBuilder.getRegionManager();
+ const StringLiteral *SL = cast<StringLiteral>(E);
+ return svalBuilder.makeLoc(MRMgr.getStringRegion(SL));
+ }
+ case Stmt::ReturnStmtClass: {
+ const ReturnStmt *RS = cast<ReturnStmt>(E);
+ if (const Expr *RE = RS->getRetValue()) {
+ E = RE;
+ continue;
+ }
+ return UndefinedVal();
+ }
// Handle all other Stmt* using a lookup.
default:
@@ -93,32 +118,33 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
};
break;
}
- return lookupExpr(E);
+ return lookupExpr(EnvironmentEntry(E, LCtx));
}
-Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S,
- SVal V, bool Invalidate) {
- assert(S);
-
+Environment EnvironmentManager::bindExpr(Environment Env,
+ const EnvironmentEntry &E,
+ SVal V,
+ bool Invalidate) {
if (V.isUnknown()) {
if (Invalidate)
- return Environment(F.remove(Env.ExprBindings, S));
+ return Environment(F.remove(Env.ExprBindings, E));
else
return Env;
}
-
- return Environment(F.add(Env.ExprBindings, S, V));
+ return Environment(F.add(Env.ExprBindings, E, V));
}
-static inline const Stmt *MakeLocation(const Stmt *S) {
- return (const Stmt*) (((uintptr_t) S) | 0x1);
+static inline EnvironmentEntry MakeLocation(const EnvironmentEntry &E) {
+ const Stmt *S = E.getStmt();
+ S = (const Stmt*) (((uintptr_t) S) | 0x1);
+ return EnvironmentEntry(S, E.getLocationContext());
}
Environment EnvironmentManager::bindExprAndLocation(Environment Env,
- const Stmt *S,
+ const EnvironmentEntry &E,
SVal location, SVal V) {
- return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(S), location),
- S, V));
+ return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(E), location),
+ E, V));
}
namespace {
@@ -126,14 +152,22 @@ class MarkLiveCallback : public SymbolVisitor {
SymbolReaper &SymReaper;
public:
MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
- bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; }
+ bool VisitSymbol(SymbolRef sym) {
+ SymReaper.markLive(sym);
+ return true;
+ }
+ bool VisitMemRegion(const MemRegion *R) {
+ SymReaper.markLive(R);
+ return true;
+ }
};
} // end anonymous namespace
-// 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) {
+// In addition to mapping from EnvironmentEntry - > SVals in the Environment,
+// we also maintain a mapping from EnvironmentEntry -> SVals (locations)
+// that were used during a load and store.
+static inline bool IsLocation(const EnvironmentEntry &E) {
+ const Stmt *S = E.getStmt();
return (bool) (((uintptr_t) S) & 0x1);
}
@@ -147,19 +181,19 @@ static inline bool IsLocation(const Stmt *S) {
Environment
EnvironmentManager::removeDeadBindings(Environment Env,
SymbolReaper &SymReaper,
- const ProgramState *ST) {
+ ProgramStateRef ST) {
// We construct a new Environment object entirely, as this is cheaper than
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
Environment NewEnv = getInitialEnvironment();
- SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations;
+ SmallVector<std::pair<EnvironmentEntry, SVal>, 10> deferredLocations;
MarkLiveCallback CB(SymReaper);
ScanReachableSymbols RSScaner(ST, CB);
- llvm::ImmutableMapRef<const Stmt*,SVal>
+ llvm::ImmutableMapRef<EnvironmentEntry,SVal>
EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
F.getTreeFactory());
@@ -167,7 +201,7 @@ EnvironmentManager::removeDeadBindings(Environment Env,
for (Environment::iterator I = Env.begin(), E = Env.end();
I != E; ++I) {
- const Stmt *BlkExpr = I.getKey();
+ const EnvironmentEntry &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.
@@ -179,7 +213,7 @@ EnvironmentManager::removeDeadBindings(Environment Env,
}
const SVal &X = I.getData();
- if (SymReaper.isLive(BlkExpr)) {
+ if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
@@ -204,13 +238,58 @@ EnvironmentManager::removeDeadBindings(Environment Env,
// Go through he deferred locations and add them to the new environment if
// the correspond Stmt* is in the map as well.
- for (SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator
+ for (SmallVectorImpl<std::pair<EnvironmentEntry, SVal> >::iterator
I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) {
- const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1);
- if (EBMapRef.lookup(S))
- EBMapRef = EBMapRef.add(I->first, I->second);
+ const EnvironmentEntry &En = I->first;
+ const Stmt *S = (Stmt*) (((uintptr_t) En.getStmt()) & (uintptr_t) ~0x1);
+ if (EBMapRef.lookup(EnvironmentEntry(S, En.getLocationContext())))
+ EBMapRef = EBMapRef.add(En, I->second);
}
NewEnv.ExprBindings = EBMapRef.asImmutableMap();
return NewEnv;
}
+
+void Environment::print(raw_ostream &Out, const char *NL,
+ const char *Sep) const {
+ printAux(Out, false, NL, Sep);
+ printAux(Out, true, NL, Sep);
+}
+
+void Environment::printAux(raw_ostream &Out, bool printLocations,
+ const char *NL,
+ const char *Sep) const{
+
+ bool isFirst = true;
+
+ for (Environment::iterator I = begin(), E = end(); I != E; ++I) {
+ const EnvironmentEntry &En = I.getKey();
+ if (IsLocation(En)) {
+ if (!printLocations)
+ continue;
+ }
+ else {
+ if (printLocations)
+ continue;
+ }
+
+ if (isFirst) {
+ Out << NL << NL
+ << (printLocations ? "Load/Store locations:" : "Expressions:")
+ << NL;
+ isFirst = false;
+ } else {
+ Out << NL;
+ }
+
+ const Stmt *S = En.getStmt();
+ if (printLocations) {
+ S = (Stmt*) (((uintptr_t) S) & ((uintptr_t) ~0x1));
+ }
+
+ Out << " (" << (void*) En.getLocationContext() << ',' << (void*) S << ") ";
+ LangOptions LO; // FIXME.
+ S->printPretty(Out, 0, PrintingPolicy(LO));
+ Out << " : " << I.getData();
+ }
+}
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 5762a21..0dcbe1f 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/Stmt.h"
+#include "clang/AST/ParentMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
@@ -44,25 +45,18 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) {
// Cleanup.
//===----------------------------------------------------------------------===//
-typedef std::vector<ExplodedNode*> NodeList;
-static inline NodeList*& getNodeList(void *&p) { return (NodeList*&) p; }
+static const unsigned CounterTop = 1000;
-ExplodedGraph::~ExplodedGraph() {
- if (reclaimNodes) {
- delete getNodeList(recentlyAllocatedNodes);
- delete getNodeList(freeNodes);
- }
-}
+ExplodedGraph::ExplodedGraph()
+ : NumNodes(0), reclaimNodes(false), reclaimCounter(CounterTop) {}
+
+ExplodedGraph::~ExplodedGraph() {}
//===----------------------------------------------------------------------===//
// Node reclamation.
//===----------------------------------------------------------------------===//
-void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
- if (!recentlyAllocatedNodes)
- return;
- NodeList &nl = *getNodeList(recentlyAllocatedNodes);
-
+bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Reclaimn all nodes that match *all* the following criteria:
//
// (1) 1 predecessor (that has one successor)
@@ -72,61 +66,86 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
// (5) The 'store' is the same as the predecessor.
// (6) The 'GDM' is the same as the predecessor.
// (7) The LocationContext is the same as the predecessor.
- // (8) The PostStmt is for a non-CFGElement expression.
-
- for (NodeList::iterator i = nl.begin(), e = nl.end() ; i != e; ++i) {
- ExplodedNode *node = *i;
-
- // Conditions 1 and 2.
- if (node->pred_size() != 1 || node->succ_size() != 1)
- continue;
+ // (8) The PostStmt is for a non-consumed Stmt or Expr.
- ExplodedNode *pred = *(node->pred_begin());
- if (pred->succ_size() != 1)
- continue;
+ // Conditions 1 and 2.
+ if (node->pred_size() != 1 || node->succ_size() != 1)
+ return false;
- ExplodedNode *succ = *(node->succ_begin());
- if (succ->pred_size() != 1)
- continue;
+ const ExplodedNode *pred = *(node->pred_begin());
+ if (pred->succ_size() != 1)
+ return false;
+
+ const ExplodedNode *succ = *(node->succ_begin());
+ if (succ->pred_size() != 1)
+ return false;
+
+ // Condition 3.
+ ProgramPoint progPoint = node->getLocation();
+ if (!isa<PostStmt>(progPoint) ||
+ (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint)))
+ return false;
+
+ // Condition 4.
+ PostStmt ps = cast<PostStmt>(progPoint);
+ if (ps.getTag())
+ return false;
+
+ if (isa<BinaryOperator>(ps.getStmt()))
+ return false;
+
+ // Conditions 5, 6, and 7.
+ ProgramStateRef state = node->getState();
+ ProgramStateRef pred_state = pred->getState();
+ if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
+ progPoint.getLocationContext() != pred->getLocationContext())
+ return false;
+
+ // Condition 8.
+ if (const Expr *Ex = dyn_cast<Expr>(ps.getStmt())) {
+ ParentMap &PM = progPoint.getLocationContext()->getParentMap();
+ if (!PM.isConsumedExpr(Ex))
+ return false;
+ }
+
+ return true;
+}
- // Condition 3.
- ProgramPoint progPoint = node->getLocation();
- if (!isa<PostStmt>(progPoint))
- continue;
- // Condition 4.
- PostStmt ps = cast<PostStmt>(progPoint);
- if (ps.getTag())
- continue;
+void ExplodedGraph::collectNode(ExplodedNode *node) {
+ // Removing a node means:
+ // (a) changing the predecessors successor to the successor of this node
+ // (b) changing the successors predecessor to the predecessor of this node
+ // (c) Putting 'node' onto freeNodes.
+ assert(node->pred_size() == 1 || node->succ_size() == 1);
+ ExplodedNode *pred = *(node->pred_begin());
+ ExplodedNode *succ = *(node->succ_begin());
+ pred->replaceSuccessor(succ);
+ succ->replacePredecessor(pred);
+ FreeNodes.push_back(node);
+ Nodes.RemoveNode(node);
+ --NumNodes;
+ node->~ExplodedNode();
+}
- if (isa<BinaryOperator>(ps.getStmt()))
- continue;
+void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
+ if (ChangedNodes.empty())
+ return;
- // Conditions 5, 6, and 7.
- const ProgramState *state = node->getState();
- const ProgramState *pred_state = pred->getState();
- if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
- progPoint.getLocationContext() != pred->getLocationContext())
- continue;
+ // Only periodically relcaim nodes so that we can build up a set of
+ // nodes that meet the reclamation criteria. Freshly created nodes
+ // by definition have no successor, and thus cannot be reclaimed (see below).
+ assert(reclaimCounter > 0);
+ if (--reclaimCounter != 0)
+ return;
+ reclaimCounter = CounterTop;
- // Condition 8.
- if (node->getCFG().isBlkExpr(ps.getStmt()))
- continue;
-
- // If we reach here, we can remove the node. This means:
- // (a) changing the predecessors successor to the successor of this node
- // (b) changing the successors predecessor to the predecessor of this node
- // (c) Putting 'node' onto freeNodes.
- pred->replaceSuccessor(succ);
- succ->replacePredecessor(pred);
- if (!freeNodes)
- freeNodes = new NodeList();
- getNodeList(freeNodes)->push_back(node);
- Nodes.RemoveNode(node);
- --NumNodes;
- node->~ExplodedNode();
+ for (NodeVector::iterator it = ChangedNodes.begin(), et = ChangedNodes.end();
+ it != et; ++it) {
+ ExplodedNode *node = *it;
+ if (shouldCollect(node))
+ collectNode(node);
}
-
- nl.clear();
+ ChangedNodes.clear();
}
//===----------------------------------------------------------------------===//
@@ -215,36 +234,33 @@ ExplodedNode** ExplodedNode::NodeGroup::end() const {
}
ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
- const ProgramState *State, bool* IsNew) {
+ ProgramStateRef State,
+ bool IsSink,
+ bool* IsNew) {
// Profile 'State' to determine if we already have an existing node.
llvm::FoldingSetNodeID profile;
void *InsertPos = 0;
- NodeTy::Profile(profile, L, State);
+ NodeTy::Profile(profile, L, State, IsSink);
NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
if (!V) {
- if (freeNodes && !getNodeList(freeNodes)->empty()) {
- NodeList *nl = getNodeList(freeNodes);
- V = nl->back();
- nl->pop_back();
+ if (!FreeNodes.empty()) {
+ V = FreeNodes.back();
+ FreeNodes.pop_back();
}
else {
// Allocate a new node.
V = (NodeTy*) getAllocator().Allocate<NodeTy>();
}
- new (V) NodeTy(L, State);
+ new (V) NodeTy(L, State, IsSink);
- if (reclaimNodes) {
- if (!recentlyAllocatedNodes)
- recentlyAllocatedNodes = new NodeList();
- getNodeList(recentlyAllocatedNodes)->push_back(V);
- }
+ if (reclaimNodes)
+ ChangedNodes.push_back(V);
// Insert the node into the node set and return it.
Nodes.InsertNode(V, InsertPos);
-
++NumNodes;
if (IsNew) *IsNew = true;
@@ -265,7 +281,7 @@ ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd,
assert (NBeg < NEnd);
- llvm::OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap());
+ OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap());
ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap);
@@ -334,7 +350,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
// Create the corresponding node in the new graph and record the mapping
// from the old node to the new node.
- ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, NULL);
+ ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, N->isSink(), 0);
Pass2[N] = NewN;
// Also record the reverse mapping from the new node to the old node.
@@ -372,15 +388,13 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
if (Pass1.count(*I))
WL2.push_back(*I);
}
-
- // Finally, explicitly mark all nodes without any successors as sinks.
- if (N->isSink())
- NewN->markAsSink();
}
return G;
}
+void InterExplodedGraphMap::anchor() { }
+
ExplodedNode*
InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const {
llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I =
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index ac9cf0b..d2da9aa 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -13,22 +13,24 @@
//
//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "ExprEngine"
+
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtObjC.h"
+#include "clang/AST/StmtCXX.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/SourceManager.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/ImmutableList.h"
+#include "llvm/ADT/Statistic.h"
#ifndef NDEBUG
#include "llvm/Support/GraphWriter.h"
@@ -38,6 +40,19 @@ using namespace clang;
using namespace ento;
using llvm::APSInt;
+STATISTIC(NumRemoveDeadBindings,
+ "The # of times RemoveDeadBindings is called");
+STATISTIC(NumRemoveDeadBindingsSkipped,
+ "The # of times RemoveDeadBindings is skipped");
+STATISTIC(NumMaxBlockCountReached,
+ "The # of aborted paths due to reaching the maximum block count in "
+ "a top level function");
+STATISTIC(NumMaxBlockCountReachedInInlined,
+ "The # of aborted paths due to reaching the maximum block count in "
+ "an inlined function");
+STATISTIC(NumTimesRetriedWithoutInlining,
+ "The # of times we re-evaluated a call without inlining");
+
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
@@ -51,17 +66,20 @@ static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) {
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
-ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled)
+ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
+ SetOfConstDecls *VisitedCallees,
+ FunctionSummariesTy *FS)
: AMgr(mgr),
- Engine(*this),
+ AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+ Engine(*this, VisitedCallees, FS),
G(Engine.getGraph()),
- Builder(NULL),
StateMgr(getContext(), mgr.getStoreManagerCreator(),
mgr.getConstraintManagerCreator(), G.getAllocator(),
*this),
SymMgr(StateMgr.getSymbolManager()),
svalBuilder(StateMgr.getSValBuilder()),
- EntryNode(NULL), currentStmt(NULL),
+ EntryNode(NULL),
+ currentStmt(NULL), currentStmtIdx(0), currentBuilderContext(0),
NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL),
RaiseSel(GetNullarySelector("raise", getContext())),
ObjCGCEnabled(gcEnabled), BR(mgr, *this) {
@@ -81,15 +99,15 @@ ExprEngine::~ExprEngine() {
// Utility methods.
//===----------------------------------------------------------------------===//
-const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) {
- const ProgramState *state = StateMgr.getInitialState(InitLoc);
+ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
+ ProgramStateRef state = StateMgr.getInitialState(InitLoc);
+ const Decl *D = InitLoc->getDecl();
// Preconditions.
-
// FIXME: It would be nice if we had a more general mechanism to add
// such preconditions. Some day.
do {
- const Decl *D = InitLoc->getDecl();
+
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
// Precondition: the first argument of 'main' is an integer guaranteed
// to be > 0.
@@ -117,49 +135,45 @@ const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc)
if (!Constraint)
break;
- if (const ProgramState *newState = state->assume(*Constraint, true))
+ if (ProgramStateRef newState = state->assume(*Constraint, true))
state = newState;
-
- break;
}
-
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
- // Precondition: 'self' is always non-null upon entry to an Objective-C
- // method.
- const ImplicitParamDecl *SelfD = MD->getSelfDecl();
- const MemRegion *R = state->getRegion(SelfD, InitLoc);
- SVal V = state->getSVal(loc::MemRegionVal(R));
-
- if (const Loc *LV = dyn_cast<Loc>(&V)) {
- // Assume that the pointer value in 'self' is non-null.
- state = state->assume(*LV, true);
- assert(state && "'self' cannot be null");
- }
+ break;
+ }
+ while (0);
+
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ // Precondition: 'self' is always non-null upon entry to an Objective-C
+ // method.
+ const ImplicitParamDecl *SelfD = MD->getSelfDecl();
+ const MemRegion *R = state->getRegion(SelfD, InitLoc);
+ SVal V = state->getSVal(loc::MemRegionVal(R));
+
+ if (const Loc *LV = dyn_cast<Loc>(&V)) {
+ // Assume that the pointer value in 'self' is non-null.
+ state = state->assume(*LV, true);
+ assert(state && "'self' cannot be null");
}
- } while (0);
-
- return state;
-}
+ }
-bool
-ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const
-{
- if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) {
- SVal calleeV = callOrMessage.getFunctionCallee();
- if (const FunctionTextRegion *codeR =
- dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) {
-
- const FunctionDecl *fd = codeR->getDecl();
- if (const IdentifierInfo *ii = fd->getIdentifier()) {
- StringRef fname = ii->getName();
- if (fname == "strlen")
- return false;
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (!MD->isStatic()) {
+ // Precondition: 'this' is always non-null upon entry to the
+ // top-level function. This is our starting assumption for
+ // analyzing an "open" program.
+ const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
+ if (SFC->getParent() == 0) {
+ loc::MemRegionVal L(getCXXThisRegion(MD, SFC));
+ SVal V = state->getSVal(L);
+ if (const Loc *LV = dyn_cast<Loc>(&V)) {
+ state = state->assume(*LV, true);
+ assert(state && "'this' cannot be null");
+ }
}
}
}
-
- // The conservative answer: invalidates globals.
- return true;
+
+ return state;
}
//===----------------------------------------------------------------------===//
@@ -168,25 +182,26 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const
/// evalAssume - Called by ConstraintManager. Used to call checker-specific
/// logic for handling assumptions on symbolic values.
-const ProgramState *ExprEngine::processAssume(const ProgramState *state,
+ProgramStateRef ExprEngine::processAssume(ProgramStateRef state,
SVal cond, bool assumption) {
return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
}
-bool ExprEngine::wantsRegionChangeUpdate(const ProgramState *state) {
+bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) {
return getCheckerManager().wantsRegionChangeUpdate(state);
}
-const ProgramState *
-ExprEngine::processRegionChanges(const ProgramState *state,
+ProgramStateRef
+ExprEngine::processRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
- ArrayRef<const MemRegion *> Regions) {
+ ArrayRef<const MemRegion *> Regions,
+ const CallOrObjCMessage *Call) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
- Explicits, Regions);
+ Explicits, Regions, Call);
}
-void ExprEngine::printState(raw_ostream &Out, const ProgramState *State,
+void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) {
getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
}
@@ -195,54 +210,77 @@ void ExprEngine::processEndWorklist(bool hasWorkRemaining) {
getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
}
-void ExprEngine::processCFGElement(const CFGElement E,
- StmtNodeBuilder& builder) {
+void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
+ unsigned StmtIdx, NodeBuilderContext *Ctx) {
+ currentStmtIdx = StmtIdx;
+ currentBuilderContext = Ctx;
+
switch (E.getKind()) {
case CFGElement::Invalid:
llvm_unreachable("Unexpected CFGElement kind.");
case CFGElement::Statement:
- ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), builder);
+ ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred);
return;
case CFGElement::Initializer:
- ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), builder);
+ ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred);
return;
case CFGElement::AutomaticObjectDtor:
case CFGElement::BaseDtor:
case CFGElement::MemberDtor:
case CFGElement::TemporaryDtor:
- ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), builder);
+ ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred);
return;
}
}
-void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
- // TODO: Use RAII to remove the unnecessary, tagged nodes.
- //RegisterCreatedNodes registerCreatedNodes(getGraph());
+static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
+ const CFGStmt S,
+ const ExplodedNode *Pred,
+ const LocationContext *LC) {
+
+ // Are we never purging state values?
+ if (AMgr.getPurgeMode() == PurgeNone)
+ return false;
+
+ // Is this the beginning of a basic block?
+ if (isa<BlockEntrance>(Pred->getLocation()))
+ return true;
+ // Is this on a non-expression?
+ if (!isa<Expr>(S.getStmt()))
+ return true;
+
+ // Run before processing a call.
+ if (isa<CallExpr>(S.getStmt()))
+ return true;
+
+ // Is this an expression that is consumed by another expression? If so,
+ // postpone cleaning out the state.
+ ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap();
+ return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
+}
+
+void ExprEngine::ProcessStmt(const CFGStmt S,
+ ExplodedNode *Pred) {
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
- // Recycle any unused states in the ProgramStateManager.
- StateMgr.recycleUnusedStates();
currentStmt = S.getStmt();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
currentStmt->getLocStart(),
"Error evaluating statement");
- // A tag to track convenience transitions, which can be removed at cleanup.
- static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
- Builder = &builder;
- EntryNode = builder.getPredecessor();
+ EntryNode = Pred;
- const ProgramState *EntryState = EntryNode->getState();
+ ProgramStateRef EntryState = EntryNode->getState();
CleanedState = EntryState;
- ExplodedNode *CleanedNode = 0;
// Create the cleaned state.
const LocationContext *LC = EntryNode->getLocationContext();
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
- if (AMgr.getPurgeMode() != PurgeNone) {
+ if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) {
+ NumRemoveDeadBindings++;
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
const StackFrameContext *SFC = LC->getCurrentStackFrame();
@@ -251,25 +289,23 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
// and the store. TODO: The function should just return new env and store,
// not a new state.
CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
+ } else {
+ NumRemoveDeadBindingsSkipped++;
}
// Process any special transfer function for dead symbols.
ExplodedNodeSet Tmp;
+ // A tag to track convenience transitions, which can be removed at cleanup.
+ static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
+
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
- CleanedNode =
- Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag);
- Tmp.Add(CleanedNode);
+ StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
+ Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag);
} else {
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- SaveOr OldHasGen(Builder->hasGeneratedNode);
-
- SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
- Builder->PurgingDeadSymbols = true;
-
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
@@ -279,9 +315,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
// For each node in CheckedSet, generate CleanedNodes that have the
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
+ StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
- const ProgramState *CheckerState = (*I)->getState();
+ ProgramStateRef CheckerState = (*I)->getState();
// The constraint manager has not been cleaned up yet, so clean up now.
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
@@ -296,109 +333,109 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
// Create a state based on CleanedState with CheckerState GDM and
// generate a transition to that state.
- const ProgramState *CleanedCheckerSt =
+ ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
- ExplodedNode *CleanedNode = Builder->generateNode(currentStmt,
- CleanedCheckerSt, *I,
- &cleanupTag);
- Tmp.Add(CleanedNode);
+ Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag,
+ ProgramPoint::PostPurgeDeadSymbolsKind);
}
}
+ ExplodedNodeSet Dst;
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- // TODO: Remove Dest set, it's no longer needed.
- ExplodedNodeSet Dst;
+ ExplodedNodeSet DstI;
// Visit the statement.
- Visit(currentStmt, *I, Dst);
+ Visit(currentStmt, *I, DstI);
+ Dst.insert(DstI);
}
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx);
+
// NULL out these variables to cleanup.
CleanedState = NULL;
EntryNode = NULL;
currentStmt = 0;
- Builder = NULL;
}
void ExprEngine::ProcessInitializer(const CFGInitializer Init,
- StmtNodeBuilder &builder) {
+ ExplodedNode *Pred) {
+ ExplodedNodeSet Dst;
+
// We don't set EntryNode and currentStmt. And we don't clean up state.
const CXXCtorInitializer *BMI = Init.getInitializer();
-
- ExplodedNode *pred = builder.getPredecessor();
-
- const StackFrameContext *stackFrame = cast<StackFrameContext>(pred->getLocationContext());
- const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl());
+ const StackFrameContext *stackFrame =
+ cast<StackFrameContext>(Pred->getLocationContext());
+ const CXXConstructorDecl *decl =
+ cast<CXXConstructorDecl>(stackFrame->getDecl());
const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame);
- SVal thisVal = pred->getState()->getSVal(thisReg);
+ SVal thisVal = Pred->getState()->getSVal(thisReg);
if (BMI->isAnyMemberInitializer()) {
- ExplodedNodeSet Dst;
-
// Evaluate the initializer.
- Visit(BMI->getInit(), pred, Dst);
- for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){
- ExplodedNode *Pred = *I;
- const ProgramState *state = Pred->getState();
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ ProgramStateRef state = Pred->getState();
- const FieldDecl *FD = BMI->getAnyMember();
+ const FieldDecl *FD = BMI->getAnyMember();
- SVal FieldLoc = state->getLValue(FD, thisVal);
- SVal InitVal = state->getSVal(BMI->getInit());
- state = state->bindLoc(FieldLoc, InitVal);
+ SVal FieldLoc = state->getLValue(FD, thisVal);
+ SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext());
+ state = state->bindLoc(FieldLoc, InitVal);
- // Use a custom node building process.
- PostInitializer PP(BMI, stackFrame);
- // Builder automatically add the generated node to the deferred set,
- // which are processed in the builder's dtor.
- builder.generateNode(PP, state, Pred);
- }
- return;
- }
+ // Use a custom node building process.
+ PostInitializer PP(BMI, stackFrame);
+ // Builder automatically add the generated node to the deferred set,
+ // which are processed in the builder's dtor.
+ Bldr.generateNode(PP, Pred, state);
+ } else {
+ assert(BMI->isBaseInitializer());
- assert(BMI->isBaseInitializer());
+ // Get the base class declaration.
+ const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit());
- // Get the base class declaration.
- const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit());
+ // Create the base object region.
+ SVal baseVal =
+ getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType());
+ const MemRegion *baseReg = baseVal.getAsRegion();
+ assert(baseReg);
+
+ VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst);
+ }
- // Create the base object region.
- SVal baseVal =
- getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType());
- const MemRegion *baseReg = baseVal.getAsRegion();
- assert(baseReg);
- Builder = &builder;
- ExplodedNodeSet dst;
- VisitCXXConstructExpr(ctorExpr, baseReg, pred, dst);
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx);
}
void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
- StmtNodeBuilder &builder) {
- Builder = &builder;
-
+ ExplodedNode *Pred) {
+ ExplodedNodeSet Dst;
switch (D.getKind()) {
case CFGElement::AutomaticObjectDtor:
- ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), builder);
+ ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), Pred, Dst);
break;
case CFGElement::BaseDtor:
- ProcessBaseDtor(cast<CFGBaseDtor>(D), builder);
+ ProcessBaseDtor(cast<CFGBaseDtor>(D), Pred, Dst);
break;
case CFGElement::MemberDtor:
- ProcessMemberDtor(cast<CFGMemberDtor>(D), builder);
+ ProcessMemberDtor(cast<CFGMemberDtor>(D), Pred, Dst);
break;
case CFGElement::TemporaryDtor:
- ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), builder);
+ ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), Pred, Dst);
break;
default:
llvm_unreachable("Unexpected dtor kind.");
}
+
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx);
}
-void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor,
- StmtNodeBuilder &builder) {
- ExplodedNode *pred = builder.getPredecessor();
- const ProgramState *state = pred->getState();
- const VarDecl *varDecl = dtor.getVarDecl();
+void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ProgramStateRef state = Pred->getState();
+ const VarDecl *varDecl = Dtor.getVarDecl();
QualType varType = varDecl->getType();
@@ -409,30 +446,29 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor,
assert(recordDecl && "get CXXRecordDecl fail");
const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor();
- Loc dest = state->getLValue(varDecl, pred->getLocationContext());
+ Loc dest = state->getLValue(varDecl, Pred->getLocationContext());
- ExplodedNodeSet dstSet;
VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(),
- dtor.getTriggerStmt(), pred, dstSet);
+ Dtor.getTriggerStmt(), Pred, Dst);
}
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
- StmtNodeBuilder &builder) {
-}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
- StmtNodeBuilder &builder) {
-}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
- StmtNodeBuilder &builder) {
-}
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {}
void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
+ ExplodedNodeSet &DstTop) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
S->getLocStart(),
"Error evaluating statement");
+ ExplodedNodeSet Dst;
+ StmtNodeBuilder Bldr(Pred, DstTop, *currentBuilderContext);
// Expressions to ignore.
if (const Expr *Ex = dyn_cast<Expr>(S))
@@ -442,19 +478,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// this check when we KNOW that there is no block-level subexpression.
// The motivation is that this check requires a hashtable lookup.
- if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) {
- Dst.Add(Pred);
+ if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S))
return;
- }
switch (S->getStmtClass()) {
// C++ and ARC stuff we don't support yet.
case Expr::ObjCIndirectCopyRestoreExprClass:
- case Stmt::CXXBindTemporaryExprClass:
- case Stmt::CXXCatchStmtClass:
case Stmt::CXXDependentScopeMemberExprClass:
case Stmt::CXXPseudoDestructorExprClass:
- case Stmt::CXXThrowExprClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
@@ -463,6 +494,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::DependentScopeDeclRefExprClass:
case Stmt::UnaryTypeTraitExprClass:
case Stmt::BinaryTypeTraitExprClass:
+ case Stmt::TypeTraitExprClass:
case Stmt::ArrayTypeTraitExprClass:
case Stmt::ExpressionTraitExprClass:
case Stmt::UnresolvedLookupExprClass:
@@ -472,22 +504,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
- case Stmt::SEHFinallyStmtClass:
- {
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- Builder->BuildSinks = true;
- const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState());
- Engine.addAbortedBlock(node, Builder->getBlock());
+ case Stmt::LambdaExprClass:
+ case Stmt::SEHFinallyStmtClass: {
+ const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState(),
+ /* sink */ true);
+ Engine.addAbortedBlock(node, currentBuilderContext->getBlock());
break;
}
// We don't handle default arguments either yet, but we can fake it
// for now by just skipping them.
case Stmt::SubstNonTypeTemplateParmExprClass:
- case Stmt::CXXDefaultArgExprClass: {
- Dst.Add(Pred);
+ case Stmt::CXXDefaultArgExprClass:
break;
- }
case Stmt::ParenExprClass:
llvm_unreachable("ParenExprs already handled.");
@@ -511,38 +540,44 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::NullStmtClass:
case Stmt::SwitchStmtClass:
case Stmt::WhileStmtClass:
+ case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
- break;
case Stmt::GNUNullExprClass: {
// GNU __null is a pointer-width integer, not an actual pointer.
- const ProgramState *state = Pred->getState();
- state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false));
- MakeNode(Dst, S, Pred, state);
+ ProgramStateRef state = Pred->getState();
+ state = state->BindExpr(S, Pred->getLocationContext(),
+ svalBuilder.makeIntValWithPtrWidth(0, false));
+ Bldr.generateNode(S, Pred, state);
break;
}
case Stmt::ObjCAtSynchronizedStmtClass:
+ Bldr.takeNodes(Pred);
VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
+ // FIXME.
+ case Stmt::ObjCSubscriptRefExprClass:
+ break;
+
case Stmt::ObjCPropertyRefExprClass:
// Implicitly handled by Environment::getSVal().
- Dst.Add(Pred);
break;
case Stmt::ImplicitValueInitExprClass: {
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
QualType ty = cast<ImplicitValueInitExpr>(S)->getType();
SVal val = svalBuilder.makeZeroVal(ty);
- MakeNode(Dst, S, Pred, state->BindExpr(S, val));
+ Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(),
+ val));
break;
}
- case Stmt::ExprWithCleanupsClass: {
- Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst);
+ case Stmt::ExprWithCleanupsClass:
+ // Handled due to fully linearised CFG.
break;
- }
// Cases not handled yet; but will handle some day.
case Stmt::DesignatedInitExprClass:
@@ -556,7 +591,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ObjCIsaExprClass:
case Stmt::ObjCProtocolExprClass:
case Stmt::ObjCSelectorExprClass:
- case Stmt::ObjCStringLiteralClass:
+ case Expr::ObjCNumericLiteralClass:
case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass:
@@ -565,50 +600,101 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OpaqueValueExprClass:
case Stmt::AsTypeExprClass:
case Stmt::AtomicExprClass:
- // Fall through.
+ // Fall through.
+ // Currently all handling of 'throw' just falls to the CFG. We
+ // can consider doing more if necessary.
+ case Stmt::CXXThrowExprClass:
+ // Fall through.
+
// Cases we intentionally don't evaluate, since they don't need
// to be explicitly evaluated.
case Stmt::AddrLabelExprClass:
case Stmt::IntegerLiteralClass:
case Stmt::CharacterLiteralClass:
case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::ObjCBoolLiteralExprClass:
case Stmt::FloatingLiteralClass:
case Stmt::SizeOfPackExprClass:
- case Stmt::CXXNullPtrLiteralExprClass:
- Dst.Add(Pred); // No-op. Simply propagate the current state unchanged.
+ case Stmt::StringLiteralClass:
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::CXXNullPtrLiteralExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet preVisit;
+ getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
+ getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this);
+ Bldr.addNodes(Dst);
break;
+ }
+
+ case Expr::ObjCArrayLiteralClass:
+ case Expr::ObjCDictionaryLiteralClass: {
+ Bldr.takeNodes(Pred);
+
+ ExplodedNodeSet preVisit;
+ getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
+
+ // FIXME: explicitly model with a region and the actual contents
+ // of the container. For now, conjure a symbol.
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext);
+
+ for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
+ it != et; ++it) {
+ ExplodedNode *N = *it;
+ const Expr *Ex = cast<Expr>(S);
+ QualType resultType = Ex->getType();
+ const LocationContext *LCtx = N->getLocationContext();
+ SVal result =
+ svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType,
+ currentBuilderContext->getCurrentBlockCount());
+ ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result);
+ Bldr2.generateNode(S, N, state);
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
case Stmt::ArraySubscriptExprClass:
+ Bldr.takeNodes(Pred);
VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::AsmStmtClass:
+ Bldr.takeNodes(Pred);
VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
- case Stmt::BlockDeclRefExprClass: {
- const BlockDeclRefExpr *BE = cast<BlockDeclRefExpr>(S);
- VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst);
- break;
- }
-
case Stmt::BlockExprClass:
+ Bldr.takeNodes(Pred);
VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::BinaryOperatorClass: {
const BinaryOperator* B = cast<BinaryOperator>(S);
if (B->isLogicalOp()) {
+ Bldr.takeNodes(Pred);
VisitLogicalExpr(B, Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
else if (B->getOpcode() == BO_Comma) {
- const ProgramState *state = Pred->getState();
- MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS())));
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(B, Pred,
+ state->BindExpr(B, Pred->getLocationContext(),
+ state->getSVal(B->getRHS(),
+ Pred->getLocationContext())));
break;
}
+ Bldr.takeNodes(Pred);
+
if (AMgr.shouldEagerlyAssume() &&
(B->isRelationalOp() || B->isEqualityOp())) {
ExplodedNodeSet Tmp;
@@ -618,13 +704,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
else
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::CallExprClass:
case Stmt::CXXOperatorCallExprClass:
- case Stmt::CXXMemberCallExprClass: {
+ case Stmt::CXXMemberCallExprClass:
+ case Stmt::UserDefinedLiteralClass: {
+ Bldr.takeNodes(Pred);
VisitCallExpr(cast<CallExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CXXCatchStmtClass: {
+ Bldr.takeNodes(Pred);
+ VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
@@ -633,58 +730,78 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
const CXXConstructExpr *C = cast<CXXConstructExpr>(S);
// For block-level CXXConstructExpr, we don't have a destination region.
// Let VisitCXXConstructExpr() create one.
+ Bldr.takeNodes(Pred);
VisitCXXConstructExpr(C, 0, Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::CXXNewExprClass: {
+ Bldr.takeNodes(Pred);
const CXXNewExpr *NE = cast<CXXNewExpr>(S);
VisitCXXNewExpr(NE, Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::CXXDeleteExprClass: {
+ Bldr.takeNodes(Pred);
const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
VisitCXXDeleteExpr(CDE, Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
// FIXME: ChooseExpr is really a constant. We need to fix
// the CFG do not model them as explicit control-flow.
case Stmt::ChooseExprClass: { // __builtin_choose_expr
+ Bldr.takeNodes(Pred);
const ChooseExpr *C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::CompoundAssignOperatorClass:
+ Bldr.takeNodes(Pred);
VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::CompoundLiteralExprClass:
+ Bldr.takeNodes(Pred);
VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass: { // '?' operator
+ Bldr.takeNodes(Pred);
const AbstractConditionalOperator *C
= cast<AbstractConditionalOperator>(S);
VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::CXXThisExprClass:
+ Bldr.takeNodes(Pred);
VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::DeclRefExprClass: {
+ Bldr.takeNodes(Pred);
const DeclRefExpr *DE = cast<DeclRefExpr>(S);
VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::DeclStmtClass:
+ Bldr.takeNodes(Pred);
VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::ImplicitCastExprClass:
@@ -695,6 +812,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
+ Bldr.takeNodes(Pred);
const CastExpr *C = cast<CastExpr>(S);
// Handle the previsit checks.
ExplodedNodeSet dstPrevisit;
@@ -709,58 +827,98 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// Handle the postvisit checks.
getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);
+ Bldr.addNodes(Dst);
break;
}
case Expr::MaterializeTemporaryExprClass: {
+ Bldr.takeNodes(Pred);
const MaterializeTemporaryExpr *Materialize
= cast<MaterializeTemporaryExpr>(S);
- if (!Materialize->getType()->isRecordType())
- CreateCXXTemporaryObject(Materialize, Pred, Dst);
+ if (Materialize->getType()->isRecordType())
+ Dst.Add(Pred);
else
- Visit(Materialize->GetTemporaryExpr(), Pred, Dst);
+ CreateCXXTemporaryObject(Materialize, Pred, Dst);
+ Bldr.addNodes(Dst);
break;
}
case Stmt::InitListExprClass:
+ Bldr.takeNodes(Pred);
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::MemberExprClass:
+ Bldr.takeNodes(Pred);
VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
+
case Stmt::ObjCIvarRefExprClass:
+ Bldr.takeNodes(Pred);
VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::ObjCForCollectionStmtClass:
+ Bldr.takeNodes(Pred);
VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
- case Stmt::ObjCMessageExprClass:
- VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
+ case Stmt::ObjCMessageExprClass: {
+ Bldr.takeNodes(Pred);
+ // Is this a property access?
+ const ParentMap &PM = Pred->getLocationContext()->getParentMap();
+ const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S);
+ bool evaluated = false;
+
+ if (const PseudoObjectExpr *PO =
+ dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) {
+ const Expr *syntactic = PO->getSyntacticForm();
+ if (const ObjCPropertyRefExpr *PR =
+ dyn_cast<ObjCPropertyRefExpr>(syntactic)) {
+ bool isSetter = ME->getNumArgs() > 0;
+ VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst);
+ evaluated = true;
+ }
+ else if (isa<BinaryOperator>(syntactic)) {
+ VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst);
+ }
+ }
+
+ if (!evaluated)
+ VisitObjCMessage(ME, Pred, Dst);
+
+ Bldr.addNodes(Dst);
break;
+ }
case Stmt::ObjCAtThrowStmtClass: {
// FIXME: This is not complete. We basically treat @throw as
// an abort.
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- Builder->BuildSinks = true;
- MakeNode(Dst, S, Pred, Pred->getState());
+ Bldr.generateNode(S, Pred, Pred->getState());
break;
}
case Stmt::ReturnStmtClass:
+ Bldr.takeNodes(Pred);
VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::OffsetOfExprClass:
+ Bldr.takeNodes(Pred);
VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::UnaryExprOrTypeTraitExprClass:
+ Bldr.takeNodes(Pred);
VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S),
Pred, Dst);
+ Bldr.addNodes(Dst);
break;
case Stmt::StmtExprClass: {
@@ -770,81 +928,154 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// Empty statement expression.
assert(SE->getType() == getContext().VoidTy
&& "Empty statement expression must have void type.");
- Dst.Add(Pred);
break;
}
if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
- const ProgramState *state = Pred->getState();
- MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr)));
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(SE, Pred,
+ state->BindExpr(SE, Pred->getLocationContext(),
+ state->getSVal(LastExpr,
+ Pred->getLocationContext())));
}
- else
- Dst.Add(Pred);
-
break;
}
- case Stmt::StringLiteralClass: {
- const ProgramState *state = Pred->getState();
- SVal V = state->getLValue(cast<StringLiteral>(S));
- MakeNode(Dst, S, Pred, state->BindExpr(S, V));
- return;
- }
-
case Stmt::UnaryOperatorClass: {
+ Bldr.takeNodes(Pred);
const UnaryOperator *U = cast<UnaryOperator>(S);
- if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) {
+ if (AMgr.shouldEagerlyAssume() && (U->getOpcode() == UO_LNot)) {
ExplodedNodeSet Tmp;
VisitUnaryOperator(U, Pred, Tmp);
evalEagerlyAssume(Dst, Tmp, U);
}
else
VisitUnaryOperator(U, Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::PseudoObjectExprClass: {
+ Bldr.takeNodes(Pred);
+ ProgramStateRef state = Pred->getState();
+ const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S);
+ if (const Expr *Result = PE->getResultExpr()) {
+ SVal V = state->getSVal(Result, Pred->getLocationContext());
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(), V));
+ }
+ else
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(),
+ UnknownVal()));
+
+ Bldr.addNodes(Dst);
break;
}
}
}
-//===----------------------------------------------------------------------===//
-// Block entrance. (Update counters).
-//===----------------------------------------------------------------------===//
+bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
+ const LocationContext *CalleeLC) {
+ const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame();
+ const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame();
+ assert(CalleeSF && CallerSF);
+ ExplodedNode *BeforeProcessingCall = 0;
+
+ // Find the first node before we started processing the call expression.
+ while (N) {
+ ProgramPoint L = N->getLocation();
+ BeforeProcessingCall = N;
+ N = N->pred_empty() ? NULL : *(N->pred_begin());
+
+ // Skip the nodes corresponding to the inlined code.
+ if (L.getLocationContext()->getCurrentStackFrame() != CallerSF)
+ continue;
+ // We reached the caller. Find the node right before we started
+ // processing the CallExpr.
+ if (isa<PostPurgeDeadSymbols>(L))
+ continue;
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L))
+ if (SP->getStmt() == CalleeSF->getCallSite())
+ continue;
+ break;
+ }
+
+ if (!BeforeProcessingCall)
+ return false;
+
+ // TODO: Clean up the unneeded nodes.
+
+ // Build an Epsilon node from which we will restart the analyzes.
+ const Stmt *CE = CalleeSF->getCallSite();
+ ProgramPoint NewNodeLoc =
+ EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+ // Add the special flag to GDM to signal retrying with no inlining.
+ // Note, changing the state ensures that we are not going to cache out.
+ ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
+ NewNodeState = NewNodeState->set<ReplayWithoutInlining>((void*)CE);
+
+ // Make the new node a successor of BeforeProcessingCall.
+ bool IsNew = false;
+ ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew);
+ // We cached out at this point. Caching out is common due to us backtracking
+ // from the inlined function, which might spawn several paths.
+ if (!IsNew)
+ return true;
+
+ NewNode->addPredecessor(BeforeProcessingCall, G);
-void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
- GenericNodeBuilder<BlockEntrance> &nodeBuilder){
+ // Add the new node to the work list.
+ Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(),
+ CalleeSF->getIndex());
+ NumTimesRetriedWithoutInlining++;
+ return true;
+}
+
+/// Block entrance. (Update counters).
+void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
+ NodeBuilderWithSinks &nodeBuilder) {
// FIXME: Refactor this into a checker.
- const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock();
- ExplodedNode *pred = nodeBuilder.getPredecessor();
+ ExplodedNode *pred = nodeBuilder.getContext().getPred();
- if (nodeBuilder.getBlockCounter().getNumVisited(
- pred->getLocationContext()->getCurrentStackFrame(),
- block->getBlockID()) >= AMgr.getMaxVisit()) {
+ if (nodeBuilder.getContext().getCurrentBlockCount() >= AMgr.getMaxVisit()) {
static SimpleProgramPointTag tag("ExprEngine : Block count exceeded");
- nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Generic node creation.
-//===----------------------------------------------------------------------===//
+ const ExplodedNode *Sink =
+ nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
+
+ // Check if we stopped at the top level function or not.
+ // Root node should have the location context of the top most function.
+ const LocationContext *CalleeLC = pred->getLocation().getLocationContext();
+ const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame();
+ const LocationContext *RootLC =
+ (*G.roots_begin())->getLocation().getLocationContext();
+ if (RootLC->getCurrentStackFrame() != CalleeSF) {
+ Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl());
+
+ // Re-run the call evaluation without inlining it, by storing the
+ // no-inlining policy in the state and enqueuing the new work item on
+ // the list. Replay should almost never fail. Use the stats to catch it
+ // if it does.
+ if ((!AMgr.NoRetryExhausted && replayWithoutInlining(pred, CalleeLC)))
+ return;
+ NumMaxBlockCountReachedInInlined++;
+ } else
+ NumMaxBlockCountReached++;
-ExplodedNode *ExprEngine::MakeNode(ExplodedNodeSet &Dst, const Stmt *S,
- ExplodedNode *Pred, const ProgramState *St,
- ProgramPoint::Kind K,
- const ProgramPointTag *tag) {
- assert (Builder && "StmtNodeBuilder not present.");
- SaveAndRestore<const ProgramPointTag*> OldTag(Builder->Tag);
- Builder->Tag = tag;
- return Builder->MakeNode(Dst, S, Pred, St, K);
+ // Make sink nodes as exhausted(for stats) only if retry failed.
+ Engine.blocksExhausted.push_back(std::make_pair(L, Sink));
+ }
}
//===----------------------------------------------------------------------===//
// Branch processing.
//===----------------------------------------------------------------------===//
-const ProgramState *ExprEngine::MarkBranch(const ProgramState *state,
- const Stmt *Terminator,
- bool branchTaken) {
+ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state,
+ const Stmt *Terminator,
+ const LocationContext *LCtx,
+ bool branchTaken) {
switch (Terminator->getStmtClass()) {
default:
@@ -867,7 +1098,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state,
(Op == BO_LOr && !branchTaken)
? B->getRHS() : B->getLHS();
- return state->BindExpr(B, UndefinedVal(Ex));
+ return state->BindExpr(B, LCtx, UndefinedVal(Ex));
}
case Stmt::BinaryConditionalOperatorClass:
@@ -885,7 +1116,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state,
else
Ex = C->getFalseExpr();
- return state->BindExpr(C, UndefinedVal(Ex));
+ return state->BindExpr(C, LCtx, UndefinedVal(Ex));
}
case Stmt::ChooseExprClass: { // ?:
@@ -893,7 +1124,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state,
const ChooseExpr *C = cast<ChooseExpr>(Terminator);
const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS();
- return state->BindExpr(C, UndefinedVal(Ex));
+ return state->BindExpr(C, LCtx, UndefinedVal(Ex));
}
}
}
@@ -904,8 +1135,9 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *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(ProgramStateManager& StateMgr,
- const ProgramState *state,
+ ProgramStateRef state,
const Stmt *Condition,
+ const LocationContext *LCtx,
ASTContext &Ctx) {
const Expr *Ex = dyn_cast<Expr>(Condition);
@@ -936,15 +1168,22 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits)
return UnknownVal();
- return state->getSVal(Ex);
+ return state->getSVal(Ex, LCtx);
}
void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
- BranchNodeBuilder& builder) {
+ NodeBuilderContext& BldCtx,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
+ currentBuilderContext = &BldCtx;
// Check for NULL conditions; e.g. "for(;;)"
if (!Condition) {
- builder.markInfeasible(false);
+ BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF);
+ NullCondBldr.markInfeasible(false);
+ NullCondBldr.generateNode(Pred->getState(), true, Pred);
return;
}
@@ -952,65 +1191,84 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
Condition->getLocStart(),
"Error evaluating branch");
- getCheckerManager().runCheckersForBranchCondition(Condition, builder, *this);
-
- // If the branch condition is undefined, return;
- if (!builder.isFeasible(true) && !builder.isFeasible(false))
+ ExplodedNodeSet CheckersOutSet;
+ getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet,
+ Pred, *this);
+ // We generated only sinks.
+ if (CheckersOutSet.empty())
return;
- const ProgramState *PrevState = builder.getState();
- SVal X = PrevState->getSVal(Condition);
-
- if (X.isUnknownOrUndef()) {
- // Give it a chance to recover from unknown.
- if (const Expr *Ex = dyn_cast<Expr>(Condition)) {
- if (Ex->getType()->isIntegerType()) {
- // Try to recover some path-sensitivity. Right now casts of symbolic
- // integers that promote their values are currently not tracked well.
- // If 'Condition' is such an expression, try and recover the
- // underlying value and use that instead.
- SVal recovered = RecoverCastedSymbol(getStateManager(),
- builder.getState(), Condition,
- getContext());
-
- if (!recovered.isUnknown()) {
- X = recovered;
+ BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
+ for (NodeBuilder::iterator I = CheckersOutSet.begin(),
+ E = CheckersOutSet.end(); E != I; ++I) {
+ ExplodedNode *PredI = *I;
+
+ if (PredI->isSink())
+ continue;
+
+ ProgramStateRef PrevState = Pred->getState();
+ SVal X = PrevState->getSVal(Condition, Pred->getLocationContext());
+
+ if (X.isUnknownOrUndef()) {
+ // Give it a chance to recover from unknown.
+ if (const Expr *Ex = dyn_cast<Expr>(Condition)) {
+ if (Ex->getType()->isIntegerType()) {
+ // Try to recover some path-sensitivity. Right now casts of symbolic
+ // integers that promote their values are currently not tracked well.
+ // If 'Condition' is such an expression, try and recover the
+ // underlying value and use that instead.
+ SVal recovered = RecoverCastedSymbol(getStateManager(),
+ PrevState, Condition,
+ Pred->getLocationContext(),
+ getContext());
+
+ if (!recovered.isUnknown()) {
+ X = recovered;
+ }
}
}
}
+
+ const LocationContext *LCtx = PredI->getLocationContext();
+
// If the condition is still unknown, give up.
if (X.isUnknownOrUndef()) {
- builder.generateNode(MarkBranch(PrevState, Term, true), true);
- builder.generateNode(MarkBranch(PrevState, Term, false), false);
- return;
+ builder.generateNode(MarkBranch(PrevState, Term, LCtx, true),
+ true, PredI);
+ builder.generateNode(MarkBranch(PrevState, Term, LCtx, false),
+ false, PredI);
+ continue;
}
- }
- DefinedSVal V = cast<DefinedSVal>(X);
+ DefinedSVal V = cast<DefinedSVal>(X);
- // Process the true branch.
- if (builder.isFeasible(true)) {
- if (const ProgramState *state = PrevState->assume(V, true))
- builder.generateNode(MarkBranch(state, Term, true), true);
- else
- builder.markInfeasible(true);
- }
+ // Process the true branch.
+ if (builder.isFeasible(true)) {
+ if (ProgramStateRef state = PrevState->assume(V, true))
+ builder.generateNode(MarkBranch(state, Term, LCtx, true),
+ true, PredI);
+ else
+ builder.markInfeasible(true);
+ }
- // Process the false branch.
- if (builder.isFeasible(false)) {
- if (const ProgramState *state = PrevState->assume(V, false))
- builder.generateNode(MarkBranch(state, Term, false), false);
- else
- builder.markInfeasible(false);
+ // Process the false branch.
+ if (builder.isFeasible(false)) {
+ if (ProgramStateRef state = PrevState->assume(V, false))
+ builder.generateNode(MarkBranch(state, Term, LCtx, false),
+ false, PredI);
+ else
+ builder.markInfeasible(false);
+ }
}
+ currentBuilderContext = 0;
}
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump.
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
- const ProgramState *state = builder.getState();
- SVal V = state->getSVal(builder.getTarget());
+ ProgramStateRef state = builder.getState();
+ SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext());
// Three possibilities:
//
@@ -1051,18 +1309,20 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
-void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) {
- StateMgr.EndPath(builder.getState());
- getCheckerManager().runCheckersForEndPath(builder, *this);
+void ExprEngine::processEndOfFunction(NodeBuilderContext& BC) {
+ StateMgr.EndPath(BC.Pred->getState());
+ ExplodedNodeSet Dst;
+ getCheckerManager().runCheckersForEndPath(BC, Dst, *this);
+ Engine.enqueueEndOfFunction(Dst);
}
/// ProcessSwitch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
typedef SwitchNodeBuilder::iterator iterator;
- const ProgramState *state = builder.getState();
+ ProgramStateRef state = builder.getState();
const Expr *CondE = builder.getCondition();
- SVal CondV_untested = state->getSVal(CondE);
+ SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext());
if (CondV_untested.isUndef()) {
//ExplodedNode* N = builder.generateDefaultCaseNode(state, true);
@@ -1073,7 +1333,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
}
DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested);
- const ProgramState *DefaultSt = state;
+ ProgramStateRef DefaultSt = state;
iterator I = builder.begin(), EI = builder.end();
bool defaultIsFeasible = I == EI;
@@ -1106,7 +1366,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
CondV, CaseVal);
// Now "assume" that the case matches.
- if (const ProgramState *stateNew = state->assume(Res, true)) {
+ if (ProgramStateRef stateNew = state->assume(Res, true)) {
builder.generateCaseStmtNode(I, stateNew);
// If CondV evaluates to a constant, then we know that this
@@ -1119,7 +1379,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
// Now "assume" that the case doesn't match. Add this state
// to the default state (if it is feasible).
if (DefaultSt) {
- if (const ProgramState *stateNew = DefaultSt->assume(Res, false)) {
+ if (ProgramStateRef stateNew = DefaultSt->assume(Res, false)) {
defaultIsFeasible = true;
DefaultSt = stateNew;
}
@@ -1166,7 +1426,10 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- const ProgramState *state = Pred->getState();
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
assert(Ex->isLValue());
@@ -1181,22 +1444,29 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
V = UnknownVal();
}
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V),
- ProgramPoint::PostLValueKind);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0,
+ ProgramPoint::PostLValueKind);
return;
}
if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!Ex->isLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V));
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
return;
}
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
SVal V = svalBuilder.getFunctionPointer(FD);
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V),
- ProgramPoint::PostLValueKind);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0,
+ ProgramPoint::PostLValueKind);
+ return;
+ }
+ if (isa<FieldDecl>(D)) {
+ // FIXME: Compute lvalue of fields.
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()),
+ false, 0, ProgramPoint::PostLValueKind);
return;
}
+
assert (false &&
"ValueDecl support for this ValueDecl not implemented.");
}
@@ -1213,24 +1483,33 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
ExplodedNodeSet checkerPreStmt;
getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this);
+ StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currentBuilderContext);
+
for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(),
ei = checkerPreStmt.end(); it != ei; ++it) {
- const ProgramState *state = (*it)->getState();
- SVal V = state->getLValue(A->getType(), state->getSVal(Idx),
- state->getSVal(Base));
+ const LocationContext *LCtx = (*it)->getLocationContext();
+ ProgramStateRef state = (*it)->getState();
+ SVal V = state->getLValue(A->getType(),
+ state->getSVal(Idx, LCtx),
+ state->getSVal(Base, LCtx));
assert(A->isLValue());
- MakeNode(Dst, A, *it, state->BindExpr(A, V), ProgramPoint::PostLValueKind);
+ Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V),
+ false, 0, ProgramPoint::PostLValueKind);
}
}
/// VisitMemberExpr - Transfer function for member expressions.
void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
+ ExplodedNodeSet &TopDst) {
+ StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
+ ExplodedNodeSet Dst;
Decl *member = M->getMemberDecl();
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
assert(M->isLValue());
+ Bldr.takeNodes(Pred);
VisitCommonDeclRefExpr(M, VD, Pred, Dst);
+ Bldr.addNodes(Dst);
return;
}
@@ -1239,15 +1518,16 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
return;
Expr *baseExpr = M->getBase()->IgnoreParens();
- const ProgramState *state = Pred->getState();
- SVal baseExprVal = state->getSVal(baseExpr);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal baseExprVal = state->getSVal(baseExpr, Pred->getLocationContext());
if (isa<nonloc::LazyCompoundVal>(baseExprVal) ||
isa<nonloc::CompoundVal>(baseExprVal) ||
// FIXME: This can originate by conjuring a symbol for an unknown
// temporary struct object, see test/Analysis/fields.c:
// (p = getit()).x
isa<nonloc::SymbolVal>(baseExprVal)) {
- MakeNode(Dst, M, Pred, state->BindExpr(M, UnknownVal()));
+ Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, UnknownVal()));
return;
}
@@ -1258,9 +1538,13 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// For all other cases, compute an lvalue.
SVal L = state->getLValue(field, baseExprVal);
if (M->isLValue())
- MakeNode(Dst, M, Pred, state->BindExpr(M, L), ProgramPoint::PostLValueKind);
- else
- evalLoad(Dst, M, Pred, state, L);
+ Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0,
+ ProgramPoint::PostLValueKind);
+ else {
+ Bldr.takeNodes(Pred);
+ evalLoad(Dst, M, M, Pred, state, L);
+ Bldr.addNodes(Dst);
+ }
}
/// evalBind - Handle the semantics of binding a value to a specific location.
@@ -1272,12 +1556,17 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
// Do a previsit of the bind.
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val,
- StoreE, *this);
+ StoreE, *this,
+ ProgramPoint::PostStmtKind);
+ ExplodedNodeSet TmpDst;
+ StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currentBuilderContext);
+
+ const LocationContext *LC = Pred->getLocationContext();
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I!=E; ++I) {
-
- const ProgramState *state = (*I)->getState();
+ ExplodedNode *PredI = *I;
+ ProgramStateRef state = PredI->getState();
if (atDeclInit) {
const VarRegion *VR =
@@ -1288,8 +1577,15 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
state = state->bindLoc(location, Val);
}
- MakeNode(Dst, StoreE, *I, state);
+ const MemRegion *LocReg = 0;
+ if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location))
+ LocReg = LocRegVal->getRegion();
+
+ const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0);
+ Bldr.generateNode(L, PredI, state, false);
}
+
+ Dst.insert(TmpDst);
}
/// evalStore - Handle the semantics of a store via an assignment.
@@ -1303,24 +1599,19 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
const Expr *LocationE,
ExplodedNode *Pred,
- const ProgramState *state, SVal location, SVal Val,
+ ProgramStateRef state, SVal location, SVal Val,
const ProgramPointTag *tag) {
-
- assert(Builder && "StmtNodeBuilder must be defined.");
-
// 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;
if (isa<loc::ObjCPropRef>(location)) {
- loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
- return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(),
- StoreE, Val), Pred, Dst);
+ assert(false);
}
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
- evalLocation(Tmp, LocationE, Pred, state, location, tag, false);
+ evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false);
if (Tmp.empty())
return;
@@ -1328,24 +1619,21 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
if (location.isUndef())
return;
- SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind,
- ProgramPoint::PostStoreKind);
-
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
- evalBind(Dst, StoreE, *NI, location, Val);
+ evalBind(Dst, StoreE, *NI, location, Val, false);
}
-void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex,
- ExplodedNode *Pred,
- const ProgramState *state, SVal location,
- const ProgramPointTag *tag, QualType LoadTy) {
+void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
+ const Expr *NodeEx,
+ const Expr *BoundEx,
+ ExplodedNode *Pred,
+ ProgramStateRef state,
+ SVal location,
+ const ProgramPointTag *tag,
+ QualType LoadTy)
+{
assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
-
- if (isa<loc::ObjCPropRef>(location)) {
- loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
- return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex),
- Pred, Dst);
- }
+ assert(!isa<loc::ObjCPropRef>(location));
// Are we loading from a region? This actually results in two loads; one
// to fetch the address of the referenced value and one to fetch the
@@ -1358,72 +1646,84 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex,
static SimpleProgramPointTag
loadReferenceTag("ExprEngine : Load Reference");
ExplodedNodeSet Tmp;
- evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag,
+ evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state,
+ location, &loadReferenceTag,
getContext().getPointerType(RT->getPointeeType()));
// Perform the load from the referenced value.
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) {
state = (*I)->getState();
- location = state->getSVal(Ex);
- evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy);
+ location = state->getSVal(BoundEx, (*I)->getLocationContext());
+ evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy);
}
return;
}
}
- evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy);
+ evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy);
}
-void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *Ex,
- ExplodedNode *Pred,
- const ProgramState *state, SVal location,
- const ProgramPointTag *tag, QualType LoadTy) {
-
+void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
+ const Expr *NodeEx,
+ const Expr *BoundEx,
+ ExplodedNode *Pred,
+ ProgramStateRef state,
+ SVal location,
+ const ProgramPointTag *tag,
+ QualType LoadTy) {
+ assert(NodeEx);
+ assert(BoundEx);
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
- evalLocation(Tmp, Ex, Pred, state, location, tag, true);
-
+ evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, tag, true);
if (Tmp.empty())
return;
+ StmtNodeBuilder Bldr(Tmp, Dst, *currentBuilderContext);
if (location.isUndef())
return;
- SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind);
-
// Proceed with the load.
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) {
state = (*NI)->getState();
+ const LocationContext *LCtx = (*NI)->getLocationContext();
if (location.isUnknown()) {
// This is important. We must nuke the old binding.
- MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()),
- ProgramPoint::PostLoadKind, tag);
+ Bldr.generateNode(NodeEx, *NI,
+ state->BindExpr(BoundEx, LCtx, UnknownVal()),
+ false, tag,
+ ProgramPoint::PostLoadKind);
}
else {
if (LoadTy.isNull())
- LoadTy = Ex->getType();
+ LoadTy = BoundEx->getType();
SVal V = state->getSVal(cast<Loc>(location), LoadTy);
- MakeNode(Dst, Ex, *NI, state->bindExprAndLocation(Ex, location, V),
- ProgramPoint::PostLoadKind, tag);
+ Bldr.generateNode(NodeEx, *NI,
+ state->bindExprAndLocation(BoundEx, LCtx, location, V),
+ false, tag, ProgramPoint::PostLoadKind);
}
}
}
-void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
- ExplodedNode *Pred,
- const ProgramState *state, SVal location,
- const ProgramPointTag *tag, bool isLoad) {
+void ExprEngine::evalLocation(ExplodedNodeSet &Dst,
+ const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExplodedNode *Pred,
+ ProgramStateRef state,
+ SVal location,
+ const ProgramPointTag *tag,
+ bool isLoad) {
+ StmtNodeBuilder BldrTop(Pred, Dst, *currentBuilderContext);
// Early checks for performance reason.
if (location.isUnknown()) {
- Dst.Add(Pred);
return;
}
ExplodedNodeSet Src;
- if (Pred->getState() == state) {
- Src.Add(Pred);
- } else {
+ BldrTop.takeNodes(Pred);
+ StmtNodeBuilder Bldr(Pred, Src, *currentBuilderContext);
+ if (Pred->getState() != state) {
// Associate this new state with an ExplodedNode.
// FIXME: If I pass null tag, the graph is incorrect, e.g for
// int *p;
@@ -1435,88 +1735,12 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
// FIXME: why is 'tag' not used instead of etag?
static SimpleProgramPointTag etag("ExprEngine: Location");
-
- ExplodedNode *N = Builder->generateNode(S, state, Pred, &etag);
- Src.Add(N ? N : Pred);
+ Bldr.generateNode(NodeEx, Pred, state, false, &etag);
}
- getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S,
- *this);
-}
-
-bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
- ExplodedNode *Pred) {
- return false;
-
- // Inlining isn't correct right now because we:
- // (a) don't generate CallExit nodes.
- // (b) we need a way to postpone doing post-visits of CallExprs until
- // the CallExit. This means we need CallExits for the non-inline
- // cases as well.
-
-#if 0
- const ProgramState *state = Pred->getState();
- const Expr *Callee = CE->getCallee();
- SVal L = state->getSVal(Callee);
-
- const FunctionDecl *FD = L.getAsFunctionDecl();
- if (!FD)
- return false;
-
- // Specially handle CXXMethods.
- const CXXMethodDecl *methodDecl = 0;
-
- switch (CE->getStmtClass()) {
- default: break;
- case Stmt::CXXOperatorCallExprClass: {
- const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE);
- methodDecl =
- dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl());
- break;
- }
- case Stmt::CXXMemberCallExprClass: {
- const CXXMemberCallExpr *memberCall = cast<CXXMemberCallExpr>(CE);
- const MemberExpr *memberExpr =
- cast<MemberExpr>(memberCall->getCallee()->IgnoreParens());
- methodDecl = cast<CXXMethodDecl>(memberExpr->getMemberDecl());
- break;
- }
- }
-
-
-
-
- // Check if the function definition is in the same translation unit.
- if (FD->hasBody(FD)) {
- const StackFrameContext *stackFrame =
- AMgr.getStackFrame(AMgr.getAnalysisContext(FD),
- Pred->getLocationContext(),
- CE, Builder->getBlock(), Builder->getIndex());
- // Now we have the definition of the callee, create a CallEnter node.
- CallEnter Loc(CE, stackFrame, Pred->getLocationContext());
-
- ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
- Dst.Add(N);
- return true;
- }
-
- // Check if we can find the function definition in other translation units.
- if (AMgr.hasIndexer()) {
- AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD);
- if (C == 0)
- return false;
- const StackFrameContext *stackFrame =
- AMgr.getStackFrame(C, Pred->getLocationContext(),
- CE, Builder->getBlock(), Builder->getIndex());
- CallEnter Loc(CE, stackFrame, Pred->getLocationContext());
- ExplodedNode *N = Builder->generateNode(Loc, state, Pred);
- Dst.Add(N);
- return true;
- }
-
- // Generate the CallExit node.
-
- return false;
-#endif
+ ExplodedNodeSet Tmp;
+ getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad,
+ NodeEx, BoundEx, *this);
+ BldrTop.addNodes(Tmp);
}
std::pair<const ProgramPointTag *, const ProgramPointTag*>
@@ -1528,108 +1752,67 @@ ExprEngine::getEagerlyAssumeTags() {
}
void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
- const Expr *Ex) {
-
+ const Expr *Ex) {
+ StmtNodeBuilder Bldr(Src, Dst, *currentBuilderContext);
for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
ExplodedNode *Pred = *I;
-
// Test if the previous node was as the same expression. This can happen
// when the expression fails to evaluate to anything meaningful and
// (as an optimization) we don't generate a node.
ProgramPoint P = Pred->getLocation();
if (!isa<PostStmt>(P) || cast<PostStmt>(P).getStmt() != Ex) {
- Dst.Add(Pred);
continue;
}
- const ProgramState *state = Pred->getState();
- SVal V = state->getSVal(Ex);
- if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) {
+ ProgramStateRef state = Pred->getState();
+ SVal V = state->getSVal(Ex, Pred->getLocationContext());
+ nonloc::SymbolVal *SEV = dyn_cast<nonloc::SymbolVal>(&V);
+ if (SEV && SEV->isExpression()) {
const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags =
getEagerlyAssumeTags();
// First assume that the condition is true.
- if (const ProgramState *StateTrue = state->assume(*SEV, true)) {
+ if (ProgramStateRef StateTrue = state->assume(*SEV, true)) {
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
- StateTrue = StateTrue->BindExpr(Ex, Val);
- Dst.Add(Builder->generateNode(Ex, StateTrue, Pred, tags.first));
+ StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val);
+ Bldr.generateNode(Ex, Pred, StateTrue, false, tags.first);
}
// Next, assume that the condition is false.
- if (const ProgramState *StateFalse = state->assume(*SEV, false)) {
+ if (ProgramStateRef StateFalse = state->assume(*SEV, false)) {
SVal Val = svalBuilder.makeIntVal(0U, Ex->getType());
- StateFalse = StateFalse->BindExpr(Ex, Val);
- Dst.Add(Builder->generateNode(Ex, StateFalse, Pred, tags.second));
+ StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val);
+ Bldr.generateNode(Ex, Pred, StateFalse, false, tags.second);
}
}
- else
- Dst.Add(Pred);
}
}
void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst);
-}
-
-void ExprEngine::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);
- return;
- }
-
- ExplodedNodeSet Tmp;
- Visit(*I, Pred, Tmp);
- ++I;
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ // We have processed both the inputs and the outputs. All of the outputs
+ // should evaluate to Locs. Nuke all of their values.
- for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI)
- VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst);
-}
-
-void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt *A,
- AsmStmt::const_inputs_iterator I,
- AsmStmt::const_inputs_iterator E,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- if (I == E) {
-
- // We have processed both the inputs and the outputs. All of the outputs
- // should evaluate to Locs. Nuke all of their values.
-
- // FIXME: Some day in the future it would be nice to allow a "plug-in"
- // which interprets the inline asm and stores proper results in the
- // outputs.
+ // FIXME: Some day in the future it would be nice to allow a "plug-in"
+ // which interprets the inline asm and stores proper results in the
+ // outputs.
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
- for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(),
- OE = A->end_outputs(); OI != OE; ++OI) {
-
- SVal X = state->getSVal(*OI);
- assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef.
-
- if (isa<Loc>(X))
- state = state->bindLoc(cast<Loc>(X), UnknownVal());
- }
+ for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(),
+ OE = A->end_outputs(); OI != OE; ++OI) {
+ SVal X = state->getSVal(*OI, Pred->getLocationContext());
+ assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef.
- MakeNode(Dst, A, Pred, state);
- return;
+ if (isa<Loc>(X))
+ state = state->bindLoc(cast<Loc>(X), UnknownVal());
}
- ExplodedNodeSet Tmp;
- Visit(*I, Pred, Tmp);
-
- ++I;
-
- for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI)
- VisitAsmStmtHelperInputs(A, I, E, *NI, Dst);
+ Bldr.generateNode(A, Pred, state);
}
-
//===----------------------------------------------------------------------===//
// Visualization.
//===----------------------------------------------------------------------===//
@@ -1694,6 +1877,10 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "CallExit";
break;
+ case ProgramPoint::EpsilonKind:
+ Out << "Epsilon Point";
+ break;
+
default: {
if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) {
const Stmt *S = L->getStmt();
@@ -1811,10 +1998,10 @@ struct DOTGraphTraits<ExplodedNode*> :
}
}
- const ProgramState *state = N->getState();
- Out << "\\|StateID: " << (void*) state
+ ProgramStateRef state = N->getState();
+ Out << "\\|StateID: " << (void*) state.getPtr()
<< " NodeID: " << (void*) N << "\\|";
- state->printDOT(Out, *N->getLocationContext()->getCFG());
+ state->printDOT(Out);
Out << "\\l";
@@ -1852,9 +2039,7 @@ void ExprEngine::ViewGraph(bool trim) {
// Iterate through the reports and get their nodes.
for (BugReporter::EQClasses_iterator
EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) {
- BugReportEquivClass& EQ = *EI;
- const BugReport &R = **EQ.begin();
- ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode());
+ ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode());
if (N) Src.push_back(N);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 68ccc59..93e598a 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -13,7 +13,6 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
@@ -35,38 +34,40 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
it != ei; ++it) {
- const ProgramState *state = (*it)->getState();
- SVal LeftV = state->getSVal(LHS);
- SVal RightV = state->getSVal(RHS);
+ ProgramStateRef state = (*it)->getState();
+ const LocationContext *LCtx = (*it)->getLocationContext();
+ SVal LeftV = state->getSVal(LHS, LCtx);
+ SVal RightV = state->getSVal(RHS, LCtx);
BinaryOperator::Opcode Op = B->getOpcode();
if (Op == BO_Assign) {
// EXPERIMENTAL: "Conjured" symbols.
// FIXME: Handle structs.
- if (RightV.isUnknown() ||
- !getConstraintManager().canReasonAbout(RightV)) {
- unsigned Count = Builder->getCurrentBlockCount();
- RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count);
+ if (RightV.isUnknown()) {
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx, Count);
}
// Simulate the effects of a "store": bind the value of the RHS
// to the L-Value represented by the LHS.
SVal ExprVal = B->isLValue() ? LeftV : RightV;
- evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, ExprVal), LeftV, RightV);
+ evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal),
+ LeftV, RightV);
continue;
}
if (!B->isAssignmentOp()) {
+ StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext);
// Process non-assignments except commas or short-circuited
// logical expressions (LAnd and LOr).
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
if (Result.isUnknown()) {
- MakeNode(Tmp2, B, *it, state);
+ Bldr.generateNode(B, *it, state);
continue;
}
- state = state->BindExpr(B, Result);
- MakeNode(Tmp2, B, *it, state);
+ state = state->BindExpr(B, LCtx, Result);
+ Bldr.generateNode(B, *it, state);
continue;
}
@@ -91,13 +92,14 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// null dereferences, and so on.
ExplodedNodeSet Tmp;
SVal location = LeftV;
- evalLoad(Tmp, LHS, *it, state, location);
+ evalLoad(Tmp, B, LHS, *it, state, location);
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
++I) {
state = (*I)->getState();
- SVal V = state->getSVal(LHS);
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ SVal V = state->getSVal(LHS, LCtx);
// Get the computation type.
QualType CTy =
@@ -122,16 +124,15 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
SVal LHSVal;
- if (Result.isUnknown() ||
- !getConstraintManager().canReasonAbout(Result)) {
+ if (Result.isUnknown()) {
- unsigned Count = Builder->getCurrentBlockCount();
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
// The symbolic value is actually for the type of the left-hand side
// expression, not the computation type, as this is the value the
// LValue on the LHS will bind to.
- LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy,
- Count);
+ LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx,
+ LTy, Count);
// However, we need to convert the symbol to the computation type.
Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
@@ -145,9 +146,9 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// In C++, assignment and compound assignment operators return an
// lvalue.
if (B->isLValue())
- state = state->BindExpr(B, location);
+ state = state->BindExpr(B, LCtx, location);
else
- state = state->BindExpr(B, Result);
+ state = state->BindExpr(B, LCtx, Result);
evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
}
@@ -165,8 +166,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
Pred->getLocationContext());
ExplodedNodeSet Tmp;
- MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V),
- ProgramPoint::PostLValueKind);
+ StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
+ Bldr.generateNode(BE, Pred,
+ Pred->getState()->BindExpr(BE, Pred->getLocationContext(),
+ V),
+ false, 0,
+ ProgramPoint::PostLValueKind);
// FIXME: Move all post/pre visits to ::Visit().
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
@@ -178,13 +183,13 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
ExplodedNodeSet dstPreStmt;
getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
- if (CastE->getCastKind() == CK_LValueToRValue ||
- CastE->getCastKind() == CK_GetObjCProperty) {
+ if (CastE->getCastKind() == CK_LValueToRValue) {
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
I!=E; ++I) {
ExplodedNode *subExprNode = *I;
- const ProgramState *state = subExprNode->getState();
- evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex));
+ ProgramStateRef state = subExprNode->getState();
+ const LocationContext *LCtx = subExprNode->getLocationContext();
+ evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx));
}
return;
}
@@ -196,6 +201,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
T = ExCast->getTypeAsWritten();
+ StmtNodeBuilder Bldr(dstPreStmt, Dst, *currentBuilderContext);
for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
I != E; ++I) {
@@ -204,10 +210,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
switch (CastE->getCastKind()) {
case CK_LValueToRValue:
llvm_unreachable("LValueToRValue casts handled earlier.");
- case CK_GetObjCProperty:
- llvm_unreachable("GetObjCProperty casts handled earlier.");
case CK_ToVoid:
- Dst.Add(Pred);
continue;
// The analyzer doesn't do anything special with these casts,
// since it understands retain/release semantics already.
@@ -215,14 +218,21 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_ARCConsumeObject:
case CK_ARCReclaimReturnedObject:
case CK_ARCExtendBlockObject: // Fall-through.
+ case CK_CopyAndAutoreleaseBlockObject:
+ // The analyser can ignore atomic casts for now, although some future
+ // checkers may want to make certain that you're not modifying the same
+ // value through atomic and nonatomic pointers.
+ case CK_AtomicToNonAtomic:
+ case CK_NonAtomicToAtomic:
// True no-ops.
case CK_NoOp:
case CK_FunctionToPointerDecay: {
// Copy the SVal of Ex to CastE.
- const ProgramState *state = Pred->getState();
- SVal V = state->getSVal(Ex);
- state = state->BindExpr(CastE, V);
- MakeNode(Dst, CastE, Pred, state);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = state->getSVal(Ex, LCtx);
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
continue;
}
case CK_Dependent:
@@ -254,30 +264,76 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_AnyPointerToBlockPointerCast:
case CK_ObjCObjectLValueCast: {
// Delegate to SValBuilder to process.
- const ProgramState *state = Pred->getState();
- SVal V = state->getSVal(Ex);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = state->getSVal(Ex, LCtx);
V = svalBuilder.evalCast(V, T, ExTy);
- state = state->BindExpr(CastE, V);
- MakeNode(Dst, CastE, Pred, state);
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
continue;
}
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase: {
// For DerivedToBase cast, delegate to the store manager.
- const ProgramState *state = Pred->getState();
- SVal val = state->getSVal(Ex);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal val = state->getSVal(Ex, LCtx);
val = getStoreManager().evalDerivedToBase(val, T);
- state = state->BindExpr(CastE, val);
- MakeNode(Dst, CastE, Pred, state);
+ state = state->BindExpr(CastE, LCtx, val);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Handle C++ dyn_cast.
+ case CK_Dynamic: {
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal val = state->getSVal(Ex, LCtx);
+
+ // Compute the type of the result.
+ QualType resultType = CastE->getType();
+ if (CastE->isLValue())
+ resultType = getContext().getPointerType(resultType);
+
+ bool Failed = false;
+
+ // Check if the value being cast evaluates to 0.
+ if (val.isZeroConstant())
+ Failed = true;
+ // Else, evaluate the cast.
+ else
+ val = getStoreManager().evalDynamicCast(val, T, Failed);
+
+ if (Failed) {
+ if (T->isReferenceType()) {
+ // A bad_cast exception is thrown if input value is a reference.
+ // Currently, we model this, by generating a sink.
+ Bldr.generateNode(CastE, Pred, state, true);
+ continue;
+ } else {
+ // If the cast fails on a pointer, bind to 0.
+ state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull());
+ }
+ } else {
+ // If we don't know if the cast succeeded, conjure a new symbol.
+ if (val.isUnknown()) {
+ DefinedOrUnknownSVal NewSym = svalBuilder.getConjuredSymbolVal(NULL,
+ CastE, LCtx, resultType,
+ currentBuilderContext->getCurrentBlockCount());
+ state = state->BindExpr(CastE, LCtx, NewSym);
+ } else
+ // Else, bind to the derived region value.
+ state = state->BindExpr(CastE, LCtx, val);
+ }
+ Bldr.generateNode(CastE, Pred, state);
continue;
}
- // Various C++ casts that are not handled yet.
- case CK_Dynamic:
+ // Various C++ casts that are not handled yet.
case CK_ToUnion:
case CK_BaseToDerived:
case CK_NullToMemberPointer:
case CK_BaseToDerivedMemberPointer:
case CK_DerivedToBaseMemberPointer:
+ case CK_ReinterpretMemberPointer:
case CK_UserDefinedConversion:
case CK_ConstructorConversion:
case CK_VectorSplat:
@@ -286,13 +342,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
QualType resultType = CastE->getType();
if (CastE->isLValue())
resultType = getContext().getPointerType(resultType);
-
- SVal result =
- svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType,
- Builder->getCurrentBlockCount());
-
- const ProgramState *state = Pred->getState()->BindExpr(CastE, result);
- MakeNode(Dst, CastE, Pred, state);
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx,
+ resultType, currentBuilderContext->getCurrentBlockCount());
+ ProgramStateRef state = Pred->getState()->BindExpr(CastE, LCtx,
+ result);
+ Bldr.generateNode(CastE, Pred, state);
continue;
}
}
@@ -302,18 +357,20 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
+
const InitListExpr *ILE
= cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
- const ProgramState *state = Pred->getState();
- SVal ILV = state->getSVal(ILE);
+ ProgramStateRef state = Pred->getState();
+ SVal ILV = state->getSVal(ILE, Pred->getLocationContext());
const LocationContext *LC = Pred->getLocationContext();
state = state->bindCompoundLiteral(CL, LC, ILV);
if (CL->isLValue())
- MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC)));
+ B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC)));
else
- MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV));
+ B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
}
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
@@ -326,29 +383,32 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
// Assumption: The CFG has one DeclStmt per Decl.
const Decl *D = *DS->decl_begin();
- if (!D || !isa<VarDecl>(D))
+ if (!D || !isa<VarDecl>(D)) {
+ //TODO:AZ: remove explicit insertion after refactoring is done.
+ Dst.insert(Pred);
return;
+ }
// FIXME: all pre/post visits should eventually be handled by ::Visit().
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
+ StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext);
const VarDecl *VD = dyn_cast<VarDecl>(D);
-
for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
I!=E; ++I) {
ExplodedNode *N = *I;
- const ProgramState *state = N->getState();
+ ProgramStateRef state = N->getState();
// Decls without InitExpr are not initialized explicitly.
const LocationContext *LC = N->getLocationContext();
if (const Expr *InitEx = VD->getInit()) {
- SVal InitVal = state->getSVal(InitEx);
+ SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext());
// We bound the temp obj region to the CXXConstructExpr. Now recover
// the lazy compound value when the variable is not a reference.
- if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() &&
+ if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() &&
!VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
assert(isa<nonloc::LazyCompoundVal>(InitVal));
@@ -356,40 +416,46 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
// Recover some path-sensitivity if a scalar value evaluated to
// UnknownVal.
- if ((InitVal.isUnknown() ||
- !getConstraintManager().canReasonAbout(InitVal)) &&
- !VD->getType()->isReferenceType()) {
- InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx,
- Builder->getCurrentBlockCount());
+ if (InitVal.isUnknown()) {
+ QualType Ty = InitEx->getType();
+ if (InitEx->isLValue()) {
+ Ty = getContext().getPointerType(Ty);
+ }
+
+ InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty,
+ currentBuilderContext->getCurrentBlockCount());
}
-
- evalBind(Dst, DS, N, state->getLValue(VD, LC), InitVal, true);
+ B.takeNodes(N);
+ ExplodedNodeSet Dst2;
+ evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
+ B.addNodes(Dst2);
}
else {
- MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC)));
+ B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC)));
}
}
}
void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
assert(B->getOpcode() == BO_LAnd ||
B->getOpcode() == BO_LOr);
-
- const ProgramState *state = Pred->getState();
- SVal X = state->getSVal(B);
+
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal X = state->getSVal(B, LCtx);
assert(X.isUndef());
const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
assert(Ex);
if (Ex == B->getRHS()) {
- X = state->getSVal(Ex);
+ X = state->getSVal(Ex, LCtx);
// Handle undefined values.
if (X.isUndef()) {
- MakeNode(Dst, B, Pred, state->BindExpr(B, X));
+ Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
return;
}
@@ -401,13 +467,15 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
// value later when necessary. We don't have the machinery in place for
// this right now, and since most logical expressions are used for branches,
// the payoff is not likely to be large. Instead, we do eager evaluation.
- if (const ProgramState *newState = state->assume(XD, true))
- MakeNode(Dst, B, Pred,
- newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType())));
+ if (ProgramStateRef newState = state->assume(XD, true))
+ Bldr.generateNode(B, Pred,
+ newState->BindExpr(B, LCtx,
+ svalBuilder.makeIntVal(1U, B->getType())));
- if (const ProgramState *newState = state->assume(XD, false))
- MakeNode(Dst, B, Pred,
- newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType())));
+ if (ProgramStateRef newState = state->assume(XD, false))
+ Bldr.generateNode(B, Pred,
+ newState->BindExpr(B, LCtx,
+ svalBuilder.makeIntVal(0U, B->getType())));
}
else {
// We took the LHS expression. Depending on whether we are '&&' or
@@ -415,15 +483,17 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
// the short-circuiting.
X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
B->getType());
- MakeNode(Dst, B, Pred, state->BindExpr(B, X));
+ Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
}
}
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
QualType T = getContext().getCanonicalType(IE->getType());
unsigned NumInitElements = IE->getNumInits();
@@ -434,24 +504,27 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
// e.g: static int* myArray[] = {};
if (NumInitElements == 0) {
SVal V = svalBuilder.makeCompoundVal(T, vals);
- MakeNode(Dst, IE, Pred, state->BindExpr(IE, V));
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
return;
}
for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
ei = IE->rend(); it != ei; ++it) {
- vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals);
+ vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it), LCtx),
+ vals);
}
- MakeNode(Dst, IE, Pred,
- state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals)));
+ B.generateNode(IE, Pred,
+ state->BindExpr(IE, LCtx,
+ svalBuilder.makeCompoundVal(T, vals)));
return;
}
if (Loc::isLocType(T) || T->isIntegerType()) {
assert(IE->getNumInits() == 1);
const Expr *initEx = IE->getInit(0);
- MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx)));
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx,
+ state->getSVal(initEx, LCtx)));
return;
}
@@ -463,33 +536,35 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
const Expr *R,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
- const ProgramState *state = Pred->getState();
- SVal X = state->getSVal(Ex);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal X = state->getSVal(Ex, LCtx);
assert (X.isUndef());
const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
assert(SE);
- X = state->getSVal(SE);
+ X = state->getSVal(SE, LCtx);
// Make sure that we invalidate the previous binding.
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true));
+ B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true));
}
void ExprEngine::
VisitOffsetOfExpr(const OffsetOfExpr *OOE,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
- Expr::EvalResult Res;
- if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) {
- const APSInt &IV = Res.Val.getInt();
+ StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
+ APSInt IV;
+ if (OOE->EvaluateAsInt(IV, getContext())) {
assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
assert(OOE->getType()->isIntegerType());
assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType());
SVal X = svalBuilder.makeIntVal(IV);
- MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X));
- return;
+ B.generateNode(OOE, Pred,
+ Pred->getState()->BindExpr(OOE, Pred->getLocationContext(),
+ X));
}
// FIXME: Handle the case where __builtin_offsetof is not a constant.
- Dst.Add(Pred);
}
@@ -497,6 +572,7 @@ void ExprEngine::
VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
QualType T = Ex->getTypeOfArgument();
@@ -506,78 +582,69 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
// FIXME: Add support for VLA type arguments and VLA expressions.
// When that happens, we should probably refactor VLASizeChecker's code.
- Dst.Add(Pred);
return;
}
else if (T->getAs<ObjCObjectType>()) {
// Some code tries to take the sizeof an ObjCObjectType, relying that
// the compiler has laid out its representation. Just report Unknown
// for these.
- Dst.Add(Pred);
return;
}
}
- Expr::EvalResult Result;
- Ex->Evaluate(Result, getContext());
- CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue());
+ APSInt Value = Ex->EvaluateKnownConstInt(getContext());
+ CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
- const ProgramState *state = Pred->getState();
- state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(),
+ ProgramStateRef state = Pred->getState();
+ state = state->BindExpr(Ex, Pred->getLocationContext(),
+ svalBuilder.makeIntVal(amt.getQuantity(),
Ex->getType()));
- MakeNode(Dst, Ex, Pred, state);
+ Bldr.generateNode(Ex, Pred, state);
}
void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
switch (U->getOpcode()) {
- default:
+ default: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet Tmp;
+ VisitIncrementDecrementOperator(U, Pred, Tmp);
+ Bldr.addNodes(Tmp);
+ }
break;
case UO_Real: {
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
-
- // FIXME: We don't have complex SValues yet.
- if (Ex->getType()->isAnyComplexType()) {
- // Just report "Unknown."
- Dst.Add(*I);
- continue;
- }
- // For all other types, UO_Real is an identity operation.
- assert (U->getType() == Ex->getType());
- const ProgramState *state = (*I)->getState();
- MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ break;
}
-
- return;
+
+ // For all other types, UO_Real is an identity operation.
+ assert (U->getType() == Ex->getType());
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx,
+ state->getSVal(Ex, LCtx)));
+ break;
}
- case UO_Imag: {
-
+ case UO_Imag: {
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- // FIXME: We don't have complex SValues yet.
- if (Ex->getType()->isAnyComplexType()) {
- // Just report "Unknown."
- Dst.Add(*I);
- continue;
- }
-
- // For all other types, UO_Imag returns 0.
- const ProgramState *state = (*I)->getState();
- SVal X = svalBuilder.makeZeroVal(Ex->getType());
- MakeNode(Dst, U, *I, state->BindExpr(U, X));
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ break;
}
-
- return;
+ // For all other types, UO_Imag returns 0.
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal X = svalBuilder.makeZeroVal(Ex->getType());
+ Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, X));
+ break;
}
case UO_Plus:
@@ -586,22 +653,19 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
case UO_Deref:
case UO_AddrOf:
case UO_Extension: {
-
+ // FIXME: We can probably just have some magic in Environment::getSVal()
+ // that propagates values, instead of creating a new node here.
+ //
// 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.
-
+ // subexpression.
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- const ProgramState *state = (*I)->getState();
- MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
- }
-
- return;
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx,
+ state->getSVal(Ex, LCtx)));
+ break;
}
case UO_LNot:
@@ -609,144 +673,139 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
case UO_Not: {
assert (!U->isLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- const ProgramState *state = (*I)->getState();
-
- // Get the value of the subexpression.
- SVal V = state->getSVal(Ex);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
- if (V.isUnknownOrUndef()) {
- MakeNode(Dst, U, *I, state->BindExpr(U, V));
- continue;
- }
+ // Get the value of the subexpression.
+ SVal V = state->getSVal(Ex, LCtx);
- switch (U->getOpcode()) {
- default:
- llvm_unreachable("Invalid Opcode.");
-
- case UO_Not:
- // FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, evalComplement(cast<NonLoc>(V)));
- break;
-
- case UO_Minus:
- // FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, evalMinus(cast<NonLoc>(V)));
- break;
-
- case UO_LNot:
-
- // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
- //
- // Note: technically we do "E == 0", but this is the same in the
- // transfer functions as "0 == E".
- SVal Result;
-
- if (isa<Loc>(V)) {
- Loc X = svalBuilder.makeNull();
- Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X,
- U->getType());
- }
- else {
- nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
- Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
- U->getType());
- }
-
- state = state->BindExpr(U, Result);
-
- break;
- }
+ if (V.isUnknownOrUndef()) {
+ Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, V));
+ break;
+ }
- MakeNode(Dst, U, *I, state);
+ switch (U->getOpcode()) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+ case UO_Not:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, LCtx, evalComplement(cast<NonLoc>(V)));
+ break;
+ case UO_Minus:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, LCtx, evalMinus(cast<NonLoc>(V)));
+ break;
+ case UO_LNot:
+ // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
+ //
+ // Note: technically we do "E == 0", but this is the same in the
+ // transfer functions as "0 == E".
+ SVal Result;
+ if (isa<Loc>(V)) {
+ Loc X = svalBuilder.makeNull();
+ Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X,
+ U->getType());
+ }
+ else {
+ nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
+ Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
+ U->getType());
+ }
+
+ state = state->BindExpr(U, LCtx, Result);
+ break;
}
-
- return;
+ Bldr.generateNode(U, Pred, state);
+ break;
}
}
-
+
+}
+
+void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
// Handle ++ and -- (both pre- and post-increment).
assert (U->isIncrementDecrementOp());
- ExplodedNodeSet Tmp;
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- Visit(Ex, Pred, Tmp);
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef state = Pred->getState();
+ SVal loc = state->getSVal(Ex, LCtx);
+
+ // Perform a load.
+ ExplodedNodeSet Tmp;
+ evalLoad(Tmp, U, Ex, Pred, state, loc);
+
+ ExplodedNodeSet Dst2;
+ StmtNodeBuilder Bldr(Tmp, Dst2, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) {
- const ProgramState *state = (*I)->getState();
- SVal loc = state->getSVal(Ex);
+ state = (*I)->getState();
+ assert(LCtx == (*I)->getLocationContext());
+ SVal V2_untested = state->getSVal(Ex, LCtx);
- // Perform a load.
- ExplodedNodeSet Tmp2;
- evalLoad(Tmp2, Ex, *I, state, loc);
+ // Propagate unknown and undefined values.
+ if (V2_untested.isUnknownOrUndef()) {
+ Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested));
+ continue;
+ }
+ DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
- for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) {
-
- state = (*I2)->getState();
- SVal V2_untested = state->getSVal(Ex);
-
- // Propagate unknown and undefined values.
- if (V2_untested.isUnknownOrUndef()) {
- MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested));
- continue;
- }
- DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
-
- // Handle all other values.
- BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add
- : BO_Sub;
-
- // If the UnaryOperator has non-location type, use its type to create the
- // constant value. If the UnaryOperator has location type, create the
- // constant with int type and pointer width.
- SVal RHS;
-
- if (U->getType()->isAnyPointerType())
- RHS = svalBuilder.makeArrayIndex(1);
- else
- RHS = svalBuilder.makeIntVal(1, U->getType());
-
- SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
-
- // Conjure a new symbol if necessary to recover precision.
- if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){
- DefinedOrUnknownSVal SymVal =
- svalBuilder.getConjuredSymbolVal(NULL, Ex,
- Builder->getCurrentBlockCount());
- Result = SymVal;
+ // Handle all other values.
+ BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub;
+
+ // If the UnaryOperator has non-location type, use its type to create the
+ // constant value. If the UnaryOperator has location type, create the
+ // constant with int type and pointer width.
+ SVal RHS;
+
+ if (U->getType()->isAnyPointerType())
+ RHS = svalBuilder.makeArrayIndex(1);
+ else
+ RHS = svalBuilder.makeIntVal(1, U->getType());
+
+ SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
+
+ // Conjure a new symbol if necessary to recover precision.
+ if (Result.isUnknown()){
+ DefinedOrUnknownSVal SymVal =
+ svalBuilder.getConjuredSymbolVal(NULL, Ex, LCtx,
+ currentBuilderContext->getCurrentBlockCount());
+ Result = SymVal;
+
+ // If the value is a location, ++/-- should always preserve
+ // non-nullness. Check if the original value was non-null, and if so
+ // propagate that constraint.
+ if (Loc::isLocType(U->getType())) {
+ DefinedOrUnknownSVal Constraint =
+ svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
- // If the value is a location, ++/-- should always preserve
- // non-nullness. Check if the original value was non-null, and if so
- // propagate that constraint.
- if (Loc::isLocType(U->getType())) {
- DefinedOrUnknownSVal Constraint =
- svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
+ if (!state->assume(Constraint, true)) {
+ // It isn't feasible for the original value to be null.
+ // Propagate this constraint.
+ Constraint = svalBuilder.evalEQ(state, SymVal,
+ svalBuilder.makeZeroVal(U->getType()));
- if (!state->assume(Constraint, true)) {
- // It isn't feasible for the original value to be null.
- // Propagate this constraint.
- Constraint = svalBuilder.evalEQ(state, SymVal,
- svalBuilder.makeZeroVal(U->getType()));
-
-
- state = state->assume(Constraint, false);
- assert(state);
- }
+
+ state = state->assume(Constraint, false);
+ assert(state);
}
}
-
- // Since the lvalue-to-rvalue conversion is explicit in the AST,
- // we bind an l-value if the operator is prefix and an lvalue (in C++).
- if (U->isLValue())
- state = state->BindExpr(U, loc);
- else
- state = state->BindExpr(U, U->isPostfix() ? V2 : Result);
-
- // Perform the store.
- evalStore(Dst, NULL, U, *I2, state, loc, Result);
}
+
+ // Since the lvalue-to-rvalue conversion is explicit in the AST,
+ // we bind an l-value if the operator is prefix and an lvalue (in C++).
+ if (U->isLValue())
+ state = state->BindExpr(U, LCtx, loc);
+ else
+ state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
+
+ // Perform the store.
+ Bldr.takeNodes(*I);
+ ExplodedNodeSet Dst3;
+ evalStore(Dst3, U, U, *I, state, loc, Result);
+ Bldr.addNodes(Dst3);
}
+ Dst.insert(Dst2);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index acb0074..a14a491 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -16,81 +16,11 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/StmtCXX.h"
using namespace clang;
using namespace ento;
-namespace {
-class CallExprWLItem {
-public:
- CallExpr::const_arg_iterator I;
- ExplodedNode *N;
-
- CallExprWLItem(const CallExpr::const_arg_iterator &i, ExplodedNode *n)
- : I(i), N(n) {}
-};
-}
-
-void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE,
- const FunctionProtoType *FnType,
- ExplodedNode *Pred, ExplodedNodeSet &Dst,
- bool FstArgAsLValue) {
-
-
- SmallVector<CallExprWLItem, 20> WorkList;
- WorkList.reserve(AE - AI);
- WorkList.push_back(CallExprWLItem(AI, Pred));
-
- while (!WorkList.empty()) {
- CallExprWLItem Item = WorkList.back();
- WorkList.pop_back();
-
- if (Item.I == AE) {
- Dst.insert(Item.N);
- continue;
- }
-
- // Evaluate the argument.
- ExplodedNodeSet Tmp;
- if (FstArgAsLValue) {
- FstArgAsLValue = false;
- }
-
- Visit(*Item.I, Item.N, Tmp);
- ++(Item.I);
- for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI != NE; ++NI)
- WorkList.push_back(CallExprWLItem(Item.I, *NI));
- }
-}
-
-void ExprEngine::evalCallee(const CallExpr *callExpr,
- const ExplodedNodeSet &src,
- ExplodedNodeSet &dest) {
-
- const Expr *callee = 0;
-
- switch (callExpr->getStmtClass()) {
- case Stmt::CXXMemberCallExprClass: {
- // Evaluate the implicit object argument that is the recipient of the
- // call.
- callee = cast<CXXMemberCallExpr>(callExpr)->getImplicitObjectArgument();
-
- // FIXME: handle member pointers.
- if (!callee)
- return;
-
- break;
- }
- default: {
- callee = callExpr->getCallee()->IgnoreParens();
- break;
- }
- }
-
- for (ExplodedNodeSet::iterator i = src.begin(), e = src.end(); i != e; ++i)
- Visit(callee, *i, dest);
-}
-
const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D,
const StackFrameContext *SFC) {
const Type *T = D->getTypeForDecl();
@@ -107,19 +37,26 @@ const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl,
void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens();
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
// Bind the temporary object to the value of the expression. Then bind
// the expression to the location of the object.
- SVal V = state->getSVal(tempExpr);
+ SVal V = state->getSVal(tempExpr, Pred->getLocationContext());
const MemRegion *R =
- svalBuilder.getRegionManager().getCXXTempObjectRegion(ME,
- Pred->getLocationContext());
+ svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx);
state = state->bindLoc(loc::MemRegionVal(R), V);
- MakeNode(Dst, ME, Pred, state->BindExpr(ME, loc::MemRegionVal(R)));
+ Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R)));
+}
+
+void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ VisitCXXConstructExpr(expr, 0, Pred, Dst);
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
@@ -127,8 +64,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
+#if 0
const CXXConstructorDecl *CD = E->getConstructor();
assert(CD);
+#endif
#if 0
if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
@@ -136,26 +75,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
return;
#endif
- // Evaluate other arguments.
- ExplodedNodeSet argsEvaluated;
- const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>();
- evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated);
-
#if 0
// Is the constructor elidable?
if (E->isElidable()) {
- VisitAggExpr(E->getArg(0), destNodes, Pred, Dst);
- // FIXME: this is here to force propagation if VisitAggExpr doesn't
- if (destNodes.empty())
- destNodes.Add(Pred);
+ destNodes.Add(Pred);
return;
}
#endif
// Perform the previsit of the constructor.
- ExplodedNodeSet destPreVisit;
- getCheckerManager().runCheckersForPreStmt(destPreVisit, argsEvaluated, E,
- *this);
+ ExplodedNodeSet SrcNodes;
+ SrcNodes.Add(Pred);
+ ExplodedNodeSet TmpNodes;
+ getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this);
// Evaluate the constructor. Currently we don't now allow checker-specific
// implementations of specific constructors (as we do with ordinary
@@ -174,7 +106,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
// parameter region.
const StackFrameContext *SFC =
AMgr.getStackFrame(CD, Pred->getLocationContext(),
- E, Builder->getBlock(), Builder->getIndex());
+ E, currentBuilderContext->getBlock(),
+ currentStmtIdx);
// Create the 'this' region.
const CXXThisRegion *ThisR =
@@ -182,34 +115,33 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
CallEnter Loc(E, SFC, Pred->getLocationContext());
-
- for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(),
- NE = argsEvaluated.end(); NI != NE; ++NI) {
- const ProgramState *state = (*NI)->getState();
+ StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator NI = SrcNodes.begin(),
+ NE = SrcNodes.end(); NI != NE; ++NI) {
+ ProgramStateRef state = (*NI)->getState();
// Setup 'this' region, so that the ctor is evaluated on the object pointed
// by 'Dest'.
state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
- if (ExplodedNode *N = Builder->generateNode(Loc, state, *NI))
- destNodes.Add(N);
+ Bldr.generateNode(Loc, *NI, state);
}
}
#endif
// Default semantics: invalidate all regions passed as arguments.
ExplodedNodeSet destCall;
-
- for (ExplodedNodeSet::iterator
- i = destPreVisit.begin(), e = destPreVisit.end();
- i != e; ++i)
{
- ExplodedNode *Pred = *i;
- const LocationContext *LC = Pred->getLocationContext();
- const ProgramState *state = Pred->getState();
+ StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end();
+ i != e; ++i)
+ {
+ ExplodedNode *Pred = *i;
+ const LocationContext *LC = Pred->getLocationContext();
+ ProgramStateRef state = Pred->getState();
- state = invalidateArguments(state, CallOrObjCMessage(E, state), LC);
- Builder->MakeNode(destCall, E, Pred, state);
+ state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC);
+ Bldr.generateNode(E, Pred, state);
+ }
}
-
// Do the post visit.
getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this);
}
@@ -219,31 +151,33 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
const Stmt *S,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
return;
+
// Create the context for 'this' region.
- const StackFrameContext *SFC = AMgr.getStackFrame(DD,
- Pred->getLocationContext(),
- S, Builder->getBlock(),
- Builder->getIndex());
+ const StackFrameContext *SFC =
+ AnalysisDeclContexts.getContext(DD)->
+ getStackFrame(Pred->getLocationContext(), S,
+ currentBuilderContext->getBlock(), currentStmtIdx);
const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC);
CallEnter PP(S, SFC, Pred->getLocationContext());
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
- ExplodedNode *N = Builder->generateNode(PP, state, Pred);
- if (N)
- Dst.Add(N);
+ Bldr.generateNode(PP, Pred, state);
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
- unsigned blockCount = Builder->getCurrentBlockCount();
+ unsigned blockCount = currentBuilderContext->getCurrentBlockCount();
+ const LocationContext *LCtx = Pred->getLocationContext();
DefinedOrUnknownSVal symVal =
- svalBuilder.getConjuredSymbolVal(NULL, CNE, CNE->getType(), blockCount);
+ svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount);
const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
const ElementRegion *EleReg =
@@ -252,26 +186,31 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- const ProgramState *state = Pred->getState();
- state = state->BindExpr(CNE, loc::MemRegionVal(EleReg));
- MakeNode(Dst, CNE, Pred, state);
+ ProgramStateRef state = Pred->getState();
+ state = state->BindExpr(CNE, Pred->getLocationContext(),
+ loc::MemRegionVal(EleReg));
+ Bldr.generateNode(CNE, Pred, state);
return;
}
+ // FIXME: Update for AST changes.
+#if 0
// Evaluate constructor arguments.
const FunctionProtoType *FnType = NULL;
const CXXConstructorDecl *CD = CNE->getConstructor();
if (CD)
FnType = CD->getType()->getAs<FunctionProtoType>();
ExplodedNodeSet argsEvaluated;
+ Bldr.takeNodes(Pred);
evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(),
FnType, Pred, argsEvaluated);
+ Bldr.addNodes(argsEvaluated);
// Initialize the object region and bind the 'new' expression.
for (ExplodedNodeSet::iterator I = argsEvaluated.begin(),
E = argsEvaluated.end(); I != E; ++I) {
- const ProgramState *state = (*I)->getState();
+ ProgramStateRef state = (*I)->getState();
// Accumulate list of regions that are invalidated.
// FIXME: Eventually we should unify the logic for constructor
@@ -281,7 +220,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end();
ai != ae; ++ai)
{
- SVal val = state->getSVal(*ai);
+ SVal val = state->getSVal(*ai, (*I)->getLocationContext());
if (const MemRegion *region = val.getAsRegion())
regionsToInvalidate.push_back(region);
}
@@ -289,18 +228,21 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (ObjTy->isRecordType()) {
regionsToInvalidate.push_back(EleReg);
// Invalidate the regions.
+ // TODO: Pass the call to new information as the last argument, to limit
+ // the globals which will get invalidated.
state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0,
- /* invalidateGlobals = */ true);
+ CNE, blockCount, 0, 0);
} else {
// Invalidate the regions.
+ // TODO: Pass the call to new information as the last argument, to limit
+ // the globals which will get invalidated.
state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0,
- /* invalidateGlobals = */ true);
+ CNE, blockCount, 0, 0);
if (CNE->hasInitializer()) {
- SVal V = state->getSVal(*CNE->constructor_arg_begin());
+ SVal V = state->getSVal(*CNE->constructor_arg_begin(),
+ (*I)->getLocationContext());
state = state->bindLoc(loc::MemRegionVal(EleReg), V);
} else {
// Explicitly set to undefined, because currently we retrieve symbolic
@@ -308,32 +250,51 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal());
}
}
- state = state->BindExpr(CNE, loc::MemRegionVal(EleReg));
- MakeNode(Dst, CNE, *I, state);
+ state = state->BindExpr(CNE, (*I)->getLocationContext(),
+ loc::MemRegionVal(EleReg));
+ Bldr.generateNode(CNE, *I, state);
}
+#endif
}
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
- ExplodedNode *Pred,ExplodedNodeSet &Dst) {
- // Should do more checking.
- ExplodedNodeSet Argevaluated;
- Visit(CDE->getArgument(), Pred, Argevaluated);
- for (ExplodedNodeSet::iterator I = Argevaluated.begin(),
- E = Argevaluated.end(); I != E; ++I) {
- const ProgramState *state = (*I)->getState();
- MakeNode(Dst, CDE, *I, state);
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(CDE, Pred, state);
+}
+
+void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const VarDecl *VD = CS->getExceptionDecl();
+ if (!VD) {
+ Dst.Add(Pred);
+ return;
}
+
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = svalBuilder.getConjuredSymbolVal(CS, LCtx, VD->getType(),
+ currentBuilderContext->getCurrentBlockCount());
+ ProgramStateRef state = Pred->getState();
+ state = state->bindLoc(state->getLValue(VD, LCtx), V);
+
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ Bldr.generateNode(CS, Pred, state);
}
void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+
// Get the this object region from StoreManager.
+ const LocationContext *LCtx = Pred->getLocationContext();
const MemRegion *R =
svalBuilder.getRegionManager().getCXXThisRegion(
getContext().getCanonicalType(TE->getType()),
- Pred->getLocationContext());
+ LCtx);
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
SVal V = state->getSVal(loc::MemRegionVal(R));
- MakeNode(Dst, TE, Pred, state->BindExpr(TE, V));
+ Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V));
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 6d377b9..b99bd54 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -15,40 +15,72 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/Analysis/Support/SaveAndRestore.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
-namespace {
- // Trait class for recording returned expression in the state.
- struct ReturnExpr {
- static int TagInt;
- typedef const Stmt *data_type;
- };
- int ReturnExpr::TagInt;
+void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
+ // Get the entry block in the CFG of the callee.
+ const StackFrameContext *calleeCtx = CE.getCalleeContext();
+ const CFG *CalleeCFG = calleeCtx->getCFG();
+ const CFGBlock *Entry = &(CalleeCFG->getEntry());
+
+ // Validate the CFG.
+ assert(Entry->empty());
+ assert(Entry->succ_size() == 1);
+
+ // Get the solitary sucessor.
+ const CFGBlock *Succ = *(Entry->succ_begin());
+
+ // Construct an edge representing the starting location in the callee.
+ BlockEdge Loc(Entry, Succ, calleeCtx);
+
+ // Construct a new state which contains the mapping from actual to
+ // formal arguments.
+ const LocationContext *callerCtx = Pred->getLocationContext();
+ ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx,
+ calleeCtx);
+
+ // Construct a new node and add it to the worklist.
+ bool isNew;
+ ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
+ Node->addPredecessor(Pred, G);
+ if (isNew)
+ Engine.getWorkList()->enqueue(Node);
}
-void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) {
- const ProgramState *state =
- B.getState()->enterStackFrame(B.getCalleeContext());
- B.generateNode(state);
+static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) {
+ while (Node) {
+ const ProgramPoint &PP = Node->getLocation();
+ // Skip any BlockEdges.
+ if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) {
+ assert(Node->pred_size() == 1);
+ Node = *Node->pred_begin();
+ continue;
+ }
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
+ const Stmt *S = SP->getStmt();
+ return dyn_cast<ReturnStmt>(S);
+ }
+ break;
+ }
+ return 0;
}
-void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
- const ProgramState *state = B.getState();
- const ExplodedNode *Pred = B.getPredecessor();
+void ExprEngine::processCallExit(ExplodedNode *Pred) {
+ ProgramStateRef state = Pred->getState();
const StackFrameContext *calleeCtx =
- cast<StackFrameContext>(Pred->getLocationContext());
+ Pred->getLocationContext()->getCurrentStackFrame();
+ const LocationContext *callerCtx = calleeCtx->getParent();
const Stmt *CE = calleeCtx->getCallSite();
// If the callee returns an expression, bind its value to CallExpr.
- const Stmt *ReturnedExpr = state->get<ReturnExpr>();
- if (ReturnedExpr) {
- SVal RetVal = state->getSVal(ReturnedExpr);
- state = state->BindExpr(CE, RetVal);
- // Clear the return expr GDM.
- state = state->remove<ReturnExpr>();
+ if (const ReturnStmt *RS = getReturnStmt(Pred)) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = state->getSVal(RS, LCtx);
+ state = state->BindExpr(CE, callerCtx, V);
}
// Bind the constructed object value to CXXConstructExpr.
@@ -58,14 +90,197 @@ void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
SVal ThisV = state->getSVal(ThisR);
// Always bind the region to the CXXConstructExpr.
- state = state->BindExpr(CCE, ThisV);
+ state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV);
}
- B.generateNode(state);
+ static SimpleProgramPointTag returnTag("ExprEngine : Call Return");
+ PostStmt Loc(CE, callerCtx, &returnTag);
+ bool isNew;
+ ExplodedNode *N = G.getNode(Loc, state, false, &isNew);
+ N->addPredecessor(Pred, G);
+ if (!isNew)
+ return;
+
+ // Perform the post-condition check of the CallExpr.
+ ExplodedNodeSet Dst;
+ NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N);
+ SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
+ &Ctx);
+ SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
+
+ getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this,
+ /* wasInlined */ true);
+
+ // Enqueue the next element in the block.
+ for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) {
+ Engine.getWorkList()->enqueue(*I,
+ calleeCtx->getCallSiteBlock(),
+ calleeCtx->getIndex()+1);
+ }
}
-const ProgramState *
-ExprEngine::invalidateArguments(const ProgramState *State,
+static unsigned getNumberStackFrames(const LocationContext *LCtx) {
+ unsigned count = 0;
+ while (LCtx) {
+ if (isa<StackFrameContext>(LCtx))
+ ++count;
+ LCtx = LCtx->getParent();
+ }
+ return count;
+}
+
+// Determine if we should inline the call.
+bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) {
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
+ const CFG *CalleeCFG = CalleeADC->getCFG();
+
+ if (getNumberStackFrames(Pred->getLocationContext())
+ == AMgr.InlineMaxStackDepth)
+ return false;
+
+ if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD))
+ return false;
+
+ if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
+ return false;
+
+ return true;
+}
+
+// For now, skip inlining variadic functions.
+// We also don't inline blocks.
+static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) {
+ if (!E->getAnalysisManager().shouldInlineCall())
+ return false;
+ QualType callee = CE->getCallee()->getType();
+ const FunctionProtoType *FT = 0;
+ if (const PointerType *PT = callee->getAs<PointerType>())
+ FT = dyn_cast<FunctionProtoType>(PT->getPointeeType());
+ else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) {
+ // FIXME: inline blocks.
+ // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType());
+ (void) BT;
+ return false;
+ }
+ // If we have no prototype, assume the function is okay.
+ if (!FT)
+ return true;
+
+ // Skip inlining of variadic functions.
+ return !FT->isVariadic();
+}
+
+bool ExprEngine::InlineCall(ExplodedNodeSet &Dst,
+ const CallExpr *CE,
+ ExplodedNode *Pred) {
+ if (!shouldInlineCallExpr(CE, this))
+ return false;
+
+ ProgramStateRef state = Pred->getState();
+ const Expr *Callee = CE->getCallee();
+ const FunctionDecl *FD =
+ state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl();
+ if (!FD || !FD->hasBody(FD))
+ return false;
+
+ switch (CE->getStmtClass()) {
+ default:
+ // FIXME: Handle C++.
+ break;
+ case Stmt::CallExprClass: {
+ if (!shouldInlineDecl(FD, Pred))
+ return false;
+
+ // Construct a new stack frame for the callee.
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
+ const StackFrameContext *CallerSFC =
+ Pred->getLocationContext()->getCurrentStackFrame();
+ const StackFrameContext *CalleeSFC =
+ CalleeADC->getStackFrame(CallerSFC, CE,
+ currentBuilderContext->getBlock(),
+ currentStmtIdx);
+
+ CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext());
+ bool isNew;
+ if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) {
+ N->addPredecessor(Pred, G);
+ if (isNew)
+ Engine.getWorkList()->enqueue(N);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool isPointerToConst(const ParmVarDecl *ParamDecl) {
+ QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType();
+ if (PointeeTy != QualType() && PointeeTy.isConstQualified() &&
+ !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) {
+ return true;
+ }
+ return false;
+}
+
+// Try to retrieve the function declaration and find the function parameter
+// types which are pointers/references to a non-pointer const.
+// We do not invalidate the corresponding argument regions.
+static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
+ const CallOrObjCMessage &Call) {
+ const Decl *CallDecl = Call.getDecl();
+ if (!CallDecl)
+ return;
+
+ if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) {
+ const IdentifierInfo *II = FDecl->getIdentifier();
+
+ // List the cases, where the region should be invalidated even if the
+ // argument is const.
+ if (II) {
+ StringRef FName = II->getName();
+ // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+ // value into thread local storage. The value can later be retrieved with
+ // 'void *ptheread_getspecific(pthread_key)'. So even thought the
+ // parameter is 'const void *', the region escapes through the call.
+ // - funopen - sets a buffer for future IO calls.
+ // - ObjC functions that end with "NoCopy" can free memory, of the passed
+ // in buffer.
+ // - Many CF containers allow objects to escape through custom
+ // allocators/deallocators upon container construction.
+ // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove.
+ if (FName == "pthread_setspecific" ||
+ FName == "funopen" ||
+ FName.endswith("NoCopy") ||
+ (FName.startswith("NS") &&
+ (FName.find("Insert") != StringRef::npos)) ||
+ Call.isCFCGAllowingEscape(FName))
+ return;
+ }
+
+ for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) {
+ if (FDecl && Idx < FDecl->getNumParams()) {
+ if (isPointerToConst(FDecl->getParamDecl(Idx)))
+ PreserveArgs.insert(Idx);
+ }
+ }
+ return;
+ }
+
+ if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) {
+ assert(MDecl->param_size() <= Call.getNumArgs());
+ unsigned Idx = 0;
+ for (clang::ObjCMethodDecl::param_const_iterator
+ I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) {
+ if (isPointerToConst(*I))
+ PreserveArgs.insert(Idx);
+ }
+ return;
+ }
+}
+
+ProgramStateRef
+ExprEngine::invalidateArguments(ProgramStateRef State,
const CallOrObjCMessage &Call,
const LocationContext *LC) {
SmallVector<const MemRegion *, 8> RegionsToInvalidate;
@@ -85,13 +300,21 @@ ExprEngine::invalidateArguments(const ProgramState *State,
} else if (Call.isFunctionCall()) {
// Block calls invalidate all captured-by-reference values.
- if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) {
+ SVal CalleeVal = Call.getFunctionCallee();
+ if (const MemRegion *Callee = CalleeVal.getAsRegion()) {
if (isa<BlockDataRegion>(Callee))
RegionsToInvalidate.push_back(Callee);
}
}
+ // Indexes of arguments whose values will be preserved by the call.
+ llvm::SmallSet<unsigned, 1> PreserveArgs;
+ findPtrToConstParams(PreserveArgs, Call);
+
for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) {
+ if (PreserveArgs.count(idx))
+ continue;
+
SVal V = Call.getArgSVal(idx);
// If we are passing a location wrapped as an integer, unwrap it and
@@ -105,7 +328,7 @@ ExprEngine::invalidateArguments(const ProgramState *State,
// Invalidate the value of the variable passed by reference.
// Are we dealing with an ElementRegion? If the element type is
- // a basic integer type (e.g., char, int) and the underying region
+ // a basic integer type (e.g., char, int) and the underlying region
// is a variable region then strip off the ElementRegion.
// FIXME: We really need to think about this for the general case
// as sometimes we are reasoning about arrays and other times
@@ -116,7 +339,7 @@ ExprEngine::invalidateArguments(const ProgramState *State,
// we'll leave it in for now until we have a systematic way of
// handling all of these cases. Eventually we need to come up
// with an interface to StoreManager so that this logic can be
- // approriately delegated to the respective StoreManagers while
+ // appropriately delegated to the respective StoreManagers while
// still allowing us to do checker-specific logic (e.g.,
// invalidating reference counts), probably via callbacks.
if (ER->getElementType()->isIntegralOrEnumerationType()) {
@@ -146,18 +369,29 @@ ExprEngine::invalidateArguments(const ProgramState *State,
// to identify conjured symbols by an expression pair: the enclosing
// expression (the context) and the expression itself. This should
// disambiguate conjured symbols.
- assert(Builder && "Invalidating arguments outside of a statement context");
- unsigned Count = Builder->getCurrentBlockCount();
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
StoreManager::InvalidatedSymbols IS;
// NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
// global variables.
return State->invalidateRegions(RegionsToInvalidate,
- Call.getOriginExpr(), Count,
- &IS, doesInvalidateGlobals(Call));
+ Call.getOriginExpr(), Count, LC,
+ &IS, &Call);
}
+static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N,
+ const CallExpr *CE) {
+ void *ReplayState = N->getState()->get<ReplayWithoutInlining>();
+ if (!ReplayState)
+ return 0;
+ const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState);
+ if (CE == ReplayCE) {
+ return N->getState()->remove<ReplayWithoutInlining>();
+ }
+ return 0;
+}
+
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
ExplodedNodeSet &dst) {
// Perform the previsit of the CallExpr.
@@ -173,20 +407,21 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
DefaultEval(ExprEngine &eng, const CallExpr *ce)
: Eng(eng), CE(ce) {}
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- // Should we inline the call?
- if (Eng.getAnalysisManager().shouldInlineCall() &&
- Eng.InlineCall(Dst, CE, Pred)) {
+
+ ProgramStateRef state = getReplayWithoutInliningState(Pred, CE);
+
+ // First, try to inline the call.
+ if (state == 0 && Eng.InlineCall(Dst, CE, Pred))
return;
- }
// First handle the return value.
- StmtNodeBuilder &Builder = Eng.getBuilder();
- assert(&Builder && "StmtNodeBuilder must be defined.");
+ StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext);
// Get the callee.
const Expr *Callee = CE->getCallee()->IgnoreParens();
- const ProgramState *state = Pred->getState();
- SVal L = state->getSVal(Callee);
+ if (state == 0)
+ state = Pred->getState();
+ SVal L = state->getSVal(Callee, Pred->getLocationContext());
// Figure out the result type. We do this dance to handle references.
QualType ResultTy;
@@ -200,18 +435,19 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
// Conjure a symbol value to use as the result.
SValBuilder &SVB = Eng.getSValBuilder();
- unsigned Count = Builder.getCurrentBlockCount();
- SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count);
+ unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
// Generate a new state with the return value set.
- state = state->BindExpr(CE, RetVal);
+ state = state->BindExpr(CE, LCtx, RetVal);
// Invalidate the arguments.
- const LocationContext *LC = Pred->getLocationContext();
- state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC);
+ state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx),
+ LCtx);
// And make the result node.
- Eng.MakeNode(Dst, CE, Pred, state);
+ Bldr.generateNode(CE, Pred, state);
}
};
@@ -231,23 +467,16 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- ExplodedNodeSet Src;
- if (const Expr *RetE = RS->getRetValue()) {
- // Record the returned expression in the state. It will be used in
- // processCallExit to bind the return value to the call expr.
- {
- static SimpleProgramPointTag tag("ExprEngine: ReturnStmt");
- const ProgramState *state = Pred->getState();
- state = state->set<ReturnExpr>(RetE);
- Pred = Builder->generateNode(RetE, state, Pred, &tag);
+
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this);
+
+ StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext);
+
+ if (RS->getRetValue()) {
+ for (ExplodedNodeSet::iterator it = dstPreVisit.begin(),
+ ei = dstPreVisit.end(); it != ei; ++it) {
+ B.generateNode(RS, *it, (*it)->getState());
}
- // We may get a NULL Pred because we generated a cached node.
- if (Pred)
- Visit(RetE, Pred, Src);
}
- else {
- Src.Add(Pred);
- }
-
- getCheckerManager().runCheckersForPreStmt(Dst, Src, RS, *this);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index e0560fd..c8ad70a 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -11,10 +11,10 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/StmtObjC.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
-#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
using namespace ento;
@@ -22,13 +22,14 @@ using namespace ento;
void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
- const ProgramState *state = Pred->getState();
- SVal baseVal = state->getSVal(Ex->getBase());
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
SVal location = state->getLValue(Ex->getDecl(), baseVal);
ExplodedNodeSet dstIvar;
- MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location));
+ StmtNodeBuilder Bldr(Pred, dstIvar, *currentBuilderContext);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
// Perform the post-condition check of the ObjCIvarRefExpr and store
// the created nodes in 'Dst'.
@@ -69,10 +70,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
// For now: simulate (1) by assigning either a symbol or nil if the
// container is empty. Thus this transfer function will by default
// result in state splitting.
-
+
const Stmt *elem = S->getElement();
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
SVal elementV;
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
@@ -80,27 +82,27 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
elementV = state->getLValue(elemD, Pred->getLocationContext());
}
else {
- elementV = state->getSVal(elem);
+ elementV = state->getSVal(elem, Pred->getLocationContext());
}
ExplodedNodeSet dstLocation;
- evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false);
-
- if (dstLocation.empty())
- return;
+ Bldr.takeNodes(Pred);
+ evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false);
+ Bldr.addNodes(dstLocation);
for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
NE = dstLocation.end(); NI!=NE; ++NI) {
Pred = *NI;
- const ProgramState *state = Pred->getState();
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
// Handle the case where the container still has elements.
SVal TrueV = svalBuilder.makeTruthVal(1);
- const ProgramState *hasElems = state->BindExpr(S, TrueV);
+ ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV);
// Handle the case where the container has no elements.
SVal FalseV = svalBuilder.makeTruthVal(0);
- const ProgramState *noElems = state->BindExpr(S, FalseV);
+ ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV);
if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV))
if (const TypedValueRegion *R =
@@ -110,8 +112,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
// For now, just 'conjure' up a symbolic value.
QualType T = R->getValueType();
assert(Loc::isLocType(T));
- unsigned Count = Builder->getCurrentBlockCount();
- SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count);
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ SymbolRef Sym = SymMgr.getConjuredSymbol(elem, LCtx, T, Count);
SVal V = svalBuilder.makeLoc(Sym);
hasElems = hasElems->bindLoc(elementV, V);
@@ -121,8 +123,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
}
// Create the new nodes.
- MakeNode(Dst, S, Pred, hasElems);
- MakeNode(Dst, S, Pred, noElems);
+ Bldr.generateNode(S, Pred, hasElems);
+ Bldr.generateNode(S, Pred, noElems);
}
}
@@ -137,29 +139,27 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
-
+ StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext);
+
for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
DE = dstPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
bool RaisesException = false;
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- SaveOr OldHasGen(Builder->hasGeneratedNode);
if (const Expr *Receiver = msg.getInstanceReceiver()) {
- const ProgramState *state = Pred->getState();
- SVal recVal = state->getSVal(Receiver);
+ ProgramStateRef state = Pred->getState();
+ SVal recVal = state->getSVal(Receiver, Pred->getLocationContext());
if (!recVal.isUndef()) {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
- const ProgramState *notNilState, *nilState;
+ ProgramStateRef notNilState, nilState;
llvm::tie(notNilState, nilState) = state->assume(receiverVal);
// There are three cases: can be nil or non-nil, must be nil, must be
// non-nil. We ignore must be nil, and merge the rest two into non-nil.
if (nilState && !notNilState) {
- dstEval.insert(Pred);
continue;
}
@@ -168,13 +168,10 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
if (msg.getSelector() == RaiseSel)
RaisesException = true;
- // Check if we raise an exception. For now treat these as sinks.
+ // If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
- if (RaisesException)
- Builder->BuildSinks = true;
-
// Dispatch to plug-in transfer function.
- evalObjCMessage(dstEval, msg, Pred, notNilState);
+ evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
}
}
else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
@@ -217,16 +214,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
}
}
- // Check if we raise an exception. For now treat these as sinks.
+ // If we raise an exception, for now treat it as a sink.
// Eventually we will want to handle exceptions properly.
- if (RaisesException)
- Builder->BuildSinks = true;
-
// Dispatch to plug-in transfer function.
- evalObjCMessage(dstEval, msg, Pred, Pred->getState());
+ evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException);
}
-
- assert(Builder->BuildSinks || Builder->hasGeneratedNode);
}
// Finally, perform the post-condition check of the ObjCMessageExpr and store
@@ -234,11 +226,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
}
-void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
+void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
+ const ObjCMessage &msg,
ExplodedNode *Pred,
- const ProgramState *state) {
- assert (Builder && "StmtNodeBuilder must be defined.");
-
+ ProgramStateRef state,
+ bool GenSink) {
// First handle the return value.
SVal ReturnValue = UnknownVal();
@@ -252,7 +244,7 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
// These methods return their receivers.
const Expr *ReceiverE = msg.getInstanceReceiver();
if (ReceiverE)
- ReturnValue = state->getSVal(ReceiverE);
+ ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext());
break;
}
}
@@ -261,19 +253,21 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
if (ReturnValue.isUnknown()) {
SValBuilder &SVB = getSValBuilder();
QualType ResultTy = msg.getResultType(getContext());
- unsigned Count = Builder->getCurrentBlockCount();
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
const Expr *CurrentE = cast<Expr>(currentStmt);
- ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count);
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count);
}
// Bind the return value.
- state = state->BindExpr(currentStmt, ReturnValue);
+ const LocationContext *LCtx = Pred->getLocationContext();
+ state = state->BindExpr(currentStmt, LCtx, ReturnValue);
// Invalidate the arguments (and the receiver)
- const LocationContext *LC = Pred->getLocationContext();
- state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC);
+ state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx);
// And create the new node.
- MakeNode(Dst, msg.getOriginExpr(), Pred, state);
+ Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink);
+ assert(Bldr.hasGeneratedNodes());
}
diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
new file mode 100644
index 0000000..c227aac
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
@@ -0,0 +1,38 @@
+//== FunctionSummary.h - Stores summaries of functions. ------------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a summary of a function gathered/used by static analyzes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
+using namespace clang;
+using namespace ento;
+
+FunctionSummariesTy::~FunctionSummariesTy() {
+ for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) {
+ delete(I->second);
+ }
+}
+
+unsigned FunctionSummariesTy::getTotalNumBasicBlocks() {
+ unsigned Total = 0;
+ for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) {
+ Total += I->second->TotalBasicBlocks;
+ }
+ return Total;
+}
+
+unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() {
+ unsigned Total = 0;
+ for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) {
+ Total += I->second->VisitedBasicBlocks.count();
+ }
+ return Total;
+}
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 0c4e427..629f1ea 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -39,15 +39,13 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
llvm::sys::Path Directory, FilePrefix;
bool createdDir, noDir;
const Preprocessor &PP;
- std::vector<const PathDiagnostic*> BatchedDiags;
public:
HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp);
virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); }
- virtual void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade);
-
- virtual void HandlePathDiagnosticImpl(const PathDiagnostic* D);
+ virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade);
virtual StringRef getName() const {
return "HTMLDiagnostics";
@@ -88,34 +86,49 @@ ento::createHTMLDiagnosticConsumer(const std::string& prefix,
// Report processing.
//===----------------------------------------------------------------------===//
-void HTMLDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
- if (!D)
- return;
-
- if (D->empty()) {
- delete D;
- return;
+void HTMLDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade) {
+ for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
+ et = Diags.end(); it != et; ++it) {
+ ReportDiag(**it, FilesMade);
}
-
- const_cast<PathDiagnostic*>(D)->flattenLocations();
- BatchedDiags.push_back(D);
}
-void
-HTMLDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade)
-{
- while (!BatchedDiags.empty()) {
- const PathDiagnostic* D = BatchedDiags.back();
- BatchedDiags.pop_back();
- ReportDiag(*D, FilesMade);
- delete D;
+static void flattenPath(PathPieces &primaryPath, PathPieces &currentPath,
+ const PathPieces &oldPath) {
+ for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end();
+ it != et; ++it ) {
+ PathDiagnosticPiece *piece = it->getPtr();
+ if (const PathDiagnosticCallPiece *call =
+ dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter =
+ call->getCallEnterEvent();
+ if (callEnter)
+ currentPath.push_back(callEnter);
+ flattenPath(primaryPath, primaryPath, call->path);
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
+ call->getCallExitEvent();
+ if (callExit)
+ currentPath.push_back(callExit);
+ continue;
+ }
+ if (PathDiagnosticMacroPiece *macro =
+ dyn_cast<PathDiagnosticMacroPiece>(piece)) {
+ currentPath.push_back(piece);
+ PathPieces newPath;
+ flattenPath(primaryPath, newPath, macro->subPieces);
+ macro->subPieces = newPath;
+ continue;
+ }
+
+ currentPath.push_back(piece);
}
-
- BatchedDiags.clear();
}
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
- SmallVectorImpl<std::string> *FilesMade){
+ SmallVectorImpl<std::string> *FilesMade) {
+
// Create the HTML directory if it is missing.
if (!createdDir) {
createdDir = true;
@@ -138,47 +151,29 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
if (noDir)
return;
- const SourceManager &SMgr = D.begin()->getLocation().getManager();
- FileID FID;
-
- // Verify that the entire path is from the same FileID.
- for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) {
- FullSourceLoc L = I->getLocation().asLocation().getExpansionLoc();
-
- if (FID.isInvalid()) {
- FID = SMgr.getFileID(L);
- } else if (SMgr.getFileID(L) != FID)
- return; // FIXME: Emit a warning?
-
- // Check the source ranges.
- for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
- RE=I->ranges_end(); RI!=RE; ++RI) {
-
- SourceLocation L = SMgr.getExpansionLoc(RI->getBegin());
-
- if (!L.isFileID() || SMgr.getFileID(L) != FID)
- return; // FIXME: Emit a warning?
-
- L = SMgr.getExpansionLoc(RI->getEnd());
-
- if (!L.isFileID() || SMgr.getFileID(L) != FID)
- return; // FIXME: Emit a warning?
- }
- }
+ // First flatten out the entire path to make it easier to use.
+ PathPieces path;
+ flattenPath(path, path, D.path);
- if (FID.isInvalid())
- return; // FIXME: Emit a warning?
+ // The path as already been prechecked that all parts of the path are
+ // from the same file and that it is non-empty.
+ const SourceManager &SMgr = (*path.begin())->getLocation().getManager();
+ assert(!path.empty());
+ FileID FID =
+ (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID();
+ assert(!FID.isInvalid());
// Create a new rewriter to generate HTML.
- Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOptions());
+ Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
// Process the path.
- unsigned n = D.size();
+ unsigned n = path.size();
unsigned max = n;
- for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend();
- I!=E; ++I, --n)
- HandlePiece(R, FID, *I, n, max);
+ for (PathPieces::const_reverse_iterator I = path.rbegin(),
+ E = path.rend();
+ I != E; ++I, --n)
+ HandlePiece(R, FID, **I, n, max);
// Add line numbers, header, footer, etc.
@@ -221,9 +216,9 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
<< html::EscapeText(Entry->getName())
<< "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>"
"<a href=\"#EndPath\">line "
- << (*D.rbegin()).getLocation().asLocation().getExpansionLineNumber()
+ << (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber()
<< ", column "
- << (*D.rbegin()).getLocation().asLocation().getExpansionColumnNumber()
+ << (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber()
<< "</a></td></tr>\n"
"<tr><td class=\"rowname\">Description:</td><td>"
<< D.getDescription() << "</td></tr>\n";
@@ -261,10 +256,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
os << "\n<!-- BUGLINE "
- << D.back()->getLocation().asLocation().getExpansionLineNumber()
+ << path.back()->getLocation().asLocation().getExpansionLineNumber()
<< " -->\n";
- os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n";
+ os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";
// Mark the end of the tags.
os << "\n<!-- BUGMETAEND -->\n";
@@ -353,6 +348,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
const char *Kind = 0;
switch (P.getKind()) {
+ case PathDiagnosticPiece::Call:
+ llvm_unreachable("Calls should already be handled");
case PathDiagnosticPiece::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
// Setting Kind to "Control" is intentional.
@@ -445,7 +442,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
assert(L.isFileID());
StringRef BufferInfo = L.getBufferData();
const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data();
- Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(),
+ Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(),
MacroName, BufferInfo.end());
Token TheTok;
@@ -518,7 +515,7 @@ unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
const PathDiagnosticMacroPiece& P,
unsigned num) {
- for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
+ for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
I!=E; ++I) {
if (const PathDiagnosticMacroPiece *MP =
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index 6f92da8..ed94c79 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -18,7 +18,9 @@
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/Support/BumpVector.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/AST/RecordLayout.h"
+#include "clang/Basic/SourceManager.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -219,6 +221,17 @@ DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const {
svalBuilder.getArrayIndexType());
}
+ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg)
+ : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
+ return cast<ObjCIvarDecl>(D);
+}
+
+QualType ObjCIvarRegion::getValueType() const {
+ return getDecl()->getType();
+}
+
QualType CXXBaseObjectRegion::getValueType() const {
return QualType(decl->getTypeForDecl(), 0);
}
@@ -249,6 +262,14 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
ID.AddPointer(superRegion);
}
+void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const ObjCStringLiteral* Str,
+ const MemRegion* superRegion) {
+ ID.AddInteger((unsigned) ObjCStringRegionKind);
+ ID.AddPointer(Str);
+ ID.AddPointer(superRegion);
+}
+
void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
const Expr *Ex, unsigned cnt,
const MemRegion *) {
@@ -285,6 +306,12 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
}
+void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const ObjCIvarDecl *ivd,
+ const MemRegion* superRegion) {
+ DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+}
+
void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
const MemRegion* superRegion, Kind k) {
ID.AddInteger((unsigned) k);
@@ -337,7 +364,7 @@ void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const {
void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
const BlockDecl *BD, CanQualType,
- const AnalysisContext *AC,
+ const AnalysisDeclContext *AC,
const MemRegion*) {
ID.AddInteger(MemRegion::BlockTextRegionKind);
ID.AddPointer(BD);
@@ -384,6 +411,20 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
}
//===----------------------------------------------------------------------===//
+// Region anchors.
+//===----------------------------------------------------------------------===//
+
+void GlobalsSpaceRegion::anchor() { }
+void HeapSpaceRegion::anchor() { }
+void UnknownSpaceRegion::anchor() { }
+void StackLocalsSpaceRegion::anchor() { }
+void StackArgumentsSpaceRegion::anchor() { }
+void TypedRegion::anchor() { }
+void TypedValueRegion::anchor() { }
+void CodeTextRegion::anchor() { }
+void SubRegion::anchor() { }
+
+//===----------------------------------------------------------------------===//
// Region pretty-printing.
//===----------------------------------------------------------------------===//
@@ -445,16 +486,16 @@ void FieldRegion::dumpToStream(raw_ostream &os) const {
os << superRegion << "->" << *getDecl();
}
-void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
- os << "NonStaticGlobalSpaceRegion";
-}
-
void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
os << "ivar{" << superRegion << ',' << *getDecl() << '}';
}
void StringRegion::dumpToStream(raw_ostream &os) const {
- Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions()));
+ Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts()));
+}
+
+void ObjCStringRegion::dumpToStream(raw_ostream &os) const {
+ Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts()));
}
void SymbolicRegion::dumpToStream(raw_ostream &os) const {
@@ -477,6 +518,35 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "StaticGlobalsMemSpace{" << CR << '}';
}
+void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "NonStaticGlobalSpaceRegion";
+}
+
+void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalInternalSpaceRegion";
+}
+
+void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalSystemSpaceRegion";
+}
+
+void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalImmutableSpaceRegion";
+}
+
+void MemRegion::dumpPretty(raw_ostream &os) const {
+ return;
+}
+
+void VarRegion::dumpPretty(raw_ostream &os) const {
+ os << getDecl()->getName();
+}
+
+void FieldRegion::dumpPretty(raw_ostream &os) const {
+ superRegion->dumpPretty(os);
+ os << "->" << getDecl();
+}
+
//===----------------------------------------------------------------------===//
// MemRegionManager methods.
//===----------------------------------------------------------------------===//
@@ -528,10 +598,18 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) {
}
const GlobalsSpaceRegion
-*MemRegionManager::getGlobalsRegion(const CodeTextRegion *CR) {
- if (!CR)
- return LazyAllocate(globals);
+*MemRegionManager::getGlobalsRegion(MemRegion::Kind K,
+ const CodeTextRegion *CR) {
+ if (!CR) {
+ if (K == MemRegion::GlobalSystemSpaceRegionKind)
+ return LazyAllocate(SystemGlobals);
+ if (K == MemRegion::GlobalImmutableSpaceRegionKind)
+ return LazyAllocate(ImmutableGlobals);
+ assert(K == MemRegion::GlobalInternalSpaceRegionKind);
+ return LazyAllocate(InternalGlobals);
+ }
+ assert(K == MemRegion::StaticGlobalSpaceRegionKind);
StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR];
if (R)
return R;
@@ -556,18 +634,44 @@ const MemSpaceRegion *MemRegionManager::getCodeRegion() {
//===----------------------------------------------------------------------===//
// Constructing regions.
//===----------------------------------------------------------------------===//
-
const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){
return getSubRegion<StringRegion>(Str, getGlobalsRegion());
}
+const ObjCStringRegion *
+MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
+ return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion());
+}
+
const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
const MemRegion *sReg = 0;
- if (D->hasGlobalStorage() && !D->isStaticLocal())
- sReg = getGlobalsRegion();
- else {
+ if (D->hasGlobalStorage() && !D->isStaticLocal()) {
+
+ // First handle the globals defined in system headers.
+ if (C.getSourceManager().isInSystemHeader(D->getLocation())) {
+ // Whitelist the system globals which often DO GET modified, assume the
+ // rest are immutable.
+ if (D->getName().find("errno") != StringRef::npos)
+ sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
+ else
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+
+ // Treat other globals as GlobalInternal unless they are constants.
+ } else {
+ QualType GQT = D->getType();
+ const Type *GT = GQT.getTypePtrOrNull();
+ // TODO: We could walk the complex types here and see if everything is
+ // constified.
+ if (GT && GQT.isConstQualified() && GT->isArithmeticType())
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+ else
+ sReg = getGlobalsRegion();
+ }
+
+ // Finally handle static locals.
+ } else {
// FIXME: Once we implement scope handling, we will need to properly lookup
// 'D' to the proper LocationContext.
const DeclContext *DC = D->getDeclContext();
@@ -585,13 +689,15 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
assert(D->isStaticLocal());
const Decl *D = STC->getDecl();
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
- sReg = getGlobalsRegion(getFunctionTextRegion(FD));
+ sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
+ getFunctionTextRegion(FD));
else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
const BlockTextRegion *BTR =
getBlockTextRegion(BD,
C.getCanonicalType(BD->getSignatureAsWritten()->getType()),
- STC->getAnalysisContext());
- sReg = getGlobalsRegion(BTR);
+ STC->getAnalysisDeclContext());
+ sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
+ BTR);
}
else {
// FIXME: For ObjC-methods, we need a new CodeTextRegion. For now
@@ -614,18 +720,24 @@ const BlockDataRegion *
MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC,
const LocationContext *LC) {
const MemRegion *sReg = 0;
-
- if (LC) {
- // FIXME: Once we implement scope handling, we want the parent region
- // to be the scope.
- const StackFrameContext *STC = LC->getCurrentStackFrame();
- assert(STC);
- sReg = getStackLocalsRegion(STC);
+ const BlockDecl *BD = BC->getDecl();
+ if (!BD->hasCaptures()) {
+ // This handles 'static' blocks.
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
}
else {
- // We allow 'LC' to be NULL for cases where want BlockDataRegions
- // without context-sensitivity.
- sReg = getUnknownRegion();
+ if (LC) {
+ // FIXME: Once we implement scope handling, we want the parent region
+ // to be the scope.
+ const StackFrameContext *STC = LC->getCurrentStackFrame();
+ assert(STC);
+ sReg = getStackLocalsRegion(STC);
+ }
+ else {
+ // We allow 'LC' to be NULL for cases where want BlockDataRegions
+ // without context-sensitivity.
+ sReg = getUnknownRegion();
+ }
}
return getSubRegion<BlockDataRegion>(BC, LC, sReg);
@@ -678,7 +790,7 @@ MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) {
const BlockTextRegion *
MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy,
- AnalysisContext *AC) {
+ AnalysisDeclContext *AC) {
return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion());
}
@@ -928,8 +1040,8 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
if (ReferencedVars)
return;
- AnalysisContext *AC = getCodeRegion()->getAnalysisContext();
- AnalysisContext::referenced_decls_iterator I, E;
+ AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext();
+ AnalysisDeclContext::referenced_decls_iterator I, E;
llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl());
if (I == E) {
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
index 0974fe8..65cdcd9 100644
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
@@ -13,108 +13,16 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/AST/DeclCXX.h"
using namespace clang;
using namespace ento;
-QualType ObjCMessage::getType(ASTContext &ctx) const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getType();
- const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
- if (isPropertySetter())
- return ctx.VoidTy;
- return propE->getType();
-}
-
-Selector ObjCMessage::getSelector() const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getSelector();
- const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
- if (isPropertySetter())
- return propE->getSetterSelector();
- return propE->getGetterSelector();
-}
-
-ObjCMethodFamily ObjCMessage::getMethodFamily() const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- // Case 1. Explicit message send.
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getMethodFamily();
-
- const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
-
- // Case 2. Reference to implicit property.
- if (propE->isImplicitProperty()) {
- if (isPropertySetter())
- return propE->getImplicitPropertySetter()->getMethodFamily();
- else
- return propE->getImplicitPropertyGetter()->getMethodFamily();
- }
-
- // Case 3. Reference to explicit property.
- const ObjCPropertyDecl *prop = propE->getExplicitProperty();
- if (isPropertySetter()) {
- if (prop->getSetterMethodDecl())
- return prop->getSetterMethodDecl()->getMethodFamily();
- return prop->getSetterName().getMethodFamily();
- } else {
- if (prop->getGetterMethodDecl())
- return prop->getGetterMethodDecl()->getMethodFamily();
- return prop->getGetterName().getMethodFamily();
- }
-}
-
-const ObjCMethodDecl *ObjCMessage::getMethodDecl() const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getMethodDecl();
- const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
- if (propE->isImplicitProperty())
- return isPropertySetter() ? propE->getImplicitPropertySetter()
- : propE->getImplicitPropertyGetter();
- return 0;
-}
-
-const ObjCInterfaceDecl *ObjCMessage::getReceiverInterface() const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getReceiverInterface();
- const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
- if (propE->isClassReceiver())
- return propE->getClassReceiver();
- QualType recT;
- if (const Expr *recE = getInstanceReceiver())
- recT = recE->getType();
- else {
- assert(propE->isSuperReceiver());
- recT = propE->getSuperReceiverType();
- }
- if (const ObjCObjectPointerType *Ptr = recT->getAs<ObjCObjectPointerType>())
- return Ptr->getInterfaceDecl();
- return 0;
-}
-
-const Expr *ObjCMessage::getArgExpr(unsigned i) const {
- assert(isValid() && "This ObjCMessage is uninitialized!");
- assert(i < getNumArgs() && "Invalid index for argument");
- if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
- return msgE->getArg(i);
- assert(isPropertySetter());
- if (const BinaryOperator *bop = dyn_cast<BinaryOperator>(OriginE))
- if (bop->isAssignmentOp())
- return bop->getRHS();
- return 0;
-}
-
QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
QualType resultTy;
bool isLVal = false;
if (isObjCMessage()) {
- isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) &&
- Msg.getOriginExpr()->isLValue();
resultTy = Msg.getResultType(ctx);
} else if (const CXXConstructExpr *Ctor =
CallE.dyn_cast<const CXXConstructExpr *>()) {
@@ -124,7 +32,7 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
isLVal = FunctionCall->isLValue();
const Expr *Callee = FunctionCall->getCallee();
- if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl())
+ if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl())
resultTy = FD->getResultType();
else
resultTy = FunctionCall->getType();
@@ -140,7 +48,7 @@ SVal CallOrObjCMessage::getFunctionCallee() const {
assert(isFunctionCall());
assert(!isCXXCall());
const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
- return State->getSVal(Fun);
+ return State->getSVal(Fun, LCtx);
}
SVal CallOrObjCMessage::getCXXCallee() const {
@@ -154,7 +62,7 @@ SVal CallOrObjCMessage::getCXXCallee() const {
if (!callee)
return UnknownVal();
- return State->getSVal(callee);
+ return State->getSVal(callee, LCtx);
}
SVal
@@ -162,3 +70,21 @@ CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
assert(isObjCMessage());
return Msg.getInstanceReceiverSVal(State, LC);
}
+
+const Decl *CallOrObjCMessage::getDecl() const {
+ if (isCXXCall()) {
+ const CXXMemberCallExpr *CE =
+ cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>());
+ assert(CE);
+ return CE->getMethodDecl();
+ } else if (isObjCMessage()) {
+ return Msg.getMethodDecl();
+ } else if (isFunctionCall()) {
+ // In case of a C style call, use the path sensitive information to find
+ // the function declaration.
+ SVal CalleeVal = getFunctionCallee();
+ return CalleeVal.getAsFunctionDecl();
+ }
+ return 0;
+}
+
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 3a87903..01dd965 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
@@ -24,15 +25,14 @@ using namespace clang;
using namespace ento;
bool PathDiagnosticMacroPiece::containsEvent() const {
- for (const_iterator I = begin(), E = end(); I!=E; ++I) {
+ for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
+ I!=E; ++I) {
if (isa<PathDiagnosticEventPiece>(*I))
return true;
-
if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I))
if (MP->containsEvent())
return true;
}
-
return false;
}
@@ -52,41 +52,189 @@ PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
PathDiagnosticPiece::~PathDiagnosticPiece() {}
PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
+PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {}
PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
+PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
-PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {
- for (iterator I = begin(), E = end(); I != E; ++I) delete *I;
-}
-PathDiagnostic::PathDiagnostic() : Size(0) {}
+PathPieces::~PathPieces() {}
+PathDiagnostic::~PathDiagnostic() {}
+
+PathDiagnostic::PathDiagnostic(const Decl *declWithIssue,
+ StringRef bugtype, StringRef desc,
+ StringRef category)
+ : DeclWithIssue(declWithIssue),
+ BugType(StripTrailingDots(bugtype)),
+ Desc(StripTrailingDots(desc)),
+ Category(StripTrailingDots(category)),
+ path(pathImpl) {}
+
+void PathDiagnosticConsumer::anchor() { }
-PathDiagnostic::~PathDiagnostic() {
- for (iterator I = begin(), E = end(); I != E; ++I) delete &*I;
+PathDiagnosticConsumer::~PathDiagnosticConsumer() {
+ // Delete the contents of the FoldingSet if it isn't empty already.
+ for (llvm::FoldingSet<PathDiagnostic>::iterator it =
+ Diags.begin(), et = Diags.end() ; it != et ; ++it) {
+ delete &*it;
+ }
}
-void PathDiagnostic::resetPath(bool deletePieces) {
- Size = 0;
+void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) {
+ llvm::OwningPtr<PathDiagnostic> OwningD(D);
+
+ if (!D || D->path.empty())
+ return;
+
+ // We need to flatten the locations (convert Stmt* to locations) because
+ // the referenced statements may be freed by the time the diagnostics
+ // are emitted.
+ D->flattenLocations();
+
+ // If the PathDiagnosticConsumer does not support diagnostics that
+ // cross file boundaries, prune out such diagnostics now.
+ if (!supportsCrossFileDiagnostics()) {
+ // Verify that the entire path is from the same FileID.
+ FileID FID;
+ const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager();
+ llvm::SmallVector<const PathPieces *, 5> WorkList;
+ WorkList.push_back(&D->path);
+
+ while (!WorkList.empty()) {
+ const PathPieces &path = *WorkList.back();
+ WorkList.pop_back();
+
+ for (PathPieces::const_iterator I = path.begin(), E = path.end();
+ I != E; ++I) {
+ const PathDiagnosticPiece *piece = I->getPtr();
+ FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
+
+ if (FID.isInvalid()) {
+ FID = SMgr.getFileID(L);
+ } else if (SMgr.getFileID(L) != FID)
+ return; // FIXME: Emit a warning?
+
+ // Check the source ranges.
+ for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(),
+ RE = piece->ranges_end();
+ RI != RE; ++RI) {
+ SourceLocation L = SMgr.getExpansionLoc(RI->getBegin());
+ if (!L.isFileID() || SMgr.getFileID(L) != FID)
+ return; // FIXME: Emit a warning?
+ L = SMgr.getExpansionLoc(RI->getEnd());
+ if (!L.isFileID() || SMgr.getFileID(L) != FID)
+ return; // FIXME: Emit a warning?
+ }
+
+ if (const PathDiagnosticCallPiece *call =
+ dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ WorkList.push_back(&call->path);
+ }
+ else if (const PathDiagnosticMacroPiece *macro =
+ dyn_cast<PathDiagnosticMacroPiece>(piece)) {
+ WorkList.push_back(&macro->subPieces);
+ }
+ }
+ }
+
+ if (FID.isInvalid())
+ return; // FIXME: Emit a warning?
+ }
- if (deletePieces)
- for (iterator I=begin(), E=end(); I!=E; ++I)
- delete &*I;
+ // Profile the node to see if we already have something matching it
+ llvm::FoldingSetNodeID profile;
+ D->Profile(profile);
+ void *InsertPos = 0;
+
+ if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
+ // Keep the PathDiagnostic with the shorter path.
+ const unsigned orig_size = orig->full_size();
+ const unsigned new_size = D->full_size();
+
+ if (orig_size <= new_size) {
+ bool shouldKeepOriginal = true;
+ if (orig_size == new_size) {
+ // Here we break ties in a fairly arbitrary, but deterministic, way.
+ llvm::FoldingSetNodeID fullProfile, fullProfileOrig;
+ D->FullProfile(fullProfile);
+ orig->FullProfile(fullProfileOrig);
+ if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash())
+ shouldKeepOriginal = false;
+ }
- path.clear();
+ if (shouldKeepOriginal)
+ return;
+ }
+ Diags.RemoveNode(orig);
+ delete orig;
+ }
+
+ Diags.InsertNode(OwningD.take());
}
-PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc,
- StringRef category)
- : Size(0),
- BugType(StripTrailingDots(bugtype)),
- Desc(StripTrailingDots(desc)),
- Category(StripTrailingDots(category)) {}
+namespace {
+struct CompareDiagnostics {
+ // Compare if 'X' is "<" than 'Y'.
+ bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
+ // First compare by location
+ const FullSourceLoc &XLoc = X->getLocation().asLocation();
+ const FullSourceLoc &YLoc = Y->getLocation().asLocation();
+ if (XLoc < YLoc)
+ return true;
+ if (XLoc != YLoc)
+ return false;
+
+ // Next, compare by bug type.
+ StringRef XBugType = X->getBugType();
+ StringRef YBugType = Y->getBugType();
+ if (XBugType < YBugType)
+ return true;
+ if (XBugType != YBugType)
+ return false;
+
+ // Next, compare by bug description.
+ StringRef XDesc = X->getDescription();
+ StringRef YDesc = Y->getDescription();
+ if (XDesc < YDesc)
+ return true;
+ if (XDesc != YDesc)
+ return false;
+
+ // FIXME: Further refine by comparing PathDiagnosticPieces?
+ return false;
+ }
+};
+}
-void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) {
- // For now this simply forwards to HandlePathDiagnosticImpl. In the future
- // we can use this indirection to control for multi-threaded access to
- // the PathDiagnosticConsumer from multiple bug reporters.
- HandlePathDiagnosticImpl(D);
+void
+PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
+ if (flushed)
+ return;
+
+ flushed = true;
+
+ std::vector<const PathDiagnostic *> BatchDiags;
+ for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
+ et = Diags.end(); it != et; ++it) {
+ BatchDiags.push_back(&*it);
+ }
+
+ // Clear out the FoldingSet.
+ Diags.clear();
+
+ // Sort the diagnostics so that they are always emitted in a deterministic
+ // order.
+ if (!BatchDiags.empty())
+ std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics());
+
+ FlushDiagnosticsImpl(BatchDiags, Files);
+
+ // Delete the flushed diagnostics.
+ for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(),
+ et = BatchDiags.end(); it != et; ++it) {
+ const PathDiagnostic *D = *it;
+ delete D;
+ }
}
//===----------------------------------------------------------------------===//
@@ -94,9 +242,9 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) {
//===----------------------------------------------------------------------===//
static SourceLocation getValidSourceLocation(const Stmt* S,
- LocationOrAnalysisContext LAC) {
+ LocationOrAnalysisDeclContext LAC) {
SourceLocation L = S->getLocStart();
- assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext should "
+ assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
"be passed to PathDiagnosticLocation upon creation.");
// S might be a temporary statement that does not have a location in the
@@ -107,7 +255,7 @@ static SourceLocation getValidSourceLocation(const Stmt* S,
if (LAC.is<const LocationContext*>())
PM = &LAC.get<const LocationContext*>()->getParentMap();
else
- PM = &LAC.get<AnalysisContext*>()->getParentMap();
+ PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
while (!L.isValid()) {
S = PM->getParent(S);
@@ -127,7 +275,7 @@ PathDiagnosticLocation
PathDiagnosticLocation
PathDiagnosticLocation::createBegin(const Stmt *S,
const SourceManager &SM,
- LocationOrAnalysisContext LAC) {
+ LocationOrAnalysisDeclContext LAC) {
return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
SM, SingleLocK);
}
@@ -193,9 +341,6 @@ PathDiagnosticLocation
}
return PathDiagnosticLocation(S, SMng, P.getLocationContext());
-
- if (!S)
- return PathDiagnosticLocation();
}
PathDiagnosticLocation
@@ -212,8 +357,9 @@ PathDiagnosticLocation
return PathDiagnosticLocation(PS->getStmt(), SM, LC);
else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
const Stmt *Term = BE->getSrc()->getTerminator();
- assert(Term);
- return PathDiagnosticLocation(Term, SM, LC);
+ if (Term) {
+ return PathDiagnosticLocation(Term, SM, LC);
+ }
}
NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
}
@@ -229,7 +375,7 @@ PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
FullSourceLoc
PathDiagnosticLocation::genLocation(SourceLocation L,
- LocationOrAnalysisContext LAC) const {
+ LocationOrAnalysisDeclContext LAC) const {
assert(isValid());
// Note that we want a 'switch' here so that the compiler can warn us in
// case we add more cases.
@@ -238,9 +384,15 @@ FullSourceLoc
case RangeK:
break;
case StmtK:
+ // Defensive checking.
+ if (!S)
+ break;
return FullSourceLoc(getValidSourceLocation(S, LAC),
const_cast<SourceManager&>(*SM));
case DeclK:
+ // Defensive checking.
+ if (!D)
+ break;
return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
}
@@ -248,7 +400,7 @@ FullSourceLoc
}
PathDiagnosticRange
- PathDiagnosticLocation::genRange(LocationOrAnalysisContext LAC) const {
+ PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
assert(isValid());
// Note that we want a 'switch' here so that the compiler can warn us in
// case we add more cases.
@@ -321,6 +473,132 @@ void PathDiagnosticLocation::flatten() {
}
}
+PathDiagnosticLocation PathDiagnostic::getLocation() const {
+ assert(path.size() > 0 &&
+ "getLocation() requires a non-empty PathDiagnostic.");
+
+ PathDiagnosticPiece *p = path.rbegin()->getPtr();
+
+ while (true) {
+ if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) {
+ assert(!cp->path.empty());
+ p = cp->path.rbegin()->getPtr();
+ continue;
+ }
+ break;
+ }
+
+ return p->getLocation();
+}
+
+//===----------------------------------------------------------------------===//
+// Manipulation of PathDiagnosticCallPieces.
+//===----------------------------------------------------------------------===//
+
+static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
+ const SourceManager &SM) {
+ while (N) {
+ ProgramPoint PP = N->getLocation();
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP))
+ return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext());
+ if (N->pred_empty())
+ break;
+ N = *N->pred_begin();
+ }
+ return PathDiagnosticLocation();
+}
+
+PathDiagnosticCallPiece *
+PathDiagnosticCallPiece::construct(const ExplodedNode *N,
+ const CallExit &CE,
+ const SourceManager &SM) {
+ const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
+ PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
+ return new PathDiagnosticCallPiece(caller, pos);
+}
+
+PathDiagnosticCallPiece *
+PathDiagnosticCallPiece::construct(PathPieces &path,
+ const Decl *caller) {
+ PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path, caller);
+ path.clear();
+ path.push_front(C);
+ return C;
+}
+
+void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
+ const SourceManager &SM) {
+ const Decl *D = CE.getCalleeContext()->getDecl();
+ Callee = D;
+ callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM,
+ CE.getLocationContext());
+ callEnterWithin = PathDiagnosticLocation::createBegin(D, SM);
+}
+
+IntrusiveRefCntPtr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallEnterEvent() const {
+ if (!Callee)
+ return 0;
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ if (isa<BlockDecl>(Callee))
+ Out << "Calling anonymous block";
+ else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee))
+ Out << "Calling '" << *ND << "'";
+ StringRef msg = Out.str();
+ if (msg.empty())
+ return 0;
+ return new PathDiagnosticEventPiece(callEnter, msg);
+}
+
+IntrusiveRefCntPtr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller))
+ Out << "Entered call from '" << *ND << "'";
+ else
+ Out << "Entered call";
+ StringRef msg = Out.str();
+ if (msg.empty())
+ return 0;
+ return new PathDiagnosticEventPiece(callEnterWithin, msg);
+}
+
+IntrusiveRefCntPtr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallExitEvent() const {
+ if (NoExit)
+ return 0;
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ if (!CallStackMessage.empty())
+ Out << CallStackMessage;
+ else if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee))
+ Out << "Returning from '" << *ND << "'";
+ else
+ Out << "Returning to caller";
+ return new PathDiagnosticEventPiece(callReturn, Out.str());
+}
+
+static void compute_path_size(const PathPieces &pieces, unsigned &size) {
+ for (PathPieces::const_iterator it = pieces.begin(),
+ et = pieces.end(); it != et; ++it) {
+ const PathDiagnosticPiece *piece = it->getPtr();
+ if (const PathDiagnosticCallPiece *cp =
+ dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ compute_path_size(cp->path, size);
+ }
+ else
+ ++size;
+ }
+}
+
+unsigned PathDiagnostic::full_size() {
+ unsigned size = 0;
+ compute_path_size(path, size);
+ return size;
+}
+
//===----------------------------------------------------------------------===//
// FoldingSet profiling methods.
//===----------------------------------------------------------------------===//
@@ -343,6 +621,14 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
}
}
+void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticPiece::Profile(ID);
+ for (PathPieces::const_iterator it = path.begin(),
+ et = path.end(); it != et; ++it) {
+ ID.Add(**it);
+ }
+}
+
void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticPiece::Profile(ID);
ID.Add(Pos);
@@ -356,18 +642,114 @@ void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticSpotPiece::Profile(ID);
- for (const_iterator I = begin(), E = end(); I != E; ++I)
+ for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
+ I != E; ++I)
ID.Add(**I);
}
void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(Size);
+ if (!path.empty())
+ getLocation().Profile(ID);
ID.AddString(BugType);
ID.AddString(Desc);
ID.AddString(Category);
- for (const_iterator I = begin(), E = end(); I != E; ++I)
- ID.Add(*I);
-
+}
+
+void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
+ Profile(ID);
+ for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I)
+ ID.Add(**I);
for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
ID.AddString(*I);
}
+
+StackHintGenerator::~StackHintGenerator() {}
+
+std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
+ ProgramPoint P = N->getLocation();
+ const CallExit *CExit = dyn_cast<CallExit>(&P);
+ assert(CExit && "Stack Hints should be constructed at CallExit points.");
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
+ if (!CE)
+ return "";
+
+ // Get the successor node to make sure the return statement is evaluated and
+ // CE is set to the result value.
+ N = *N->succ_begin();
+ if (!N)
+ return getMessageForSymbolNotFound();
+
+ // Check if one of the parameters are set to the interesting symbol.
+ ProgramStateRef State = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+ unsigned ArgIndex = 0;
+ for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+ E = CE->arg_end(); I != E; ++I, ++ArgIndex){
+ SVal SV = State->getSVal(*I, LCtx);
+
+ // Check if the variable corresponding to the symbol is passed by value.
+ SymbolRef AS = SV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+
+ // Check if the parameter is a pointer to the symbol.
+ if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) {
+ SVal PSV = State->getSVal(Reg->getRegion());
+ SymbolRef AS = PSV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+ }
+ }
+
+ // Check if we are returning the interesting symbol.
+ SVal SV = State->getSVal(CE, LCtx);
+ SymbolRef RetSym = SV.getAsLocSymbol();
+ if (RetSym == Sym) {
+ return getMessageForReturn(CE);
+ }
+
+ return getMessageForSymbolNotFound();
+}
+
+/// TODO: This is copied from clang diagnostics. Maybe we could just move it to
+/// some common place. (Same as HandleOrdinalModifier.)
+void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo,
+ llvm::raw_svector_ostream &Out) {
+ assert(ValNo != 0 && "ValNo must be strictly positive!");
+
+ // We could use text forms for the first N ordinals, but the numeric
+ // forms are actually nicer in diagnostics because they stand out.
+ Out << ValNo;
+
+ // It is critically important that we do this perfectly for
+ // user-written sequences with over 100 elements.
+ switch (ValNo % 100) {
+ case 11:
+ case 12:
+ case 13:
+ Out << "th"; return;
+ default:
+ switch (ValNo % 10) {
+ case 1: Out << "st"; return;
+ case 2: Out << "nd"; return;
+ case 3: Out << "rd"; return;
+ default: Out << "th"; return;
+ }
+ }
+}
+
+std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << Msg << " via ";
+ // Printed parameters start at 1, not 0.
+ printOrdinal(++ArgIndex, os);
+ os << " parameter";
+
+ return os.str();
+}
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 5ae95c6..323cede 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -25,56 +25,23 @@ using namespace ento;
typedef llvm::DenseMap<FileID, unsigned> FIDMap;
-namespace {
-struct CompareDiagnostics {
- // Compare if 'X' is "<" than 'Y'.
- bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
- // First compare by location
- const FullSourceLoc &XLoc = X->getLocation().asLocation();
- const FullSourceLoc &YLoc = Y->getLocation().asLocation();
- if (XLoc < YLoc)
- return true;
- if (XLoc != YLoc)
- return false;
-
- // Next, compare by bug type.
- StringRef XBugType = X->getBugType();
- StringRef YBugType = Y->getBugType();
- if (XBugType < YBugType)
- return true;
- if (XBugType != YBugType)
- return false;
-
- // Next, compare by bug description.
- StringRef XDesc = X->getDescription();
- StringRef YDesc = Y->getDescription();
- if (XDesc < YDesc)
- return true;
- if (XDesc != YDesc)
- return false;
-
- // FIXME: Further refine by comparing PathDiagnosticPieces?
- return false;
- }
-};
-}
namespace {
class PlistDiagnostics : public PathDiagnosticConsumer {
- std::vector<const PathDiagnostic*> BatchedDiags;
const std::string OutputFile;
const LangOptions &LangOpts;
- llvm::OwningPtr<PathDiagnosticConsumer> SubPD;
+ OwningPtr<PathDiagnosticConsumer> SubPD;
bool flushed;
+ const bool SupportsCrossFileDiagnostics;
public:
PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
+ bool supportsMultipleFiles,
PathDiagnosticConsumer *subPD);
- ~PlistDiagnostics() { FlushDiagnostics(NULL); }
+ virtual ~PlistDiagnostics() {}
- void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade);
-
- void HandlePathDiagnosticImpl(const PathDiagnostic* D);
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade);
virtual StringRef getName() const {
return "PlistDiagnostics";
@@ -84,18 +51,29 @@ namespace {
bool supportsLogicalOpControlFlow() const { return true; }
bool supportsAllBlockEdges() const { return true; }
virtual bool useVerboseDescription() const { return false; }
+ virtual bool supportsCrossFileDiagnostics() const {
+ return SupportsCrossFileDiagnostics;
+ }
};
} // end anonymous namespace
PlistDiagnostics::PlistDiagnostics(const std::string& output,
const LangOptions &LO,
+ bool supportsMultipleFiles,
PathDiagnosticConsumer *subPD)
- : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {}
+ : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
PathDiagnosticConsumer*
ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP,
PathDiagnosticConsumer *subPD) {
- return new PlistDiagnostics(s, PP.getLangOptions(), subPD);
+ return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD);
+}
+
+PathDiagnosticConsumer*
+ento::createPlistMultiFileDiagnosticConsumer(const std::string &s,
+ const Preprocessor &PP) {
+ return new PlistDiagnostics(s, PP.getLangOpts(), true, 0);
}
PathDiagnosticConsumer::PathGenerationScheme
@@ -167,10 +145,9 @@ static void EmitRange(raw_ostream &o, const SourceManager &SM,
Indent(o, indent) << "</array>\n";
}
-static raw_ostream &EmitString(raw_ostream &o,
- const std::string& s) {
+static raw_ostream &EmitString(raw_ostream &o, StringRef s) {
o << "<string>";
- for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) {
+ for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) {
char c = *I;
switch (c) {
default: o << c; break;
@@ -232,7 +209,8 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM,
const SourceManager &SM,
const LangOptions &LangOpts,
- unsigned indent) {
+ unsigned indent,
+ unsigned depth) {
Indent(o, indent) << "<dict>\n";
++indent;
@@ -258,6 +236,10 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
--indent;
Indent(o, indent) << "</array>\n";
}
+
+ // Output the call depth.
+ Indent(o, indent) << "<key>depth</key>"
+ << "<integer>" << depth << "</integer>\n";
// Output the text.
assert(!P.getString().empty());
@@ -269,108 +251,143 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
// FIXME: Really use a short string.
Indent(o, indent) << "<key>message</key>\n";
EmitString(o, P.getString()) << '\n';
-
+
// Finish up.
--indent;
Indent(o, indent); o << "</dict>\n";
}
+static void ReportPiece(raw_ostream &o,
+ const PathDiagnosticPiece &P,
+ const FIDMap& FM, const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent,
+ unsigned depth,
+ bool includeControlFlow);
+
+static void ReportCall(raw_ostream &o,
+ const PathDiagnosticCallPiece &P,
+ const FIDMap& FM, const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent,
+ unsigned depth) {
+
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter =
+ P.getCallEnterEvent();
+
+ if (callEnter)
+ ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true);
+
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller =
+ P.getCallEnterWithinCallerEvent();
+
+ ++depth;
+
+ if (callEnterWithinCaller)
+ ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts,
+ indent, depth, true);
+
+ for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
+ ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true);
+
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
+ P.getCallExitEvent();
+
+ if (callExit)
+ ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true);
+}
+
static void ReportMacro(raw_ostream &o,
const PathDiagnosticMacroPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts,
- unsigned indent) {
+ unsigned indent,
+ unsigned depth) {
- for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
+ for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
I!=E; ++I) {
-
- switch ((*I)->getKind()) {
- default:
- break;
- case PathDiagnosticPiece::Event:
- ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts,
- indent);
- break;
- case PathDiagnosticPiece::Macro:
- ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts,
- indent);
- break;
- }
+ ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false);
}
}
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts) {
-
- unsigned indent = 4;
-
- switch (P.getKind()) {
- case PathDiagnosticPiece::ControlFlow:
- ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
- LangOpts, indent);
- break;
- case PathDiagnosticPiece::Event:
- ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
- indent);
- break;
- case PathDiagnosticPiece::Macro:
- ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
- indent);
- break;
- }
+ ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true);
}
-void PlistDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
- if (!D)
- return;
-
- if (D->empty()) {
- delete D;
- return;
+static void ReportPiece(raw_ostream &o,
+ const PathDiagnosticPiece &P,
+ const FIDMap& FM, const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent,
+ unsigned depth,
+ bool includeControlFlow) {
+ switch (P.getKind()) {
+ case PathDiagnosticPiece::ControlFlow:
+ if (includeControlFlow)
+ ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
+ LangOpts, indent);
+ break;
+ case PathDiagnosticPiece::Call:
+ ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts,
+ indent, depth);
+ break;
+ case PathDiagnosticPiece::Event:
+ ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
+ indent, depth);
+ break;
+ case PathDiagnosticPiece::Macro:
+ ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
+ indent, depth);
+ break;
}
-
- // We need to flatten the locations (convert Stmt* to locations) because
- // the referenced statements may be freed by the time the diagnostics
- // are emitted.
- const_cast<PathDiagnostic*>(D)->flattenLocations();
- BatchedDiags.push_back(D);
}
-void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
- *FilesMade) {
-
- if (flushed)
- return;
-
- flushed = true;
-
- // Sort the diagnostics so that they are always emitted in a deterministic
- // order.
- if (!BatchedDiags.empty())
- std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics());
-
+void PlistDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade) {
// Build up a set of FIDs that we use by scanning the locations and
// ranges of the diagnostics.
FIDMap FM;
SmallVector<FileID, 10> Fids;
const SourceManager* SM = 0;
- if (!BatchedDiags.empty())
- SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager();
+ if (!Diags.empty())
+ SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager();
- for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(),
- DE = BatchedDiags.end(); DI != DE; ++DI) {
+
+ for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(),
+ DE = Diags.end(); DI != DE; ++DI) {
const PathDiagnostic *D = *DI;
- for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) {
- AddFID(FM, Fids, SM, I->getLocation().asLocation());
+ llvm::SmallVector<const PathPieces *, 5> WorkList;
+ WorkList.push_back(&D->path);
- for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
- RE=I->ranges_end(); RI!=RE; ++RI) {
- AddFID(FM, Fids, SM, RI->getBegin());
- AddFID(FM, Fids, SM, RI->getEnd());
+ while (!WorkList.empty()) {
+ const PathPieces &path = *WorkList.back();
+ WorkList.pop_back();
+
+ for (PathPieces::const_iterator I = path.begin(), E = path.end();
+ I!=E; ++I) {
+ const PathDiagnosticPiece *piece = I->getPtr();
+ AddFID(FM, Fids, SM, piece->getLocation().asLocation());
+
+ for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(),
+ RE= piece->ranges_end(); RI != RE; ++RI) {
+ AddFID(FM, Fids, SM, RI->getBegin());
+ AddFID(FM, Fids, SM, RI->getEnd());
+ }
+
+ if (const PathDiagnosticCallPiece *call =
+ dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ WorkList.push_back(&call->path);
+ }
+ else if (const PathDiagnosticMacroPiece *macro =
+ dyn_cast<PathDiagnosticMacroPiece>(piece)) {
+ WorkList.push_back(&macro->subPieces);
+ }
}
}
}
@@ -379,7 +396,7 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
std::string ErrMsg;
llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg);
if (!ErrMsg.empty()) {
- llvm::errs() << "warning: could not creat file: " << OutputFile << '\n';
+ llvm::errs() << "warning: could not create file: " << OutputFile << '\n';
return;
}
@@ -406,21 +423,19 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
" <key>diagnostics</key>\n"
" <array>\n";
- for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(),
- DE = BatchedDiags.end(); DI!=DE; ++DI) {
+ for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
+ DE = Diags.end(); DI!=DE; ++DI) {
o << " <dict>\n"
" <key>path</key>\n";
const PathDiagnostic *D = *DI;
- // Create an owning smart pointer for 'D' just so that we auto-free it
- // when we exit this method.
- llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D));
o << " <array>\n";
- for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I)
- ReportDiag(o, *I, FM, *SM, LangOpts);
+ for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
+ I != E; ++I)
+ ReportDiag(o, **I, FM, *SM, LangOpts);
o << " </array>\n";
@@ -431,6 +446,38 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
EmitString(o, D->getCategory()) << '\n';
o << " <key>type</key>";
EmitString(o, D->getBugType()) << '\n';
+
+ // Output information about the semantic context where
+ // the issue occurred.
+ if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
+ // FIXME: handle blocks, which have no name.
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
+ StringRef declKind;
+ switch (ND->getKind()) {
+ case Decl::CXXRecord:
+ declKind = "C++ class";
+ break;
+ case Decl::CXXMethod:
+ declKind = "C++ method";
+ break;
+ case Decl::ObjCMethod:
+ declKind = "Objective-C method";
+ break;
+ case Decl::Function:
+ declKind = "function";
+ break;
+ default:
+ break;
+ }
+ if (!declKind.empty()) {
+ const std::string &declName = ND->getDeclName().getAsString();
+ o << " <key>issue_context_kind</key>";
+ EmitString(o, declKind) << '\n';
+ o << " <key>issue_context</key>";
+ EmitString(o, declName) << '\n';
+ }
+ }
+ }
// Output the location of the bug.
o << " <key>location</key>\n";
@@ -438,9 +485,10 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
// Output the diagnostic to the sub-diagnostic client, if any.
if (SubPD) {
- SubPD->HandlePathDiagnostic(OwnedD.take());
+ std::vector<const PathDiagnostic *> SubDiags;
+ SubDiags.push_back(D);
SmallVector<std::string, 1> SubFilesMade;
- SubPD->FlushDiagnostics(SubFilesMade);
+ SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade);
if (!SubFilesMade.empty()) {
o << " <key>" << SubPD->getName() << "_files</key>\n";
@@ -462,6 +510,4 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
if (FilesMade)
FilesMade->push_back(OutputFile);
-
- BatchedDiags.clear();
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 73788cc..b9cfa27 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -24,6 +25,26 @@ using namespace ento;
// FIXME: Move this elsewhere.
ConstraintManager::~ConstraintManager() {}
+namespace clang { namespace ento {
+/// Increments the number of times this state is referenced.
+
+void ProgramStateRetain(const ProgramState *state) {
+ ++const_cast<ProgramState*>(state)->refCount;
+}
+
+/// Decrement the number of times this state is referenced.
+void ProgramStateRelease(const ProgramState *state) {
+ assert(state->refCount > 0);
+ ProgramState *s = const_cast<ProgramState*>(state);
+ if (--s->refCount == 0) {
+ ProgramStateManager &Mgr = s->getStateManager();
+ Mgr.StateSet.RemoveNode(s);
+ s->~ProgramState();
+ Mgr.freeStates.push_back(s);
+ }
+}
+}}
+
ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env,
StoreRef st, GenericDataMap gdm)
: stateMgr(mgr),
@@ -55,8 +76,8 @@ ProgramStateManager::~ProgramStateManager() {
I->second.second(I->second.first);
}
-const ProgramState*
-ProgramStateManager::removeDeadBindings(const ProgramState *state,
+ProgramStateRef
+ProgramStateManager::removeDeadBindings(ProgramStateRef state,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper) {
@@ -79,7 +100,7 @@ ProgramStateManager::removeDeadBindings(const ProgramState *state,
return getPersistentState(NewState);
}
-const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state,
+ProgramStateRef ProgramStateManager::MarshalState(ProgramStateRef state,
const StackFrameContext *InitLoc) {
// make up an empty state for now.
ProgramState State(this,
@@ -90,7 +111,7 @@ const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state,
return getPersistentState(State);
}
-const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL,
+ProgramStateRef ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL,
const LocationContext *LC,
SVal V) const {
const StoreRef &newStore =
@@ -98,21 +119,21 @@ const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr
return makeWithStore(newStore);
}
-const ProgramState *ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const {
+ProgramStateRef ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const {
const StoreRef &newStore =
getStateManager().StoreMgr->BindDecl(getStore(), VR, IVal);
return makeWithStore(newStore);
}
-const ProgramState *ProgramState::bindDeclWithNoInit(const VarRegion* VR) const {
+ProgramStateRef ProgramState::bindDeclWithNoInit(const VarRegion* VR) const {
const StoreRef &newStore =
getStateManager().StoreMgr->BindDeclWithNoInit(getStore(), VR);
return makeWithStore(newStore);
}
-const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const {
+ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V) const {
ProgramStateManager &Mgr = getStateManager();
- const ProgramState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
+ ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
LV, V));
const MemRegion *MR = LV.getAsRegion();
if (MR && Mgr.getOwningEngine())
@@ -121,53 +142,55 @@ const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const {
return newState;
}
-const ProgramState *ProgramState::bindDefault(SVal loc, SVal V) const {
+ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
- const ProgramState *new_state = makeWithStore(newStore);
+ ProgramStateRef new_state = makeWithStore(newStore);
return Mgr.getOwningEngine() ?
Mgr.getOwningEngine()->processRegionChange(new_state, R) :
new_state;
}
-const ProgramState *
+ProgramStateRef
ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
StoreManager::InvalidatedSymbols *IS,
- bool invalidateGlobals) const {
+ const CallOrObjCMessage *Call) const {
if (!IS) {
StoreManager::InvalidatedSymbols invalidated;
- return invalidateRegionsImpl(Regions, E, Count,
- invalidated, invalidateGlobals);
+ return invalidateRegionsImpl(Regions, E, Count, LCtx,
+ invalidated, Call);
}
- return invalidateRegionsImpl(Regions, E, Count, *IS, invalidateGlobals);
+ return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call);
}
-const ProgramState *
+ProgramStateRef
ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
StoreManager::InvalidatedSymbols &IS,
- bool invalidateGlobals) const {
+ const CallOrObjCMessage *Call) const {
ProgramStateManager &Mgr = getStateManager();
SubEngine* Eng = Mgr.getOwningEngine();
if (Eng && Eng->wantsRegionChangeUpdate(this)) {
StoreManager::InvalidatedRegions Invalidated;
const StoreRef &newStore
- = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS,
- invalidateGlobals, &Invalidated);
- const ProgramState *newState = makeWithStore(newStore);
- return Eng->processRegionChanges(newState, &IS, Regions, Invalidated);
+ = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS,
+ Call, &Invalidated);
+ ProgramStateRef newState = makeWithStore(newStore);
+ return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call);
}
const StoreRef &newStore =
- Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS,
- invalidateGlobals, NULL);
+ Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS,
+ Call, NULL);
return makeWithStore(newStore);
}
-const ProgramState *ProgramState::unbindLoc(Loc LV) const {
+ProgramStateRef ProgramState::unbindLoc(Loc LV) const {
assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead.");
Store OldStore = getStore();
@@ -179,9 +202,11 @@ const ProgramState *ProgramState::unbindLoc(Loc LV) const {
return makeWithStore(newStore);
}
-const ProgramState *ProgramState::enterStackFrame(const StackFrameContext *frame) const {
+ProgramStateRef
+ProgramState::enterStackFrame(const LocationContext *callerCtx,
+ const StackFrameContext *calleeCtx) const {
const StoreRef &new_store =
- getStateManager().StoreMgr->enterStackFrame(this, frame);
+ getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx);
return makeWithStore(new_store);
}
@@ -238,9 +263,12 @@ SVal ProgramState::getSVal(Loc location, QualType T) const {
return V;
}
-const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidate) const{
- Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V,
- Invalidate);
+ProgramStateRef ProgramState::BindExpr(const Stmt *S,
+ const LocationContext *LCtx,
+ SVal V, bool Invalidate) const{
+ Environment NewEnv =
+ getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V,
+ Invalidate);
if (NewEnv == Env)
return this;
@@ -249,10 +277,14 @@ const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidat
return getStateManager().getPersistentState(NewSt);
}
-const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal location,
- SVal V) const {
+ProgramStateRef
+ProgramState::bindExprAndLocation(const Stmt *S, const LocationContext *LCtx,
+ SVal location,
+ SVal V) const {
Environment NewEnv =
- getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V);
+ getStateManager().EnvMgr.bindExprAndLocation(Env,
+ EnvironmentEntry(S, LCtx),
+ location, V);
if (NewEnv == Env)
return this;
@@ -262,9 +294,10 @@ const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal locati
return getStateManager().getPersistentState(NewSt);
}
-const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
+ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
DefinedOrUnknownSVal UpperBound,
- bool Assumption) const {
+ bool Assumption,
+ QualType indexTy) const {
if (Idx.isUnknown() || UpperBound.isUnknown())
return this;
@@ -278,7 +311,8 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
// Get the offset: the minimum value of the array index type.
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
// FIXME: This should be using ValueManager::ArrayindexTy...somehow.
- QualType indexTy = Ctx.IntTy;
+ if (indexTy.isNull())
+ indexTy = Ctx.IntTy;
nonloc::ConcreteInt Min(BVF.getMinValue(indexTy));
// Adjust the index.
@@ -307,7 +341,7 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
return CM.assume(this, cast<DefinedSVal>(inBound), Assumption);
}
-const ProgramState *ProgramStateManager::getInitialState(const LocationContext *InitLoc) {
+ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) {
ProgramState State(this,
EnvMgr.getInitialEnvironment(),
StoreMgr->getInitialStore(InitLoc),
@@ -316,28 +350,15 @@ const ProgramState *ProgramStateManager::getInitialState(const LocationContext *
return getPersistentState(State);
}
-void ProgramStateManager::recycleUnusedStates() {
- for (std::vector<ProgramState*>::iterator i = recentlyAllocatedStates.begin(),
- e = recentlyAllocatedStates.end(); i != e; ++i) {
- ProgramState *state = *i;
- if (state->referencedByExplodedNode())
- continue;
- StateSet.RemoveNode(state);
- freeStates.push_back(state);
- state->~ProgramState();
- }
- recentlyAllocatedStates.clear();
-}
-
-const ProgramState *ProgramStateManager::getPersistentStateWithGDM(
- const ProgramState *FromState,
- const ProgramState *GDMState) {
- ProgramState NewState = *FromState;
+ProgramStateRef ProgramStateManager::getPersistentStateWithGDM(
+ ProgramStateRef FromState,
+ ProgramStateRef GDMState) {
+ ProgramState NewState(*FromState);
NewState.GDM = GDMState->GDM;
return getPersistentState(NewState);
}
-const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) {
+ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) {
llvm::FoldingSetNodeID ID;
State.Profile(ID);
@@ -356,12 +377,11 @@ const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State)
}
new (newState) ProgramState(State);
StateSet.InsertNode(newState, InsertPos);
- recentlyAllocatedStates.push_back(newState);
return newState;
}
-const ProgramState *ProgramState::makeWithStore(const StoreRef &store) const {
- ProgramState NewSt = *this;
+ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const {
+ ProgramState NewSt(*this);
NewSt.setStore(store);
return getStateManager().getPersistentState(NewSt);
}
@@ -379,92 +399,44 @@ void ProgramState::setStore(const StoreRef &newStore) {
// 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 ProgramState::print(raw_ostream &Out, CFG &C,
+void ProgramState::print(raw_ostream &Out,
const char *NL, const char *Sep) const {
// Print the store.
ProgramStateManager &Mgr = getStateManager();
Mgr.getStoreManager().print(getStore(), Out, NL, Sep);
- // Print Subexpression bindings.
- bool isFirst = true;
+ // Print out the environment.
+ Env.print(Out, NL, Sep);
- // 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()) || IsEnvLoc(I.getKey()))
- continue;
-
- if (isFirst) {
- Out << NL << NL << "Sub-Expressions:" << NL;
- isFirst = false;
- } else {
- Out << NL;
- }
+ // Print out the constraints.
+ Mgr.getConstraintManager().print(this, Out, NL, Sep);
- Out << " (" << (void*) I.getKey() << ") ";
- LangOptions LO; // FIXME.
- I.getKey()->printPretty(Out, 0, PrintingPolicy(LO));
- Out << " : " << I.getData();
- }
+ // Print checker-specific data.
+ Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
+}
- // Print block-expression bindings.
- isFirst = true;
+void ProgramState::printDOT(raw_ostream &Out) const {
+ print(Out, "\\l", "\\|");
+}
- for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
- if (!C.isBlkExpr(I.getKey()))
- continue;
+void ProgramState::dump() const {
+ print(llvm::errs());
+}
- if (isFirst) {
- Out << NL << NL << "Block-level Expressions:" << NL;
- isFirst = false;
- } else {
- Out << NL;
- }
+void ProgramState::printTaint(raw_ostream &Out,
+ const char *NL, const char *Sep) const {
+ TaintMapImpl TM = get<TaintMap>();
- Out << " (" << (void*) I.getKey() << ") ";
- LangOptions LO; // FIXME.
- 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;
- }
+ if (!TM.isEmpty())
+ Out <<"Tainted Symbols:" << 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();
+ for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) {
+ Out << I->first << " : " << I->second << NL;
}
-
- Mgr.getConstraintManager().print(this, Out, NL, Sep);
-
- // Print checker-specific data.
- Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
-}
-
-void ProgramState::printDOT(raw_ostream &Out, CFG &C) const {
- print(Out, C, "\\l", "\\|");
}
-void ProgramState::printStdErr(CFG &C) const {
- print(llvm::errs(), C);
+void ProgramState::dumpTaint() const {
+ printTaint(llvm::errs());
}
//===----------------------------------------------------------------------===//
@@ -489,7 +461,7 @@ ProgramStateManager::FindGDMContext(void *K,
return p.first;
}
-const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Key, void *Data){
+ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){
ProgramState::GenericDataMap M1 = St->getGDM();
ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data);
@@ -501,7 +473,7 @@ const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Ke
return getPersistentState(NewSt);
}
-const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, void *Key) {
+ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) {
ProgramState::GenericDataMap OldM = state->getGDM();
ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key);
@@ -513,6 +485,8 @@ const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, vo
return getPersistentState(NewState);
}
+void ScanReachableSymbols::anchor() { }
+
bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
if (!scan(*I))
@@ -527,10 +501,10 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) {
return true;
isVisited = 1;
- if (const SymbolData *sData = dyn_cast<SymbolData>(sym))
- if (!visitor.VisitSymbol(sData))
- return false;
+ if (!visitor.VisitSymbol(sym))
+ return false;
+ // TODO: should be rewritten using SymExpr::symbol_iterator.
switch (sym->getKind()) {
case SymExpr::RegionValueKind:
case SymExpr::ConjuredKind:
@@ -538,8 +512,12 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) {
case SymExpr::ExtentKind:
case SymExpr::MetadataKind:
break;
+ case SymExpr::CastSymbolKind:
+ return scan(cast<SymbolCast>(sym)->getOperand());
case SymExpr::SymIntKind:
return scan(cast<SymIntExpr>(sym)->getLHS());
+ case SymExpr::IntSymKind:
+ return scan(cast<IntSymExpr>(sym)->getRHS());
case SymExpr::SymSymKind: {
const SymSymExpr *x = cast<SymSymExpr>(sym);
return scan(x->getLHS()) && scan(x->getRHS());
@@ -575,6 +553,10 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
if (isVisited)
return true;
isVisited = 1;
+
+
+ if (!visitor.VisitMemRegion(R))
+ return false;
// If this is a symbolic region, visit the symbol for the region.
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
@@ -623,3 +605,105 @@ bool ProgramState::scanReachableSymbols(const MemRegion * const *I,
}
return true;
}
+
+ProgramStateRef ProgramState::addTaint(const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind) const {
+ if (const Expr *E = dyn_cast_or_null<Expr>(S))
+ S = E->IgnoreParens();
+
+ SymbolRef Sym = getSVal(S, LCtx).getAsSymbol();
+ if (Sym)
+ return addTaint(Sym, Kind);
+
+ const MemRegion *R = getSVal(S, LCtx).getAsRegion();
+ addTaint(R, Kind);
+
+ // Cannot add taint, so just return the state.
+ return this;
+}
+
+ProgramStateRef ProgramState::addTaint(const MemRegion *R,
+ TaintTagType Kind) const {
+ if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R))
+ return addTaint(SR->getSymbol(), Kind);
+ return this;
+}
+
+ProgramStateRef ProgramState::addTaint(SymbolRef Sym,
+ TaintTagType Kind) const {
+ // If this is a symbol cast, remove the cast before adding the taint. Taint
+ // is cast agnostic.
+ while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
+ Sym = SC->getOperand();
+
+ ProgramStateRef NewState = set<TaintMap>(Sym, Kind);
+ assert(NewState);
+ return NewState;
+}
+
+bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx,
+ TaintTagType Kind) const {
+ if (const Expr *E = dyn_cast_or_null<Expr>(S))
+ S = E->IgnoreParens();
+
+ SVal val = getSVal(S, LCtx);
+ return isTainted(val, Kind);
+}
+
+bool ProgramState::isTainted(SVal V, TaintTagType Kind) const {
+ if (const SymExpr *Sym = V.getAsSymExpr())
+ return isTainted(Sym, Kind);
+ if (const MemRegion *Reg = V.getAsRegion())
+ return isTainted(Reg, Kind);
+ return false;
+}
+
+bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const {
+ if (!Reg)
+ return false;
+
+ // Element region (array element) is tainted if either the base or the offset
+ // are tainted.
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg))
+ return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K);
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg))
+ return isTainted(SR->getSymbol(), K);
+
+ if (const SubRegion *ER = dyn_cast<SubRegion>(Reg))
+ return isTainted(ER->getSuperRegion(), K);
+
+ return false;
+}
+
+bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
+ if (!Sym)
+ return false;
+
+ // Traverse all the symbols this symbol depends on to see if any are tainted.
+ bool Tainted = false;
+ for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end();
+ SI != SE; ++SI) {
+ assert(isa<SymbolData>(*SI));
+ const TaintTagType *Tag = get<TaintMap>(*SI);
+ Tainted = (Tag && *Tag == Kind);
+
+ // If this is a SymbolDerived with a tainted parent, it's also tainted.
+ if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI))
+ Tainted = Tainted || isTainted(SD->getParentSymbol(), Kind);
+
+ // If memory region is tainted, data is also tainted.
+ if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI))
+ Tainted = Tainted || isTainted(SRV->getRegion(), Kind);
+
+ // If If this is a SymbolCast from a tainted value, it's also tainted.
+ if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI))
+ Tainted = Tainted || isTainted(SC->getOperand(), Kind);
+
+ if (Tainted)
+ return true;
+ }
+
+ return Tainted;
+}
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 9337788..98eb958 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -204,46 +204,46 @@ struct ProgramStateTrait<ConstraintRange>
namespace {
class RangeConstraintManager : public SimpleConstraintManager{
- RangeSet GetRange(const ProgramState *state, SymbolRef sym);
+ RangeSet GetRange(ProgramStateRef state, SymbolRef sym);
public:
RangeConstraintManager(SubEngine &subengine)
: SimpleConstraintManager(subengine) {}
- const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym,
+ ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const llvm::APSInt* getSymVal(const ProgramState *St, SymbolRef sym) const;
+ const llvm::APSInt* getSymVal(ProgramStateRef St, SymbolRef sym) const;
// FIXME: Refactor into SimpleConstraintManager?
- bool isEqual(const ProgramState *St, SymbolRef sym, const llvm::APSInt& V) const {
+ bool isEqual(ProgramStateRef St, SymbolRef sym, const llvm::APSInt& V) const {
const llvm::APSInt *i = getSymVal(St, sym);
return i ? *i == V : false;
}
- const ProgramState *removeDeadBindings(const ProgramState *St, SymbolReaper& SymReaper);
+ ProgramStateRef removeDeadBindings(ProgramStateRef St, SymbolReaper& SymReaper);
- void print(const ProgramState *St, raw_ostream &Out,
+ void print(ProgramStateRef St, raw_ostream &Out,
const char* nl, const char *sep);
private:
@@ -257,7 +257,7 @@ ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&,
return new RangeConstraintManager(subeng);
}
-const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St,
+const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St,
SymbolRef sym) const {
const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym);
return T ? T->getConcreteValue() : NULL;
@@ -265,8 +265,8 @@ const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St,
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
-const ProgramState*
-RangeConstraintManager::removeDeadBindings(const ProgramState *state,
+ProgramStateRef
+RangeConstraintManager::removeDeadBindings(ProgramStateRef state,
SymbolReaper& SymReaper) {
ConstraintRangeTy CR = state->get<ConstraintRange>();
@@ -282,7 +282,7 @@ RangeConstraintManager::removeDeadBindings(const ProgramState *state,
}
RangeSet
-RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) {
+RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym))
return *V;
@@ -305,8 +305,8 @@ RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) {
// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1,
// UINT_MAX, 0, 1, and 2.
-const ProgramState*
-RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -322,8 +322,8 @@ RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const ProgramState*
-RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
// [Int-Adjustment, Int-Adjustment]
@@ -333,8 +333,8 @@ RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const ProgramState*
-RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -354,8 +354,8 @@ RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const ProgramState*
-RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -375,8 +375,8 @@ RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const ProgramState*
-RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -397,8 +397,8 @@ RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const ProgramState*
-RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym,
+ProgramStateRef
+RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -423,18 +423,20 @@ RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym,
// Pretty-printing.
//===------------------------------------------------------------------------===/
-void RangeConstraintManager::print(const ProgramState *St, raw_ostream &Out,
+void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out,
const char* nl, const char *sep) {
ConstraintRangeTy Ranges = St->get<ConstraintRange>();
- if (Ranges.isEmpty())
+ if (Ranges.isEmpty()) {
+ Out << nl << sep << "Ranges are empty." << nl;
return;
+ }
- Out << nl << sep << "ranges of symbol values:";
-
+ Out << nl << sep << "Ranges of symbol values:";
for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){
Out << nl << ' ' << I.getKey() << " : ";
I.getData().print(Out);
}
+ Out << nl;
}
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index 4b76cf1..cc3ea8c3 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -20,6 +20,7 @@
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -228,6 +229,16 @@ public:
/// For DerivedToBase casts, create a CXXBaseObjectRegion and return it.
virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType);
+ /// \brief Evaluates C++ dynamic_cast cast.
+ /// The callback may result in the following 3 scenarios:
+ /// - Successful cast (ex: derived is subclass of base).
+ /// - Failed cast (ex: derived is definitely not a subclass of base).
+ /// - We don't know (base is a symbolic region and we don't have
+ /// enough info to determine if the cast will succeed at run time).
+ /// The function returns an SVal representing the derived class; it's
+ /// valid only if Failed flag is set to false.
+ virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,bool &Failed);
+
StoreRef getInitialStore(const LocationContext *InitLoc) {
return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this);
}
@@ -235,11 +246,18 @@ public:
//===-------------------------------------------------------------------===//
// Binding values to regions.
//===-------------------------------------------------------------------===//
+ RegionBindings invalidateGlobalRegion(MemRegion::Kind K,
+ const Expr *Ex,
+ unsigned Count,
+ const LocationContext *LCtx,
+ RegionBindings B,
+ InvalidatedRegions *Invalidated);
StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
InvalidatedSymbols &IS,
- bool invalidateGlobals,
+ const CallOrObjCMessage *Call,
InvalidatedRegions *Invalidated);
public: // Made public for helper classes.
@@ -273,7 +291,8 @@ public: // Part of public interface to class.
RegionBindings B = GetRegionBindings(store);
assert(!lookup(B, R, BindingKey::Default));
assert(!lookup(B, R, BindingKey::Direct));
- return StoreRef(addBinding(B, R, BindingKey::Default, V).getRootWithoutRetain(), *this);
+ return StoreRef(addBinding(B, R, BindingKey::Default, V)
+ .getRootWithoutRetain(), *this);
}
StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr *CL,
@@ -308,12 +327,10 @@ public: // Part of public interface to class.
bool includedInBindings(Store store, const MemRegion *region) const;
- //===------------------------------------------------------------------===//
- // Loading values from regions.
- //===------------------------------------------------------------------===//
-
+ /// \brief Return the value bound to specified location in a given state.
+ ///
/// The high level logic for this method is this:
- /// Retrieve (L)
+ /// getBinding (L)
/// if L has binding
/// return L's binding
/// else if L is in killset
@@ -323,39 +340,39 @@ public: // Part of public interface to class.
/// return undefined
/// else
/// return symbolic
- SVal Retrieve(Store store, Loc L, QualType T = QualType());
+ SVal getBinding(Store store, Loc L, QualType T = QualType());
- SVal RetrieveElement(Store store, const ElementRegion *R);
+ SVal getBindingForElement(Store store, const ElementRegion *R);
- SVal RetrieveField(Store store, const FieldRegion *R);
+ SVal getBindingForField(Store store, const FieldRegion *R);
- SVal RetrieveObjCIvar(Store store, const ObjCIvarRegion *R);
+ SVal getBindingForObjCIvar(Store store, const ObjCIvarRegion *R);
- SVal RetrieveVar(Store store, const VarRegion *R);
+ SVal getBindingForVar(Store store, const VarRegion *R);
- SVal RetrieveLazySymbol(const TypedValueRegion *R);
+ SVal getBindingForLazySymbol(const TypedValueRegion *R);
- SVal RetrieveFieldOrElementCommon(Store store, const TypedValueRegion *R,
- QualType Ty, const MemRegion *superR);
+ SVal getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R,
+ QualType Ty, const MemRegion *superR);
- SVal RetrieveLazyBinding(const MemRegion *lazyBindingRegion,
- Store lazyBindingStore);
+ SVal getLazyBinding(const MemRegion *lazyBindingRegion,
+ Store lazyBindingStore);
- /// Retrieve the values in a struct and return a CompoundVal, used when doing
- /// struct copy:
+ /// Get bindings for the values in a struct and return a CompoundVal, used
+ /// when doing struct copy:
/// struct s x, y;
/// x = y;
/// y's value is retrieved by this method.
- SVal RetrieveStruct(Store store, const TypedValueRegion* R);
+ SVal getBindingForStruct(Store store, const TypedValueRegion* R);
- SVal RetrieveArray(Store store, const TypedValueRegion* R);
+ SVal getBindingForArray(Store store, const TypedValueRegion* R);
/// Used to lazily generate derived symbols for bindings that are defined
/// implicitly by default bindings in a super region.
- Optional<SVal> RetrieveDerivedDefaultValue(RegionBindings B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty);
+ Optional<SVal> getBindingForDerivedDefaultValue(RegionBindings B,
+ const MemRegion *superR,
+ const TypedValueRegion *R,
+ QualType Ty);
/// Get the state and region whose binding this region R corresponds to.
std::pair<Store, const MemRegion*>
@@ -374,15 +391,16 @@ public: // Part of public interface to class.
StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
SymbolReaper& SymReaper);
- StoreRef enterStackFrame(const ProgramState *state,
- const StackFrameContext *frame);
+ StoreRef enterStackFrame(ProgramStateRef state,
+ const LocationContext *callerCtx,
+ const StackFrameContext *calleeCtx);
//===------------------------------------------------------------------===//
// Region "extents".
//===------------------------------------------------------------------===//
// FIXME: This method will soon be eliminated; see the note in Store.h.
- DefinedOrUnknownSVal getSizeInElements(const ProgramState *state,
+ DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state,
const MemRegion* R, QualType EleTy);
//===------------------------------------------------------------------===//
@@ -422,7 +440,8 @@ StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) {
return new RegionStoreManager(StMgr, F);
}
-StoreManager *ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
+StoreManager *
+ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
RegionStoreFeatures F = minimal_features_tag();
F.enableFields(true);
return new RegionStoreManager(StMgr, F);
@@ -587,6 +606,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker>
{
const Expr *Ex;
unsigned Count;
+ const LocationContext *LCtx;
StoreManager::InvalidatedSymbols &IS;
StoreManager::InvalidatedRegions *Regions;
public:
@@ -594,11 +614,12 @@ public:
ProgramStateManager &stateMgr,
RegionBindings b,
const Expr *ex, unsigned count,
+ const LocationContext *lctx,
StoreManager::InvalidatedSymbols &is,
StoreManager::InvalidatedRegions *r,
bool includeGlobals)
: ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals),
- Ex(ex), Count(count), IS(is), Regions(r) {}
+ Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {}
void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
void VisitBaseRegion(const MemRegion *baseR);
@@ -674,7 +695,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelavant.
DefinedOrUnknownSVal V =
- svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count);
+ svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count);
B = RM.addBinding(B, baseR, BindingKey::Default, V);
return;
}
@@ -690,7 +711,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelavant.
DefinedOrUnknownSVal V =
- svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count);
+ svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count);
B = RM.addBinding(B, baseR, BindingKey::Default, V);
return;
}
@@ -698,7 +719,8 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
if (const ArrayType *AT = Ctx.getAsArrayType(T)) {
// Set the default value of the array to conjured symbol.
DefinedOrUnknownSVal V =
- svalBuilder.getConjuredSymbolVal(baseR, Ex, AT->getElementType(), Count);
+ svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx,
+ AT->getElementType(), Count);
B = RM.addBinding(B, baseR, BindingKey::Default, V);
return;
}
@@ -713,20 +735,47 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
}
- DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, T, Count);
+ DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx,
+ T,Count);
assert(SymbolManager::canSymbolicate(T) || V.isUnknown());
B = RM.addBinding(B, baseR, BindingKey::Direct, V);
}
+RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K,
+ const Expr *Ex,
+ unsigned Count,
+ const LocationContext *LCtx,
+ RegionBindings B,
+ InvalidatedRegions *Invalidated) {
+ // Bind the globals memory space to a new symbol that we will use to derive
+ // the bindings for all globals.
+ const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K);
+ SVal V =
+ svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex, LCtx,
+ /* symbol type, doesn't matter */ Ctx.IntTy,
+ Count);
+
+ B = removeBinding(B, GS);
+ B = addBinding(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 (Invalidated)
+ Invalidated->push_back(GS);
+
+ return B;
+}
+
StoreRef RegionStoreManager::invalidateRegions(Store store,
ArrayRef<const MemRegion *> Regions,
const Expr *Ex, unsigned Count,
+ const LocationContext *LCtx,
InvalidatedSymbols &IS,
- bool invalidateGlobals,
+ const CallOrObjCMessage *Call,
InvalidatedRegions *Invalidated) {
invalidateRegionsWorker W(*this, StateMgr,
RegionStoreManager::GetRegionBindings(store),
- Ex, Count, IS, Invalidated, invalidateGlobals);
+ Ex, Count, LCtx, IS, Invalidated, false);
// Scan the bindings and generate the clusters.
W.GenerateClusters();
@@ -741,20 +790,20 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
// Return the new bindings.
RegionBindings B = W.getRegionBindings();
- if (invalidateGlobals) {
- // Bind the non-static globals memory space to a new symbol that we will
- // use to derive the bindings for all non-static globals.
- const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion();
- SVal V =
- svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex,
- /* symbol type, doesn't matter */ Ctx.IntTy,
- Count);
- B = addBinding(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 (Invalidated)
- Invalidated->push_back(GS);
+ // For all globals which are not static nor immutable: determine which global
+ // regions should be invalidated and invalidate them.
+ // TODO: This could possibly be more precise with modules.
+ //
+ // System calls invalidate only system globals.
+ if (Call && Call->isInSystemHeader()) {
+ B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind,
+ Ex, Count, LCtx, B, Invalidated);
+ // Internal calls might invalidate both system and internal globals.
+ } else {
+ B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind,
+ Ex, Count, LCtx, B, Invalidated);
+ B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind,
+ Ex, Count, LCtx, B, Invalidated);
}
return StoreRef(B.getRootWithoutRetain(), *this);
@@ -764,9 +813,10 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
// Extents for regions.
//===----------------------------------------------------------------------===//
-DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const ProgramState *state,
- const MemRegion *R,
- QualType EleTy) {
+DefinedOrUnknownSVal
+RegionStoreManager::getSizeInElements(ProgramStateRef state,
+ const MemRegion *R,
+ QualType EleTy) {
SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder);
const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
if (!SizeInt)
@@ -837,6 +887,75 @@ SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) {
return loc::MemRegionVal(baseReg);
}
+SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
+ bool &Failed) {
+ Failed = false;
+
+ loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base);
+ if (!baseRegVal)
+ return UnknownVal();
+ const MemRegion *BaseRegion = baseRegVal->stripCasts();
+
+ // Assume the derived class is a pointer or a reference to a CXX record.
+ derivedType = derivedType->getPointeeType();
+ assert(!derivedType.isNull());
+ const CXXRecordDecl *DerivedDecl = derivedType->getAsCXXRecordDecl();
+ if (!DerivedDecl && !derivedType->isVoidType())
+ return UnknownVal();
+
+ // Drill down the CXXBaseObject chains, which represent upcasts (casts from
+ // derived to base).
+ const MemRegion *SR = BaseRegion;
+ while (const TypedRegion *TSR = dyn_cast_or_null<TypedRegion>(SR)) {
+ QualType BaseType = TSR->getLocationType()->getPointeeType();
+ assert(!BaseType.isNull());
+ const CXXRecordDecl *SRDecl = BaseType->getAsCXXRecordDecl();
+ if (!SRDecl)
+ return UnknownVal();
+
+ // If found the derived class, the cast succeeds.
+ if (SRDecl == DerivedDecl)
+ return loc::MemRegionVal(TSR);
+
+ // If the region type is a subclass of the derived type.
+ if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) {
+ // This occurs in two cases.
+ // 1) We are processing an upcast.
+ // 2) We are processing a downcast but we jumped directly from the
+ // ancestor to a child of the cast value, so conjure the
+ // appropriate region to represent value (the intermediate node).
+ return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl,
+ BaseRegion));
+ }
+
+ // If super region is not a parent of derived class, the cast definitely
+ // fails.
+ if (!derivedType->isVoidType() &&
+ DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) {
+ Failed = true;
+ return UnknownVal();
+ }
+
+ if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR))
+ // Drill down the chain to get the derived classes.
+ SR = R->getSuperRegion();
+ else {
+ // We reached the bottom of the hierarchy.
+
+ // If this is a cast to void*, return the region.
+ if (derivedType->isVoidType())
+ return loc::MemRegionVal(TSR);
+
+ // We did not find the derived class. We we must be casting the base to
+ // derived, so the cast should fail.
+ Failed = true;
+ return UnknownVal();
+ }
+ }
+
+ return UnknownVal();
+}
+
//===----------------------------------------------------------------------===//
// Loading values from regions.
//===----------------------------------------------------------------------===//
@@ -863,7 +982,7 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B,
return Optional<SVal>();
}
-SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
+SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) {
assert(!isa<UnknownVal>(L) && "location unknown");
assert(!isa<UndefinedVal>(L) && "location undefined");
@@ -882,18 +1001,20 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion();
- if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR)) {
+ if (isa<AllocaRegion>(MR) ||
+ isa<SymbolicRegion>(MR) ||
+ isa<CodeTextRegion>(MR)) {
if (T.isNull()) {
- const SymbolicRegion *SR = cast<SymbolicRegion>(MR);
- T = SR->getSymbol()->getType(Ctx);
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR))
+ T = TR->getLocationType();
+ else {
+ const SymbolicRegion *SR = cast<SymbolicRegion>(MR);
+ T = SR->getSymbol()->getType(Ctx);
+ }
}
MR = GetElementZeroRegion(MR, T);
}
- if (isa<CodeTextRegion>(MR)) {
- llvm_unreachable("Why load from a code text region?");
- }
-
// 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 TypedValueRegion *R = cast<TypedValueRegion>(MR);
@@ -909,21 +1030,21 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
// Such funny addressing will occur due to layering of regions.
if (RTy->isStructureOrClassType())
- return RetrieveStruct(store, R);
+ return getBindingForStruct(store, R);
// FIXME: Handle unions.
if (RTy->isUnionType())
return UnknownVal();
if (RTy->isArrayType())
- return RetrieveArray(store, R);
+ return getBindingForArray(store, R);
// FIXME: handle Vector types.
if (RTy->isVectorType())
return UnknownVal();
if (const FieldRegion* FR = dyn_cast<FieldRegion>(R))
- return CastRetrievedVal(RetrieveField(store, FR), FR, T, false);
+ return CastRetrievedVal(getBindingForField(store, FR), FR, T, false);
if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) {
// FIXME: Here we actually perform an implicit conversion from the loaded
@@ -931,7 +1052,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
// more intelligently. For example, an 'element' can encompass multiple
// bound regions (e.g., several bound bytes), or could be a subset of
// a larger value.
- return CastRetrievedVal(RetrieveElement(store, ER), ER, T, false);
+ return CastRetrievedVal(getBindingForElement(store, ER), ER, T, false);
}
if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) {
@@ -941,7 +1062,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
// reinterpretted, it is possible we stored a different value that could
// fit within the ivar. Either we need to cast these when storing them
// or reinterpret them lazily (as we do here).
- return CastRetrievedVal(RetrieveObjCIvar(store, IVR), IVR, T, false);
+ return CastRetrievedVal(getBindingForObjCIvar(store, IVR), IVR, T, false);
}
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
@@ -951,7 +1072,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
// variable is reinterpretted, it is possible we stored a different value
// that could fit within the variable. Either we need to cast these when
// storing them or reinterpret them lazily (as we do here).
- return CastRetrievedVal(RetrieveVar(store, VR), VR, T, false);
+ return CastRetrievedVal(getBindingForVar(store, VR), VR, T, false);
}
RegionBindings B = GetRegionBindings(store);
@@ -1021,7 +1142,7 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
return std::make_pair((Store) 0, (const MemRegion *) 0);
}
-SVal RegionStoreManager::RetrieveElement(Store store,
+SVal RegionStoreManager::getBindingForElement(Store store,
const ElementRegion* R) {
// Check if the region has a binding.
RegionBindings B = GetRegionBindings(store);
@@ -1043,15 +1164,15 @@ SVal RegionStoreManager::RetrieveElement(Store store,
if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) {
int64_t i = CI->getValue().getSExtValue();
// Abort on string underrun. This can be possible by arbitrary
- // clients of RetrieveElement().
+ // clients of getBindingForElement().
if (i < 0)
return UndefinedVal();
- int64_t byteLength = Str->getByteLength();
- // Technically, only i == byteLength is guaranteed to be null.
+ int64_t length = Str->getLength();
+ // Technically, only i == length 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];
+ char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
return svalBuilder.makeIntVal(c, T);
}
}
@@ -1093,10 +1214,11 @@ SVal RegionStoreManager::RetrieveElement(Store store,
}
}
}
- return RetrieveFieldOrElementCommon(store, R, R->getElementType(), superR);
+ return getBindingForFieldOrElementCommon(store, R, R->getElementType(),
+ superR);
}
-SVal RegionStoreManager::RetrieveField(Store store,
+SVal RegionStoreManager::getBindingForField(Store store,
const FieldRegion* R) {
// Check if the region has a binding.
@@ -1105,14 +1227,14 @@ SVal RegionStoreManager::RetrieveField(Store store,
return *V;
QualType Ty = R->getValueType();
- return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion());
+ return getBindingForFieldOrElementCommon(store, R, Ty, R->getSuperRegion());
}
Optional<SVal>
-RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty) {
+RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B,
+ const MemRegion *superR,
+ const TypedValueRegion *R,
+ QualType Ty) {
if (const Optional<SVal> &D = getDefaultBinding(B, superR)) {
const SVal &val = D.getValue();
@@ -1135,30 +1257,39 @@ RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B,
return Optional<SVal>();
}
-SVal RegionStoreManager::RetrieveLazyBinding(const MemRegion *lazyBindingRegion,
+SVal RegionStoreManager::getLazyBinding(const MemRegion *lazyBindingRegion,
Store lazyBindingStore) {
if (const ElementRegion *ER = dyn_cast<ElementRegion>(lazyBindingRegion))
- return RetrieveElement(lazyBindingStore, ER);
+ return getBindingForElement(lazyBindingStore, ER);
- return RetrieveField(lazyBindingStore,
- cast<FieldRegion>(lazyBindingRegion));
+ return getBindingForField(lazyBindingStore,
+ cast<FieldRegion>(lazyBindingRegion));
}
-SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
+SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store,
const TypedValueRegion *R,
QualType Ty,
const MemRegion *superR) {
- // At this point we have already checked in either RetrieveElement or
- // RetrieveField if 'R' has a direct binding.
-
+ // At this point we have already checked in either getBindingForElement or
+ // getBindingForField if 'R' has a direct binding.
RegionBindings B = GetRegionBindings(store);
+
+ // Record whether or not we see a symbolic index. That can completely
+ // be out of scope of our lookup.
+ bool hasSymbolicIndex = false;
while (superR) {
if (const Optional<SVal> &D =
- RetrieveDerivedDefaultValue(B, superR, R, Ty))
+ getBindingForDerivedDefaultValue(B, superR, R, Ty))
return *D;
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(superR)) {
+ NonLoc index = ER->getIndex();
+ if (!index.isConstant())
+ hasSymbolicIndex = true;
+ }
+
// If our super region is a field or element itself, walk up the region
// hierarchy to see if there is a default value installed in an ancestor.
if (const SubRegion *SR = dyn_cast<SubRegion>(superR)) {
@@ -1174,10 +1305,10 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R);
if (lazyBindingRegion)
- return RetrieveLazyBinding(lazyBindingRegion, lazyBindingStore);
+ return getLazyBinding(lazyBindingRegion, lazyBindingStore);
if (R->hasStackNonParametersStorage()) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ if (isa<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 TypedValueRegion *typedSuperR =
@@ -1185,13 +1316,15 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
if (typedSuperR->getValueType()->isVectorType())
return UnknownVal();
}
-
- // FIXME: We also need to take ElementRegions with symbolic indexes into
- // account.
- if (!ER->getIndex().isConstant())
- return UnknownVal();
}
+ // FIXME: We also need to take ElementRegions with symbolic indexes into
+ // account. This case handles both directly accessing an ElementRegion
+ // with a symbolic offset, but also fields within an element with
+ // a symbolic offset.
+ if (hasSymbolicIndex)
+ return UnknownVal();
+
return UndefinedVal();
}
@@ -1199,7 +1332,8 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
return svalBuilder.getRegionValueSymbolVal(R);
}
-SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){
+SVal RegionStoreManager::getBindingForObjCIvar(Store store,
+ const ObjCIvarRegion* R) {
// Check if the region has a binding.
RegionBindings B = GetRegionBindings(store);
@@ -1218,10 +1352,10 @@ SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){
return UnknownVal();
}
- return RetrieveLazySymbol(R);
+ return getBindingForLazySymbol(R);
}
-SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) {
+SVal RegionStoreManager::getBindingForVar(Store store, const VarRegion *R) {
// Check if the region has a binding.
RegionBindings B = GetRegionBindings(store);
@@ -1253,7 +1387,8 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) {
}
}
- if (const Optional<SVal> &V = RetrieveDerivedDefaultValue(B, MS, R, CT))
+ if (const Optional<SVal> &V
+ = getBindingForDerivedDefaultValue(B, MS, R, CT))
return V.getValue();
return svalBuilder.getRegionValueSymbolVal(R);
@@ -1270,19 +1405,18 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) {
return UndefinedVal();
}
-SVal RegionStoreManager::RetrieveLazySymbol(const TypedValueRegion *R) {
+SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) {
// All other values are symbolic.
return svalBuilder.getRegionValueSymbolVal(R);
}
-SVal RegionStoreManager::RetrieveStruct(Store store,
+SVal RegionStoreManager::getBindingForStruct(Store store,
const TypedValueRegion* R) {
- QualType T = R->getValueType();
- assert(T->isStructureOrClassType());
+ assert(R->getValueType()->isStructureOrClassType());
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
-SVal RegionStoreManager::RetrieveArray(Store store,
+SVal RegionStoreManager::getBindingForArray(Store store,
const TypedValueRegion * R) {
assert(Ctx.getAsConstantArrayType(R->getValueType()));
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
@@ -1506,11 +1640,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
RecordDecl::field_iterator FI, FE;
StoreRef newStore(store, *this);
- for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) {
+ for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) {
if (VI == VE)
break;
+ // Skip any unnamed bitfields to stay in sync with the initializers.
+ if ((*FI)->isUnnamedBitfield())
+ continue;
+
QualType FTy = (*FI)->getType();
const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);
@@ -1520,6 +1658,7 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
newStore = BindStruct(newStore.getStore(), FR, *VI);
else
newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(FR), *VI);
+ ++VI;
}
// There may be fewer values in the initialize list than the fields of struct.
@@ -1556,7 +1695,7 @@ StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R,
// Remove the old bindings, using 'subReg' as the root of all regions
// we will invalidate.
RegionBindings B = GetRegionBindings(store);
- llvm::OwningPtr<RegionStoreSubRegionMap>
+ OwningPtr<RegionStoreSubRegionMap>
SubRegions(getRegionStoreSubRegionMap(store));
RemoveSubRegionBindings(B, subReg, *SubRegions);
@@ -1574,7 +1713,7 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
// Nuke the old bindings stemming from R.
RegionBindings B = GetRegionBindings(store);
- llvm::OwningPtr<RegionStoreSubRegionMap>
+ OwningPtr<RegionStoreSubRegionMap>
SubRegions(getRegionStoreSubRegionMap(store));
// B and DVM are updated after the call to RemoveSubRegionBindings.
@@ -1641,7 +1780,8 @@ class removeDeadBindingsWorker :
const StackFrameContext *CurrentLCtx;
public:
- removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr,
+ removeDeadBindingsWorker(RegionStoreManager &rm,
+ ProgramStateManager &stateMgr,
RegionBindings b, SymbolReaper &symReaper,
const StackFrameContext *LCtx)
: ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b,
@@ -1717,9 +1857,9 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) {
if (const MemRegion *R = V.getAsRegion())
AddToWorkList(R);
- // Update the set of live symbols.
- for (SVal::symbol_iterator SI=V.symbol_begin(), SE=V.symbol_end();
- SI!=SE;++SI)
+ // Update the set of live symbols.
+ for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end();
+ SI!=SE; ++SI)
SymReaper.markLive(*SI);
}
@@ -1807,7 +1947,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
SymReaper.maybeDead(SymR->getSymbol());
SVal X = I.getData();
- SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
+ SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
for (; SI != SE; ++SI)
SymReaper.maybeDead(*SI);
}
@@ -1816,37 +1956,41 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
}
-StoreRef RegionStoreManager::enterStackFrame(const ProgramState *state,
- const StackFrameContext *frame) {
- FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl());
+StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
+ const LocationContext *callerCtx,
+ const StackFrameContext *calleeCtx)
+{
+ FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl());
FunctionDecl::param_const_iterator PI = FD->param_begin(),
PE = FD->param_end();
StoreRef store = StoreRef(state->getStore(), *this);
- if (CallExpr const *CE = dyn_cast<CallExpr>(frame->getCallSite())) {
+ if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) {
CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
// Copy the arg expression value to the arg variables. We check that
// PI != PE because the actual number of arguments may be different than
// the function declaration.
for (; AI != AE && PI != PE; ++AI, ++PI) {
- SVal ArgVal = state->getSVal(*AI);
+ SVal ArgVal = state->getSVal(*AI, callerCtx);
store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal);
+ svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
+ ArgVal);
}
} else if (const CXXConstructExpr *CE =
- dyn_cast<CXXConstructExpr>(frame->getCallSite())) {
+ dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) {
CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(),
AE = CE->arg_end();
// Copy the arg expression value to the arg variables.
for (; AI != AE; ++AI, ++PI) {
- SVal ArgVal = state->getSVal(*AI);
+ SVal ArgVal = state->getSVal(*AI, callerCtx);
store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI,frame)), ArgVal);
+ svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
+ ArgVal);
}
} else
- assert(isa<CXXDestructorDecl>(frame->getDecl()));
+ assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
return store;
}
diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp
index ebf7ae2..9e97f5e 100644
--- a/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ExprCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
@@ -25,6 +26,8 @@ using namespace ento;
// Basic SVal creation.
//===----------------------------------------------------------------------===//
+void SValBuilder::anchor() { }
+
DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
if (Loc::isLocType(type))
return makeNull();
@@ -37,23 +40,38 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
return UnknownVal();
}
-
NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
const llvm::APSInt& rhs, QualType type) {
// The Environment ensures we always get a persistent APSInt in
// BasicValueFactory, so we don't need to get the APSInt from
// BasicValueFactory again.
+ assert(lhs);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type));
+}
+
+NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs,
+ BinaryOperator::Opcode op, const SymExpr *rhs,
+ QualType type) {
+ assert(rhs);
assert(!Loc::isLocType(type));
- return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, rhs, type));
+ return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type));
}
NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
const SymExpr *rhs, QualType type) {
- assert(SymMgr.getType(lhs) == SymMgr.getType(rhs));
+ assert(lhs && rhs);
+ assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true);
assert(!Loc::isLocType(type));
- return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
+ return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
}
+NonLoc SValBuilder::makeNonLoc(const SymExpr *operand,
+ QualType fromTy, QualType toTy) {
+ assert(operand);
+ assert(!Loc::isLocType(toTy));
+ return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy));
+}
SVal SValBuilder::convertToArrayIndex(SVal val) {
if (val.isUnknownOrUndef())
@@ -69,6 +87,10 @@ SVal SValBuilder::convertToArrayIndex(SVal val) {
return evalCastFromNonLoc(cast<NonLoc>(val), ArrayIndexTy);
}
+nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){
+ return makeTruthVal(boolean->getValue());
+}
+
DefinedOrUnknownSVal
SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
QualType T = region->getValueType();
@@ -84,35 +106,46 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
return nonloc::SymbolVal(sym);
}
-DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag,
- const Expr *expr,
- unsigned count) {
+DefinedOrUnknownSVal
+SValBuilder::getConjuredSymbolVal(const void *symbolTag,
+ const Expr *expr,
+ const LocationContext *LCtx,
+ unsigned count) {
QualType T = expr->getType();
+ return getConjuredSymbolVal(symbolTag, expr, LCtx, T, count);
+}
- if (!SymbolManager::canSymbolicate(T))
+DefinedOrUnknownSVal
+SValBuilder::getConjuredSymbolVal(const void *symbolTag,
+ const Expr *expr,
+ const LocationContext *LCtx,
+ QualType type,
+ unsigned count) {
+ if (!SymbolManager::canSymbolicate(type))
return UnknownVal();
- SymbolRef sym = SymMgr.getConjuredSymbol(expr, count, symbolTag);
+ SymbolRef sym = SymMgr.getConjuredSymbol(expr, LCtx, type, count, symbolTag);
- if (Loc::isLocType(T))
+ if (Loc::isLocType(type))
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
return nonloc::SymbolVal(sym);
}
-DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag,
- const Expr *expr,
- QualType type,
- unsigned count) {
-
+
+DefinedOrUnknownSVal
+SValBuilder::getConjuredSymbolVal(const Stmt *stmt,
+ const LocationContext *LCtx,
+ QualType type,
+ unsigned visitCount) {
if (!SymbolManager::canSymbolicate(type))
return UnknownVal();
- SymbolRef sym = SymMgr.getConjuredSymbol(expr, type, count, symbolTag);
-
+ SymbolRef sym = SymMgr.getConjuredSymbol(stmt, LCtx, type, visitCount);
+
if (Loc::isLocType(type))
return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
-
+
return nonloc::SymbolVal(sym);
}
@@ -155,14 +188,41 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
CanQualType locTy,
const LocationContext *locContext) {
const BlockTextRegion *BC =
- MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisContext());
+ MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisDeclContext());
const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext);
return loc::MemRegionVal(BD);
}
//===----------------------------------------------------------------------===//
-SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op,
+SVal SValBuilder::makeGenericVal(ProgramStateRef State,
+ BinaryOperator::Opcode Op,
+ NonLoc LHS, NonLoc RHS,
+ QualType ResultTy) {
+ // If operands are tainted, create a symbol to ensure that we propagate taint.
+ if (State->isTainted(RHS) || State->isTainted(LHS)) {
+ const SymExpr *symLHS;
+ const SymExpr *symRHS;
+
+ if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) {
+ symLHS = LHS.getAsSymExpr();
+ return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
+ }
+
+ if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) {
+ symRHS = RHS.getAsSymExpr();
+ return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
+ }
+
+ symLHS = LHS.getAsSymExpr();
+ symRHS = RHS.getAsSymExpr();
+ return makeNonLoc(symLHS, Op, symRHS, ResultTy);
+ }
+ return UnknownVal();
+}
+
+
+SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type) {
if (lhs.isUndef() || rhs.isUndef())
@@ -190,37 +250,50 @@ SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op
return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type);
}
-DefinedOrUnknownSVal SValBuilder::evalEQ(const ProgramState *state,
+DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state,
DefinedOrUnknownSVal lhs,
DefinedOrUnknownSVal rhs) {
return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs,
Context.IntTy));
}
+/// Recursively check if the pointer types are equal modulo const, volatile,
+/// and restrict qualifiers. Assumes the input types are canonical.
+/// TODO: This is based off of code in SemaCast; can we reuse it.
+static bool haveSimilarTypes(ASTContext &Context, QualType T1,
+ QualType T2) {
+ while (Context.UnwrapSimilarPointerTypes(T1, T2)) {
+ Qualifiers Quals1, Quals2;
+ T1 = Context.getUnqualifiedArrayType(T1, Quals1);
+ T2 = Context.getUnqualifiedArrayType(T2, Quals2);
+
+ // Make sure that non cvr-qualifiers the other qualifiers (e.g., address
+ // spaces) are identical.
+ Quals1.removeCVRQualifiers();
+ Quals2.removeCVRQualifiers();
+ if (Quals1 != Quals2)
+ return false;
+ }
+
+ if (T1 != T2)
+ return false;
+
+ return true;
+}
+
// FIXME: should rewrite according to the cast kind.
SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
+ castTy = Context.getCanonicalType(castTy);
+ originalTy = Context.getCanonicalType(originalTy);
if (val.isUnknownOrUndef() || castTy == originalTy)
return val;
// For const casts, just propagate the value.
if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType())
- if (Context.hasSameUnqualifiedType(castTy, originalTy))
+ if (haveSimilarTypes(Context, Context.getPointerType(castTy),
+ Context.getPointerType(originalTy)))
return val;
-
- // Check for casts to real or complex numbers. We don't handle these at all
- // right now.
- if (castTy->isFloatingType() || castTy->isAnyComplexType())
- return UnknownVal();
- // Check for casts from integers to integers.
- if (castTy->isIntegerType() && originalTy->isIntegerType()) {
- if (isa<Loc>(val))
- // This can be a cast to ObjC property of type int.
- return evalCastFromLoc(cast<Loc>(val), castTy);
- else
- return evalCastFromNonLoc(cast<NonLoc>(val), castTy);
- }
-
// Check for casts from pointers to integers.
if (castTy->isIntegerType() && Loc::isLocType(originalTy))
return evalCastFromLoc(cast<Loc>(val), castTy);
@@ -235,7 +308,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
}
return LV->getLoc();
}
- goto DispatchCast;
+ return dispatchCast(val, castTy);
}
// Just pass through function and block pointers.
@@ -309,8 +382,5 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
return R ? SVal(loc::MemRegionVal(R)) : UnknownVal();
}
-DispatchCast:
- // All other cases.
- return isa<Loc>(val) ? evalCastFromLoc(cast<Loc>(val), castTy)
- : evalCastFromNonLoc(cast<NonLoc>(val), castTy);
+ return dispatchCast(val, castTy);
}
diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp
index b5980b9..b94aff4 100644
--- a/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/lib/StaticAnalyzer/Core/SVals.cpp
@@ -54,13 +54,16 @@ const FunctionDecl *SVal::getAsFunctionDecl() const {
return CTR->getDecl();
}
- return NULL;
+ return 0;
}
-/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and
-/// wraps a symbol, return that SymbolRef. Otherwise return 0.
-// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
+/// \brief If this SVal is a location (subclasses Loc) and wraps a symbol,
+/// return that SymbolRef. Otherwise return 0.
+///
+/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element
+/// region. If that is the case, gets the underlining region.
SymbolRef SVal::getAsLocSymbol() const {
+ // FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this))
return X->getLoc().getAsLocSymbol();
@@ -69,7 +72,7 @@ SymbolRef SVal::getAsLocSymbol() const {
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R))
return SymR->getSymbol();
}
- return NULL;
+ return 0;
}
/// Get the symbol in the SVal or its base region.
@@ -91,29 +94,34 @@ SymbolRef SVal::getLocSymbolInBase() const {
return 0;
}
-/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef.
+// TODO: The next 3 functions have to be simplified.
+
+/// \brief If this SVal wraps a symbol return that SymbolRef.
/// Otherwise return 0.
-// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
SymbolRef SVal::getAsSymbol() const {
+ // FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this))
return X->getSymbol();
- if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this))
- if (SymbolRef Y = dyn_cast<SymbolData>(X->getSymbolicExpression()))
- return Y;
-
return getAsLocSymbol();
}
/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then
/// return that expression. Otherwise return NULL.
const SymExpr *SVal::getAsSymbolicExpression() const {
- if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this))
- return X->getSymbolicExpression();
+ if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this))
+ return X->getSymbol();
return getAsSymbol();
}
+const SymExpr* SVal::getAsSymExpr() const {
+ const SymExpr* Sym = getAsSymbol();
+ if (!Sym)
+ Sym = getAsSymbolicExpression();
+ return Sym;
+}
+
const MemRegion *SVal::getAsRegion() const {
if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this))
return X->getRegion();
@@ -130,50 +138,6 @@ const MemRegion *loc::MemRegionVal::stripCasts() const {
return R ? R->StripCasts() : NULL;
}
-bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const {
- return itr == X.itr;
-}
-
-bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const {
- return itr != X.itr;
-}
-
-SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) {
- itr.push_back(SE);
- while (!isa<SymbolData>(itr.back())) expand();
-}
-
-SVal::symbol_iterator &SVal::symbol_iterator::operator++() {
- assert(!itr.empty() && "attempting to iterate on an 'end' iterator");
- assert(isa<SymbolData>(itr.back()));
- itr.pop_back();
- if (!itr.empty())
- while (!isa<SymbolData>(itr.back())) expand();
- return *this;
-}
-
-SymbolRef SVal::symbol_iterator::operator*() {
- assert(!itr.empty() && "attempting to dereference an 'end' iterator");
- return cast<SymbolData>(itr.back());
-}
-
-void SVal::symbol_iterator::expand() {
- const SymExpr *SE = itr.back();
- itr.pop_back();
-
- if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
- itr.push_back(SIE->getLHS());
- return;
- }
- else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
- itr.push_back(SSE->getLHS());
- itr.push_back(SSE->getRHS());
- return;
- }
-
- llvm_unreachable("unhandled expansion case");
-}
-
const void *nonloc::LazyCompoundVal::getStore() const {
return static_cast<const LazyCompoundValData*>(Data)->getStore();
}
@@ -281,8 +245,6 @@ void SVal::dumpToStream(raw_ostream &os) const {
case UndefinedKind:
os << "Undefined";
break;
- default:
- assert (false && "Invalid SVal.");
}
}
@@ -298,13 +260,8 @@ void NonLoc::dumpToStream(raw_ostream &os) const {
<< C.getValue().getBitWidth() << 'b';
break;
}
- case nonloc::SymbolValKind:
- os << '$' << cast<nonloc::SymbolVal>(this)->getSymbol();
- break;
- case nonloc::SymExprValKind: {
- const nonloc::SymExprVal& C = *cast<nonloc::SymExprVal>(this);
- const SymExpr *SE = C.getSymbolicExpression();
- os << SE;
+ case nonloc::SymbolValKind: {
+ os << cast<nonloc::SymbolVal>(this)->getSymbol();
break;
}
case nonloc::LocAsIntegerKind: {
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 79d8b8b..a76a2da 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -23,11 +23,9 @@ namespace ento {
SimpleConstraintManager::~SimpleConstraintManager() {}
bool SimpleConstraintManager::canReasonAbout(SVal X) const {
- if (nonloc::SymExprVal *SymVal = dyn_cast<nonloc::SymExprVal>(&X)) {
- const SymExpr *SE = SymVal->getSymbolicExpression();
-
- if (isa<SymbolData>(SE))
- return true;
+ nonloc::SymbolVal *SymVal = dyn_cast<nonloc::SymbolVal>(&X);
+ if (SymVal && SymVal->isExpression()) {
+ const SymExpr *SE = SymVal->getSymbol();
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
switch (SIE->getOpcode()) {
@@ -56,7 +54,7 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const {
return true;
}
-const ProgramState *SimpleConstraintManager::assume(const ProgramState *state,
+ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state,
DefinedSVal Cond,
bool Assumption) {
if (isa<NonLoc>(Cond))
@@ -65,13 +63,13 @@ const ProgramState *SimpleConstraintManager::assume(const ProgramState *state,
return assume(state, cast<Loc>(Cond), Assumption);
}
-const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, Loc cond,
+ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond,
bool assumption) {
state = assumeAux(state, cond, assumption);
return SU.processAssume(state, cond, assumption);
}
-const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state,
+ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
Loc Cond, bool Assumption) {
BasicValueFactory &BasicVals = state->getBasicVals();
@@ -113,7 +111,7 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state
} // end switch
}
-const ProgramState *SimpleConstraintManager::assume(const ProgramState *state,
+ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state,
NonLoc cond,
bool assumption) {
state = assumeAux(state, cond, assumption);
@@ -135,16 +133,29 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
}
}
-const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state,
+
+ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(
+ ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption) {
+ QualType T = State->getSymbolManager().getType(Sym);
+ const llvm::APSInt &zero = State->getBasicVals().getValue(0, T);
+ if (Assumption)
+ return assumeSymNE(State, Sym, zero, zero);
+ else
+ return assumeSymEQ(State, Sym, zero, zero);
+}
+
+ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
NonLoc Cond,
bool Assumption) {
- // We cannot reason about SymSymExprs,
- // and can only reason about some SymIntExprs.
+ // We cannot reason about SymSymExprs, and can only reason about some
+ // SymIntExprs.
if (!canReasonAbout(Cond)) {
- // Just return the current state indicating that the path is feasible.
- // This may be an over-approximation of what is possible.
- return state;
+ // Just add the constraint to the expression without trying to simplify.
+ SymbolRef sym = Cond.getAsSymExpr();
+ return assumeAuxForSymbol(state, sym, Assumption);
}
BasicValueFactory &BasicVals = state->getBasicVals();
@@ -157,37 +168,33 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state
case nonloc::SymbolValKind: {
nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond);
SymbolRef sym = SV.getSymbol();
- QualType T = SymMgr.getType(sym);
- const llvm::APSInt &zero = BasicVals.getValue(0, T);
- if (Assumption)
- return assumeSymNE(state, sym, zero, zero);
- else
- return assumeSymEQ(state, sym, zero, zero);
- }
+ assert(sym);
+
+ // Handle SymbolData.
+ if (!SV.isExpression()) {
+ return assumeAuxForSymbol(state, sym, Assumption);
+
+ // Handle symbolic expression.
+ } else {
+ // We can only simplify expressions whose RHS is an integer.
+ const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym);
+ if (!SE)
+ return assumeAuxForSymbol(state, sym, Assumption);
+
+ BinaryOperator::Opcode op = SE->getOpcode();
+ // Implicitly compare non-comparison expressions to 0.
+ if (!BinaryOperator::isComparisonOp(op)) {
+ QualType T = SymMgr.getType(SE);
+ const llvm::APSInt &zero = BasicVals.getValue(0, T);
+ op = (Assumption ? BO_NE : BO_EQ);
+ return assumeSymRel(state, SE, op, zero);
+ }
+ // From here on out, op is the real comparison we'll be testing.
+ if (!Assumption)
+ op = NegateComparison(op);
- case nonloc::SymExprValKind: {
- nonloc::SymExprVal V = cast<nonloc::SymExprVal>(Cond);
-
- // For now, we only handle expressions whose RHS is an integer.
- // All other expressions are assumed to be feasible.
- const SymIntExpr *SE = dyn_cast<SymIntExpr>(V.getSymbolicExpression());
- if (!SE)
- return state;
-
- BinaryOperator::Opcode op = SE->getOpcode();
- // Implicitly compare non-comparison expressions to 0.
- if (!BinaryOperator::isComparisonOp(op)) {
- QualType T = SymMgr.getType(SE);
- const llvm::APSInt &zero = BasicVals.getValue(0, T);
- op = (Assumption ? BO_NE : BO_EQ);
- return assumeSymRel(state, SE, op, zero);
+ return assumeSymRel(state, SE->getLHS(), op, SE->getRHS());
}
-
- // From here on out, op is the real comparison we'll be testing.
- if (!Assumption)
- op = NegateComparison(op);
-
- return assumeSymRel(state, SE->getLHS(), op, SE->getRHS());
}
case nonloc::ConcreteIntKind: {
@@ -202,55 +209,52 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state
} // end switch
}
-const ProgramState *SimpleConstraintManager::assumeSymRel(const ProgramState *state,
+static llvm::APSInt computeAdjustment(const SymExpr *LHS,
+ SymbolRef &Sym) {
+ llvm::APSInt DefaultAdjustment;
+ DefaultAdjustment = 0;
+
+ // First check if the LHS is a simple symbol reference.
+ if (isa<SymbolData>(LHS))
+ return DefaultAdjustment;
+
+ // Next, see if it's a "($sym+constant1)" expression.
+ const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS);
+
+ // We cannot simplify "($sym1+$sym2)".
+ if (!SE)
+ return DefaultAdjustment;
+
+ // Get the constant out of the expression "($sym+constant1)" or
+ // "<expr>+constant1".
+ Sym = SE->getLHS();
+ switch (SE->getOpcode()) {
+ case BO_Add:
+ return SE->getRHS();
+ case BO_Sub:
+ return -SE->getRHS();
+ default:
+ // We cannot simplify non-additive operators.
+ return DefaultAdjustment;
+ }
+}
+
+ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state,
const SymExpr *LHS,
BinaryOperator::Opcode op,
const llvm::APSInt& Int) {
assert(BinaryOperator::isComparisonOp(op) &&
"Non-comparison ops should be rewritten as comparisons to zero.");
- // We only handle simple comparisons of the form "$sym == constant"
- // or "($sym+constant1) == constant2".
- // The adjustment is "constant1" in the above expression. It's used to
- // "slide" the solution range around for modular arithmetic. For example,
- // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which
- // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
- // the subclasses of SimpleConstraintManager to handle the adjustment.
- llvm::APSInt Adjustment;
-
- // First check if the LHS is a simple symbol reference.
- SymbolRef Sym = dyn_cast<SymbolData>(LHS);
- if (Sym) {
- Adjustment = 0;
- } else {
- // Next, see if it's a "($sym+constant1)" expression.
- const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS);
-
- // We don't handle "($sym1+$sym2)".
- // Give up and assume the constraint is feasible.
- if (!SE)
- return state;
-
- // We don't handle "(<expr>+constant1)".
- // Give up and assume the constraint is feasible.
- Sym = dyn_cast<SymbolData>(SE->getLHS());
- if (!Sym)
- return state;
-
- // Get the constant out of the expression "($sym+constant1)".
- switch (SE->getOpcode()) {
- case BO_Add:
- Adjustment = SE->getRHS();
- break;
- case BO_Sub:
- Adjustment = -SE->getRHS();
- break;
- default:
- // We don't handle non-additive operators.
- // Give up and assume the constraint is feasible.
- return state;
- }
- }
+ // We only handle simple comparisons of the form "$sym == constant"
+ // or "($sym+constant1) == constant2".
+ // The adjustment is "constant1" in the above expression. It's used to
+ // "slide" the solution range around for modular arithmetic. For example,
+ // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which
+ // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
+ // the subclasses of SimpleConstraintManager to handle the adjustment.
+ SymbolRef Sym = LHS;
+ llvm::APSInt Adjustment = computeAdjustment(LHS, Sym);
// FIXME: This next section is a hack. It silently converts the integers to
// be of the same type as the symbol, which is not always correct. Really the
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
index d4295d4..e082d9d 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
@@ -31,16 +31,14 @@ public:
// Common implementation for the interface provided by ConstraintManager.
//===------------------------------------------------------------------===//
- bool canReasonAbout(SVal X) const;
-
- const ProgramState *assume(const ProgramState *state, DefinedSVal Cond,
+ ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond,
bool Assumption);
- const ProgramState *assume(const ProgramState *state, Loc Cond, bool Assumption);
+ ProgramStateRef assume(ProgramStateRef state, Loc Cond, bool Assumption);
- const ProgramState *assume(const ProgramState *state, NonLoc Cond, bool Assumption);
+ ProgramStateRef assume(ProgramStateRef state, NonLoc Cond, bool Assumption);
- const ProgramState *assumeSymRel(const ProgramState *state,
+ ProgramStateRef assumeSymRel(ProgramStateRef state,
const SymExpr *LHS,
BinaryOperator::Opcode op,
const llvm::APSInt& Int);
@@ -53,27 +51,27 @@ protected:
// Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison
// operation for the method being invoked.
- virtual const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym,
+ virtual ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
@@ -81,9 +79,19 @@ protected:
// Internal implementation.
//===------------------------------------------------------------------===//
- const ProgramState *assumeAux(const ProgramState *state, Loc Cond,bool Assumption);
+ bool canReasonAbout(SVal X) const;
+
+ ProgramStateRef assumeAux(ProgramStateRef state,
+ Loc Cond,
+ bool Assumption);
+
+ ProgramStateRef assumeAux(ProgramStateRef state,
+ NonLoc Cond,
+ bool Assumption);
- const ProgramState *assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption);
+ ProgramStateRef assumeAuxForSymbol(ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption);
};
} // end GR namespace
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index bd63ecf..d0558f1 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -20,6 +20,7 @@ using namespace ento;
namespace {
class SimpleSValBuilder : public SValBuilder {
protected:
+ virtual SVal dispatchCast(SVal val, QualType castTy);
virtual SVal evalCastFromNonLoc(NonLoc val, QualType castTy);
virtual SVal evalCastFromLoc(Loc val, QualType castTy);
@@ -31,16 +32,16 @@ public:
virtual SVal evalMinus(NonLoc val);
virtual SVal evalComplement(NonLoc val);
- virtual SVal evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs, QualType resultTy);
- virtual SVal evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op,
Loc lhs, Loc rhs, QualType resultTy);
- virtual SVal evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op,
Loc lhs, NonLoc rhs, QualType resultTy);
/// getKnownValue - evaluates a given SVal. If the SVal has only one possible
/// (integer) value, that value is returned. Otherwise, returns NULL.
- virtual const llvm::APSInt *getKnownValue(const ProgramState *state, SVal V);
+ virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V);
SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
const llvm::APSInt &RHS, QualType resultTy);
@@ -57,6 +58,12 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
// Transfer function for Casts.
//===----------------------------------------------------------------------===//
+SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) {
+ assert(isa<Loc>(&Val) || isa<NonLoc>(&Val));
+ return isa<Loc>(Val) ? evalCastFromLoc(cast<Loc>(Val), CastTy)
+ : evalCastFromNonLoc(cast<NonLoc>(Val), CastTy);
+}
+
SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
bool isLocType = Loc::isLocType(castTy);
@@ -74,25 +81,27 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
if (const SymExpr *se = val.getAsSymbolicExpression()) {
QualType T = Context.getCanonicalType(se->getType(Context));
- if (T == Context.getCanonicalType(castTy))
- return val;
-
+ // If types are the same or both are integers, ignore the cast.
// FIXME: Remove this hack when we support symbolic truncation/extension.
// HACK: If both castTy and T are integers, ignore the cast. This is
// not a permanent solution. Eventually we want to precisely handle
// extension/truncation of symbolic integers. This prevents us from losing
// precision when we assign 'x = y' and 'y' is symbolic and x and y are
// different integer types.
- if (T->isIntegerType() && castTy->isIntegerType())
+ if (haveSameType(T, castTy))
return val;
+ if (!isLocType)
+ return makeNonLoc(se, T, castTy);
return UnknownVal();
}
+ // If value is a non integer constant, produce unknown.
if (!isa<nonloc::ConcreteInt>(val))
return UnknownVal();
- // Only handle casts from integers to integers.
+ // Only handle casts from integers to integers - if val is an integer constant
+ // being cast to a non integer type, produce unknown.
if (!isLocType && !castTy->isIntegerType())
return UnknownVal();
@@ -259,18 +268,15 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
// Idempotent ops (like a*1) can still change the type of an expression.
// Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the
// dirty work.
- if (isIdempotent) {
- if (SymbolRef LHSSym = dyn_cast<SymbolData>(LHS))
- return evalCastFromNonLoc(nonloc::SymbolVal(LHSSym), resultTy);
- return evalCastFromNonLoc(nonloc::SymExprVal(LHS), resultTy);
- }
+ if (isIdempotent)
+ return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy);
// If we reach this point, the expression cannot be simplified.
- // Make a SymExprVal for the entire thing.
+ // Make a SymbolVal for the entire expression.
return makeNonLoc(LHS, op, RHS, resultTy);
}
-SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state,
+SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs,
QualType resultTy) {
@@ -298,7 +304,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state,
while (1) {
switch (lhs.getSubKind()) {
default:
- return UnknownVal();
+ return makeGenericVal(state, op, lhs, rhs, resultTy);
case nonloc::LocAsIntegerKind: {
Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc();
switch (rhs.getSubKind()) {
@@ -321,94 +327,10 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state,
return makeTruthVal(true, resultTy);
default:
// This case also handles pointer arithmetic.
- return UnknownVal();
+ return makeGenericVal(state, op, lhs, rhs, resultTy);
}
}
}
- case nonloc::SymExprValKind: {
- nonloc::SymExprVal *selhs = cast<nonloc::SymExprVal>(&lhs);
-
- // Only handle LHS of the form "$sym op constant", at least for now.
- const SymIntExpr *symIntExpr =
- dyn_cast<SymIntExpr>(selhs->getSymbolicExpression());
-
- if (!symIntExpr)
- return UnknownVal();
-
- // Is this a logical not? (!x is represented as x == 0.)
- if (op == BO_EQ && rhs.isZeroConstant()) {
- // We know how to negate certain expressions. Simplify them here.
-
- BinaryOperator::Opcode opc = symIntExpr->getOpcode();
- switch (opc) {
- default:
- // We don't know how to negate this operation.
- // Just handle it as if it were a normal comparison to 0.
- break;
- case BO_LAnd:
- case BO_LOr:
- llvm_unreachable("Logical operators handled by branching logic.");
- 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:
- llvm_unreachable("'=' and ',' operators handled by ExprEngine.");
- case BO_PtrMemD:
- case BO_PtrMemI:
- llvm_unreachable("Pointer arithmetic not handled here.");
- 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(Context) == resultTy);
- return makeNonLoc(symIntExpr->getLHS(), opc,
- symIntExpr->getRHS(), resultTy);
- }
- }
-
- // For now, only handle expressions whose RHS is a constant.
- const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs);
- if (!rhsInt)
- return UnknownVal();
-
- // If both the LHS and the current expression are additive,
- // fold their constants.
- if (BinaryOperator::isAdditiveOp(op)) {
- BinaryOperator::Opcode lop = symIntExpr->getOpcode();
- if (BinaryOperator::isAdditiveOp(lop)) {
- // resultTy may not be the best type to convert to, but it's
- // probably the best choice in expressions with mixed type
- // (such as x+1U+2LL). The rules for implicit conversions should
- // choose a reasonable type to preserve the expression, and will
- // at least match how the value is going to be used.
- const llvm::APSInt &first =
- BasicVals.Convert(resultTy, symIntExpr->getRHS());
- const llvm::APSInt &second =
- BasicVals.Convert(resultTy, rhsInt->getValue());
- const llvm::APSInt *newRHS;
- if (lop == op)
- newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
- else
- newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
- return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy);
- }
- }
-
- // Otherwise, make a SymExprVal out of the expression.
- return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy);
- }
case nonloc::ConcreteIntKind: {
const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
@@ -467,76 +389,165 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state,
if (lhsValue == 0)
// At this point lhs and rhs have been swapped.
return rhs;
- return UnknownVal();
+ return makeGenericVal(state, op, rhs, lhs, resultTy);
default:
- return UnknownVal();
+ return makeGenericVal(state, op, rhs, lhs, resultTy);
}
}
}
case nonloc::SymbolValKind: {
- nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);
- SymbolRef Sym = slhs->getSymbol();
- QualType lhsType = Sym->getType(Context);
-
- // The conversion type is usually the result type, but not in the case
- // of relational expressions.
- QualType conversionType = resultTy;
- if (BinaryOperator::isRelationalOp(op))
- conversionType = lhsType;
-
- // Does the symbol simplify to a constant? If so, "fold" the constant
- // by setting 'lhs' to a ConcreteInt and try again.
- if (lhsType->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.
- const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType,
- *Constant);
- lhs = nonloc::ConcreteInt(lhs_I);
-
- // Also promote the RHS (if necessary).
-
- // For shifts, it is not necessary to promote the RHS.
- if (BinaryOperator::isShiftOp(op))
- continue;
-
- // Other operators: do an implicit conversion. This shouldn't be
- // necessary once we support truncation/extension of symbolic values.
- if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){
- rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType,
- rhs_I->getValue()));
+ nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs);
+
+ // LHS is a symbolic expression.
+ if (selhs->isExpression()) {
+
+ // Only handle LHS of the form "$sym op constant", at least for now.
+ const SymIntExpr *symIntExpr =
+ dyn_cast<SymIntExpr>(selhs->getSymbol());
+
+ if (!symIntExpr)
+ return makeGenericVal(state, op, lhs, rhs, resultTy);
+
+ // Is this a logical not? (!x is represented as x == 0.)
+ if (op == BO_EQ && rhs.isZeroConstant()) {
+ // We know how to negate certain expressions. Simplify them here.
+
+ BinaryOperator::Opcode opc = symIntExpr->getOpcode();
+ switch (opc) {
+ default:
+ // We don't know how to negate this operation.
+ // Just handle it as if it were a normal comparison to 0.
+ break;
+ case BO_LAnd:
+ case BO_LOr:
+ llvm_unreachable("Logical operators handled by branching logic.");
+ 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:
+ llvm_unreachable("'=' and ',' operators handled by ExprEngine.");
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ llvm_unreachable("Pointer arithmetic not handled here.");
+ 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(Context) == resultTy);
+ return makeNonLoc(symIntExpr->getLHS(), opc,
+ symIntExpr->getRHS(), resultTy);
}
-
- 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(Context)->isIntegerType()) {
- if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
- // The symbol evaluates to a constant.
- const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType,
- *Constant);
- rhs = nonloc::ConcreteInt(rhs_I);
+ // For now, only handle expressions whose RHS is a constant.
+ const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs);
+ if (!rhsInt)
+ return makeGenericVal(state, op, lhs, rhs, resultTy);
+
+ // If both the LHS and the current expression are additive,
+ // fold their constants.
+ if (BinaryOperator::isAdditiveOp(op)) {
+ BinaryOperator::Opcode lop = symIntExpr->getOpcode();
+ if (BinaryOperator::isAdditiveOp(lop)) {
+ // resultTy may not be the best type to convert to, but it's
+ // probably the best choice in expressions with mixed type
+ // (such as x+1U+2LL). The rules for implicit conversions should
+ // choose a reasonable type to preserve the expression, and will
+ // at least match how the value is going to be used.
+ const llvm::APSInt &first =
+ BasicVals.Convert(resultTy, symIntExpr->getRHS());
+ const llvm::APSInt &second =
+ BasicVals.Convert(resultTy, rhsInt->getValue());
+ const llvm::APSInt *newRHS;
+ if (lop == op)
+ newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
+ else
+ newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
+ return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy);
}
}
- }
- if (isa<nonloc::ConcreteInt>(rhs)) {
- return MakeSymIntVal(slhs->getSymbol(), op,
- cast<nonloc::ConcreteInt>(rhs).getValue(),
- resultTy);
- }
+ // Otherwise, make a SymbolVal out of the expression.
+ return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy);
- return UnknownVal();
+ // LHS is a simple symbol (not a symbolic expression).
+ } else {
+ nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);
+ SymbolRef Sym = slhs->getSymbol();
+ QualType lhsType = Sym->getType(Context);
+
+ // The conversion type is usually the result type, but not in the case
+ // of relational expressions.
+ QualType conversionType = resultTy;
+ if (BinaryOperator::isRelationalOp(op))
+ conversionType = lhsType;
+
+ // Does the symbol simplify to a constant? If so, "fold" the constant
+ // by setting 'lhs' to a ConcreteInt and try again.
+ if (lhsType->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.
+ const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType,
+ *Constant);
+ lhs = nonloc::ConcreteInt(lhs_I);
+
+ // Also promote the RHS (if necessary).
+
+ // For shifts, it is not necessary to promote the RHS.
+ if (BinaryOperator::isShiftOp(op))
+ continue;
+
+ // Other operators: do an implicit conversion. This shouldn't be
+ // necessary once we support truncation/extension of symbolic values.
+ if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){
+ rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType,
+ rhs_I->getValue()));
+ }
+
+ 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(Context)->isIntegerType()) {
+ if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
+ // The symbol evaluates to a constant.
+ const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType,
+ *Constant);
+ rhs = nonloc::ConcreteInt(rhs_I);
+ }
+ }
+ }
+
+ if (isa<nonloc::ConcreteInt>(rhs)) {
+ return MakeSymIntVal(slhs->getSymbol(), op,
+ cast<nonloc::ConcreteInt>(rhs).getValue(),
+ resultTy);
+ }
+
+ return makeGenericVal(state, op, lhs, rhs, resultTy);
+ }
}
}
}
}
// FIXME: all this logic will change if/when we have MemRegion::getLocation().
-SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state,
+SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
BinaryOperator::Opcode op,
Loc lhs, Loc rhs,
QualType resultTy) {
@@ -703,6 +714,24 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state,
// The two regions are from the same base region. See if they're both a
// type of region we know how to compare.
+ const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace();
+ const MemSpaceRegion *RightMS = RightBase->getMemorySpace();
+
+ // Heuristic: assume that no symbolic region (whose memory space is
+ // unknown) is on the stack.
+ // FIXME: we should be able to be more precise once we can do better
+ // aliasing constraints for symbolic regions, but this is a reasonable,
+ // albeit unsound, assumption that holds most of the time.
+ if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) {
+ switch (op) {
+ default:
+ break;
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
+ }
+ }
// FIXME: If/when there is a getAsRawOffset() for FieldRegions, this
// ElementRegion path and the FieldRegion path below should be unified.
@@ -831,7 +860,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state,
}
}
-SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state,
+SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
BinaryOperator::Opcode op,
Loc lhs, NonLoc rhs, QualType resultTy) {
@@ -925,7 +954,7 @@ SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state,
return UnknownVal();
}
-const llvm::APSInt *SimpleSValBuilder::getKnownValue(const ProgramState *state,
+const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
SVal V) {
if (V.isUnknownOrUndef())
return NULL;
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 48a6f4f..11748ae 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclObjC.h"
using namespace clang;
using namespace ento;
@@ -22,8 +23,9 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr)
: svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
-StoreRef StoreManager::enterStackFrame(const ProgramState *state,
- const StackFrameContext *frame) {
+StoreRef StoreManager::enterStackFrame(ProgramStateRef state,
+ const LocationContext *callerCtx,
+ const StackFrameContext *calleeCtx) {
return StoreRef(state->getStore(), *this);
}
@@ -101,8 +103,10 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::StackArgumentsSpaceRegionKind:
case MemRegion::HeapSpaceRegionKind:
case MemRegion::UnknownSpaceRegionKind:
- case MemRegion::NonStaticGlobalSpaceRegionKind:
- case MemRegion::StaticGlobalSpaceRegionKind: {
+ case MemRegion::StaticGlobalSpaceRegionKind:
+ case MemRegion::GlobalInternalSpaceRegionKind:
+ case MemRegion::GlobalSystemSpaceRegionKind:
+ case MemRegion::GlobalImmutableSpaceRegionKind: {
llvm_unreachable("Invalid region cast");
}
@@ -116,6 +120,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::CompoundLiteralRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::ObjCStringRegionKind:
case MemRegion::VarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
@@ -212,7 +217,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
QualType castTy, bool performTestOnly) {
- if (castTy.isNull())
+ if (castTy.isNull() || V.isUnknownOrUndef())
return V;
ASTContext &Ctx = svalBuilder.getContext();
@@ -227,12 +232,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
return V;
}
- if (const Loc *L = dyn_cast<Loc>(&V))
- return svalBuilder.evalCastFromLoc(*L, castTy);
- else if (const NonLoc *NL = dyn_cast<NonLoc>(&V))
- return svalBuilder.evalCastFromNonLoc(*NL, castTy);
-
- return V;
+ return svalBuilder.dispatchCast(V, castTy);
}
SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
@@ -270,6 +270,10 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR));
}
+SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
+ return getLValueFieldOrIvar(decl, base);
+}
+
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
@@ -336,3 +340,23 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
StoreManager::BindingsHandler::~BindingsHandler() {}
+bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
+ Store store,
+ const MemRegion* R,
+ SVal val) {
+ SymbolRef SymV = val.getAsLocSymbol();
+ if (!SymV || SymV != Sym)
+ return true;
+
+ if (Binding) {
+ First = false;
+ return false;
+ }
+ else
+ Binding = R;
+
+ return true;
+}
+
+void SubRegionMap::anchor() { }
+void SubRegionMap::Visitor::anchor() { }
diff --git a/lib/StaticAnalyzer/Core/SubEngine.cpp b/lib/StaticAnalyzer/Core/SubEngine.cpp
new file mode 100644
index 0000000..350f4b8
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/SubEngine.cpp
@@ -0,0 +1,14 @@
+//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+
+using namespace clang::ento;
+
+void SubEngine::anchor() { }
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index b843ab1..adefb58 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -21,6 +21,8 @@
using namespace clang;
using namespace ento;
+void SymExpr::anchor() { }
+
void SymExpr::dump() const {
dumpToStream(llvm::errs());
}
@@ -57,6 +59,15 @@ void SymIntExpr::dumpToStream(raw_ostream &os) const {
if (getRHS().isUnsigned()) os << 'U';
}
+void IntSymExpr::dumpToStream(raw_ostream &os) const {
+ os << ' ' << getLHS().getZExtValue();
+ if (getLHS().isUnsigned()) os << 'U';
+ print(os, getOpcode());
+ os << '(';
+ getRHS()->dumpToStream(os);
+ os << ") ";
+}
+
void SymSymExpr::dumpToStream(raw_ostream &os) const {
os << '(';
getLHS()->dumpToStream(os);
@@ -66,6 +77,12 @@ void SymSymExpr::dumpToStream(raw_ostream &os) const {
os << ')';
}
+void SymbolCast::dumpToStream(raw_ostream &os) const {
+ os << '(' << ToTy.getAsString() << ") (";
+ Operand->dumpToStream(os);
+ os << ')';
+}
+
void SymbolConjured::dumpToStream(raw_ostream &os) const {
os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}';
}
@@ -84,10 +101,69 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const {
<< getRegion() << ',' << T.getAsString() << '}';
}
+void SymbolData::anchor() { }
+
void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
os << "reg_$" << getSymbolID() << "<" << R << ">";
}
+bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const {
+ return itr == X.itr;
+}
+
+bool SymExpr::symbol_iterator::operator!=(const symbol_iterator &X) const {
+ return itr != X.itr;
+}
+
+SymExpr::symbol_iterator::symbol_iterator(const SymExpr *SE) {
+ itr.push_back(SE);
+ while (!isa<SymbolData>(itr.back())) expand();
+}
+
+SymExpr::symbol_iterator &SymExpr::symbol_iterator::operator++() {
+ assert(!itr.empty() && "attempting to iterate on an 'end' iterator");
+ assert(isa<SymbolData>(itr.back()));
+ itr.pop_back();
+ if (!itr.empty())
+ while (!isa<SymbolData>(itr.back())) expand();
+ return *this;
+}
+
+SymbolRef SymExpr::symbol_iterator::operator*() {
+ assert(!itr.empty() && "attempting to dereference an 'end' iterator");
+ return cast<SymbolData>(itr.back());
+}
+
+void SymExpr::symbol_iterator::expand() {
+ const SymExpr *SE = itr.back();
+ itr.pop_back();
+
+ switch (SE->getKind()) {
+ case SymExpr::RegionValueKind:
+ case SymExpr::ConjuredKind:
+ case SymExpr::DerivedKind:
+ case SymExpr::ExtentKind:
+ case SymExpr::MetadataKind:
+ return;
+ case SymExpr::CastSymbolKind:
+ itr.push_back(cast<SymbolCast>(SE)->getOperand());
+ return;
+ case SymExpr::SymIntKind:
+ itr.push_back(cast<SymIntExpr>(SE)->getLHS());
+ return;
+ case SymExpr::IntSymKind:
+ itr.push_back(cast<IntSymExpr>(SE)->getRHS());
+ return;
+ case SymExpr::SymSymKind: {
+ const SymSymExpr *x = cast<SymSymExpr>(SE);
+ itr.push_back(x->getLHS());
+ itr.push_back(x->getRHS());
+ return;
+ }
+ }
+ llvm_unreachable("unhandled expansion case");
+}
+
const SymbolRegionValue*
SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
llvm::FoldingSetNodeID profile;
@@ -105,16 +181,17 @@ SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
}
const SymbolConjured*
-SymbolManager::getConjuredSymbol(const Stmt *E, QualType T, unsigned Count,
+SymbolManager::getConjuredSymbol(const Stmt *E, const LocationContext *LCtx,
+ QualType T, unsigned Count,
const void *SymbolTag) {
llvm::FoldingSetNodeID profile;
- SymbolConjured::Profile(profile, E, T, Count, SymbolTag);
+ SymbolConjured::Profile(profile, E, T, Count, LCtx, SymbolTag);
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>();
- new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag);
+ new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -174,6 +251,22 @@ SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T,
return cast<SymbolMetadata>(SD);
}
+const SymbolCast*
+SymbolManager::getCastSymbol(const SymExpr *Op,
+ QualType From, QualType To) {
+ llvm::FoldingSetNodeID ID;
+ SymbolCast::Profile(ID, Op, From, To);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+ if (!data) {
+ data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>();
+ new (data) SymbolCast(Op, From, To);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<SymbolCast>(data);
+}
+
const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs,
BinaryOperator::Opcode op,
const llvm::APSInt& v,
@@ -192,6 +285,24 @@ const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs,
return cast<SymIntExpr>(data);
}
+const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs,
+ BinaryOperator::Opcode op,
+ const SymExpr *rhs,
+ QualType t) {
+ llvm::FoldingSetNodeID ID;
+ IntSymExpr::Profile(ID, lhs, op, rhs, t);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!data) {
+ data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>();
+ new (data) IntSymExpr(lhs, op, rhs, t);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<IntSymExpr>(data);
+}
+
const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs,
BinaryOperator::Opcode op,
const SymExpr *rhs,
@@ -381,7 +492,16 @@ bool SymbolReaper::isLive(SymbolRef sym) {
return isa<SymbolRegionValue>(sym);
}
-bool SymbolReaper::isLive(const Stmt *ExprVal) const {
+bool
+SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
+ if (LCtx != ELCtx) {
+ // If the reaper's location context is a parent of the expression's
+ // location context, then the expression value is now "out of scope".
+ if (LCtx->isParentOf(ELCtx))
+ return false;
+ return true;
+ }
+
return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
index 3543f7f..fe912df 100644
--- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
@@ -31,9 +31,8 @@ public:
TextPathDiagnostics(const std::string& output, DiagnosticsEngine &diag)
: OutputFile(output), Diag(diag) {}
- void HandlePathDiagnosticImpl(const PathDiagnostic* D);
-
- void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { }
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade);
virtual StringRef getName() const {
return "TextPathDiagnostics";
@@ -53,18 +52,18 @@ ento::createTextPathDiagnosticConsumer(const std::string& out,
return new TextPathDiagnostics(out, PP.getDiagnostics());
}
-void TextPathDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
- if (!D)
- return;
-
- if (D->empty()) {
- delete D;
- return;
- }
-
- for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) {
- unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID(
- DiagnosticIDs::Note, I->getString());
- Diag.Report(I->getLocation().asLocation(), diagID);
+void TextPathDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags,
+ SmallVectorImpl<std::string> *FilesMade) {
+ for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
+ et = Diags.end(); it != et; ++it) {
+ const PathDiagnostic *D = *it;
+ for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
+ I != E; ++I) {
+ unsigned diagID =
+ Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note,
+ (*I)->getString());
+ Diag.Report((*I)->getLocation().asLocation(), diagID);
+ }
}
}
OpenPOWER on IntegriCloud