summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r--lib/StaticAnalyzer/Core/APSIntType.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/BasicConstraintManager.cpp241
-rw-r--r--lib/StaticAnalyzer/Core/BasicValueFactory.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp179
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp86
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt23
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp856
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp133
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp29
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp17
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp18
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp538
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp270
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp366
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp822
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp211
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp84
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp274
-rw-r--r--lib/StaticAnalyzer/Core/ObjCMessage.cpp90
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp204
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp31
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp99
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp275
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp949
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp76
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp20
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp103
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.h6
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp338
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp34
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp14
-rw-r--r--lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp5
33 files changed, 4184 insertions, 2253 deletions
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp
new file mode 100644
index 0000000..884b0fa
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/APSIntType.cpp
@@ -0,0 +1,38 @@
+//===--- APSIntType.cpp - Simple record of the type of APSInts ------------===//
+//
+// 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/APSIntType.h"
+
+using namespace clang;
+using namespace ento;
+
+APSIntType::RangeTestResultKind
+APSIntType::testInRange(const llvm::APSInt &Value) const {
+ // Negative numbers cannot be losslessly converted to unsigned type.
+ if (IsUnsigned && Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+
+ // Signed integers can be converted to signed integers of the same width
+ // or (if positive) unsigned integers with one fewer bit.
+ // Unsigned integers can be converted to unsigned integers of the same width
+ // or signed integers with one more bit.
+ unsigned MinBits;
+ if (Value.isSigned())
+ MinBits = Value.getMinSignedBits() - IsUnsigned;
+ else
+ MinBits = Value.getActiveBits() + !IsUnsigned;
+
+ if (MinBits <= BitWidth)
+ return RTR_Within;
+
+ if (Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+ else
+ return RTR_Above;
+}
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index eeaed2d..5aac640 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -25,18 +25,18 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
AnalysisPurgeMode purge,
bool eager, bool trim,
bool useUnoptimizedCFG,
- bool addImplicitDtors, bool addInitializers,
+ bool addImplicitDtors,
bool eagerlyTrimEGraph,
AnalysisIPAMode ipa,
unsigned inlineMaxStack,
unsigned inlineMaxFunctionSize,
AnalysisInliningMode IMode,
bool NoRetry)
- : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
+ : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true),
Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd),
CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
CheckerMgr(checkerMgr),
- AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
+ MaxNodes(maxnodes), MaxVisit(maxvisit),
VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
EagerlyAssume(eager), TrimGraph(trim),
EagerlyTrimEGraph(eagerlyTrimEGraph),
@@ -59,7 +59,6 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
CreateStoreMgr(ParentAM.CreateStoreMgr),
CreateConstraintMgr(ParentAM.CreateConstraintMgr),
CheckerMgr(ParentAM.CheckerMgr),
- AScope(ScopeDecl),
MaxNodes(ParentAM.MaxNodes),
MaxVisit(ParentAM.MaxVisit),
VisualizeEGDot(ParentAM.VisualizeEGDot),
diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
index 2d9addd..8897756 100644
--- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/raw_ostream.h"
@@ -53,18 +54,25 @@ class BasicConstraintManager
ProgramState::IntSetTy::Factory ISetFactory;
public:
BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine)
- : SimpleConstraintManager(subengine),
+ : SimpleConstraintManager(subengine, statemgr.getBasicVals()),
ISetFactory(statemgr.getAllocator()) {}
- ProgramStateRef assumeSymNE(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
+ ProgramStateRef assumeSymEquality(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment,
+ bool Assumption);
- ProgramStateRef assumeSymEQ(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
+ ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) {
+ return assumeSymEquality(State, Sym, V, Adjustment, false);
+ }
+
+ ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) {
+ return assumeSymEquality(State, Sym, V, Adjustment, true);
+ }
ProgramStateRef assumeSymLT(ProgramStateRef state,
SymbolRef sym,
@@ -108,6 +116,9 @@ public:
ProgramStateRef removeDeadBindings(ProgramStateRef state,
SymbolReaper& SymReaper);
+ bool performTest(llvm::APSInt SymVal, llvm::APSInt Adjustment,
+ BinaryOperator::Opcode Op, llvm::APSInt ComparisonVal);
+
void print(ProgramStateRef state,
raw_ostream &Out,
const char* nl,
@@ -122,60 +133,94 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr,
return new BasicConstraintManager(statemgr, subengine);
}
-ProgramStateRef
-BasicConstraintManager::assumeSymNE(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) {
- // First, determine if sym == X, where X+Adjustment != V.
- llvm::APSInt Adjusted = V-Adjustment;
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X != Adjusted);
- return isFeasible ? state : NULL;
- }
-
- // Second, determine if sym+Adjustment != V.
- if (isNotEqual(state, sym, Adjusted))
- return state;
-
- // If we reach here, sym is not a constant and we don't know if it is != V.
- // Make that assumption.
- return AddNE(state, sym, Adjusted);
+// FIXME: This is a more general utility and should live somewhere else.
+bool BasicConstraintManager::performTest(llvm::APSInt SymVal,
+ llvm::APSInt Adjustment,
+ BinaryOperator::Opcode Op,
+ llvm::APSInt ComparisonVal) {
+ APSIntType Type(Adjustment);
+ Type.apply(SymVal);
+ Type.apply(ComparisonVal);
+ SymVal += Adjustment;
+
+ assert(BinaryOperator::isComparisonOp(Op));
+ BasicValueFactory &BVF = getBasicVals();
+ const llvm::APSInt *Result = BVF.evalAPSInt(Op, SymVal, ComparisonVal);
+ assert(Result && "Comparisons should always have valid results.");
+
+ return Result->getBoolValue();
}
-ProgramStateRef
-BasicConstraintManager::assumeSymEQ(ProgramStateRef state,
- SymbolRef sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) {
- // First, determine if sym == X, where X+Adjustment != V.
- llvm::APSInt Adjusted = V-Adjustment;
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X == Adjusted);
- return isFeasible ? state : NULL;
+ProgramStateRef
+BasicConstraintManager::assumeSymEquality(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment,
+ bool Assumption) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(V) != APSIntType::RTR_Within)
+ return Assumption ? NULL : State;
+
+ // Get the symbol type.
+ BasicValueFactory &BVF = getBasicVals();
+ ASTContext &Ctx = BVF.getContext();
+ APSIntType SymbolType = BVF.getAPSIntType(Sym->getType(Ctx));
+
+ // First, see if the adjusted value is within range for the symbol.
+ llvm::APSInt Adjusted = AdjustmentType.convert(V) - Adjustment;
+ if (SymbolType.testInRange(Adjusted) != APSIntType::RTR_Within)
+ return Assumption ? NULL : State;
+
+ // Now we can do things properly in the symbol space.
+ SymbolType.apply(Adjusted);
+
+ // Second, determine if sym == X, where X+Adjustment != V.
+ if (const llvm::APSInt *X = getSymVal(State, Sym)) {
+ bool IsFeasible = (*X == Adjusted);
+ return (IsFeasible == Assumption) ? State : NULL;
}
- // Second, determine if sym+Adjustment != V.
- if (isNotEqual(state, sym, Adjusted))
- return NULL;
+ // Third, determine if we already know sym+Adjustment != V.
+ if (isNotEqual(State, Sym, Adjusted))
+ return Assumption ? NULL : State;
- // If we reach here, sym is not a constant and we don't know if it is == V.
- // Make that assumption.
- return AddEQ(state, sym, Adjusted);
+ // If we reach here, sym is not a constant and we don't know if it is != V.
+ // Make the correct assumption.
+ if (Assumption)
+ return AddEQ(State, Sym, Adjusted);
+ else
+ return AddNE(State, Sym, Adjusted);
}
// The logic for these will be handled in another ConstraintManager.
+// Approximate it here anyway by handling some edge cases.
ProgramStateRef
BasicConstraintManager::assumeSymLT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Is 'V' the smallest possible value?
- if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) {
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
+
+ // Is 'V' out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V > ComparisonType.convert(Max)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Is 'V' the smallest possible value, or out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V <= ComparisonType.convert(Min)) {
// sym cannot be any value less than 'V'. This path is infeasible.
return NULL;
}
+ // Reject a path if the value of sym is a constant X and !(X+Adj < V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_LT, V);
+ return isFeasible ? state : NULL;
+ }
+
// FIXME: For now have assuming x < y be the same as assuming sym != V;
return assumeSymNE(state, sym, V, Adjustment);
}
@@ -185,12 +230,28 @@ BasicConstraintManager::assumeSymGT(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Is 'V' the largest possible value?
- if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) {
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
+
+ // Is 'V' the largest possible value, or out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V >= ComparisonType.convert(Max)) {
// sym cannot be any value greater than 'V'. This path is infeasible.
return NULL;
}
+ // Is 'V' out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V < ComparisonType.convert(Min)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj > V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_GT, V);
+ return isFeasible ? state : NULL;
+ }
+
// FIXME: For now have assuming x > y be the same as assuming sym != V;
return assumeSymNE(state, sym, V, Adjustment);
}
@@ -200,25 +261,33 @@ BasicConstraintManager::assumeSymGE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
- if (const llvm::APSInt *X = getSymVal(state, sym)) {
- bool isFeasible = (*X >= V-Adjustment);
- return isFeasible ? state : NULL;
- }
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
- // Sym is not a constant, but it is worth looking to see if V is the
- // maximum integer value.
- if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) {
- llvm::APSInt Adjusted = V-Adjustment;
+ // Is 'V' the largest possible value, or out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ ComparisonType.apply(Max);
- // If we know that sym != V (after adjustment), then this condition
- // is infeasible since there is no other value greater than V.
- bool isFeasible = !isNotEqual(state, sym, Adjusted);
-
- // If the path is still feasible then as a consequence we know that
+ if (V > Max) {
+ // sym cannot be any value greater than 'V'. This path is infeasible.
+ return NULL;
+ } else if (V == Max) {
+ // If the path is feasible then as a consequence we know that
// 'sym+Adjustment == V' because there are no larger values.
// Add this constraint.
- return isFeasible ? AddEQ(state, sym, Adjusted) : NULL;
+ return assumeSymEQ(state, sym, V, Adjustment);
+ }
+
+ // Is 'V' out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (V < ComparisonType.convert(Min)) {
+ // This path is trivially feasible.
+ return state;
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_GE, V);
+ return isFeasible ? state : NULL;
}
return state;
@@ -229,25 +298,33 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef state,
SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
- // Reject a path if the value of sym is a constant X and !(X+Adj <= V).
- if (const llvm::APSInt* X = getSymVal(state, sym)) {
- bool isFeasible = (*X <= V-Adjustment);
- return isFeasible ? state : NULL;
- }
+ APSIntType ComparisonType(V), AdjustmentType(Adjustment);
- // Sym is not a constant, but it is worth looking to see if V is the
- // minimum integer value.
- if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) {
- llvm::APSInt Adjusted = V-Adjustment;
+ // Is 'V' out of range above the type?
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (V > ComparisonType.convert(Max)) {
+ // This path is trivially feasible.
+ return state;
+ }
- // If we know that sym != V (after adjustment), then this condition
- // is infeasible since there is no other value less than V.
- bool isFeasible = !isNotEqual(state, sym, Adjusted);
+ // Is 'V' the smallest possible value, or out of range below the type?
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ ComparisonType.apply(Min);
- // If the path is still feasible then as a consequence we know that
+ if (V < Min) {
+ // sym cannot be any value less than 'V'. This path is infeasible.
+ return NULL;
+ } else if (V == Min) {
+ // If the path is feasible then as a consequence we know that
// 'sym+Adjustment == V' because there are no smaller values.
// Add this constraint.
- return isFeasible ? AddEQ(state, sym, Adjusted) : NULL;
+ return assumeSymEQ(state, sym, V, Adjustment);
+ }
+
+ // Reject a path if the value of sym is a constant X and !(X+Adj >= V).
+ if (const llvm::APSInt *X = getSymVal(state, sym)) {
+ bool isFeasible = performTest(*X, Adjustment, BO_LE, V);
+ return isFeasible ? state : NULL;
}
return state;
@@ -256,8 +333,10 @@ BasicConstraintManager::assumeSymLE(ProgramStateRef 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));
+ // Now that we have an actual value, we can throw out the NE-set.
+ // Create a new state with the old bindings replaced.
+ state = state->remove<ConstNotEq>(sym);
+ return state->set<ConstEq>(sym, &getBasicVals().getValue(V));
}
ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state,
@@ -269,7 +348,7 @@ ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state,
ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet();
// Now add V to the NE set.
- S = ISetFactory.add(S, &state->getBasicVals().getValue(V));
+ S = ISetFactory.add(S, &getBasicVals().getValue(V));
// Create a new state with the old binding replaced.
return state->set<ConstNotEq>(sym, S);
@@ -289,7 +368,7 @@ bool BasicConstraintManager::isNotEqual(ProgramStateRef state,
const ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym);
// See if V is present in the NE-set.
- return T ? T->contains(&state->getBasicVals().getValue(V)) : false;
+ return T ? T->contains(&getBasicVals().getValue(V)) : false;
}
bool BasicConstraintManager::isEqual(ProgramStateRef state,
diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index fe96700..20c7361 100644
--- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -13,6 +13,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index a264212..7ba2fa7 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -48,6 +48,10 @@ static inline const Stmt *GetStmt(const ProgramPoint &P) {
return SP->getStmt();
else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P))
return BE->getSrc()->getTerminator();
+ else if (const CallEnter *CE = dyn_cast<CallEnter>(&P))
+ return CE->getCallExpr();
+ else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P))
+ return CEE->getCalleeContext()->getCallSite();
return 0;
}
@@ -427,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
- if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
PD.getActivePath().push_front(C);
@@ -437,21 +441,23 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
}
if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ // Flush all locations, and pop the active path.
+ bool VisitedEntireCall = PD.isWithinCall();
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
+
+ // Either we just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExitEnd. If the former, 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) {
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ } else {
const Decl *Caller = CE->getLocationContext()->getDecl();
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
}
+
C->setCallee(*CE, SMgr);
if (!CallStack.empty()) {
assert(CallStack.back().first == C);
@@ -864,6 +870,7 @@ public:
void rawAddEdge(PathDiagnosticLocation NewLoc);
void addContext(const Stmt *S);
+ void addContext(const PathDiagnosticLocation &L);
void addExtendedContext(const Stmt *S);
};
} // end anonymous namespace
@@ -1031,7 +1038,10 @@ void EdgeBuilder::addContext(const Stmt *S) {
return;
PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC);
+ addContext(L);
+}
+void EdgeBuilder::addContext(const PathDiagnosticLocation &L) {
while (!CLocs.empty()) {
const PathDiagnosticLocation &TopContextLoc = CLocs.back();
@@ -1051,6 +1061,78 @@ void EdgeBuilder::addContext(const Stmt *S) {
CLocs.push_back(L);
}
+// Cone-of-influence: support the reverse propagation of "interesting" symbols
+// and values by tracing interesting calculations backwards through evaluated
+// expressions along a path. This is probably overly complicated, but the idea
+// is that if an expression computed an "interesting" value, the child
+// expressions are are also likely to be "interesting" as well (which then
+// propagates to the values they in turn compute). This reverse propagation
+// is needed to track interesting correlations across function call boundaries,
+// where formal arguments bind to actual arguments, etc. This is also needed
+// because the constraint solver sometimes simplifies certain symbolic values
+// into constants when appropriate, and this complicates reasoning about
+// interesting values.
+typedef llvm::DenseSet<const Expr *> InterestingExprs;
+
+static void reversePropagateIntererstingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const Expr *Ex,
+ const LocationContext *LCtx) {
+ SVal V = State->getSVal(Ex, LCtx);
+ if (!(R.isInteresting(V) || IE.count(Ex)))
+ return;
+
+ switch (Ex->getStmtClass()) {
+ default:
+ if (!isa<CastExpr>(Ex))
+ break;
+ // Fall through.
+ case Stmt::BinaryOperatorClass:
+ case Stmt::UnaryOperatorClass: {
+ for (Stmt::const_child_iterator CI = Ex->child_begin(),
+ CE = Ex->child_end();
+ CI != CE; ++CI) {
+ if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) {
+ IE.insert(child);
+ SVal ChildV = State->getSVal(child, LCtx);
+ R.markInteresting(ChildV);
+ }
+ break;
+ }
+ }
+ }
+
+ R.markInteresting(V);
+}
+
+static void reversePropagateInterestingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const LocationContext *CalleeCtx,
+ const LocationContext *CallerCtx)
+{
+ // FIXME: Handle non-CallExpr-based CallEvents.
+ const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame();
+ const Stmt *CallSite = Callee->getCallSite();
+ if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
+ FunctionDecl::param_const_iterator PI = FD->param_begin(),
+ PE = FD->param_end();
+ CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
+ for (; AI != AE && PI != PE; ++AI, ++PI) {
+ if (const Expr *ArgE = *AI) {
+ if (const ParmVarDecl *PD = *PI) {
+ Loc LV = State->getLValue(PD, CalleeCtx);
+ if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV)))
+ IE.insert(ArgE);
+ }
+ }
+ }
+ }
+ }
+}
+
static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticBuilder &PDB,
const ExplodedNode *N,
@@ -1058,6 +1140,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
EdgeBuilder EB(PD, PDB);
const SourceManager& SM = PDB.getSourceManager();
StackDiagVector CallStack;
+ InterestingExprs IE;
const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
while (NextNode) {
@@ -1066,16 +1149,27 @@ 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();
+ if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
+ if (const Expr *Ex = PS->getStmtAs<Expr>())
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(), Ex,
+ N->getLocationContext());
+ }
+
+ if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
+ const Stmt *S = CE->getCalleeContext()->getCallSite();
+ if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(), Ex,
+ N->getLocationContext());
+ }
+
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SM);
+
+ EB.addEdge(C->callReturn, true);
+ EB.flushLocations();
+
PD.getActivePath().push_front(C);
PD.pushActivePath(&C->path);
CallStack.push_back(StackDiagPair(C, N));
@@ -1092,26 +1186,26 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
EB.addEdge(pos);
// Flush all locations, and pop the active path.
+ bool VisitedEntireCall = PD.isWithinCall();
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
+ // Either we just added a bunch of stuff to the top-level path, or
+ // we have a previous CallExitEnd. If the former, 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();
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
+ } else {
+ const Decl *Caller = CE->getLocationContext()->getDecl();
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
}
+
C->setCallee(*CE, SM);
- EB.addContext(CE->getCallExpr());
+ EB.addContext(C->getLocation());
if (!CallStack.empty()) {
assert(CallStack.back().first == C);
@@ -1127,7 +1221,19 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PDB.LC = N->getLocationContext();
// Block edges.
- if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ // Does this represent entering a call? If so, look at propagating
+ // interesting symbols across call boundaries.
+ if (NextNode) {
+ const LocationContext *CallerCtx = NextNode->getLocationContext();
+ const LocationContext *CalleeCtx = PDB.LC;
+ if (CallerCtx != CalleeCtx) {
+ reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
+ N->getState().getPtr(),
+ CalleeCtx, CallerCtx);
+ }
+ }
+
const CFGBlock &Blk = *BE->getSrc();
const Stmt *Term = Blk.getTerminator();
@@ -1430,9 +1536,12 @@ void BugReporter::FlushReports() {
I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
const_cast<BugType*>(*I)->FlushReports(*this);
- typedef llvm::FoldingSet<BugReportEquivClass> SetTy;
- for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){
- BugReportEquivClass& EQ = *EI;
+ // We need to flush reports in deterministic order to ensure the order
+ // of the reports is consistent between runs.
+ typedef std::vector<BugReportEquivClass *> ContVecTy;
+ for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end();
+ EI != EE; ++EI){
+ BugReportEquivClass& EQ = **EI;
FlushReport(EQ);
}
@@ -1768,9 +1877,11 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
} while(finalReportConfigToken != originalReportConfigToken);
// Finally, prune the diagnostic path of uninteresting stuff.
- bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
- assert(hasSomethingInteresting);
- (void) hasSomethingInteresting;
+ if (R->shouldPrunePath()) {
+ bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces());
+ assert(hasSomethingInteresting);
+ (void) hasSomethingInteresting;
+ }
}
void BugReporter::Register(BugType *BT) {
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 6532486..46aa9e2 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -34,15 +34,23 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
// a[0], p->f, *p
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
- if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
- if (U->getOpcode() == UO_Deref)
- return U->getSubExpr()->IgnoreParenCasts();
- }
- else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
- return ME->getBase()->IgnoreParenCasts();
- }
- else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
- return AE->getBase();
+ while (true) {
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) {
+ assert(B->isAssignmentOp());
+ S = B->getLHS()->IgnoreParenCasts();
+ continue;
+ }
+ else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
+ if (U->getOpcode() == UO_Deref)
+ return U->getSubExpr()->IgnoreParenCasts();
+ }
+ else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
+ return ME->getBase()->IgnoreParenCasts();
+ }
+ else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
+ return AE->getBase();
+ }
+ break;
}
return NULL;
@@ -55,14 +63,6 @@ const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) {
return NULL;
}
-const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) {
- // Callee is checked as a PreVisit to the CallExpr.
- const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
- if (const CallExpr *CE = dyn_cast<CallExpr>(S))
- return CE->getCallee();
- return NULL;
-}
-
const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
@@ -119,10 +119,16 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
if (!StoreSite) {
- const ExplodedNode *Node = N, *Last = NULL;
+ // Make sure the region is actually bound to value V here.
+ // This is necessary because the region may not actually be live at the
+ // report's error node.
+ if (N->getState()->getSVal(R) != V)
+ return NULL;
- for ( ; Node ; Node = Node->getFirstPred()) {
+ const ExplodedNode *Node = N, *Last = N;
+ // Now look for the store of V.
+ 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>())
@@ -137,9 +143,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
// looking for store sites.
if (Node->getState()->getSVal(R) != V)
break;
+
+ Last = Node;
}
- if (!Node || !Last) {
+ if (!Node) {
satisfied = true;
return NULL;
}
@@ -189,6 +197,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
os << "declared without an initial value";
}
}
+ else {
+ os << "initialized here";
+ }
}
}
@@ -215,7 +226,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
<< " is assigned to ";
}
else
- return NULL;
+ os << "Value assigned to ";
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << '\'' << *VR->getDecl() << '\'';
@@ -285,12 +296,11 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
}
-BugReporterVisitor *
-bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
- const Stmt *S,
- BugReport *report) {
+void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N,
+ const Stmt *S,
+ BugReport *report) {
if (!S || !N)
- return 0;
+ return;
ProgramStateManager &StateMgr = N->getState()->getStateManager();
@@ -306,7 +316,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
}
if (!N)
- return 0;
+ return;
ProgramStateRef state = N->getState();
@@ -320,10 +330,18 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
// What did we load?
- SVal V = state->getSVal(loc::MemRegionVal(R));
+ SVal V = state->getRawSVal(loc::MemRegionVal(R));
report->markInteresting(R);
report->markInteresting(V);
- return new FindLastStoreBRVisitor(V, R);
+
+ if (V.getAsLocSymbol()) {
+ BugReporterVisitor *ConstraintTracker
+ = new TrackConstraintBRVisitor(cast<loc::MemRegionVal>(V), false);
+ report->addVisitor(ConstraintTracker);
+ }
+
+ report->addVisitor(new FindLastStoreBRVisitor(V, R));
+ return;
}
}
}
@@ -343,11 +361,10 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
if (R) {
report->markInteresting(R);
- return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false);
+ report->addVisitor(new TrackConstraintBRVisitor(loc::MemRegionVal(R),
+ false));
}
}
-
- return 0;
}
BugReporterVisitor *
@@ -389,7 +406,7 @@ 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));
+ bugreporter::addTrackNullOrUndefValueVisitor(N, Receiver, &BR);
// Issue a message saying that the method was skipped.
PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
N->getLocationContext());
@@ -699,6 +716,9 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
BugReporterContext &BRC,
BugReport &report,
const ExplodedNode *N) {
+ // FIXME: If there's already a constraint tracker for this variable,
+ // we shouldn't emit anything here (c.f. the double note in
+ // test/Analysis/inlining/path-notes.c)
SmallString<256> buf;
llvm::raw_svector_ostream Out(buf);
Out << "Assuming " << LhsString << " is ";
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index 1ea25bd..b16b233d 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -1,14 +1,14 @@
set(LLVM_LINK_COMPONENTS support)
-set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite)
-
add_clang_library(clangStaticAnalyzerCore
AnalysisManager.cpp
+ APSIntType.cpp
BasicConstraintManager.cpp
BasicValueFactory.cpp
BlockCounter.cpp
BugReporter.cpp
BugReporterVisitors.cpp
+ CallEvent.cpp
Checker.cpp
CheckerContext.cpp
CheckerHelpers.cpp
@@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore
FunctionSummary.cpp
HTMLDiagnostics.cpp
MemRegion.cpp
- ObjCMessage.cpp
PathDiagnostic.cpp
PlistDiagnostics.cpp
ProgramState.cpp
@@ -41,5 +40,19 @@ add_clang_library(clangStaticAnalyzerCore
TextPathDiagnostics.cpp
)
-add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes
- ClangStmtNodes)
+add_dependencies(clangStaticAnalyzerCore
+ ClangAttrClasses
+ ClangAttrList
+ ClangCommentNodes
+ ClangDeclNodes
+ ClangDiagnosticCommon
+ ClangStmtNodes
+ )
+
+target_link_libraries(clangStaticAnalyzerCore
+ clangBasic
+ clangLex
+ clangAST
+ clangFrontend
+ clangRewrite
+ )
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
new file mode 100644
index 0000000..e3f4c61
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -0,0 +1,856 @@
+//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines CallEvent and its subclasses, which represent path-
+/// sensitive instances of different kinds of function and method calls
+/// (C, C++, and Objective-C).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/AST/ParentMap.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang;
+using namespace ento;
+
+QualType CallEvent::getResultType() const {
+ QualType ResultTy = getDeclaredResultType();
+
+ if (ResultTy.isNull())
+ ResultTy = getOriginExpr()->getType();
+
+ return ResultTy;
+}
+
+static bool isCallbackArg(SVal V, QualType T) {
+ // If the parameter is 0, it's harmless.
+ if (V.isZeroConstant())
+ return false;
+
+ // If a parameter is a block or a callback, assume it can modify pointer.
+ if (T->isBlockPointerType() ||
+ T->isFunctionPointerType() ||
+ T->isObjCSelType())
+ return true;
+
+ // Check if a callback is passed inside a struct (for both, struct passed by
+ // reference and by value). Dig just one level into the struct for now.
+
+ if (isa<PointerType>(T) || isa<ReferenceType>(T))
+ T = T->getPointeeType();
+
+ if (const RecordType *RT = T->getAsStructureType()) {
+ const RecordDecl *RD = RT->getDecl();
+ for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
+ I != E; ++I) {
+ QualType FieldT = I->getType();
+ if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CallEvent::hasNonZeroCallbackArg() const {
+ unsigned NumOfArgs = getNumArgs();
+
+ // If calling using a function pointer, assume the function does not
+ // have a callback. TODO: We could check the types of the arguments here.
+ if (!getDecl())
+ return false;
+
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = param_type_begin(),
+ E = param_type_end();
+ I != E && Idx < NumOfArgs; ++I, ++Idx) {
+ if (NumOfArgs <= Idx)
+ break;
+
+ if (isCallbackArg(getArgSVal(Idx), *I))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Returns true if a type is a pointer-to-const or reference-to-const
+/// with no further indirection.
+static bool isPointerToConst(QualType Ty) {
+ QualType PointeeTy = Ty->getPointeeType();
+ if (PointeeTy == QualType())
+ return false;
+ if (!PointeeTy.isConstQualified())
+ return false;
+ if (PointeeTy->isAnyPointerType())
+ return false;
+ return true;
+}
+
+// Try to retrieve the function declaration and find the function parameter
+// types which are pointers/references to a non-pointer const.
+// We will not invalidate the corresponding argument regions.
+static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
+ const CallEvent &Call) {
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = Call.param_type_begin(),
+ E = Call.param_type_end();
+ I != E; ++I, ++Idx) {
+ if (isPointerToConst(*I))
+ PreserveArgs.insert(Idx);
+ }
+}
+
+ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
+ ProgramStateRef Orig) const {
+ ProgramStateRef Result = (Orig ? Orig : getState());
+
+ SmallVector<const MemRegion *, 8> RegionsToInvalidate;
+ getExtraInvalidatedRegions(RegionsToInvalidate);
+
+ // Indexes of arguments whose values will be preserved by the call.
+ llvm::SmallSet<unsigned, 1> PreserveArgs;
+ if (!argumentsMayEscape())
+ findPtrToConstParams(PreserveArgs, *this);
+
+ for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) {
+ if (PreserveArgs.count(Idx))
+ continue;
+
+ SVal V = getArgSVal(Idx);
+
+ // If we are passing a location wrapped as an integer, unwrap it and
+ // invalidate the values referred by the location.
+ if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
+ V = Wrapped->getLoc();
+ else if (!isa<Loc>(V))
+ continue;
+
+ if (const MemRegion *R = V.getAsRegion()) {
+ // 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 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
+ // about (char*), etc., is just a form of passing raw bytes.
+ // e.g., void *p = alloca(); foo((char*)p);
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ // Checking for 'integral type' is probably too promiscuous, but
+ // 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
+ // 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()) {
+ const MemRegion *superReg = ER->getSuperRegion();
+ if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
+ isa<ObjCIvarRegion>(superReg))
+ R = cast<TypedRegion>(superReg);
+ }
+ // FIXME: What about layers of ElementRegions?
+ }
+
+ // Mark this region for invalidation. We batch invalidate regions
+ // below for efficiency.
+ RegionsToInvalidate.push_back(R);
+ }
+ }
+
+ // Invalidate designated regions using the batch invalidation API.
+ // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
+ // global variables.
+ return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(),
+ BlockCount, getLocationContext(),
+ /*Symbols=*/0, this);
+}
+
+ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
+ const ProgramPointTag *Tag) const {
+ if (const Expr *E = getOriginExpr()) {
+ if (IsPreVisit)
+ return PreStmt(E, getLocationContext(), Tag);
+ return PostStmt(E, getLocationContext(), Tag);
+ }
+
+ const Decl *D = getDecl();
+ assert(D && "Cannot get a program point without a statement or decl");
+
+ SourceLocation Loc = getSourceRange().getBegin();
+ if (IsPreVisit)
+ return PreImplicitCall(D, Loc, getLocationContext(), Tag);
+ return PostImplicitCall(D, Loc, getLocationContext(), Tag);
+}
+
+SVal CallEvent::getArgSVal(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return UnknownVal();
+ return getSVal(ArgE);
+}
+
+SourceRange CallEvent::getArgSourceRange(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return SourceRange();
+ return ArgE->getSourceRange();
+}
+
+void CallEvent::dump(raw_ostream &Out) const {
+ ASTContext &Ctx = getState()->getStateManager().getContext();
+ if (const Expr *E = getOriginExpr()) {
+ E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy());
+ Out << "\n";
+ return;
+ }
+
+ if (const Decl *D = getDecl()) {
+ Out << "Call to ";
+ D->print(Out, Ctx.getPrintingPolicy());
+ return;
+ }
+
+ // FIXME: a string representation of the kind would be nice.
+ Out << "Unknown call (type " << getKind() << ")";
+}
+
+
+bool CallEvent::mayBeInlined(const Stmt *S) {
+ // FIXME: Kill this.
+ return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
+ || isa<CXXConstructExpr>(S);
+}
+
+static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
+ CallEvent::BindingsTy &Bindings,
+ SValBuilder &SVB,
+ const CallEvent &Call,
+ CallEvent::param_iterator I,
+ CallEvent::param_iterator E) {
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+
+ unsigned Idx = 0;
+ for (; I != E; ++I, ++Idx) {
+ const ParmVarDecl *ParamDecl = *I;
+ assert(ParamDecl && "Formal parameter has no decl?");
+
+ SVal ArgVal = Call.getArgSVal(Idx);
+ if (!ArgVal.isUnknown()) {
+ Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
+ }
+ }
+
+ // FIXME: Variadic arguments are not handled at all right now.
+}
+
+
+CallEvent::param_iterator AnyFunctionCall::param_begin() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_begin();
+}
+
+CallEvent::param_iterator AnyFunctionCall::param_end() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_end();
+}
+
+void AnyFunctionCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+}
+
+QualType AnyFunctionCall::getDeclaredResultType() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return QualType();
+
+ return D->getResultType();
+}
+
+bool AnyFunctionCall::argumentsMayEscape() const {
+ if (hasNonZeroCallbackArg())
+ return true;
+
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return true;
+
+ const IdentifierInfo *II = D->getIdentifier();
+ if (!II)
+ return true;
+
+ // This set of "escaping" APIs is
+
+ // - '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.
+ if (II->isStr("pthread_setspecific"))
+ return true;
+
+ // - xpc_connection_set_context stores a value which can be retrieved later
+ // with xpc_connection_get_context.
+ if (II->isStr("xpc_connection_set_context"))
+ return true;
+
+ // - funopen - sets a buffer for future IO calls.
+ if (II->isStr("funopen"))
+ return true;
+
+ StringRef FName = II->getName();
+
+ // - CoreFoundation functions that end with "NoCopy" can free a passed-in
+ // buffer even if it is const.
+ if (FName.endswith("NoCopy"))
+ return true;
+
+ // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove.
+ if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ return true;
+
+ // - Many CF containers allow objects to escape through custom
+ // allocators/deallocators upon container construction. (PR12101)
+ if (FName.startswith("CF") || FName.startswith("CG")) {
+ return StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "WithData") != StringRef::npos ||
+ StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetAttribute") != StringRef::npos;
+ }
+
+ return false;
+}
+
+
+const FunctionDecl *SimpleCall::getDecl() const {
+ const FunctionDecl *D = getOriginExpr()->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
+}
+
+
+const FunctionDecl *CXXInstanceCall::getDecl() const {
+ const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr());
+ if (!CE)
+ return AnyFunctionCall::getDecl();
+
+ const FunctionDecl *D = CE->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(CE->getCallee()).getAsFunctionDecl();
+}
+
+void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (const MemRegion *R = getCXXThisVal().getAsRegion())
+ Regions.push_back(R);
+}
+
+static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){
+ const MemRegion *R = ThisVal.getAsRegion();
+ if (!R)
+ return 0;
+
+ const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts());
+ if (!TR)
+ return 0;
+
+ const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl();
+ if (!RD)
+ return 0;
+
+ const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD);
+ const FunctionDecl *Definition;
+ if (!Result->hasBody(Definition))
+ return 0;
+
+ return cast<CXXMethodDecl>(Definition);
+}
+
+
+RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
+ const Decl *D = getDecl();
+ if (!D)
+ return RuntimeDefinition();
+
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
+ if (!MD->isVirtual())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ // If the method is virtual, see if we can find the actual implementation
+ // based on context-sensitivity.
+ // FIXME: Virtual method calls behave differently when an object is being
+ // constructed or destructed. It's not as simple as "no devirtualization"
+ // because a /partially/ constructed object can be referred to through a
+ // base pointer. We'll eventually want to use DynamicTypeInfo here.
+ if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal()))
+ return RuntimeDefinition(Devirtualized);
+
+ return RuntimeDefinition();
+}
+
+void CXXInstanceCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ // Handle the binding of 'this' in the new stack frame.
+ // We need to make sure we have the proper layering of CXXBaseObjectRegions.
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ ProgramStateManager &StateMgr = getState()->getStateManager();
+ SValBuilder &SVB = StateMgr.getSValBuilder();
+
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+
+ if (const MemRegion *ThisReg = ThisVal.getAsRegion()) {
+ ASTContext &Ctx = SVB.getContext();
+ const CXXRecordDecl *Class = MD->getParent();
+ QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
+
+ // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
+ bool Failed;
+ ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed);
+ assert(!Failed && "Calling an incorrectly devirtualized method");
+
+ // If we couldn't build the correct cast, just strip off all casts.
+ if (ThisVal.isUnknown())
+ ThisVal = loc::MemRegionVal(ThisReg->StripCasts());
+ }
+
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+
+
+const Expr *CXXMemberCall::getCXXThisExpr() const {
+ return getOriginExpr()->getImplicitObjectArgument();
+}
+
+
+const Expr *CXXMemberOperatorCall::getCXXThisExpr() const {
+ return getOriginExpr()->getArg(0);
+}
+
+
+const BlockDataRegion *BlockCall::getBlockRegion() const {
+ const Expr *Callee = getOriginExpr()->getCallee();
+ const MemRegion *DataReg = getSVal(Callee).getAsRegion();
+
+ return dyn_cast_or_null<BlockDataRegion>(DataReg);
+}
+
+CallEvent::param_iterator BlockCall::param_begin() const {
+ const BlockDecl *D = getBlockDecl();
+ if (!D)
+ return 0;
+ return D->param_begin();
+}
+
+CallEvent::param_iterator BlockCall::param_end() const {
+ const BlockDecl *D = getBlockDecl();
+ if (!D)
+ return 0;
+ return D->param_end();
+}
+
+void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ // FIXME: This also needs to invalidate captured globals.
+ if (const MemRegion *R = getBlockRegion())
+ Regions.push_back(R);
+}
+
+void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+}
+
+
+QualType BlockCall::getDeclaredResultType() const {
+ const BlockDataRegion *BR = getBlockRegion();
+ if (!BR)
+ return QualType();
+ QualType BlockTy = BR->getCodeRegion()->getLocationType();
+ return cast<FunctionType>(BlockTy->getPointeeType())->getResultType();
+}
+
+
+SVal CXXConstructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return UnknownVal();
+}
+
+void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (Data)
+ Regions.push_back(static_cast<const MemRegion *>(Data));
+}
+
+void CXXConstructorCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+
+
+SVal CXXDestructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return UnknownVal();
+}
+
+
+CallEvent::param_iterator ObjCMethodCall::param_begin() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_begin();
+}
+
+CallEvent::param_iterator ObjCMethodCall::param_end() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return 0;
+
+ return D->param_end();
+}
+
+void
+ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const {
+ if (const MemRegion *R = getReceiverSVal().getAsRegion())
+ Regions.push_back(R);
+}
+
+QualType ObjCMethodCall::getDeclaredResultType() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return QualType();
+
+ return D->getResultType();
+}
+
+SVal ObjCMethodCall::getReceiverSVal() const {
+ // FIXME: Is this the best way to handle class receivers?
+ if (!isInstanceMessage())
+ return UnknownVal();
+
+ if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
+ return getSVal(RecE);
+
+ // An instance message with no expression means we are sending to super.
+ // In this case the object reference is the same as 'self'.
+ const LocationContext *LCtx = getLocationContext();
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ assert(SelfDecl && "No message receiver Expr, but not in an ObjC method");
+ return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
+}
+
+SourceRange ObjCMethodCall::getSourceRange() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ return getOriginExpr()->getSourceRange();
+ case OCM_PropertyAccess:
+ case OCM_Subscript:
+ return getContainingPseudoObjectExpr()->getSourceRange();
+ }
+ llvm_unreachable("unknown message kind");
+}
+
+typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy;
+
+const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
+ assert(Data != 0 && "Lazy lookup not yet performed.");
+ assert(getMessageKind() != OCM_Message && "Explicit message send.");
+ return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer();
+}
+
+ObjCMessageKind ObjCMethodCall::getMessageKind() const {
+ if (Data == 0) {
+ ParentMap &PM = getLocationContext()->getParentMap();
+ const Stmt *S = PM.getParent(getOriginExpr());
+ if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
+ const Expr *Syntactic = POE->getSyntacticForm();
+
+ // This handles the funny case of assigning to the result of a getter.
+ // This can happen if the getter returns a non-const reference.
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic))
+ Syntactic = BO->getLHS();
+
+ ObjCMessageKind K;
+ switch (Syntactic->getStmtClass()) {
+ case Stmt::ObjCPropertyRefExprClass:
+ K = OCM_PropertyAccess;
+ break;
+ case Stmt::ObjCSubscriptRefExprClass:
+ K = OCM_Subscript;
+ break;
+ default:
+ // FIXME: Can this ever happen?
+ K = OCM_Message;
+ break;
+ }
+
+ if (K != OCM_Message) {
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(POE, K).getOpaqueValue();
+ assert(getMessageKind() == K);
+ return K;
+ }
+ }
+
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(0, 1).getOpaqueValue();
+ assert(getMessageKind() == OCM_Message);
+ return OCM_Message;
+ }
+
+ ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data);
+ if (!Info.getPointer())
+ return OCM_Message;
+ return static_cast<ObjCMessageKind>(Info.getInt());
+}
+
+
+bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+ Selector Sel) const {
+ assert(IDecl);
+ const SourceManager &SM =
+ getState()->getStateManager().getContext().getSourceManager();
+
+ // If the class interface is declared inside the main file, assume it is not
+ // subcassed.
+ // TODO: It could actually be subclassed if the subclass is private as well.
+ // This is probably very rare.
+ SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
+ if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc))
+ return false;
+
+
+ // We assume that if the method is public (declared outside of main file) or
+ // has a parent which publicly declares the method, the method could be
+ // overridden in a subclass.
+
+ // Find the first declaration in the class hierarchy that declares
+ // the selector.
+ ObjCMethodDecl *D = 0;
+ while (true) {
+ D = IDecl->lookupMethod(Sel, true);
+
+ // Cannot find a public definition.
+ if (!D)
+ return false;
+
+ // If outside the main file,
+ if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation()))
+ return true;
+
+ if (D->isOverriding()) {
+ // Search in the superclass on the next iteration.
+ IDecl = D->getClassInterface();
+ if (!IDecl)
+ return false;
+
+ IDecl = IDecl->getSuperClass();
+ if (!IDecl)
+ return false;
+
+ continue;
+ }
+
+ return false;
+ };
+
+ llvm_unreachable("The while loop should always terminate.");
+}
+
+RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
+ const ObjCMessageExpr *E = getOriginExpr();
+ assert(E);
+ Selector Sel = E->getSelector();
+
+ if (E->isInstanceMessage()) {
+
+ // Find the the receiver type.
+ const ObjCObjectPointerType *ReceiverT = 0;
+ bool CanBeSubClassed = false;
+ QualType SupersType = E->getSuperType();
+ const MemRegion *Receiver = 0;
+
+ if (!SupersType.isNull()) {
+ // Super always means the type of immediate predecessor to the method
+ // where the call occurs.
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ } else {
+ Receiver = getReceiverSVal().getAsRegion();
+ if (!Receiver)
+ return RuntimeDefinition();
+
+ DynamicTypeInfo DTI = getState()->getDynamicTypeInfo(Receiver);
+ QualType DynType = DTI.getType();
+ CanBeSubClassed = DTI.canBeASubClass();
+ ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
+
+ if (ReceiverT && CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
+ if (!canBeOverridenInSubclass(IDecl, Sel))
+ CanBeSubClassed = false;
+ }
+
+ // Lookup the method implementation.
+ if (ReceiverT)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
+ const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel);
+ if (CanBeSubClassed)
+ return RuntimeDefinition(MD, Receiver);
+ else
+ return RuntimeDefinition(MD, 0);
+ }
+
+ } else {
+ // This is a class method.
+ // If we have type info for the receiver class, we are calling via
+ // class name.
+ if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) {
+ // Find/Return the method implementation.
+ return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel));
+ }
+ }
+
+ return RuntimeDefinition();
+}
+
+void ObjCMethodCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->param_begin(), D->param_end());
+
+ SVal SelfVal = getReceiverSVal();
+ if (!SelfVal.isUnknown()) {
+ const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl();
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+ Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx));
+ Bindings.push_back(std::make_pair(SelfLoc, SelfVal));
+ }
+}
+
+CallEventRef<>
+CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE))
+ return create<CXXMemberCall>(MCE, State, LCtx);
+
+ if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+ if (MD->isInstance())
+ return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+
+ } else if (CE->getCallee()->getType()->isBlockPointerType()) {
+ return create<BlockCall>(CE, State, LCtx);
+ }
+
+ // Otherwise, it's a normal function call, static member function call, or
+ // something we can't reason about.
+ return create<FunctionCall>(CE, State, LCtx);
+}
+
+
+CallEventRef<>
+CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
+ ProgramStateRef State) {
+ const LocationContext *ParentCtx = CalleeCtx->getParent();
+ const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame();
+ assert(CallerCtx && "This should not be used for top-level stack frames");
+
+ const Stmt *CallSite = CalleeCtx->getCallSite();
+
+ if (CallSite) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite))
+ return getSimpleCall(CE, State, CallerCtx);
+
+ switch (CallSite->getStmtClass()) {
+ case Stmt::CXXConstructExprClass: {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
+ ThisVal.getAsRegion(), State, CallerCtx);
+ }
+ case Stmt::CXXNewExprClass:
+ return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx);
+ case Stmt::ObjCMessageExprClass:
+ return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite),
+ State, CallerCtx);
+ default:
+ llvm_unreachable("This is not an inlineable statement.");
+ }
+ }
+
+ // Fall back to the CFG. The only thing we haven't handled yet is
+ // destructors, though this could change in the future.
+ const CFGBlock *B = CalleeCtx->getCallSiteBlock();
+ CFGElement E = (*B)[CalleeCtx->getIndex()];
+ assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs");
+ assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet");
+
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ const Stmt *Trigger;
+ if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E))
+ Trigger = AutoDtor->getTriggerStmt();
+ else
+ Trigger = Dtor->getBody();
+
+ return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
+ State, CallerCtx);
+}
+
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 0bcc343..c786655 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -14,7 +14,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/AST/DeclBase.h"
@@ -25,6 +25,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const {
return !StmtCheckers.empty() ||
!PreObjCMessageCheckers.empty() ||
!PostObjCMessageCheckers.empty() ||
+ !PreCallCheckers.empty() ||
+ !PostCallCheckers.empty() ||
!LocationCheckers.empty() ||
!BindCheckers.empty() ||
!EndAnalysisCheckers.empty() ||
@@ -138,7 +140,7 @@ namespace {
const CheckersTy &Checkers;
const Stmt *S;
ExprEngine &Eng;
- bool wasInlined;
+ bool WasInlined;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
@@ -146,7 +148,7 @@ namespace {
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
const Stmt *s, ExprEngine &eng, bool wasInlined = false)
: IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
- wasInlined(wasInlined) {}
+ WasInlined(wasInlined) {}
void runChecker(CheckerManager::CheckStmtFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -155,7 +157,7 @@ namespace {
ProgramPoint::PostStmtKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
Pred->getLocationContext(), checkFn.Checker);
- CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
checkFn(S, C);
}
};
@@ -167,38 +169,35 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
const ExplodedNodeSet &Src,
const Stmt *S,
ExprEngine &Eng,
- bool wasInlined) {
+ bool WasInlined) {
CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
- S, Eng, wasInlined);
+ S, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
namespace {
struct CheckObjCMessageContext {
typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
- bool IsPreVisit;
+ bool IsPreVisit, WasInlined;
const CheckersTy &Checkers;
- const ObjCMessage &Msg;
+ const ObjCMethodCall &Msg;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
- const ObjCMessage &msg, ExprEngine &eng)
- : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
+ const ObjCMethodCall &msg, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Msg(msg), Eng(eng) { }
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
- ProgramPoint::PostStmtKind;
- const ProgramPoint &L =
- ProgramPoint::getProgramPoint(Msg.getMessageExpr(),
- K, Pred->getLocationContext(),
- checkFn.Checker);
- CheckerContext C(Bldr, Eng, Pred, L);
+ const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
- checkFn(Msg, C);
+ checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
}
};
}
@@ -207,12 +206,56 @@ namespace {
void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const ObjCMessage &msg,
- ExprEngine &Eng) {
+ const ObjCMethodCall &msg,
+ ExprEngine &Eng,
+ bool WasInlined) {
CheckObjCMessageContext C(isPreVisit,
isPreVisit ? PreObjCMessageCheckers
: PostObjCMessageCheckers,
- msg, Eng);
+ msg, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+ // FIXME: This has all the same signatures as CheckObjCMessageContext.
+ // Is there a way we can merge the two?
+ struct CheckCallContext {
+ typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
+ bool IsPreVisit, WasInlined;
+ const CheckersTy &Checkers;
+ const CallEvent &Call;
+ ExprEngine &Eng;
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
+ const CallEvent &call, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Call(call), Eng(eng) { }
+
+ void runChecker(CheckerManager::CheckCallFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Call.cloneWithState(Pred->getState()), C);
+ }
+ };
+}
+
+/// \brief Run checkers for visiting an abstract call event.
+void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckCallContext C(isPreVisit,
+ isPreVisit ? PreCallCheckers
+ : PostCallCheckers,
+ Call, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -381,21 +424,25 @@ namespace {
SymbolReaper &SR;
const Stmt *S;
ExprEngine &Eng;
+ ProgramPoint::Kind ProgarmPointKind;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
- const Stmt *s, ExprEngine &eng)
- : Checkers(checkers), SR(sr), S(s), Eng(eng) { }
+ const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind K)
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
+ // Note, do not pass the statement to the checkers without letting them
+ // differentiate if we ran remove dead bindings before or after the
+ // statement.
checkFn(SR, C);
}
};
@@ -406,8 +453,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper,
const Stmt *S,
- ExprEngine &Eng) {
- CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng);
+ ExprEngine &Eng,
+ ProgramPoint::Kind K) {
+ CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -426,7 +474,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *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.
@@ -456,16 +504,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
/// Only one checker will evaluate the call.
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
- const CallExpr *CE,
- ExprEngine &Eng,
- GraphExpander *defaultEval) {
- if (EvalCallCheckers.empty() &&
- InlineCallCheckers.empty() &&
- defaultEval == 0) {
- Dst.insert(Src);
- return;
- }
-
+ const CallEvent &Call,
+ ExprEngine &Eng) {
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
for (ExplodedNodeSet::iterator
NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) {
@@ -529,10 +570,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!anyEvaluated) {
- if (defaultEval)
- defaultEval->expandGraph(Dst, Pred);
- else
- Dst.insert(Pred);
+ NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
+ Eng.defaultEvalCall(B, Pred, Call);
}
}
}
@@ -590,6 +629,13 @@ void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
PostObjCMessageCheckers.push_back(checkfn);
}
+void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
+ PreCallCheckers.push_back(checkfn);
+}
+void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
+ PostCallCheckers.push_back(checkfn);
+}
+
void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
LocationCheckers.push_back(checkfn);
}
@@ -673,6 +719,3 @@ CheckerManager::~CheckerManager() {
for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i)
CheckerDtors[i]();
}
-
-// Anchor for the vtable.
-GraphExpander::~GraphExpander() { }
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index ca662c7..1f13742 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -26,6 +26,8 @@
using namespace clang;
using namespace ento;
+STATISTIC(NumSteps,
+ "The # of steps executed.");
STATISTIC(NumReachedMaxSteps,
"The # of times we reached the max number of steps.");
STATISTIC(NumPathsExplored,
@@ -74,7 +76,7 @@ public:
}
virtual void enqueue(const WorkListUnit& U) {
- Queue.push_front(U);
+ Queue.push_back(U);
}
virtual WorkListUnit dequeue() {
@@ -207,6 +209,8 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
--Steps;
}
+ NumSteps++;
+
const WorkListUnit& WU = WList->dequeue();
// Set the current block counter.
@@ -248,7 +252,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
}
- case ProgramPoint::CallExitKind:
+ case ProgramPoint::CallExitBeginKind:
SubEng.processCallExit(Pred);
break;
@@ -261,7 +265,9 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
}
default:
assert(isa<PostStmt>(Loc) ||
- isa<PostInitializer>(Loc));
+ isa<PostInitializer>(Loc) ||
+ isa<PostImplicitCall>(Loc) ||
+ isa<CallExitEnd>(Loc));
HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred);
break;
}
@@ -502,7 +508,8 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
}
// Do not create extra nodes. Move to the next CFG element.
- if (isa<PostInitializer>(N->getLocation())) {
+ if (isa<PostInitializer>(N->getLocation()) ||
+ isa<PostImplicitCall>(N->getLocation())) {
WList->enqueue(N, Block, Idx+1);
return;
}
@@ -531,14 +538,13 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
WList->enqueue(Succ, Block, Idx+1);
}
-ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) {
- // Create a CallExit node and enqueue it.
+ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) {
+ // Create a CallExitBegin node and enqueue it.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(N->getLocationContext());
- const Stmt *CE = LocCtx->getCallSite();
- // Use the the callee location context.
- CallExit Loc(CE, LocCtx);
+ // Use the callee location context.
+ CallExitBegin Loc(LocCtx);
bool isNew;
ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew);
@@ -565,12 +571,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
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 we are in an inlined call, generate CallExitBegin node.
if (N->getLocationContext()->getParent()) {
- N = generateCallExitNode(N);
+ N = generateCallExitBeginNode(N);
if (N)
WList->enqueue(N);
} else {
+ // TODO: We should run remove dead bindings here.
G->addEndOfPath(N);
NumPathsExplored++;
}
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index b5ea3db..52644f7 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -71,6 +71,11 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
else
return svalBuilder.makeBoolVal(cast<CXXBoolLiteralExpr>(E));
}
+ case Stmt::CXXScalarValueInitExprClass:
+ case Stmt::ImplicitValueInitExprClass: {
+ QualType Ty = cast<Expr>(E)->getType();
+ return svalBuilder.makeZeroVal(Ty);
+ }
case Stmt::IntegerLiteralClass: {
// In C++, this expression may have been bound to a temporary object.
SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx));
@@ -91,8 +96,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::CXXBindTemporaryExprClass:
E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
continue;
- case Stmt::ObjCPropertyRefExprClass:
- return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E));
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
+ continue;
case Stmt::ObjCStringLiteralClass: {
MemRegionManager &MRMgr = svalBuilder.getRegionManager();
const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E);
@@ -227,13 +233,6 @@ EnvironmentManager::removeDeadBindings(Environment Env,
RSScaner.scan(X);
continue;
}
-
- // Otherwise the expression is dead with a couple exceptions.
- // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the
- // beginning of itself, but we need its UndefinedVal to determine its
- // SVal.
- if (X.isUndef() && cast<UndefinedVal>(X).getData())
- EBMapRef = EBMapRef.add(BlkExpr, X);
}
// Go through he deferred locations and add them to the new environment if
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 0dcbe1f..b79f3f5 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -13,12 +13,14 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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"
+#include "llvm/ADT/Statistic.h"
#include <vector>
using namespace clang;
@@ -57,7 +59,7 @@ ExplodedGraph::~ExplodedGraph() {}
//===----------------------------------------------------------------------===//
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
- // Reclaimn all nodes that match *all* the following criteria:
+ // Reclaim all nodes that match *all* the following criteria:
//
// (1) 1 predecessor (that has one successor)
// (2) 1 successor (that has one predecessor)
@@ -67,6 +69,9 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// (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-consumed Stmt or Expr.
+ // (9) The successor is not a CallExpr StmtPoint (so that we would be able to
+ // find it when retrying a call with no inlining).
+ // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
// Conditions 1 and 2.
if (node->pred_size() != 1 || node->succ_size() != 1)
@@ -82,8 +87,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 3.
ProgramPoint progPoint = node->getLocation();
- if (!isa<PostStmt>(progPoint) ||
- (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint)))
+ if (!isa<PostStmt>(progPoint))
return false;
// Condition 4.
@@ -108,7 +112,13 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
return false;
}
- return true;
+ // Condition 9.
+ const ProgramPoint SuccLoc = succ->getLocation();
+ if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc))
+ if (CallEvent::mayBeInlined(SP->getStmt()))
+ return false;
+
+ return true;
}
void ExplodedGraph::collectNode(ExplodedNode *node) {
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1fd9068..b0435fb 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -18,13 +18,12 @@
#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/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.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/PrettyStackTrace.h"
@@ -42,8 +41,6 @@ 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");
@@ -54,15 +51,6 @@ STATISTIC(NumTimesRetriedWithoutInlining,
"The # of times we re-evaluated a call without inlining");
//===----------------------------------------------------------------------===//
-// Utility functions.
-//===----------------------------------------------------------------------===//
-
-static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) {
- IdentifierInfo* II = &Ctx.Idents.get(name);
- return Ctx.Selectors.getSelector(0, &II);
-}
-
-//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -163,7 +151,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
// analyzing an "open" program.
const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
if (SFC->getParent() == 0) {
- loc::MemRegionVal L(getCXXThisRegion(MD, SFC));
+ loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
if (const Loc *LV = dyn_cast<Loc>(&V)) {
state = state->assume(*LV, true);
@@ -196,7 +184,7 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *Call) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
Explicits, Regions, Call);
}
@@ -231,6 +219,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred);
return;
}
+ currentBuilderContext = 0;
}
static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
@@ -251,7 +240,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return true;
// Run before processing a call.
- if (isa<CallExpr>(S.getStmt()))
+ if (CallEvent::mayBeInlined(S.getStmt()))
return true;
// Is this an expression that is consumed by another expression? If so,
@@ -260,62 +249,47 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
}
-void ExprEngine::ProcessStmt(const CFGStmt S,
- ExplodedNode *Pred) {
- // Reclaim any unnecessary nodes in the ExplodedGraph.
- G.reclaimRecentlyAllocatedNodes();
-
- currentStmt = S.getStmt();
- PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
- currentStmt->getLocStart(),
- "Error evaluating statement");
-
- EntryNode = Pred;
-
- ProgramStateRef EntryState = EntryNode->getState();
- CleanedState = EntryState;
-
- // Create the cleaned state.
- const LocationContext *LC = EntryNode->getLocationContext();
- SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
-
- if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) {
- NumRemoveDeadBindings++;
- getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
-
- const StackFrameContext *SFC = LC->getCurrentStackFrame();
-
- // Create a state in which dead bindings are removed from the environment
- // 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++;
- }
+void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt,
+ const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K) {
+ assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
+ ReferenceStmt == 0) && "PreStmt is not generally supported by "
+ "the SymbolReaper yet");
+ NumRemoveDeadBindings++;
+ CleanedState = Pred->getState();
+ SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
+
+ getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
+
+ // Create a state in which dead bindings are removed from the environment
+ // and the store. TODO: The function should just return new env and store,
+ // not a new state.
+ const StackFrameContext *SFC = LC->getCurrentStackFrame();
+ CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
// 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.
- StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
- Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag);
+ StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext);
+ Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K);
} else {
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
- getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode,
- SymReaper, currentStmt, *this);
+ getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
+ DiagnosticStmt, *this, K);
// 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);
+ StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
ProgramStateRef CheckerState = (*I)->getState();
@@ -324,10 +298,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
- assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
- assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
+ assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
@@ -335,13 +309,35 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
- Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag,
- ProgramPoint::PostPurgeDeadSymbolsKind);
+ Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false,
+ &cleanupTag, K);
}
}
+}
+
+void ExprEngine::ProcessStmt(const CFGStmt S,
+ ExplodedNode *Pred) {
+ // Reclaim any unnecessary nodes in the ExplodedGraph.
+ G.reclaimRecentlyAllocatedNodes();
+
+ currentStmt = S.getStmt();
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ currentStmt->getLocStart(),
+ "Error evaluating statement");
+ // Remove dead bindings and symbols.
+ EntryNode = Pred;
+ ExplodedNodeSet CleanedStates;
+ if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
+ removeDead(EntryNode, CleanedStates, currentStmt,
+ Pred->getLocationContext(), currentStmt);
+ } else
+ CleanedStates.Add(EntryNode);
+
+ // Visit the statement.
ExplodedNodeSet Dst;
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ for (ExplodedNodeSet::iterator I = CleanedStates.begin(),
+ E = CleanedStates.end(); I != E; ++I) {
ExplodedNodeSet DstI;
// Visit the statement.
Visit(currentStmt, *I, DstI);
@@ -360,49 +356,49 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
void ExprEngine::ProcessInitializer(const CFGInitializer Init,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
+ NodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+
+ ProgramStateRef State = Pred->getState();
- // We don't set EntryNode and currentStmt. And we don't clean up state.
const CXXCtorInitializer *BMI = Init.getInitializer();
+
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ BMI->getSourceLocation(),
+ "Error evaluating initializer");
+
+ // We don't set EntryNode and currentStmt. And we don't clean up state.
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 = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame));
+ // Evaluate the initializer, if necessary
if (BMI->isAnyMemberInitializer()) {
- // Evaluate the initializer.
-
- StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
- ProgramStateRef state = Pred->getState();
-
- const FieldDecl *FD = BMI->getAnyMember();
-
- SVal FieldLoc = state->getLValue(FD, thisVal);
- SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext());
- state = state->bindLoc(FieldLoc, InitVal);
+ // Constructors build the object directly in the field,
+ // but non-objects must be copied in from the initializer.
+ if (!isa<CXXConstructExpr>(BMI->getInit())) {
+ SVal FieldLoc;
+ if (BMI->isIndirectMemberInitializer())
+ FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal);
+ else
+ FieldLoc = State->getLValue(BMI->getMember(), thisVal);
- // 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);
+ SVal InitVal = State->getSVal(BMI->getInit(), stackFrame);
+ State = State->bindLoc(FieldLoc, InitVal);
+ }
} else {
- assert(BMI->isBaseInitializer());
-
- // 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);
+ assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
+ // We already did all the work when visiting the CXXConstructExpr.
}
+ // Construct a PostInitializer node whether the state changed or not,
+ // so that the diagnostics don't get confused.
+ 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, State, Pred);
+
// Enqueue the new nodes onto the work list.
Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx);
}
@@ -442,27 +438,51 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
if (const ReferenceType *refType = varType->getAs<ReferenceType>())
varType = refType->getPointeeType();
- const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl();
- assert(recordDecl && "get CXXRecordDecl fail");
- const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor();
-
Loc dest = state->getLValue(varDecl, Pred->getLocationContext());
- VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(),
+ VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(),
Dtor.getTriggerStmt(), Pred, Dst);
}
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
+
+ // Create the base object region.
+ QualType BaseTy = D.getBaseSpecifier()->getType();
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+
+ VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(),
+ CurDtor->getBody(), Pred, Dst);
+}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const FieldDecl *Member = D.getFieldDecl();
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getCurrentStackFrame());
+ SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal)));
+
+ VisitCXXDestructor(Member->getType(),
+ cast<loc::MemRegionVal>(FieldVal).getRegion(),
+ CurDtor->getBody(), Pred, Dst);
+}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {}
-void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
+void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet &DstTop) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
S->getLocStart(),
@@ -490,7 +510,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
case Stmt::CXXUnresolvedConstructExprClass:
- case Stmt::CXXScalarValueInitExprClass:
case Stmt::DependentScopeDeclRefExprClass:
case Stmt::UnaryTypeTraitExprClass:
case Stmt::BinaryTypeTraitExprClass:
@@ -514,7 +533,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// 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:
break;
@@ -544,6 +562,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
+ case Stmt::ObjCSubscriptRefExprClass:
+ case Stmt::ObjCPropertyRefExprClass:
+ llvm_unreachable("These are handled by PseudoObjectExpr");
+
case Stmt::GNUNullExprClass: {
// GNU __null is a pointer-width integer, not an actual pointer.
ProgramStateRef state = Pred->getState();
@@ -559,23 +581,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- // FIXME.
- case Stmt::ObjCSubscriptRefExprClass:
- break;
-
- case Stmt::ObjCPropertyRefExprClass:
- // Implicitly handled by Environment::getSVal().
- break;
-
- case Stmt::ImplicitValueInitExprClass: {
- ProgramStateRef state = Pred->getState();
- QualType ty = cast<ImplicitValueInitExpr>(S)->getType();
- SVal val = svalBuilder.makeZeroVal(ty);
- Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(),
- val));
- break;
- }
-
case Stmt::ExprWithCleanupsClass:
// Handled due to fully linearised CFG.
break;
@@ -592,7 +597,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ObjCIsaExprClass:
case Stmt::ObjCProtocolExprClass:
case Stmt::ObjCSelectorExprClass:
- case Expr::ObjCNumericLiteralClass:
case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass:
@@ -613,6 +617,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::AddrLabelExprClass:
case Stmt::IntegerLiteralClass:
case Stmt::CharacterLiteralClass:
+ case Stmt::ImplicitValueInitExprClass:
+ case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:
case Stmt::FloatingLiteralClass:
@@ -620,6 +626,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;
@@ -630,22 +637,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
case Expr::ObjCArrayLiteralClass:
- case Expr::ObjCDictionaryLiteralClass: {
+ case Expr::ObjCDictionaryLiteralClass:
+ // FIXME: explicitly model with a region and the actual contents
+ // of the container. For now, conjure a symbol.
+ case Expr::ObjCBoxedExprClass: {
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);
+ const Expr *Ex = cast<Expr>(S);
+ QualType resultType = Ex->getType();
+
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,
@@ -671,6 +680,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::MSAsmStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
case Stmt::BlockExprClass:
Bldr.takeNodes(Pred);
VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst);
@@ -727,12 +742,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
case Stmt::CXXTemporaryObjectExprClass:
- case Stmt::CXXConstructExprClass: {
- const CXXConstructExpr *C = cast<CXXConstructExpr>(S);
- // For block-level CXXConstructExpr, we don't have a destination region.
- // Let VisitCXXConstructExpr() create one.
+ case Stmt::CXXConstructExprClass: {
Bldr.takeNodes(Pred);
- VisitCXXConstructExpr(C, 0, Pred, Dst);
+ VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
}
@@ -868,33 +880,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- case Stmt::ObjCMessageExprClass: {
+ 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);
-
+ VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
case Stmt::ObjCAtThrowStmtClass: {
// FIXME: This is not complete. We basically treat @throw as
@@ -982,6 +972,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame();
assert(CalleeSF && CallerSF);
ExplodedNode *BeforeProcessingCall = 0;
+ const Stmt *CE = CalleeSF->getCallSite();
// Find the first node before we started processing the call expression.
while (N) {
@@ -993,11 +984,15 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
if (L.getLocationContext()->getCurrentStackFrame() != CallerSF)
continue;
// We reached the caller. Find the node right before we started
- // processing the CallExpr.
- if (isa<PostPurgeDeadSymbols>(L))
+ // processing the call.
+ if (L.isPurgeKind())
+ continue;
+ if (isa<PreImplicitCall>(&L))
+ continue;
+ if (isa<CallEnter>(&L))
continue;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L))
- if (SP->getStmt() == CalleeSF->getCallSite())
+ if (SP->getStmt() == CE)
continue;
break;
}
@@ -1008,7 +1003,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
// TODO: Clean up the unneeded nodes.
// Build an Epsilon node from which we will restart the analyzes.
- const Stmt *CE = CalleeSF->getCallSite();
+ // Note that CE is permitted to be NULL!
ProgramPoint NewNodeLoc =
EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
// Add the special flag to GDM to signal retrying with no inlining.
@@ -1073,63 +1068,6 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
// Branch processing.
//===----------------------------------------------------------------------===//
-ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state,
- const Stmt *Terminator,
- const LocationContext *LCtx,
- bool branchTaken) {
-
- switch (Terminator->getStmtClass()) {
- default:
- return state;
-
- case Stmt::BinaryOperatorClass: { // '&&' and '||'
-
- const BinaryOperator* B = cast<BinaryOperator>(Terminator);
- BinaryOperator::Opcode Op = B->getOpcode();
-
- assert (Op == BO_LAnd || Op == BO_LOr);
-
- // For &&, if we take the true branch, then the value of the whole
- // expression is that of the RHS expression.
- //
- // For ||, if we take the false branch, then the value of the whole
- // expression is that of the RHS expression.
-
- const Expr *Ex = (Op == BO_LAnd && branchTaken) ||
- (Op == BO_LOr && !branchTaken)
- ? B->getRHS() : B->getLHS();
-
- return state->BindExpr(B, LCtx, UndefinedVal(Ex));
- }
-
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass: { // ?:
- const AbstractConditionalOperator* C
- = cast<AbstractConditionalOperator>(Terminator);
-
- // For ?, if branchTaken == true then the value is either the LHS or
- // the condition itself. (GNU extension).
-
- const Expr *Ex;
-
- if (branchTaken)
- Ex = C->getTrueExpr();
- else
- Ex = C->getFalseExpr();
-
- return state->BindExpr(C, LCtx, UndefinedVal(Ex));
- }
-
- case Stmt::ChooseExprClass: { // ?:
-
- const ChooseExpr *C = cast<ChooseExpr>(Terminator);
-
- const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS();
- return state->BindExpr(C, LCtx, UndefinedVal(Ex));
- }
- }
-}
-
/// RecoverCastedSymbol - A helper function for ProcessBranch that is used
/// to try to recover some path-sensitivity for casts of symbolic
/// integers that promote their values (which are currently not tracked well).
@@ -1172,6 +1110,45 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
return state->getSVal(Ex, LCtx);
}
+static const Stmt *ResolveCondition(const Stmt *Condition,
+ const CFGBlock *B) {
+ if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp())
+ return Condition;
+
+ // For logical operations, we still have the case where some branches
+ // use the traditional "merge" approach and others sink the branch
+ // directly into the basic blocks representing the logical operation.
+ // We need to distinguish between those two cases here.
+
+ // The invariants are still shifting, but it is possible that the
+ // last element in a CFGBlock is not a CFGStmt. Look for the last
+ // CFGStmt as the value of the condition.
+ CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
+ for (; I != E; ++I) {
+ CFGElement Elem = *I;
+ CFGStmt *CS = dyn_cast<CFGStmt>(&Elem);
+ if (!CS)
+ continue;
+ if (CS->getStmt() != Condition)
+ break;
+ return Condition;
+ }
+
+ assert(I != E);
+
+ while (Condition) {
+ BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp())
+ return Condition;
+ Condition = BO->getRHS()->IgnoreParens();
+ }
+ llvm_unreachable("could not resolve condition");
+}
+
void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
NodeBuilderContext& BldCtx,
ExplodedNode *Pred,
@@ -1188,6 +1165,12 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
return;
}
+
+ // Resolve the condition in the precense of nested '||' and '&&'.
+ if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ Condition = ResolveCondition(Condition, BldCtx.getBlock());
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Condition->getLocStart(),
"Error evaluating branch");
@@ -1230,14 +1213,10 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
}
}
- const LocationContext *LCtx = PredI->getLocationContext();
-
// If the condition is still unknown, give up.
if (X.isUnknownOrUndef()) {
- builder.generateNode(MarkBranch(PrevState, Term, LCtx, true),
- true, PredI);
- builder.generateNode(MarkBranch(PrevState, Term, LCtx, false),
- false, PredI);
+ builder.generateNode(PrevState, true, PredI);
+ builder.generateNode(PrevState, false, PredI);
continue;
}
@@ -1246,8 +1225,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
// Process the true branch.
if (builder.isFeasible(true)) {
if (ProgramStateRef state = PrevState->assume(V, true))
- builder.generateNode(MarkBranch(state, Term, LCtx, true),
- true, PredI);
+ builder.generateNode(state, true, PredI);
else
builder.markInfeasible(true);
}
@@ -1255,8 +1233,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
// Process the false branch.
if (builder.isFeasible(false)) {
if (ProgramStateRef state = PrevState->assume(V, false))
- builder.generateNode(MarkBranch(state, Term, LCtx, false),
- false, PredI);
+ builder.generateNode(state, false, PredI);
else
builder.markInfeasible(false);
}
@@ -1433,7 +1410,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
const LocationContext *LCtx = Pred->getLocationContext();
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
- assert(Ex->isLValue());
+ assert(Ex->isGLValue());
SVal V = state->getLValue(VD, Pred->getLocationContext());
// For references, the 'lvalue' is the pointer address stored in the
@@ -1450,7 +1427,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) {
- assert(!Ex->isLValue());
+ assert(!Ex->isGLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
return;
@@ -1493,7 +1470,7 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
SVal V = state->getLValue(A->getType(),
state->getSVal(Idx, LCtx),
state->getSVal(Base, LCtx));
- assert(A->isLValue());
+ assert(A->isGLValue());
Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V),
false, 0, ProgramPoint::PostLValueKind);
}
@@ -1506,14 +1483,26 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext);
ExplodedNodeSet Dst;
Decl *member = M->getMemberDecl();
+
if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
- assert(M->isLValue());
+ assert(M->isGLValue());
Bldr.takeNodes(Pred);
VisitCommonDeclRefExpr(M, VD, Pred, Dst);
Bldr.addNodes(Dst);
return;
}
-
+
+ // Handle C++ method calls.
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(member)) {
+ Bldr.takeNodes(Pred);
+ SVal MDVal = svalBuilder.getFunctionPointer(MD);
+ ProgramStateRef state =
+ Pred->getState()->BindExpr(M, Pred->getLocationContext(), MDVal);
+ Bldr.generateNode(M, Pred, state);
+ return;
+ }
+
+
FieldDecl *field = dyn_cast<FieldDecl>(member);
if (!field) // FIXME: skipping member expressions for non-fields
return;
@@ -1538,10 +1527,17 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// For all other cases, compute an lvalue.
SVal L = state->getLValue(field, baseExprVal);
- if (M->isLValue())
+ if (M->isGLValue()) {
+ if (field->getType()->isReferenceType()) {
+ if (const MemRegion *R = L.getAsRegion())
+ L = state->getSVal(R);
+ else
+ L = UnknownVal();
+ }
+
Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0,
ProgramPoint::PostLValueKind);
- else {
+ } else {
Bldr.takeNodes(Pred);
evalLoad(Dst, M, M, Pred, state, L);
Bldr.addNodes(Dst);
@@ -1591,9 +1587,9 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
/// evalStore - Handle the semantics of a store via an assignment.
/// @param Dst The node set to store generated state nodes
-/// @param AssignE The assignment expression if the store happens in an
+/// @param AssignE The assignment expression if the store happens in an
/// assignment.
-/// @param LocatioinE The location expression that is stored to.
+/// @param LocationE The location expression that is stored to.
/// @param state The current simulation state
/// @param location The location to store the value
/// @param Val The value to be stored
@@ -1606,10 +1602,6 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
// ProgramPoint if it is non-NULL, and LocationE otherwise.
const Expr *StoreE = AssignE ? AssignE : LocationE;
- if (isa<loc::ObjCPropRef>(location)) {
- assert(false);
- }
-
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false);
@@ -1634,7 +1626,6 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
QualType LoadTy)
{
assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
- 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
@@ -1814,6 +1805,12 @@ void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred,
Bldr.generateNode(A, Pred, state);
}
+void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
+ Bldr.generateNode(A, Pred, Pred->getState());
+}
+
//===----------------------------------------------------------------------===//
// Visualization.
//===----------------------------------------------------------------------===//
@@ -1852,6 +1849,16 @@ struct DOTGraphTraits<ExplodedNode*> :
return "";
}
+ static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) {
+ if (SLoc.isFileID()) {
+ Out << "\\lline="
+ << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
+ << " col="
+ << GraphPrintSourceManager->getExpansionColumnNumber(SLoc)
+ << "\\l";
+ }
+ }
+
static std::string getNodeLabel(const ExplodedNode *N, void*){
std::string sbuf;
@@ -1861,10 +1868,17 @@ struct DOTGraphTraits<ExplodedNode*> :
ProgramPoint Loc = N->getLocation();
switch (Loc.getKind()) {
- case ProgramPoint::BlockEntranceKind:
+ case ProgramPoint::BlockEntranceKind: {
Out << "Block Entrance: B"
<< cast<BlockEntrance>(Loc).getBlock()->getBlockID();
+ if (const NamedDecl *ND =
+ dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) {
+ Out << " (";
+ ND->printName(Out);
+ Out << ")";
+ }
break;
+ }
case ProgramPoint::BlockExitKind:
assert (false);
@@ -1874,30 +1888,54 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "CallEnter";
break;
- case ProgramPoint::CallExitKind:
- Out << "CallExit";
+ case ProgramPoint::CallExitBeginKind:
+ Out << "CallExitBegin";
+ break;
+
+ case ProgramPoint::CallExitEndKind:
+ Out << "CallExitEnd";
+ break;
+
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ Out << "PostStmtPurgeDeadSymbols";
+ break;
+
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ Out << "PreStmtPurgeDeadSymbols";
break;
case ProgramPoint::EpsilonKind:
Out << "Epsilon Point";
break;
+ case ProgramPoint::PreImplicitCallKind: {
+ ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc);
+ Out << "PreCall: ";
+
+ // FIXME: Get proper printing options.
+ PC->getDecl()->print(Out, LangOptions());
+ printLocation(Out, PC->getLocation());
+ break;
+ }
+
+ case ProgramPoint::PostImplicitCallKind: {
+ ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc);
+ Out << "PostCall: ";
+
+ // FIXME: Get proper printing options.
+ PC->getDecl()->print(Out, LangOptions());
+ printLocation(Out, PC->getLocation());
+ break;
+ }
+
default: {
if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) {
const Stmt *S = L->getStmt();
- SourceLocation SLoc = S->getLocStart();
Out << S->getStmtClassName() << ' ' << (void*) S << ' ';
LangOptions LO; // FIXME.
S->printPretty(Out, 0, PrintingPolicy(LO));
-
- if (SLoc.isFileID()) {
- Out << "\\lline="
- << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
- << " col="
- << GraphPrintSourceManager->getExpansionColumnNumber(SLoc)
- << "\\l";
- }
+ printLocation(Out, S->getLocStart());
if (isa<PreStmt>(Loc))
Out << "\\lPreStmt\\l;";
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 93e598a..46cba81 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -50,7 +50,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
}
// 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;
+ SVal ExprVal = B->isGLValue() ? LeftV : RightV;
evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal),
LeftV, RightV);
continue;
@@ -58,6 +58,26 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
if (!B->isAssignmentOp()) {
StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext);
+
+ if (B->isAdditiveOp()) {
+ // If one of the operands is a location, conjure a symbol for the other
+ // one (offset) if it's unknown so that memory arithmetic always
+ // results in an ElementRegion.
+ // TODO: This can be removed after we enable history tracking with
+ // SymSymExpr.
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ if (isa<Loc>(LeftV) &&
+ RHS->getType()->isIntegerType() && RightV.isUnknown()) {
+ RightV = svalBuilder.getConjuredSymbolVal(RHS, LCtx,
+ RHS->getType(), Count);
+ }
+ if (isa<Loc>(RightV) &&
+ LHS->getType()->isIntegerType() && LeftV.isUnknown()) {
+ LeftV = svalBuilder.getConjuredSymbolVal(LHS, LCtx,
+ LHS->getType(), Count);
+ }
+ }
+
// Process non-assignments except commas or short-circuited
// logical expressions (LAnd and LOr).
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
@@ -145,7 +165,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
// In C++, assignment and compound assignment operators return an
// lvalue.
- if (B->isLValue())
+ if (B->isGLValue())
state = state->BindExpr(B, LCtx, location);
else
state = state->BindExpr(B, LCtx, Result);
@@ -162,14 +182,35 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
CanQualType T = getContext().getCanonicalType(BE->getType());
+
+ // Get the value of the block itself.
SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
Pred->getLocationContext());
+ ProgramStateRef State = Pred->getState();
+
+ // If we created a new MemRegion for the block, we should explicitly bind
+ // the captured variables.
+ if (const BlockDataRegion *BDR =
+ dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
+
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+
+ for (; I != E; ++I) {
+ const MemRegion *capturedR = I.getCapturedRegion();
+ const MemRegion *originalR = I.getOriginalRegion();
+ if (capturedR != originalR) {
+ SVal originalV = State->getSVal(loc::MemRegionVal(originalR));
+ State = State->bindLoc(loc::MemRegionVal(capturedR), originalV);
+ }
+ }
+ }
+
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
Bldr.generateNode(BE, Pred,
- Pred->getState()->BindExpr(BE, Pred->getLocationContext(),
- V),
+ State->BindExpr(BE, Pred->getLocationContext(), V),
false, 0,
ProgramPoint::PostLValueKind);
@@ -238,7 +279,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
- case CK_LValueBitCast:
case CK_IntegralCast:
case CK_NullToPointer:
case CK_IntegralToPointer:
@@ -278,7 +318,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
SVal val = state->getSVal(Ex, LCtx);
- val = getStoreManager().evalDerivedToBase(val, T);
+ val = getStoreManager().evalDerivedToBase(val, CastE);
state = state->BindExpr(CastE, LCtx, val);
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -291,7 +331,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
// Compute the type of the result.
QualType resultType = CastE->getType();
- if (CastE->isLValue())
+ if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
bool Failed = false;
@@ -337,10 +377,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_UserDefinedConversion:
case CK_ConstructorConversion:
case CK_VectorSplat:
- case CK_MemberPointerToBoolean: {
+ case CK_MemberPointerToBoolean:
+ case CK_LValueBitCast: {
// Recover some path-sensitivty by conjuring a new value.
QualType resultType = CastE->getType();
- if (CastE->isLValue())
+ if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
const LocationContext *LCtx = Pred->getLocationContext();
SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx,
@@ -366,8 +407,16 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
SVal ILV = state->getSVal(ILE, Pred->getLocationContext());
const LocationContext *LC = Pred->getLocationContext();
state = state->bindCompoundLiteral(CL, LC, ILV);
-
- if (CL->isLValue())
+
+ // Compound literal expressions are a GNU extension in C++.
+ // Unlike in C, where CLs are lvalues, in C++ CLs are prvalues,
+ // and like temporary objects created by the functional notation T()
+ // CLs are destroyed at the end of the containing full-expression.
+ // HOWEVER, an rvalue of array type is not something the analyzer can
+ // reason about, since we expect all regions to be wrapped in Locs.
+ // So we treat array CLs as lvalues as well, knowing that they will decay
+ // to pointers as soon as they are used.
+ if (CL->isGLValue() || CL->getType()->isArrayType())
B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC)));
else
B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
@@ -404,31 +453,39 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
const LocationContext *LC = N->getLocationContext();
if (const Expr *InitEx = VD->getInit()) {
- 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.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));
- }
-
- // Recover some path-sensitivity if a scalar value evaluated to
- // UnknownVal.
- if (InitVal.isUnknown()) {
- QualType Ty = InitEx->getType();
- if (InitEx->isLValue()) {
- Ty = getContext().getPointerType(Ty);
- }
-
- InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty,
- currentBuilderContext->getCurrentBlockCount());
+ SVal InitVal = state->getSVal(InitEx, LC);
+
+ if (InitVal == state->getLValue(VD, LC) ||
+ (VD->getType()->isArrayType() &&
+ isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) {
+ // We constructed the object directly in the variable.
+ // No need to bind anything.
+ B.generateNode(DS, N, state);
+ } else {
+ // We bound the temp obj region to the CXXConstructExpr. Now recover
+ // the lazy compound value when the variable is not a reference.
+ 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));
+ }
+
+ // Recover some path-sensitivity if a scalar value evaluated to
+ // UnknownVal.
+ if (InitVal.isUnknown()) {
+ QualType Ty = InitEx->getType();
+ if (InitEx->isGLValue()) {
+ Ty = getContext().getPointerType(Ty);
+ }
+
+ InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty,
+ currentBuilderContext->getCurrentBlockCount());
+ }
+ B.takeNodes(N);
+ ExplodedNodeSet Dst2;
+ evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
+ B.addNodes(Dst2);
}
- B.takeNodes(N);
- ExplodedNodeSet Dst2;
- evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
- B.addNodes(Dst2);
}
else {
B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC)));
@@ -443,48 +500,44 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
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, LCtx);
-
- // Handle undefined values.
- if (X.isUndef()) {
- Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
- return;
- }
-
- DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
-
- // We took the RHS. Because the value of the '&&' or '||' expression must
- // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
- // or 1. Alternatively, we could take a lazy approach, and calculate this
- // value later when necessary. We don't have the machinery in place for
- // this right now, and since most logical expressions are used for branches,
- // the payoff is not likely to be large. Instead, we do eager evaluation.
- if (ProgramStateRef newState = state->assume(XD, true))
- Bldr.generateNode(B, Pred,
- newState->BindExpr(B, LCtx,
- svalBuilder.makeIntVal(1U, B->getType())));
-
- if (ProgramStateRef newState = state->assume(XD, false))
- Bldr.generateNode(B, Pred,
- newState->BindExpr(B, LCtx,
- svalBuilder.makeIntVal(0U, B->getType())));
+
+ ExplodedNode *N = Pred;
+ while (!isa<BlockEntrance>(N->getLocation())) {
+ ProgramPoint P = N->getLocation();
+ assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P));
+ (void) P;
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ }
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ BlockEdge BE = cast<BlockEdge>(N->getLocation());
+ SVal X;
+
+ // Determine the value of the expression by introspecting how we
+ // got this location in the CFG. This requires looking at the previous
+ // block we were in and what kind of control-flow transfer was involved.
+ const CFGBlock *SrcBlock = BE.getSrc();
+ // The only terminator (if there is one) that makes sense is a logical op.
+ CFGTerminator T = SrcBlock->getTerminator();
+ if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) {
+ (void) Term;
+ assert(Term->isLogicalOp());
+ assert(SrcBlock->succ_size() == 2);
+ // Did we take the true or false branch?
+ unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0;
+ X = svalBuilder.makeIntVal(constant, B->getType());
}
else {
- // We took the LHS expression. Depending on whether we are '&&' or
- // '||' we know what the value of the expression is via properties of
- // the short-circuiting.
- X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
- B->getType());
- Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X));
+ // If there is no terminator, by construction the last statement
+ // in SrcBlock is the value of the enclosing expression.
+ assert(!SrcBlock->empty());
+ CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin());
+ const Stmt *S = Elem.getStmt();
+ X = N->getState()->getSVal(S, Pred->getLocationContext());
}
+
+ Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
}
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
@@ -519,16 +572,17 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
svalBuilder.makeCompoundVal(T, vals)));
return;
}
-
- if (Loc::isLocType(T) || T->isIntegerType()) {
- assert(IE->getNumInits() == 1);
- const Expr *initEx = IE->getInit(0);
- B.generateNode(IE, Pred, state->BindExpr(IE, LCtx,
- state->getSVal(initEx, LCtx)));
- return;
- }
-
- llvm_unreachable("unprocessed InitListExpr type");
+
+ // Handle scalars: int{5} and int{}.
+ assert(NumInitElements <= 1);
+
+ SVal V;
+ if (NumInitElements == 0)
+ V = getSValBuilder().makeZeroVal(T);
+ else
+ V = state->getSVal(IE->getInit(0), LCtx);
+
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
}
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
@@ -537,17 +591,41 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
StmtNodeBuilder B(Pred, Dst, *currentBuilderContext);
-
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, LCtx);
-
- // Make sure that we invalidate the previous binding.
- B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true));
+ const CFGBlock *SrcBlock = 0;
+
+ for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) {
+ ProgramPoint PP = N->getLocation();
+ if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) {
+ assert(N->pred_size() == 1);
+ continue;
+ }
+ SrcBlock = cast<BlockEdge>(&PP)->getSrc();
+ break;
+ }
+
+ // Find the last expression in the predecessor block. That is the
+ // expression that is used for the value of the ternary expression.
+ bool hasValue = false;
+ SVal V;
+
+ for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(),
+ E = SrcBlock->rend(); I != E; ++I) {
+ CFGElement CE = *I;
+ if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) {
+ const Expr *ValEx = cast<Expr>(CS->getStmt());
+ hasValue = true;
+ V = state->getSVal(ValEx, LCtx);
+ break;
+ }
+ }
+
+ assert(hasValue);
+ (void) hasValue;
+
+ // Generate a new node with the binding from the appropriate path.
+ B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true));
}
void ExprEngine::
@@ -648,7 +726,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
}
case UO_Plus:
- assert(!U->isLValue());
+ assert(!U->isGLValue());
// FALL-THROUGH.
case UO_Deref:
case UO_AddrOf:
@@ -671,7 +749,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
case UO_LNot:
case UO_Minus:
case UO_Not: {
- assert (!U->isLValue());
+ assert (!U->isGLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
@@ -796,7 +874,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
// 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())
+ if (U->isGLValue())
state = state->BindExpr(U, LCtx, loc);
else
state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index a14a491..44a860f 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -14,26 +14,14 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/Basic/PrettyStackTrace.h"
using namespace clang;
using namespace ento;
-const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D,
- const StackFrameContext *SFC) {
- const Type *T = D->getTypeForDecl();
- QualType PT = getContext().getPointerType(QualType(T, 0));
- return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC);
-}
-
-const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl,
- const StackFrameContext *frameCtx) {
- return svalBuilder.getRegionManager().
- getCXXThisRegion(decl->getThisType(getContext()), frameCtx);
-}
-
void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
@@ -53,208 +41,220 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
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,
- const MemRegion *Dest,
+void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ const MemRegion *Target = 0;
+
+ switch (CE->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete: {
+ // See if we're constructing an existing region by looking at the next
+ // element in the CFG.
+ const CFGBlock *B = currentBuilderContext->getBlock();
+ if (currentStmtIdx + 1 < B->size()) {
+ CFGElement Next = (*B)[currentStmtIdx+1];
+
+ // Is this a constructor for a local variable?
+ if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) {
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) {
+ if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) {
+ if (Var->getInit()->IgnoreImplicit() == CE) {
+ QualType Ty = Var->getType();
+ if (const ArrayType *AT = getContext().getAsArrayType(Ty)) {
+ // FIXME: Handle arrays, which run the same constructor for
+ // every element. This workaround will just run the first
+ // constructor (which should still invalidate the entire array).
+ SVal Base = State->getLValue(Var, LCtx);
+ Target = State->getLValue(AT->getElementType(),
+ getSValBuilder().makeZeroArrayIndex(),
+ Base).getAsRegion();
+ } else {
+ Target = State->getLValue(Var, LCtx).getAsRegion();
+ }
+ }
+ }
+ }
+ }
+
+ // Is this a constructor for a member?
+ if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) {
+ const CXXCtorInitializer *Init = InitElem->getInitializer();
+ assert(Init->isAnyMemberInitializer());
+
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ if (Init->isIndirectMemberInitializer()) {
+ SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal);
+ Target = Field.getAsRegion();
+ } else {
+ SVal Field = State->getLValue(Init->getMember(), ThisVal);
+ Target = Field.getAsRegion();
+ }
+ }
-#if 0
- const CXXConstructorDecl *CD = E->getConstructor();
- assert(CD);
-#endif
-
-#if 0
- if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
- // FIXME: invalidate the object.
- return;
-#endif
-
-#if 0
- // Is the constructor elidable?
- if (E->isElidable()) {
- destNodes.Add(Pred);
- return;
- }
-#endif
-
- // Perform the previsit of the constructor.
- 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
- // function calls. We can re-evaluate this in the future.
-
-#if 0
- // Inlining currently isn't fully implemented.
-
- if (AMgr.shouldInlineCall()) {
- if (!Dest)
- Dest =
- svalBuilder.getRegionManager().getCXXTempObjectRegion(E,
- Pred->getLocationContext());
-
- // The callee stack frame context used to create the 'this'
- // parameter region.
- const StackFrameContext *SFC =
- AMgr.getStackFrame(CD, Pred->getLocationContext(),
- E, currentBuilderContext->getBlock(),
- currentStmtIdx);
-
- // Create the 'this' region.
- const CXXThisRegion *ThisR =
- getCXXThisRegion(E->getConstructor()->getParent(), SFC);
-
- CallEnter Loc(E, SFC, Pred->getLocationContext());
-
- 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));
- Bldr.generateNode(Loc, *NI, state);
+ // FIXME: This will eventually need to handle new-expressions as well.
}
+
+ // If we couldn't find an existing region to construct into, we'll just
+ // generate a symbolic region, which is fine.
+
+ break;
}
-#endif
-
- // Default semantics: invalidate all regions passed as arguments.
- ExplodedNodeSet destCall;
- {
- 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), LC);
- Bldr.generateNode(E, Pred, state);
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ case CXXConstructExpr::CK_Delegating: {
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getCurrentStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+ Target = ThisVal.getAsRegion();
+ } else {
+ // Cast to the base type.
+ QualType BaseTy = CE->getType();
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+ Target = BaseVal.getAsRegion();
}
+ break;
+ }
}
- // Do the post visit.
- getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this);
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXConstructorCall> Call =
+ CEMgr.getCXXConstructorCall(CE, Target, State, LCtx);
+
+ ExplodedNodeSet DstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit,
+ *Call, *this);
+
+ ExplodedNodeSet DstInvalidated;
+ StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated,
+ *Call, *this);
+ getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
}
-void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
- const MemRegion *Dest,
- const Stmt *S,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
- if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall()))
- return;
+void ExprEngine::VisitCXXDestructor(QualType ObjectType,
+ const MemRegion *Dest,
+ const Stmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) {
+ ObjectType = AT->getElementType();
+ Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(),
+ loc::MemRegionVal(Dest)).getAsRegion();
+ }
- // Create the context for 'this' region.
- const StackFrameContext *SFC =
- AnalysisDeclContexts.getContext(DD)->
- getStackFrame(Pred->getLocationContext(), S,
- currentBuilderContext->getBlock(), currentStmtIdx);
+ const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
+ assert(RecordDecl && "Only CXXRecordDecls should have destructors");
+ const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
- const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC);
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDestructorCall> Call =
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx);
- CallEnter PP(S, SFC, Pred->getLocationContext());
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ Call->getSourceRange().getBegin(),
+ "Error evaluating destructor");
- ProgramStateRef state = Pred->getState();
- state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
- Bldr.generateNode(PP, Pred, state);
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
+ *Call, *this);
+
+ ExplodedNodeSet DstInvalidated;
+ StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currentBuilderContext);
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
+ *Call, *this);
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ // FIXME: Much of this should eventually migrate to CXXAllocatorCall.
+ // Also, we need to decide how allocators actually work -- they're not
+ // really part of the CXXNewExpr because they happen BEFORE the
+ // CXXConstructExpr subexpression. See PR12014 for some discussion.
StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
unsigned blockCount = currentBuilderContext->getCurrentBlockCount();
const LocationContext *LCtx = Pred->getLocationContext();
DefinedOrUnknownSVal symVal =
- 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 =
- getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount);
+ ProgramStateRef State = Pred->getState();
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXAllocatorCall> Call =
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+
+ // Invalidate placement args.
+ // FIXME: Once we figure out how we want allocators to work,
+ // we should be using the usual pre-/(default-)eval-/post-call checks here.
+ State = Call->invalidateRegions(blockCount);
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- ProgramStateRef state = Pred->getState();
- state = state->BindExpr(CNE, Pred->getLocationContext(),
+ const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ const ElementRegion *EleReg =
+ getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ State = State->BindExpr(CNE, Pred->getLocationContext(),
loc::MemRegionVal(EleReg));
- Bldr.generateNode(CNE, Pred, state);
+ 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) {
-
- ProgramStateRef state = (*I)->getState();
-
- // Accumulate list of regions that are invalidated.
- // FIXME: Eventually we should unify the logic for constructor
- // processing in one place.
- SmallVector<const MemRegion*, 10> regionsToInvalidate;
- for (CXXNewExpr::const_arg_iterator
- ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end();
- ai != ae; ++ai)
- {
- SVal val = state->getSVal(*ai, (*I)->getLocationContext());
- if (const MemRegion *region = val.getAsRegion())
- regionsToInvalidate.push_back(region);
- }
+ // FIXME: Once we have proper support for CXXConstructExprs inside
+ // CXXNewExpr, we need to make sure that the constructed object is not
+ // immediately invalidated here. (The placement call should happen before
+ // the constructor call anyway.)
+ FunctionDecl *FD = CNE->getOperatorNew();
+ if (FD && FD->isReservedGlobalPlacementOperator()) {
+ // Non-array placement new should always return the placement location.
+ SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
+ State = State->BindExpr(CNE, LCtx, PlacementLoc);
+ } else {
+ State = State->BindExpr(CNE, LCtx, symVal);
+ }
- 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, 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, 0);
-
- if (CNE->hasInitializer()) {
- 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
- // value from symbolic region.
- state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal());
- }
+ // If the type is not a record, we won't have a CXXConstructExpr as an
+ // initializer. Copy the value over.
+ if (const Expr *Init = CNE->getInitializer()) {
+ if (!isa<CXXConstructExpr>(Init)) {
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ (void)ObjTy;
+ assert(!ObjTy->isRecordType());
+ SVal Location = State->getSVal(CNE, LCtx);
+ if (isa<Loc>(Location))
+ State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx));
}
- state = state->BindExpr(CNE, (*I)->getLocationContext(),
- loc::MemRegionVal(EleReg));
- Bldr.generateNode(CNE, *I, state);
}
-#endif
+
+ Bldr.generateNode(CNE, Pred, State);
}
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index b9f4e15..8ee6723 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -11,16 +11,25 @@
//
//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "ExprEngine"
+
+#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/DeclCXX.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/Support/SaveAndRestore.h"
+#define CXX_INLINING_ENABLED 1
+
using namespace clang;
using namespace ento;
+STATISTIC(NumOfDynamicDispatchPathSplits,
+ "The # of times we split the path due to imprecise dynamic dispatch info");
+
void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// Get the entry block in the CFG of the callee.
const StackFrameContext *calleeCtx = CE.getCalleeContext();
@@ -37,11 +46,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
// 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);
+ ProgramStateRef state = Pred->getState();
// Construct a new node and add it to the worklist.
bool isNew;
@@ -51,71 +56,178 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
Engine.getWorkList()->enqueue(Node);
}
-static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) {
+// Find the last statement on the path to the exploded node and the
+// corresponding Block.
+static std::pair<const Stmt*,
+ const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
+ const Stmt *S = 0;
+ const StackFrameContext *SF =
+ Node->getLocation().getLocationContext()->getCurrentStackFrame();
+
+ // Back up through the ExplodedGraph until we reach a statement 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);
+ S = SP->getStmt();
+ break;
+ } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) {
+ S = CEE->getCalleeContext()->getCallSite();
+ if (S)
+ break;
+ // If we have an implicit call, we'll probably end up with a
+ // StmtPoint inside the callee, which is acceptable.
+ // (It's possible a function ONLY contains implicit calls -- such as an
+ // implicitly-generated destructor -- so we shouldn't just skip back to
+ // the CallEnter node and keep going.)
+ } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) {
+ // If we reached the CallEnter for this function, it has no statements.
+ if (CE->getCalleeContext() == SF)
+ break;
}
- break;
+
+ Node = *Node->pred_begin();
}
- return 0;
+
+ const CFGBlock *Blk = 0;
+ if (S) {
+ // Now, get the enclosing basic block.
+ while (Node && Node->pred_size() >=1 ) {
+ const ProgramPoint &PP = Node->getLocation();
+ if (isa<BlockEdge>(PP) &&
+ (PP.getLocationContext()->getCurrentStackFrame() == SF)) {
+ BlockEdge &EPP = cast<BlockEdge>(PP);
+ Blk = EPP.getDst();
+ break;
+ }
+ Node = *Node->pred_begin();
+ }
+ }
+
+ return std::pair<const Stmt*, const CFGBlock*>(S, Blk);
}
-void ExprEngine::processCallExit(ExplodedNode *Pred) {
- ProgramStateRef state = Pred->getState();
- const StackFrameContext *calleeCtx =
- Pred->getLocationContext()->getCurrentStackFrame();
- const LocationContext *callerCtx = calleeCtx->getParent();
- const Stmt *CE = calleeCtx->getCallSite();
+/// The call exit is simulated with a sequence of nodes, which occur between
+/// CallExitBegin and CallExitEnd. The following operations occur between the
+/// two program points:
+/// 1. CallExitBegin (triggers the start of call exit sequence)
+/// 2. Bind the return value
+/// 3. Run Remove dead bindings to clean up the dead symbols from the callee.
+/// 4. CallExitEnd (switch to the caller context)
+/// 5. PostStmt<CallExpr>
+void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
+ // Step 1 CEBNode was generated before the call.
+
+ const StackFrameContext *calleeCtx =
+ CEBNode->getLocationContext()->getCurrentStackFrame();
+
+ // The parent context might not be a stack frame, so make sure we
+ // look up the first enclosing stack frame.
+ const StackFrameContext *callerCtx =
+ calleeCtx->getParent()->getCurrentStackFrame();
+ const Stmt *CE = calleeCtx->getCallSite();
+ ProgramStateRef state = CEBNode->getState();
+ // Find the last statement in the function and the corresponding basic block.
+ const Stmt *LastSt = 0;
+ const CFGBlock *Blk = 0;
+ llvm::tie(LastSt, Blk) = getLastStmt(CEBNode);
+
+ // Step 2: generate node with bound return value: CEBNode -> BindedRetNode.
+
// If the callee returns an expression, bind its value to CallExpr.
- if (const ReturnStmt *RS = getReturnStmt(Pred)) {
- const LocationContext *LCtx = Pred->getLocationContext();
- SVal V = state->getSVal(RS, LCtx);
- state = state->BindExpr(CE, callerCtx, V);
+ if (CE) {
+ if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
+ const LocationContext *LCtx = CEBNode->getLocationContext();
+ SVal V = state->getSVal(RS, LCtx);
+ state = state->BindExpr(CE, callerCtx, V);
+ }
+
+ // Bind the constructed object value to CXXConstructExpr.
+ if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
+ loc::MemRegionVal This =
+ svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
+ SVal ThisV = state->getSVal(This);
+
+ // Always bind the region to the CXXConstructExpr.
+ state = state->BindExpr(CCE, callerCtx, ThisV);
+ }
}
-
- // Bind the constructed object value to CXXConstructExpr.
- if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
- const CXXThisRegion *ThisR =
- getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
-
- SVal ThisV = state->getSVal(ThisR);
- // Always bind the region to the CXXConstructExpr.
- state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV);
+
+ // Step 3: BindedRetNode -> CleanedNodes
+ // If we can find a statement and a block in the inlined function, run remove
+ // dead bindings before returning from the call. This is important to ensure
+ // that we report the issues such as leaks in the stack contexts in which
+ // they occurred.
+ ExplodedNodeSet CleanedNodes;
+ if (LastSt && Blk) {
+ static SimpleProgramPointTag retValBind("ExprEngine : Bind Return Value");
+ PostStmt Loc(LastSt, calleeCtx, &retValBind);
+ bool isNew;
+ ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
+ BindedRetNode->addPredecessor(CEBNode, G);
+ if (!isNew)
+ return;
+
+ NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
+ currentBuilderContext = &Ctx;
+ // Here, we call the Symbol Reaper with 0 statement and caller location
+ // context, telling it to clean up everything in the callee's context
+ // (and it's children). We use LastStmt as a diagnostic statement, which
+ // which the PreStmtPurge Dead point will be associated.
+ removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt,
+ ProgramPoint::PostStmtPurgeDeadSymbolsKind);
+ currentBuilderContext = 0;
+ } else {
+ CleanedNodes.Add(CEBNode);
}
-
- 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);
+
+ for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
+ E = CleanedNodes.end(); I != E; ++I) {
+
+ // Step 4: Generate the CallExit and leave the callee's context.
+ // CleanedNodes -> CEENode
+ CallExitEnd Loc(calleeCtx, callerCtx);
+ bool isNew;
+ ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState();
+ ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew);
+ CEENode->addPredecessor(*I, G);
+ if (!isNew)
+ return;
+
+ // Step 5: Perform the post-condition check of the CallExpr and enqueue the
+ // result onto the work list.
+ // CEENode -> Dst -> WorkList
+ NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
+ SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
+ &Ctx);
+ SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call,
+ *this, true);
+
+ ExplodedNodeSet Dst;
+ if (isa<ObjCMethodCall>(Call)) {
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall,
+ cast<ObjCMethodCall>(*Call),
+ *this, true);
+ } else if (CE) {
+ getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
+ *this, true);
+ } else {
+ Dst.insert(DstPostCall);
+ }
+
+ // Enqueue the next element in the block.
+ for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
+ PSI != PSE; ++PSI) {
+ Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
+ calleeCtx->getIndex()+1);
+ }
}
}
@@ -130,8 +242,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) {
}
// Determine if we should inline the call.
-bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) {
- AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
+bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
const CFG *CalleeCFG = CalleeADC->getCFG();
// It is possible that the CFG cannot be constructed.
@@ -143,258 +255,185 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) {
== AMgr.InlineMaxStackDepth)
return false;
- if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD))
+ if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D))
return false;
if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
return false;
- return true;
-}
+ // Do not inline variadic calls (for now).
+ if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
+ if (BD->isVariadic())
+ return false;
+ }
+ else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->isVariadic())
+ return false;
+ }
-// For now, skip inlining variadic functions.
-// We also don't inline blocks.
-static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) {
- if (!E->getAnalysisManager().shouldInlineCall())
+ // It is possible that the live variables analysis cannot be
+ // run. If so, bail out.
+ if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
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();
+ return true;
}
-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))
+/// The GDM component containing the dynamic dispatch bifurcation info. When
+/// the exact type of the receiver is not known, we want to explore both paths -
+/// one on which we do inline it and the other one on which we don't. This is
+/// done to ensure we do not drop coverage.
+/// This is the map from the receiver region to a bool, specifying either we
+/// consider this region's information precise or not along the given path.
+namespace clang {
+namespace ento {
+enum DynamicDispatchMode { DynamicDispatchModeInlined = 1,
+ DynamicDispatchModeConservative };
+
+struct DynamicDispatchBifurcationMap {};
+typedef llvm::ImmutableMap<const MemRegion*,
+ unsigned int> DynamicDispatchBifur;
+template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap>
+ : public ProgramStatePartialTrait<DynamicDispatchBifur> {
+ static void *GDMIndex() { static int index; return &index; }
+};
+
+}}
+
+bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred,
+ ProgramStateRef State) {
+ assert(D);
+
+ const LocationContext *CurLC = Pred->getLocationContext();
+ const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
+ const LocationContext *ParentOfCallee = 0;
+
+ // FIXME: Refactor this check into a hypothetical CallEvent::canInline.
+ switch (Call.getKind()) {
+ case CE_Function:
+ break;
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ if (!CXX_INLINING_ENABLED)
+ return false;
+ break;
+ case CE_CXXConstructor: {
+ if (!CXX_INLINING_ENABLED)
+ return false;
+
+ // Only inline constructors and destructors if we built the CFGs for them
+ // properly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
+ !ADC->getCFGBuildOptions().AddInitializers)
+ return false;
+
+ const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
+
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion();
+ if (Target && isa<ElementRegion>(Target))
+ return false;
+
+ // FIXME: This is a hack. We don't handle temporary destructors
+ // right now, so we shouldn't inline their constructors.
+ const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
+ if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
+ if (!Target || !isa<DeclRegion>(Target))
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;
- }
+ break;
}
- return false;
-}
+ case CE_CXXDestructor: {
+ if (!CXX_INLINING_ENABLED)
+ 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;
-}
+ // Only inline constructors and destructors if we built the CFGs for them
+ // properly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
+ !ADC->getCFGBuildOptions().AddInitializers)
+ 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;
+ const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call);
- 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;
- }
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion();
+ if (Target && isa<ElementRegion>(Target))
+ return false;
- for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) {
- if (FDecl && Idx < FDecl->getNumParams()) {
- if (isPointerToConst(FDecl->getParamDecl(Idx)))
- PreserveArgs.insert(Idx);
- }
- }
- return;
+ break;
}
+ case CE_CXXAllocator:
+ if (!CXX_INLINING_ENABLED)
+ return false;
- 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;
+ // Do not inline allocators until we model deallocators.
+ // This is unfortunate, but basically necessary for smart pointers and such.
+ return false;
+ case CE_Block: {
+ const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion();
+ assert(BR && "If we have the block definition we should have its region");
+ AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D);
+ ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC,
+ cast<BlockDecl>(D),
+ BR);
+ break;
}
-}
-
-ProgramStateRef
-ExprEngine::invalidateArguments(ProgramStateRef State,
- const CallOrObjCMessage &Call,
- const LocationContext *LC) {
- SmallVector<const MemRegion *, 8> RegionsToInvalidate;
-
- if (Call.isObjCMessage()) {
- // Invalidate all instance variables of the receiver of an ObjC message.
- // FIXME: We should be able to do better with inter-procedural analysis.
- if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion())
- RegionsToInvalidate.push_back(MR);
-
- } else if (Call.isCXXCall()) {
- // Invalidate all instance variables for the callee of a C++ method call.
- // FIXME: We should be able to do better with inter-procedural analysis.
- // FIXME: We can probably do better for const versus non-const methods.
- if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion())
- RegionsToInvalidate.push_back(Callee);
-
- } else if (Call.isFunctionCall()) {
- // Block calls invalidate all captured-by-reference values.
- SVal CalleeVal = Call.getFunctionCallee();
- if (const MemRegion *Callee = CalleeVal.getAsRegion()) {
- if (isa<BlockDataRegion>(Callee))
- RegionsToInvalidate.push_back(Callee);
- }
+ case CE_ObjCMessage:
+ if (!(getAnalysisManager().IPAMode == DynamicDispatch ||
+ getAnalysisManager().IPAMode == DynamicDispatchBifurcate))
+ return false;
+ break;
}
- // 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
- // invalidate the values referred by the location.
- if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
- V = Wrapped->getLoc();
- else if (!isa<Loc>(V))
- continue;
-
- if (const MemRegion *R = V.getAsRegion()) {
- // 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 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
- // about (char*), etc., is just a form of passing raw bytes.
- // e.g., void *p = alloca(); foo((char*)p);
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // Checking for 'integral type' is probably too promiscuous, but
- // 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
- // 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()) {
- const MemRegion *superReg = ER->getSuperRegion();
- if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
- isa<ObjCIvarRegion>(superReg))
- R = cast<TypedRegion>(superReg);
- }
- // FIXME: What about layers of ElementRegions?
- }
-
- // Mark this region for invalidation. We batch invalidate regions
- // below for efficiency.
- RegionsToInvalidate.push_back(R);
- } else {
- // Nuke all other arguments passed by reference.
- // FIXME: is this necessary or correct? This handles the non-Region
- // cases. Is it ever valid to store to these?
- State = State->unbindLoc(cast<Loc>(V));
- }
- }
+ if (!shouldInlineDecl(D, Pred))
+ return false;
+
+ if (!ParentOfCallee)
+ ParentOfCallee = CallerSFC;
+
+ // This may be NULL, but that's fine.
+ const Expr *CallE = Call.getOriginExpr();
+
+ // Construct a new stack frame for the callee.
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
+ const StackFrameContext *CalleeSFC =
+ CalleeADC->getStackFrame(ParentOfCallee, CallE,
+ currentBuilderContext->getBlock(),
+ currentStmtIdx);
+
+ CallEnter Loc(CallE, CalleeSFC, CurLC);
- // Invalidate designated regions using the batch invalidation API.
+ // Construct a new state which contains the mapping from actual to
+ // formal arguments.
+ State = State->enterStackFrame(Call, CalleeSFC);
- // FIXME: We can have collisions on the conjured symbol if the
- // expression *I also creates conjured symbols. We probably want
- // to identify conjured symbols by an expression pair: the enclosing
- // expression (the context) and the expression itself. This should
- // disambiguate conjured symbols.
- unsigned Count = currentBuilderContext->getCurrentBlockCount();
- StoreManager::InvalidatedSymbols IS;
+ bool isNew;
+ if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) {
+ N->addPredecessor(Pred, G);
+ if (isNew)
+ Engine.getWorkList()->enqueue(N);
+ }
- // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
- // global variables.
- return State->invalidateRegions(RegionsToInvalidate,
- Call.getOriginExpr(), Count, LC,
- &IS, &Call);
+ // If we decided to inline the call, the successor has been manually
+ // added onto the work list so remove it from the node builder.
+ Bldr.takeNodes(Pred);
+ return true;
}
-static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N,
- const CallExpr *CE) {
- void *ReplayState = N->getState()->get<ReplayWithoutInlining>();
+static ProgramStateRef getInlineFailedState(ProgramStateRef State,
+ const Stmt *CallE) {
+ void *ReplayState = State->get<ReplayWithoutInlining>();
if (!ReplayState)
return 0;
- const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState);
- if (CE == ReplayCE) {
- return N->getState()->remove<ReplayWithoutInlining>();
- }
- return 0;
+
+ assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call.");
+ (void)CallE;
+
+ return State->remove<ReplayWithoutInlining>();
}
void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
@@ -402,74 +441,175 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
// Perform the previsit of the CallExpr.
ExplodedNodeSet dstPreVisit;
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
-
- // Now evaluate the call itself.
- class DefaultEval : public GraphExpander {
- ExprEngine &Eng;
- const CallExpr *CE;
- public:
-
- DefaultEval(ExprEngine &eng, const CallExpr *ce)
- : Eng(eng), CE(ce) {}
- virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
-
- 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 Bldr(Pred, Dst, *Eng.currentBuilderContext);
+ // Get the call in its initial state. We use this as a template to perform
+ // all the checks.
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> CallTemplate
+ = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
- // Get the callee.
- const Expr *Callee = CE->getCallee()->IgnoreParens();
- if (state == 0)
- state = Pred->getState();
- SVal L = state->getSVal(Callee, Pred->getLocationContext());
+ // Evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call.
+ ExplodedNodeSet dstCallEvaluated;
+ for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
+ I != E; ++I) {
+ evalCall(dstCallEvaluated, *I, *CallTemplate);
+ }
+
+ // Finally, perform the post-condition check of the CallExpr and store
+ // the created nodes in 'Dst'.
+ // Note that if the call was inlined, dstCallEvaluated will be empty.
+ // The post-CallExpr check will occur in processCallExit.
+ getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
+ *this);
+}
- // Figure out the result type. We do this dance to handle references.
- QualType ResultTy;
- if (const FunctionDecl *FD = L.getAsFunctionDecl())
- ResultTy = FD->getResultType();
- else
- ResultTy = CE->getType();
+void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ const CallEvent &Call) {
+ // WARNING: At this time, the state attached to 'Call' may be older than the
+ // state in 'Pred'. This is a minor optimization since CheckerManager will
+ // use an updated CallEvent instance when calling checkers, but if 'Call' is
+ // ever used directly in this function all callers should be updated to pass
+ // the most recent state. (It is probably not worth doing the work here since
+ // for some callers this will not be necessary.)
- if (CE->isLValue())
- ResultTy = Eng.getContext().getPointerType(ResultTy);
+ // Run any pre-call checks using the generic call interface.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this);
- // Conjure a symbol value to use as the result.
- SValBuilder &SVB = Eng.getSValBuilder();
- unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount();
- const LocationContext *LCtx = Pred->getLocationContext();
- SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
+ // Actually evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call, and get a callback at
+ // defaultEvalCall if all of them fail.
+ ExplodedNodeSet dstCallEvaluated;
+ getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
+ Call, *this);
- // Generate a new state with the return value set.
- state = state->BindExpr(CE, LCtx, RetVal);
+ // Finally, run any post-call checks.
+ getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated,
+ Call, *this);
+}
- // Invalidate the arguments.
- state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx),
- LCtx);
+ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
+ const LocationContext *LCtx,
+ ProgramStateRef State) {
+ const Expr *E = Call.getOriginExpr();
+ if (!E)
+ return State;
- // And make the result node.
- Bldr.generateNode(CE, Pred, state);
+ // Some method families have known return values.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+ switch (Msg->getMethodFamily()) {
+ default:
+ break;
+ case OMF_autorelease:
+ case OMF_retain:
+ case OMF_self: {
+ // These methods return their receivers.
+ return State->BindExpr(E, LCtx, Msg->getReceiverSVal());
}
- };
-
- // Finally, evaluate the function call. We try each of the checkers
- // to see if the can evaluate the function call.
- ExplodedNodeSet dstCallEvaluated;
- DefaultEval defEval(*this, CE);
- getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
- dstPreVisit,
- CE, *this, &defEval);
-
- // Finally, perform the post-condition check of the CallExpr and store
- // the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
- *this);
+ }
+ } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){
+ return State->BindExpr(E, LCtx, C->getCXXThisVal());
+ }
+
+ // Conjure a symbol if the return value is unknown.
+ QualType ResultTy = Call.getResultType();
+ SValBuilder &SVB = getSValBuilder();
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ SVal R = SVB.getConjuredSymbolVal(0, E, LCtx, ResultTy, Count);
+ return State->BindExpr(E, LCtx, R);
+}
+
+// Conservatively evaluate call by invalidating regions and binding
+// a conjured return value.
+void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+ ExplodedNode *Pred, ProgramStateRef State) {
+ unsigned Count = currentBuilderContext->getCurrentBlockCount();
+ State = Call.invalidateRegions(Count, State);
+ State = bindReturnValue(Call, Pred->getLocationContext(), State);
+
+ // And make the result node.
+ Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+}
+
+void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
+ const CallEvent &CallTemplate) {
+ // Make sure we have the most recent state attached to the call.
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<> Call = CallTemplate.cloneWithState(State);
+
+ if (!getAnalysisManager().shouldInlineCall()) {
+ conservativeEvalCall(*Call, Bldr, Pred, State);
+ return;
+ }
+ // Try to inline the call.
+ // The origin expression here is just used as a kind of checksum;
+ // this should still be safe even for CallEvents that don't come from exprs.
+ const Expr *E = Call->getOriginExpr();
+ ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
+
+ if (InlinedFailedState) {
+ // If we already tried once and failed, make sure we don't retry later.
+ State = InlinedFailedState;
+ } else {
+ RuntimeDefinition RD = Call->getRuntimeDefinition();
+ const Decl *D = RD.getDecl();
+ if (D) {
+ // Explore with and without inlining the call.
+ if (RD.mayHaveOtherDefinitions() &&
+ getAnalysisManager().IPAMode == DynamicDispatchBifurcate) {
+ BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred);
+ return;
+ }
+ // We are not bifurcating and we do have a Decl, so just inline.
+ if (inlineCall(*Call, D, Bldr, Pred, State))
+ return;
+ }
+ }
+
+ // If we can't inline it, handle the return value and invalidate the regions.
+ conservativeEvalCall(*Call, Bldr, Pred, State);
}
+void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
+ const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ assert(BifurReg);
+
+ // Check if we've performed the split already - note, we only want
+ // to split the path once per memory region.
+ ProgramStateRef State = Pred->getState();
+ const unsigned int *BState =
+ State->get<DynamicDispatchBifurcationMap>(BifurReg);
+ if (BState) {
+ // If we are on "inline path", keep inlining if possible.
+ if (*BState == DynamicDispatchModeInlined)
+ if (inlineCall(Call, D, Bldr, Pred, State))
+ return;
+ // If inline failed, or we are on the path where we assume we
+ // don't have enough info about the receiver to inline, conjure the
+ // return value and invalidate the regions.
+ conservativeEvalCall(Call, Bldr, Pred, State);
+ return;
+ }
+
+ // If we got here, this is the first time we process a message to this
+ // region, so split the path.
+ ProgramStateRef IState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeInlined);
+ inlineCall(Call, D, Bldr, Pred, IState);
+
+ ProgramStateRef NoIState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeConservative);
+ conservativeEvalCall(Call, Bldr, Pred, NoIState);
+
+ NumOfDynamicDispatchPathSplits++;
+ return;
+}
+
+
void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index c8ad70a..e3bc498 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -13,8 +13,8 @@
#include "clang/AST/StmtObjC.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
using namespace clang;
using namespace ento;
@@ -74,7 +74,6 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
const Stmt *elem = S->getElement();
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());
@@ -86,10 +85,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
}
ExplodedNodeSet dstLocation;
- Bldr.takeNodes(Pred);
evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false);
- Bldr.addNodes(dstLocation);
-
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
+
for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
NE = dstLocation.end(); NI!=NE; ++NI) {
Pred = *NI;
@@ -126,148 +126,135 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
Bldr.generateNode(S, Pred, hasElems);
Bldr.generateNode(S, Pred, noElems);
}
+
+ // Finally, run any custom checkers.
+ // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+}
+
+static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) {
+ if (!Class)
+ return false;
+ if (Class->getIdentifier() == II)
+ return true;
+ return isSubclass(Class->getSuperClass(), II);
}
-void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
+void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Msg =
+ CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+
// Handle the previsits checks.
ExplodedNodeSet dstPrevisit;
- getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
- msg, *this);
-
+ getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
+ *Msg, *this);
+ ExplodedNodeSet dstGenericPrevisit;
+ getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
+ *Msg, *this);
+
// Proceed with evaluate the message expression.
ExplodedNodeSet dstEval;
- StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext);
+ StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext);
- for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
- DE = dstPrevisit.end(); DI != DE; ++DI) {
-
+ for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
+ DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
ExplodedNode *Pred = *DI;
- bool RaisesException = false;
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
- if (const Expr *Receiver = msg.getInstanceReceiver()) {
- ProgramStateRef state = Pred->getState();
- SVal recVal = state->getSVal(Receiver, Pred->getLocationContext());
+ if (UpdatedMsg->isInstanceMessage()) {
+ SVal recVal = UpdatedMsg->getReceiverSVal();
if (!recVal.isUndef()) {
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
ProgramStateRef notNilState, nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+ 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.
+ // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
+ // Revisit once we have lazier constraints.
if (nilState && !notNilState) {
continue;
}
// Check if the "raise" message was sent.
assert(notNilState);
- if (msg.getSelector() == RaiseSel)
- RaisesException = true;
+ if (Msg->getSelector() == RaiseSel) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateNode(currentStmt, Pred, State, true);
+ continue;
+ }
- // If we raise an exception, for now treat it as a sink.
- // Eventually we will want to handle exceptions properly.
- // Dispatch to plug-in transfer function.
- evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
- }
- }
- else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
- IdentifierInfo* ClsName = Iface->getIdentifier();
- Selector S = msg.getSelector();
-
- // Check for special instance methods.
- if (!NSExceptionII) {
- ASTContext &Ctx = getContext();
- NSExceptionII = &Ctx.Idents.get("NSException");
+ // Generate a transition to non-Nil state.
+ if (notNilState != State)
+ Pred = Bldr.generateNode(currentStmt, Pred, notNilState);
}
-
- if (ClsName == NSExceptionII) {
- enum { NUM_RAISE_SELECTORS = 2 };
-
- // Lazily create a cache of the selectors.
- if (!NSExceptionInstanceRaiseSelectors) {
+ } else {
+ // Check for special class methods.
+ if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) {
+ if (!NSExceptionII) {
ASTContext &Ctx = getContext();
- NSExceptionInstanceRaiseSelectors =
- new Selector[NUM_RAISE_SELECTORS];
- SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
- unsigned idx = 0;
-
- // raise:format:
- II.push_back(&Ctx.Idents.get("raise"));
- II.push_back(&Ctx.Idents.get("format"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
-
- // raise:format::arguments:
- II.push_back(&Ctx.Idents.get("arguments"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
+ NSExceptionII = &Ctx.Idents.get("NSException");
}
- for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
- if (S == NSExceptionInstanceRaiseSelectors[i]) {
- RaisesException = true;
- break;
+ if (isSubclass(Iface, NSExceptionII)) {
+ enum { NUM_RAISE_SELECTORS = 2 };
+
+ // Lazily create a cache of the selectors.
+ if (!NSExceptionInstanceRaiseSelectors) {
+ ASTContext &Ctx = getContext();
+ NSExceptionInstanceRaiseSelectors =
+ new Selector[NUM_RAISE_SELECTORS];
+ SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
+ unsigned idx = 0;
+
+ // raise:format:
+ II.push_back(&Ctx.Idents.get("raise"));
+ II.push_back(&Ctx.Idents.get("format"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
+
+ // raise:format:arguments:
+ II.push_back(&Ctx.Idents.get("arguments"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
}
+
+ Selector S = Msg->getSelector();
+ bool RaisesException = false;
+ for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
+ if (S == NSExceptionInstanceRaiseSelectors[i]) {
+ RaisesException = true;
+ break;
+ }
+ }
+ if (RaisesException) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateNode(currentStmt, Pred, Pred->getState(), true);
+ continue;
+ }
+
+ }
}
-
- // If we raise an exception, for now treat it as a sink.
- // Eventually we will want to handle exceptions properly.
- // Dispatch to plug-in transfer function.
- evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException);
}
+
+ // Evaluate the call.
+ defaultEvalCall(Bldr, Pred, *UpdatedMsg);
}
+ ExplodedNodeSet dstPostvisit;
+ getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval,
+ *Msg, *this);
+
// Finally, perform the post-condition check of the ObjCMessageExpr and store
// the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
+ *Msg, *this);
}
-
-void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr,
- const ObjCMessage &msg,
- ExplodedNode *Pred,
- ProgramStateRef state,
- bool GenSink) {
- // First handle the return value.
- SVal ReturnValue = UnknownVal();
-
- // Some method families have known return values.
- switch (msg.getMethodFamily()) {
- default:
- break;
- case OMF_autorelease:
- case OMF_retain:
- case OMF_self: {
- // These methods return their receivers.
- const Expr *ReceiverE = msg.getInstanceReceiver();
- if (ReceiverE)
- ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext());
- break;
- }
- }
-
- // If we failed to figure out the return value, use a conjured value instead.
- if (ReturnValue.isUnknown()) {
- SValBuilder &SVB = getSValBuilder();
- QualType ResultTy = msg.getResultType(getContext());
- unsigned Count = currentBuilderContext->getCurrentBlockCount();
- const Expr *CurrentE = cast<Expr>(currentStmt);
- const LocationContext *LCtx = Pred->getLocationContext();
- ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count);
- }
-
- // Bind the return value.
- const LocationContext *LCtx = Pred->getLocationContext();
- state = state->BindExpr(currentStmt, LCtx, ReturnValue);
-
- // Invalidate the arguments (and the receiver)
- state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx);
-
- // And create the new node.
- Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink);
- assert(Bldr.hasGeneratedNodes());
-}
-
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 629f1ea..0152e32 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -95,37 +95,6 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
}
}
-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);
- }
-}
-
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
SmallVectorImpl<std::string> *FilesMade) {
@@ -152,8 +121,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
return;
// First flatten out the entire path to make it easier to use.
- PathPieces path;
- flattenPath(path, path, D.path);
+ PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);
// The path as already been prechecked that all parts of the path are
// from the same file and that it is non-empty.
@@ -428,6 +396,15 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "<div class=\"PathIndex";
if (Kind) os << " PathIndex" << Kind;
os << "\">" << num << "</div>";
+
+ if (num > 1) {
+ os << "</td><td><div class=\"PathNav\"><a href=\"#Path"
+ << (num - 1)
+ << "\" title=\"Previous event ("
+ << (num - 1)
+ << ")\">&#x2190;</a></div></td>";
+ }
+
os << "</td><td>";
}
@@ -441,9 +418,10 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
assert(L.isFileID());
StringRef BufferInfo = L.getBufferData();
- const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data();
- Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(),
- MacroName, BufferInfo.end());
+ std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc();
+ const char* MacroName = LocInfo.second + BufferInfo.data();
+ Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
+ BufferInfo.begin(), MacroName, BufferInfo.end());
Token TheTok;
rawLexer.LexFromRawLexer(TheTok);
@@ -453,8 +431,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "':\n";
- if (max > 1)
- os << "</td></tr></table>";
+ if (max > 1) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
// Within a macro piece. Write out each event.
ProcessMacroPiece(os, *MP, 0);
@@ -462,8 +453,21 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
else {
os << html::EscapeText(P.getString());
- if (max > 1)
- os << "</td></tr></table>";
+ if (max > 1) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
}
os << "</div></td></tr>";
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index ed94c79..62e602a 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -179,7 +179,7 @@ const StackFrameContext *VarRegion::getStackFrame() const {
// Region extents.
//===----------------------------------------------------------------------===//
-DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const {
+DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
ASTContext &Ctx = svalBuilder.getContext();
QualType T = getDesugaredValueType(Ctx);
@@ -470,7 +470,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
}
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
- os << "base " << decl->getName();
+ os << "base{" << superRegion << ',' << decl->getName() << '}';
}
void CXXThisRegion::dumpToStream(raw_ostream &os) const {
@@ -518,10 +518,6 @@ 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";
}
@@ -534,17 +530,45 @@ void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "GlobalImmutableSpaceRegion";
}
-void MemRegion::dumpPretty(raw_ostream &os) const {
+void HeapSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "HeapSpaceRegion";
+}
+
+void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "UnknownSpaceRegion";
+}
+
+void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackArgumentsSpaceRegion";
+}
+
+void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackLocalsSpaceRegion";
+}
+
+bool MemRegion::canPrintPretty() const {
+ return false;
+}
+
+void MemRegion::printPretty(raw_ostream &os) const {
return;
}
-void VarRegion::dumpPretty(raw_ostream &os) const {
+bool VarRegion::canPrintPretty() const {
+ return true;
+}
+
+void VarRegion::printPretty(raw_ostream &os) const {
os << getDecl()->getName();
}
-void FieldRegion::dumpPretty(raw_ostream &os) const {
- superRegion->dumpPretty(os);
- os << "->" << getDecl();
+bool FieldRegion::canPrintPretty() const {
+ return superRegion->canPrintPretty();
+}
+
+void FieldRegion::printPretty(raw_ostream &os) const {
+ superRegion->printPretty(os);
+ os << "." << getDecl()->getName();
}
//===----------------------------------------------------------------------===//
@@ -643,6 +667,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion());
}
+/// Look through a chain of LocationContexts to either find the
+/// StackFrameContext that matches a DeclContext, or find a VarRegion
+/// for a variable captured by a block.
+static llvm::PointerUnion<const StackFrameContext *, const VarRegion *>
+getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
+ const DeclContext *DC,
+ const VarDecl *VD) {
+ while (LC) {
+ if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) {
+ if (cast<DeclContext>(SFC->getDecl()) == DC)
+ return SFC;
+ }
+ if (const BlockInvocationContext *BC =
+ dyn_cast<BlockInvocationContext>(LC)) {
+ const BlockDataRegion *BR =
+ static_cast<const BlockDataRegion*>(BC->getContextData());
+ // FIXME: This can be made more efficient.
+ for (BlockDataRegion::referenced_vars_iterator
+ I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end(); I != E; ++I) {
+ if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion()))
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
+ }
+
+ LC = LC->getParent();
+ }
+ return (const StackFrameContext*)0;
+}
+
const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
const MemRegion *sReg = 0;
@@ -675,7 +730,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
// FIXME: Once we implement scope handling, we will need to properly lookup
// 'D' to the proper LocationContext.
const DeclContext *DC = D->getDeclContext();
- const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC);
+ llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
+ getStackOrCaptureRegionForDeclContext(LC, DC, D);
+
+ if (V.is<const VarRegion*>())
+ return V.get<const VarRegion*>();
+
+ const StackFrameContext *STC = V.get<const StackFrameContext*>();
if (!STC)
sReg = getUnknownRegion();
@@ -800,6 +861,10 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
}
+const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) {
+ return getSubRegion<SymbolicRegion>(Sym, getHeapRegion());
+}
+
const FieldRegion*
MemRegionManager::getFieldRegion(const FieldDecl *d,
const MemRegion* superRegion){
@@ -823,6 +888,37 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E,
const CXXBaseObjectRegion *
MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl,
const MemRegion *superRegion) {
+ // Check that the base class is actually a direct base of this region.
+ if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) {
+ if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){
+ if (Class->isVirtuallyDerivedFrom(decl)) {
+ // Virtual base regions should not be layered, since the layout rules
+ // are different.
+ while (const CXXBaseObjectRegion *Base =
+ dyn_cast<CXXBaseObjectRegion>(superRegion)) {
+ superRegion = Base->getSuperRegion();
+ }
+ assert(superRegion && !isa<MemSpaceRegion>(superRegion));
+
+ } else {
+ // Non-virtual bases should always be direct bases.
+#ifndef NDEBUG
+ bool FoundBase = false;
+ for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(),
+ E = Class->bases_end();
+ I != E; ++I) {
+ if (I->getType()->getAsCXXRecordDecl() == decl) {
+ FoundBase = true;
+ break;
+ }
+ }
+
+ assert(FoundBase && "Not a direct base class of this region");
+#endif
+ }
+ }
+ }
+
return getSubRegion<CXXBaseObjectRegion>(decl, superRegion);
}
@@ -898,24 +994,26 @@ const MemRegion *MemRegion::getBaseRegion() const {
// View handling.
//===----------------------------------------------------------------------===//
-const MemRegion *MemRegion::StripCasts() const {
+const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
const MemRegion *R = this;
while (true) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // FIXME: generalize. Essentially we want to strip away ElementRegions
- // that were layered on a symbolic region because of casts. We only
- // want to strip away ElementRegions, however, where the index is 0.
- SVal index = ER->getIndex();
- if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) {
- if (CI->getValue().getSExtValue() == 0) {
- R = ER->getSuperRegion();
- continue;
- }
- }
+ switch (R->getKind()) {
+ case ElementRegionKind: {
+ const ElementRegion *ER = cast<ElementRegion>(R);
+ if (!ER->getIndex().isZeroConstant())
+ return R;
+ R = ER->getSuperRegion();
+ break;
+ }
+ case CXXBaseObjectRegionKind:
+ if (!StripBaseCasts)
+ return R;
+ R = cast<CXXBaseObjectRegion>(R)->getSuperRegion();
+ break;
+ default:
+ return R;
}
- break;
}
- return R;
}
// FIXME: Merge with the implementation of the same method in Store.cpp
@@ -973,12 +1071,14 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const {
RegionOffset MemRegion::getAsOffset() const {
const MemRegion *R = this;
+ const MemRegion *SymbolicOffsetBase = 0;
int64_t Offset = 0;
while (1) {
switch (R->getKind()) {
default:
- return RegionOffset(0);
+ return RegionOffset(R, RegionOffset::Symbolic);
+
case SymbolicRegionKind:
case AllocaRegionKind:
case CompoundLiteralRegionKind:
@@ -987,31 +1087,95 @@ RegionOffset MemRegion::getAsOffset() const {
case VarRegionKind:
case CXXTempObjectRegionKind:
goto Finish;
+
+ case ObjCIvarRegionKind:
+ // This is a little strange, but it's a compromise between
+ // ObjCIvarRegions having unknown compile-time offsets (when using the
+ // non-fragile runtime) and yet still being distinct, non-overlapping
+ // regions. Thus we treat them as "like" base regions for the purposes
+ // of computing offsets.
+ goto Finish;
+
+ case CXXBaseObjectRegionKind: {
+ const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R);
+ R = BOR->getSuperRegion();
+
+ QualType Ty;
+ if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) {
+ Ty = TVR->getDesugaredValueType(getContext());
+ } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+ // If our base region is symbolic, we don't know what type it really is.
+ // Pretend the type of the symbol is the true dynamic type.
+ // (This will at least be self-consistent for the life of the symbol.)
+ Ty = SR->getSymbol()->getType(getContext())->getPointeeType();
+ }
+
+ const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
+ if (!Child) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
+ const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child);
+
+ CharUnits BaseOffset;
+ const CXXRecordDecl *Base = BOR->getDecl();
+ if (Child->isVirtuallyDerivedFrom(Base))
+ BaseOffset = Layout.getVBaseClassOffset(Base);
+ else
+ BaseOffset = Layout.getBaseClassOffset(Base);
+
+ // The base offset is in chars, not in bits.
+ Offset += BaseOffset.getQuantity() * getContext().getCharWidth();
+ break;
+ }
case ElementRegionKind: {
const ElementRegion *ER = cast<ElementRegion>(R);
- QualType EleTy = ER->getValueType();
+ R = ER->getSuperRegion();
- if (!IsCompleteType(getContext(), EleTy))
- return RegionOffset(0);
+ QualType EleTy = ER->getValueType();
+ if (!IsCompleteType(getContext(), EleTy)) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ continue;
+ }
SVal Index = ER->getIndex();
if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) {
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
int64_t i = CI->getValue().getSExtValue();
- CharUnits Size = getContext().getTypeSizeInChars(EleTy);
- Offset += i * Size.getQuantity() * 8;
+ // This type size is in bits.
+ Offset += i * getContext().getTypeSize(EleTy);
} else {
// We cannot compute offset for non-concrete index.
- return RegionOffset(0);
+ SymbolicOffsetBase = R;
}
- R = ER->getSuperRegion();
break;
}
case FieldRegionKind: {
const FieldRegion *FR = cast<FieldRegion>(R);
+ R = FR->getSuperRegion();
+
const RecordDecl *RD = FR->getDecl()->getParent();
- if (!RD->isCompleteDefinition())
+ if (!RD->isCompleteDefinition()) {
// We cannot compute offset for incomplete type.
- return RegionOffset(0);
+ SymbolicOffsetBase = R;
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
// Get the field number.
unsigned idx = 0;
for (RecordDecl::field_iterator FI = RD->field_begin(),
@@ -1022,13 +1186,14 @@ RegionOffset MemRegion::getAsOffset() const {
const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
// This is offset in bits.
Offset += Layout.getFieldOffset(idx);
- R = FR->getSuperRegion();
break;
}
}
}
Finish:
+ if (SymbolicOffsetBase)
+ return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic);
return RegionOffset(R, Offset);
}
@@ -1056,26 +1221,37 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
typedef BumpVector<const MemRegion*> VarVec;
VarVec *BV = (VarVec*) A.Allocate<VarVec>();
new (BV) VarVec(BC, E - I);
+ VarVec *BVOriginal = (VarVec*) A.Allocate<VarVec>();
+ new (BVOriginal) VarVec(BC, E - I);
for ( ; I != E; ++I) {
const VarDecl *VD = *I;
const VarRegion *VR = 0;
+ const VarRegion *OriginalVR = 0;
- if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage())
+ if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) {
VR = MemMgr.getVarRegion(VD, this);
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
+ }
else {
- if (LC)
+ if (LC) {
VR = MemMgr.getVarRegion(VD, LC);
+ OriginalVR = VR;
+ }
else {
VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
assert(VR);
+ assert(OriginalVR);
BV->push_back(VR, BC);
+ BVOriginal->push_back(OriginalVR, BC);
}
ReferencedVars = BV;
+ OriginalVars = BVOriginal;
}
BlockDataRegion::referenced_vars_iterator
@@ -1085,8 +1261,14 @@ BlockDataRegion::referenced_vars_begin() const {
BumpVector<const MemRegion*> *Vec =
static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
- return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ?
- NULL : Vec->begin());
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(0, 0);
+
+ BumpVector<const MemRegion*> *VecOriginal =
+ static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
+ VecOriginal->begin());
}
BlockDataRegion::referenced_vars_iterator
@@ -1096,6 +1278,12 @@ BlockDataRegion::referenced_vars_end() const {
BumpVector<const MemRegion*> *Vec =
static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
- return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ?
- NULL : Vec->end());
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(0, 0);
+
+ BumpVector<const MemRegion*> *VecOriginal =
+ static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->end(),
+ VecOriginal->end());
}
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
deleted file mode 100644
index 65cdcd9..0000000
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- 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 ObjCMessage which serves as a common wrapper for ObjC
-// message expressions or implicit messages for loading/storing ObjC properties.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
-#include "clang/AST/DeclCXX.h"
-
-using namespace clang;
-using namespace ento;
-
-QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
- QualType resultTy;
- bool isLVal = false;
-
- if (isObjCMessage()) {
- resultTy = Msg.getResultType(ctx);
- } else if (const CXXConstructExpr *Ctor =
- CallE.dyn_cast<const CXXConstructExpr *>()) {
- resultTy = Ctor->getType();
- } else {
- const CallExpr *FunctionCall = CallE.get<const CallExpr *>();
-
- isLVal = FunctionCall->isLValue();
- const Expr *Callee = FunctionCall->getCallee();
- if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl())
- resultTy = FD->getResultType();
- else
- resultTy = FunctionCall->getType();
- }
-
- if (isLVal)
- resultTy = ctx.getPointerType(resultTy);
-
- return resultTy;
-}
-
-SVal CallOrObjCMessage::getFunctionCallee() const {
- assert(isFunctionCall());
- assert(!isCXXCall());
- const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
- return State->getSVal(Fun, LCtx);
-}
-
-SVal CallOrObjCMessage::getCXXCallee() const {
- assert(isCXXCall());
- const CallExpr *ActualCall = CallE.get<const CallExpr *>();
- const Expr *callee =
- cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument();
-
- // FIXME: Will eventually need to cope with member pointers. This is
- // a limitation in getImplicitObjectArgument().
- if (!callee)
- return UnknownVal();
-
- return State->getSVal(callee, LCtx);
-}
-
-SVal
-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 01dd965..7d52aac 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -16,6 +16,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtCXX.h"
@@ -58,6 +59,48 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
PathPieces::~PathPieces() {}
+
+void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
+ bool ShouldFlattenMacros) const {
+ for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {
+ PathDiagnosticPiece *Piece = I->getPtr();
+
+ switch (Piece->getKind()) {
+ case PathDiagnosticPiece::Call: {
+ PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece);
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter =
+ Call->getCallEnterEvent();
+ if (CallEnter)
+ Current.push_back(CallEnter);
+ Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit =
+ Call->getCallExitEvent();
+ if (callExit)
+ Current.push_back(callExit);
+ break;
+ }
+ case PathDiagnosticPiece::Macro: {
+ PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece);
+ if (ShouldFlattenMacros) {
+ Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ } else {
+ Current.push_back(Piece);
+ PathPieces NewPath;
+ Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);
+ // FIXME: This probably shouldn't mutate the original path piece.
+ Macro->subPieces = NewPath;
+ }
+ break;
+ }
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::ControlFlow:
+ Current.push_back(Piece);
+ break;
+ }
+ }
+}
+
+
PathDiagnostic::~PathDiagnostic() {}
PathDiagnostic::PathDiagnostic(const Decl *declWithIssue,
@@ -147,23 +190,14 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) {
if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
// Keep the PathDiagnostic with the shorter path.
+ // Note, the enclosing routine is called in deterministic order, so the
+ // results will be consistent between runs (no reason to break ties if the
+ // size is the same).
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;
- }
+ if (orig_size <= new_size)
+ return;
- if (shouldKeepOriginal)
- return;
- }
Diags.RemoveNode(orig);
delete orig;
}
@@ -242,30 +276,86 @@ PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) {
//===----------------------------------------------------------------------===//
static SourceLocation getValidSourceLocation(const Stmt* S,
- LocationOrAnalysisDeclContext LAC) {
- SourceLocation L = S->getLocStart();
+ LocationOrAnalysisDeclContext LAC,
+ bool UseEnd = false) {
+ SourceLocation L = UseEnd ? S->getLocEnd() : S->getLocStart();
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
- // source code, so find an enclosing statement and use it's location.
+ // source code, so find an enclosing statement and use its location.
if (!L.isValid()) {
- ParentMap *PM = 0;
+ AnalysisDeclContext *ADC;
if (LAC.is<const LocationContext*>())
- PM = &LAC.get<const LocationContext*>()->getParentMap();
+ ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext();
else
- PM = &LAC.get<AnalysisDeclContext*>()->getParentMap();
+ ADC = LAC.get<AnalysisDeclContext*>();
+
+ ParentMap &PM = ADC->getParentMap();
+
+ const Stmt *Parent = S;
+ do {
+ Parent = PM.getParent(Parent);
+
+ // In rare cases, we have implicit top-level expressions,
+ // such as arguments for implicit member initializers.
+ // In this case, fall back to the start of the body (even if we were
+ // asked for the statement end location).
+ if (!Parent) {
+ const Stmt *Body = ADC->getBody();
+ if (Body)
+ L = Body->getLocStart();
+ else
+ L = ADC->getDecl()->getLocEnd();
+ break;
+ }
- while (!L.isValid()) {
- S = PM->getParent(S);
- L = S->getLocStart();
- }
+ L = UseEnd ? Parent->getLocEnd() : Parent->getLocStart();
+ } while (!L.isValid());
}
return L;
}
+static PathDiagnosticLocation
+getLocationForCaller(const StackFrameContext *SFC,
+ const LocationContext *CallerCtx,
+ const SourceManager &SM) {
+ const CFGBlock &Block = *SFC->getCallSiteBlock();
+ CFGElement Source = Block[SFC->getIndex()];
+
+ switch (Source.getKind()) {
+ case CFGElement::Invalid:
+ llvm_unreachable("Invalid CFGElement");
+ case CFGElement::Statement:
+ return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(),
+ SM, CallerCtx);
+ case CFGElement::Initializer: {
+ const CFGInitializer &Init = cast<CFGInitializer>(Source);
+ return PathDiagnosticLocation(Init.getInitializer()->getInit(),
+ SM, CallerCtx);
+ }
+ case CFGElement::AutomaticObjectDtor: {
+ const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source);
+ return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),
+ SM, CallerCtx);
+ }
+ case CFGElement::BaseDtor:
+ case CFGElement::MemberDtor: {
+ const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
+ if (const Stmt *CallerBody = CallerInfo->getBody())
+ return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
+ return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
+ }
+ case CFGElement::TemporaryDtor:
+ llvm_unreachable("not yet implemented!");
+ }
+
+ llvm_unreachable("Unknown CFGElement kind");
+}
+
+
PathDiagnosticLocation
PathDiagnosticLocation::createBegin(const Decl *D,
const SourceManager &SM) {
@@ -280,6 +370,17 @@ PathDiagnosticLocation
SM, SingleLocK);
}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createEnd(const Stmt *S,
+ const SourceManager &SM,
+ LocationOrAnalysisDeclContext LAC) {
+ if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S))
+ return createEndBrace(CS, SM);
+ return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),
+ SM, SingleLocK);
+}
+
PathDiagnosticLocation
PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
const SourceManager &SM) {
@@ -339,6 +440,19 @@ PathDiagnosticLocation
else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
S = PS->getStmt();
}
+ else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) {
+ return PathDiagnosticLocation(PIE->getLocation(), SMng);
+ }
+ else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) {
+ return getLocationForCaller(CE->getCalleeContext(),
+ CE->getLocationContext(),
+ SMng);
+ }
+ else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) {
+ return getLocationForCaller(CEE->getCalleeContext(),
+ CEE->getLocationContext(),
+ SMng);
+ }
return PathDiagnosticLocation(S, SMng, P.getLocationContext());
}
@@ -495,25 +609,14 @@ PathDiagnosticLocation PathDiagnostic::getLocation() const {
// 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 CallExitEnd &CE,
const SourceManager &SM) {
- const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
- PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
+ const Decl *caller = CE.getLocationContext()->getDecl();
+ PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),
+ CE.getLocationContext(),
+ SM);
return new PathDiagnosticCallPiece(caller, pos);
}
@@ -528,11 +631,11 @@ PathDiagnosticCallPiece::construct(PathPieces &path,
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);
+ const StackFrameContext *CalleeCtx = CE.getCalleeContext();
+ Callee = CalleeCtx->getDecl();
+
+ callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);
+ callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);
}
IntrusiveRefCntPtr<PathDiagnosticEventPiece>
@@ -667,16 +770,15 @@ 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 CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P);
+ assert(CExit && "Stack Hints should be constructed at CallExitEnd points.");
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
+ // FIXME: Use CallEvent to abstract this over all calls.
+ const Stmt *CallSite = CExit->getCalleeContext()->getCallSite();
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite);
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();
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 323cede..58a4bba 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -31,7 +31,6 @@ namespace {
const std::string OutputFile;
const LangOptions &LangOpts;
OwningPtr<PathDiagnosticConsumer> SubPD;
- bool flushed;
const bool SupportsCrossFileDiagnostics;
public:
PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
@@ -61,7 +60,7 @@ 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),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
PathDiagnosticConsumer*
@@ -183,10 +182,18 @@ static void ReportControlFlow(raw_ostream &o,
I!=E; ++I) {
Indent(o, indent) << "<dict>\n";
++indent;
+
+ // Make the ranges of the start and end point self-consistent with adjacent edges
+ // by forcing to use only the beginning of the range. This simplifies the layout
+ // logic for clients.
Indent(o, indent) << "<key>start</key>\n";
- EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1);
+ SourceLocation StartEdge = I->getStart().asRange().getBegin();
+ EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1);
+
Indent(o, indent) << "<key>end</key>\n";
- EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1);
+ SourceLocation EndEdge = I->getEnd().asRange().getBegin();
+ EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1);
+
--indent;
Indent(o, indent) << "</dict>\n";
}
@@ -382,6 +389,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
if (const PathDiagnosticCallPiece *call =
dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ IntrusiveRefCntPtr<PathDiagnosticEventPiece>
+ callEnterWithin = call->getCallEnterWithinCallerEvent();
+ if (callEnterWithin)
+ AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation());
+
WorkList.push_back(&call->path);
}
else if (const PathDiagnosticMacroPiece *macro =
@@ -476,6 +488,17 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <key>issue_context</key>";
EmitString(o, declName) << '\n';
}
+
+ // Output the bug hash for issue unique-ing. Currently, it's just an
+ // offset from the beginning of the function.
+ if (const Stmt *Body = DeclWithIssue->getBody()) {
+ FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()),
+ *SM);
+ FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM);
+ o << " <key>issue_hash</key><integer>"
+ << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber()
+ << "</integer>\n";
+ }
}
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index b9cfa27..dc988cc 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
@@ -70,6 +71,19 @@ ProgramState::~ProgramState() {
stateMgr->getStoreManager().decrementReferenceCount(store);
}
+ProgramStateManager::ProgramStateManager(ASTContext &Ctx,
+ StoreManagerCreator CreateSMgr,
+ ConstraintManagerCreator CreateCMgr,
+ llvm::BumpPtrAllocator &alloc,
+ SubEngine &SubEng)
+ : Eng(&SubEng), EnvMgr(alloc), GDMFactory(alloc),
+ svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
+ CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
+ StoreMgr.reset((*CreateSMgr)(*this));
+ ConstraintMgr.reset((*CreateCMgr)(*this, SubEng));
+}
+
+
ProgramStateManager::~ProgramStateManager() {
for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end();
I!=E; ++I)
@@ -157,7 +171,7 @@ ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols *IS,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!IS) {
StoreManager::InvalidatedSymbols invalidated;
return invalidateRegionsImpl(Regions, E, Count, LCtx,
@@ -171,7 +185,7 @@ ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
ProgramStateManager &Mgr = getStateManager();
SubEngine* Eng = Mgr.getOwningEngine();
@@ -203,11 +217,11 @@ ProgramStateRef ProgramState::unbindLoc(Loc LV) const {
}
ProgramStateRef
-ProgramState::enterStackFrame(const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx) const {
- const StoreRef &new_store =
- getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx);
- return makeWithStore(new_store);
+ProgramState::enterStackFrame(const CallEvent &Call,
+ const StackFrameContext *CalleeCtx) const {
+ const StoreRef &NewStore =
+ getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx);
+ return makeWithStore(NewStore);
}
SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
@@ -485,8 +499,6 @@ ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key)
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))
@@ -530,6 +542,9 @@ bool ScanReachableSymbols::scan(SVal val) {
if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val))
return scan(X->getRegion());
+ if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val))
+ return scan(X->getRegion());
+
if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val))
return scan(X->getLoc());
@@ -564,20 +579,30 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
return false;
// If this is a subregion, also visit the parent regions.
- if (const SubRegion *SR = dyn_cast<SubRegion>(R))
- if (!scan(SR->getSuperRegion()))
+ if (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
+ const MemRegion *Super = SR->getSuperRegion();
+ if (!scan(Super))
return false;
- // Now look at the binding to this region (if any).
- if (!scan(state->getSValAsScalarOrLoc(R)))
- return false;
+ // When we reach the topmost region, scan all symbols in it.
+ if (isa<MemSpaceRegion>(Super)) {
+ StoreManager &StoreMgr = state->getStateManager().getStoreManager();
+ if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this))
+ return false;
+ }
+ }
- // Now look at the subregions.
- if (!SRM.get())
- SRM.reset(state->getStateManager().getStoreManager().
- getSubRegionMap(state->getStore()));
+ // Regions captured by a block are also implicitly reachable.
+ if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+ for ( ; I != E; ++I) {
+ if (!scan(I.getCapturedRegion()))
+ return false;
+ }
+ }
- return SRM->iterSubRegions(R, *this);
+ return true;
}
bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {
@@ -707,3 +732,39 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
return Tainted;
}
+
+/// The GDM component containing the dynamic type info. This is a map from a
+/// symbol to it's most likely type.
+namespace clang {
+namespace ento {
+typedef llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo> DynamicTypeMap;
+template<> struct ProgramStateTrait<DynamicTypeMap>
+ : public ProgramStatePartialTrait<DynamicTypeMap> {
+ static void *GDMIndex() { static int index; return &index; }
+};
+}}
+
+DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const {
+ // Look up the dynamic type in the GDM.
+ const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg);
+ if (GDMType)
+ return *GDMType;
+
+ // Otherwise, fall back to what we know about the region.
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg))
+ return DynamicTypeInfo(TR->getValueType());
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) {
+ SymbolRef Sym = SR->getSymbol();
+ return DynamicTypeInfo(Sym->getType(getStateManager().getContext()));
+ }
+
+ return DynamicTypeInfo();
+}
+
+ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg,
+ DynamicTypeInfo NewTy) const {
+ ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy);
+ assert(NewState);
+ return NewState;
+}
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 98eb958..550404a 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/Debug.h"
@@ -143,6 +144,92 @@ private:
}
}
+ const llvm::APSInt &getMinValue() const {
+ assert(!isEmpty());
+ return ranges.begin()->From();
+ }
+
+ bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ // This function has nine cases, the cartesian product of range-testing
+ // both the upper and lower bounds against the symbol's type.
+ // Each case requires a different pinning operation.
+ // The function returns false if the described range is entirely outside
+ // the range of values for the associated symbol.
+ APSIntType Type(getMinValue());
+ APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower);
+ APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper);
+
+ switch (LowerTest) {
+ case APSIntType::RTR_Below:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower < Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range starts below what's possible but ends within it. Pin.
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range spans all possible values for the symbol. Pin.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Within:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps around, but all lower values are not possible.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range may or may not wrap around, but both limits are valid.
+ Type.apply(Lower);
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range starts within what's possible but ends above it. Pin.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Above:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps but is outside the symbol's set of possible values.
+ return false;
+ case APSIntType::RTR_Within:
+ // The range starts above what's possible but ends within it (wrap).
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower < Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ }
+
+ return true;
+ }
+
public:
// Returns a set containing the values in the receiving set, intersected with
// the closed range [Lower, Upper]. Unlike the Range type, this range uses
@@ -152,8 +239,10 @@ public:
// intersection with the two ranges [Min, Upper] and [Lower, Max],
// or, alternatively, /removing/ all integers between Upper and Lower.
RangeSet Intersect(BasicValueFactory &BV, Factory &F,
- const llvm::APSInt &Lower,
- const llvm::APSInt &Upper) const {
+ llvm::APSInt Lower, llvm::APSInt Upper) const {
+ if (!pin(Lower, Upper))
+ return F.getEmptySet();
+
PrimRangeSet newRanges = F.getEmptySet();
PrimRangeSet::iterator i = begin(), e = end();
@@ -166,6 +255,7 @@ public:
IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e);
IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e);
}
+
return newRanges;
}
@@ -206,8 +296,8 @@ namespace {
class RangeConstraintManager : public SimpleConstraintManager{
RangeSet GetRange(ProgramStateRef state, SymbolRef sym);
public:
- RangeConstraintManager(SubEngine &subengine)
- : SimpleConstraintManager(subengine) {}
+ RangeConstraintManager(SubEngine &subengine, BasicValueFactory &BVF)
+ : SimpleConstraintManager(subengine, BVF) {}
ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym,
const llvm::APSInt& Int,
@@ -252,9 +342,9 @@ private:
} // end anonymous namespace
-ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&,
- SubEngine &subeng) {
- return new RangeConstraintManager(subeng);
+ConstraintManager *
+ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine &Eng) {
+ return new RangeConstraintManager(Eng, StMgr.getBasicVals());
}
const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St,
@@ -288,8 +378,8 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
// Lazily generate a new RangeSet representing all possible values for the
// given symbol type.
- QualType T = state->getSymbolManager().getType(sym);
- BasicValueFactory& BV = state->getBasicVals();
+ BasicValueFactory &BV = getBasicVals();
+ QualType T = sym->getType(BV.getContext());
return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T));
}
@@ -306,117 +396,154 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) {
// UINT_MAX, 0, 1, and 2.
ProgramStateRef
-RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- llvm::APSInt Lower = Int-Adjustment;
+RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within)
+ return St;
+
+ llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment;
llvm::APSInt Upper = Lower;
--Lower;
++Upper;
// [Int-Adjustment+1, Int-Adjustment-1]
// Notice that the lower bound is greater than the upper bound.
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Upper, Lower);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
+RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within)
+ return NULL;
+
// [Int-Adjustment, Int-Adjustment]
- BasicValueFactory &BV = state->getBasicVals();
- llvm::APSInt AdjInt = Int-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, AdjInt, AdjInt);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Min = BV.getMinValue(T);
+RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return NULL;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return St;
+ }
// Special case for Int == Min. This is always false.
- if (Int == Min)
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
return NULL;
llvm::APSInt Lower = Min-Adjustment;
- llvm::APSInt Upper = Int-Adjustment;
+ llvm::APSInt Upper = ComparisonVal-Adjustment;
--Upper;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Max = BV.getMaxValue(T);
+RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return St;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return NULL;
+ }
// Special case for Int == Max. This is always false.
- if (Int == Max)
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
return NULL;
- llvm::APSInt Lower = Int-Adjustment;
+ llvm::APSInt Lower = ComparisonVal-Adjustment;
llvm::APSInt Upper = Max-Adjustment;
++Lower;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Min = BV.getMinValue(T);
+RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return St;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return NULL;
+ }
// Special case for Int == Min. This is always feasible.
- if (Int == Min)
- return state;
-
- const llvm::APSInt &Max = BV.getMaxValue(T);
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
+ return St;
- llvm::APSInt Lower = Int-Adjustment;
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ llvm::APSInt Lower = ComparisonVal-Adjustment;
llvm::APSInt Upper = Max-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
ProgramStateRef
-RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym,
- const llvm::APSInt& Int,
- const llvm::APSInt& Adjustment) {
- BasicValueFactory &BV = state->getBasicVals();
-
- QualType T = state->getSymbolManager().getType(sym);
- const llvm::APSInt &Max = BV.getMaxValue(T);
+RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int)) {
+ case APSIntType::RTR_Below:
+ return NULL;
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return St;
+ }
// Special case for Int == Max. This is always feasible.
- if (Int == Max)
- return state;
-
- const llvm::APSInt &Min = BV.getMinValue(T);
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
+ return St;
+ llvm::APSInt Min = AdjustmentType.getMinValue();
llvm::APSInt Lower = Min-Adjustment;
- llvm::APSInt Upper = Int-Adjustment;
+ llvm::APSInt Upper = ComparisonVal-Adjustment;
- RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper);
- return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
+ RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+ return New.isEmpty() ? NULL : St->set<ConstraintRange>(Sym, New);
}
//===------------------------------------------------------------------------===
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index cc3ea8c3..bc4e4bb 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -17,10 +17,11 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/CXXInheritance.h"
#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/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -40,23 +41,49 @@ using llvm::Optional;
namespace {
class BindingKey {
public:
- enum Kind { Direct = 0x0, Default = 0x1 };
+ enum Kind { Default = 0x0, Direct = 0x1 };
private:
- llvm ::PointerIntPair<const MemRegion*, 1> P;
- uint64_t Offset;
+ enum { Symbolic = 0x2 };
+ llvm::PointerIntPair<const MemRegion *, 2> P;
+ uint64_t Data;
+
+ explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k)
+ : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) {
+ assert(r && Base && "Must have known regions.");
+ assert(getConcreteOffsetRegion() == Base && "Failed to store base region");
+ }
explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
- : P(r, (unsigned) k), Offset(offset) {}
+ : P(r, k), Data(offset) {
+ assert(r && "Must have known regions.");
+ assert(getOffset() == offset && "Failed to store offset");
+ assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base");
+ }
public:
- bool isDirect() const { return P.getInt() == Direct; }
+ bool isDirect() const { return P.getInt() & Direct; }
+ bool hasSymbolicOffset() const { return P.getInt() & Symbolic; }
const MemRegion *getRegion() const { return P.getPointer(); }
- uint64_t getOffset() const { return Offset; }
+ uint64_t getOffset() const {
+ assert(!hasSymbolicOffset());
+ return Data;
+ }
+
+ const MemRegion *getConcreteOffsetRegion() const {
+ assert(hasSymbolicOffset());
+ return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data));
+ }
+
+ const MemRegion *getBaseRegion() const {
+ if (hasSymbolicOffset())
+ return getConcreteOffsetRegion()->getBaseRegion();
+ return getRegion()->getBaseRegion();
+ }
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddPointer(P.getOpaqueValue());
- ID.AddInteger(Offset);
+ ID.AddInteger(Data);
}
static BindingKey Make(const MemRegion *R, Kind k);
@@ -66,48 +93,48 @@ public:
return true;
if (P.getOpaqueValue() > X.P.getOpaqueValue())
return false;
- return Offset < X.Offset;
+ return Data < X.Data;
}
bool operator==(const BindingKey &X) const {
return P.getOpaqueValue() == X.P.getOpaqueValue() &&
- Offset == X.Offset;
+ Data == X.Data;
}
- bool isValid() const {
- return getRegion() != NULL;
- }
+ LLVM_ATTRIBUTE_USED void dump() const;
};
} // end anonymous namespace
BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- const RegionRawOffset &O = ER->getAsArrayOffset();
-
- // FIXME: There are some ElementRegions for which we cannot compute
- // raw offsets yet, including regions with symbolic offsets. These will be
- // ignored by the store.
- return BindingKey(O.getRegion(), O.getOffset().getQuantity(), k);
- }
+ const RegionOffset &RO = R->getAsOffset();
+ if (RO.hasSymbolicOffset())
+ return BindingKey(R, RO.getRegion(), k);
- return BindingKey(R, 0, k);
+ return BindingKey(RO.getRegion(), RO.getOffset(), k);
}
namespace llvm {
static inline
raw_ostream &operator<<(raw_ostream &os, BindingKey K) {
- os << '(' << K.getRegion() << ',' << K.getOffset()
- << ',' << (K.isDirect() ? "direct" : "default")
+ os << '(' << K.getRegion();
+ if (!K.hasSymbolicOffset())
+ os << ',' << K.getOffset();
+ os << ',' << (K.isDirect() ? "direct" : "default")
<< ')';
return os;
}
} // end llvm namespace
+void BindingKey::dump() const {
+ llvm::errs() << *this;
+}
+
//===----------------------------------------------------------------------===//
// Actual Store type.
//===----------------------------------------------------------------------===//
-typedef llvm::ImmutableMap<BindingKey, SVal> RegionBindings;
+typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings;
+typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings;
//===----------------------------------------------------------------------===//
// Fine-grained control of RegionStoreManager.
@@ -138,75 +165,15 @@ public:
namespace {
-class RegionStoreSubRegionMap : public SubRegionMap {
-public:
- typedef llvm::ImmutableSet<const MemRegion*> Set;
- typedef llvm::DenseMap<const MemRegion*, Set> Map;
-private:
- Set::Factory F;
- Map M;
-public:
- bool add(const MemRegion* Parent, const MemRegion* SubRegion) {
- Map::iterator I = M.find(Parent);
-
- if (I == M.end()) {
- M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion)));
- return true;
- }
-
- I->second = F.add(I->second, SubRegion);
- return false;
- }
-
- void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R);
-
- ~RegionStoreSubRegionMap() {}
-
- const Set *getSubRegions(const MemRegion *Parent) const {
- Map::const_iterator I = M.find(Parent);
- return I == M.end() ? NULL : &I->second;
- }
-
- bool iterSubRegions(const MemRegion* Parent, Visitor& V) const {
- Map::const_iterator I = M.find(Parent);
-
- if (I == M.end())
- return true;
-
- Set S = I->second;
- for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) {
- if (!V.Visit(Parent, *SI))
- return false;
- }
-
- return true;
- }
-};
-
-void
-RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL,
- const SubRegion *R) {
- const MemRegion *superR = R->getSuperRegion();
- if (add(superR, R))
- if (const SubRegion *sr = dyn_cast<SubRegion>(superR))
- WL.push_back(sr);
-}
-
class RegionStoreManager : public StoreManager {
const RegionStoreFeatures Features;
RegionBindings::Factory RBFactory;
+ ClusterBindings::Factory CBFactory;
public:
RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f)
- : StoreManager(mgr),
- Features(f),
- RBFactory(mgr.getAllocator()) {}
-
- SubRegionMap *getSubRegionMap(Store store) {
- return getRegionStoreSubRegionMap(store);
- }
-
- RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store);
+ : StoreManager(mgr), Features(f),
+ RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {}
Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R);
/// getDefaultBinding - Returns an SVal* representing an optional default
@@ -257,13 +224,15 @@ public:
const Expr *E, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated);
+ bool scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks);
+
public: // Made public for helper classes.
- void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R,
- RegionStoreSubRegionMap &M);
+ RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R);
RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V);
@@ -282,6 +251,8 @@ public: // Made public for helper classes.
BindingKey::Default);
}
+ RegionBindings removeCluster(RegionBindings B, const MemRegion *R);
+
public: // Part of public interface to class.
StoreRef Bind(Store store, Loc LV, SVal V);
@@ -307,10 +278,14 @@ public: // Part of public interface to class.
/// BindStruct - Bind a compound value to a structure.
StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V);
+ /// BindVector - Bind a compound value to a vector.
+ StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V);
+
StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V);
- /// KillStruct - Set the entire struct to unknown.
- StoreRef KillStruct(Store store, const TypedRegion* R, SVal DefaultVal);
+ /// Clears out all bindings in the given region and assigns a new value
+ /// as a Default binding.
+ StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal);
StoreRef Remove(Store store, Loc LV);
@@ -377,10 +352,8 @@ public: // Part of public interface to class.
/// Get the state and region whose binding this region R corresponds to.
std::pair<Store, const MemRegion*>
GetLazyBinding(RegionBindings B, const MemRegion *R,
- const MemRegion *originalRegion);
-
- StoreRef CopyLazyBindings(nonloc::LazyCompoundVal V, Store store,
- const TypedRegion *R);
+ const MemRegion *originalRegion,
+ bool includeSuffix = false);
//===------------------------------------------------------------------===//
// State pruning.
@@ -390,11 +363,7 @@ public: // Part of public interface to class.
/// It returns a new Store with these values removed.
StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
SymbolReaper& SymReaper);
-
- StoreRef enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx);
-
+
//===------------------------------------------------------------------===//
// Region "extents".
//===------------------------------------------------------------------===//
@@ -416,14 +385,18 @@ public: // Part of public interface to class.
void iterBindings(Store store, BindingsHandler& f) {
RegionBindings B = GetRegionBindings(store);
- for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
- const BindingKey &K = I.getKey();
- if (!K.isDirect())
- continue;
- if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) {
- // FIXME: Possibly incorporate the offset?
- if (!f.HandleBinding(*this, store, R, I.getData()))
- return;
+ for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const BindingKey &K = CI.getKey();
+ if (!K.isDirect())
+ continue;
+ if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) {
+ // FIXME: Possibly incorporate the offset?
+ if (!f.HandleBinding(*this, store, R, CI.getData()))
+ return;
+ }
}
}
}
@@ -448,28 +421,6 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
}
-RegionStoreSubRegionMap*
-RegionStoreManager::getRegionStoreSubRegionMap(Store store) {
- RegionBindings B = GetRegionBindings(store);
- RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap();
-
- SmallVector<const SubRegion*, 10> WL;
-
- for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I)
- if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion()))
- M->process(WL, R);
-
- // We also need to record in the subregion map "intermediate" regions that
- // don't have direct bindings but are super regions of those that do.
- while (!WL.empty()) {
- const SubRegion *R = WL.back();
- WL.pop_back();
- M->process(WL, R);
- }
-
- return M;
-}
-
//===----------------------------------------------------------------------===//
// Region Cluster analysis.
//===----------------------------------------------------------------------===//
@@ -478,14 +429,11 @@ namespace {
template <typename DERIVED>
class ClusterAnalysis {
protected:
- typedef BumpVector<BindingKey> RegionCluster;
- typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap;
- llvm::DenseMap<const RegionCluster*, unsigned> Visited;
- typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10>
- WorkList;
-
- BumpVectorContext BVC;
- ClusterMap ClusterM;
+ typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap;
+ typedef SmallVector<const MemRegion *, 10> WorkList;
+
+ llvm::SmallPtrSet<const ClusterBindings *, 16> Visited;
+
WorkList WL;
RegionStoreManager &RM;
@@ -496,6 +444,10 @@ protected:
const bool includeGlobals;
+ const ClusterBindings *getCluster(const MemRegion *R) {
+ return B.lookup(R);
+ }
+
public:
ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr,
RegionBindings b, const bool includeGlobals)
@@ -505,59 +457,36 @@ public:
RegionBindings getRegionBindings() const { return B; }
- RegionCluster &AddToCluster(BindingKey K) {
- const MemRegion *R = K.getRegion();
- const MemRegion *baseR = R->getBaseRegion();
- RegionCluster &C = getCluster(baseR);
- C.push_back(K, BVC);
- static_cast<DERIVED*>(this)->VisitAddedToCluster(baseR, C);
- return C;
- }
-
bool isVisited(const MemRegion *R) {
- return (bool) Visited[&getCluster(R->getBaseRegion())];
- }
-
- RegionCluster& getCluster(const MemRegion *R) {
- RegionCluster *&CRef = ClusterM[R];
- if (!CRef) {
- void *Mem = BVC.getAllocator().template Allocate<RegionCluster>();
- CRef = new (Mem) RegionCluster(BVC, 10);
- }
- return *CRef;
+ return Visited.count(getCluster(R));
}
void GenerateClusters() {
- // Scan the entire set of bindings and make the region clusters.
+ // Scan the entire set of bindings and record the region clusters.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- RegionCluster &C = AddToCluster(RI.getKey());
- if (const MemRegion *R = RI.getData().getAsRegion()) {
- // Generate a cluster, but don't add the region to the cluster
- // if there aren't any bindings.
- getCluster(R->getBaseRegion());
- }
- if (includeGlobals) {
- const MemRegion *R = RI.getKey().getRegion();
- if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()))
- AddToWorkList(R, C);
- }
+ const MemRegion *Base = RI.getKey();
+
+ const ClusterBindings &Cluster = RI.getData();
+ assert(!Cluster.isEmpty() && "Empty clusters should be removed");
+ static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster);
+
+ if (includeGlobals)
+ if (isa<NonStaticGlobalSpaceRegion>(Base->getMemorySpace()))
+ AddToWorkList(Base, &Cluster);
}
}
- bool AddToWorkList(const MemRegion *R, RegionCluster &C) {
- if (unsigned &visited = Visited[&C])
- return false;
- else
- visited = 1;
+ bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) {
+ if (C) {
+ if (Visited.count(C))
+ return false;
+ Visited.insert(C);
+ }
- WL.push_back(std::make_pair(R, &C));
+ WL.push_back(R);
return true;
}
- bool AddToWorkList(BindingKey K) {
- return AddToWorkList(K.getRegion());
- }
-
bool AddToWorkList(const MemRegion *R) {
const MemRegion *baseR = R->getBaseRegion();
return AddToWorkList(baseR, getCluster(baseR));
@@ -565,22 +494,20 @@ public:
void RunWorkList() {
while (!WL.empty()) {
- const MemRegion *baseR;
- RegionCluster *C;
- llvm::tie(baseR, C) = WL.back();
- WL.pop_back();
+ const MemRegion *baseR = WL.pop_back_val();
- // First visit the cluster.
- static_cast<DERIVED*>(this)->VisitCluster(baseR, C->begin(), C->end());
+ // First visit the cluster.
+ if (const ClusterBindings *Cluster = getCluster(baseR))
+ static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster);
- // Next, visit the base region.
+ // Next, visit the base region.
static_cast<DERIVED*>(this)->VisitBaseRegion(baseR);
}
}
public:
- void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C) {}
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E) {}
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {}
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {}
void VisitBaseRegion(const MemRegion *baseR) {}
};
}
@@ -589,16 +516,99 @@ public:
// Binding invalidation.
//===----------------------------------------------------------------------===//
-void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B,
- const MemRegion *R,
- RegionStoreSubRegionMap &M) {
+bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks) {
+ assert(R == R->getBaseRegion() && "Should only be called for base regions");
+ RegionBindings B = GetRegionBindings(S);
+ const ClusterBindings *Cluster = B.lookup(R);
+
+ if (!Cluster)
+ return true;
- if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R))
- for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end();
- I != E; ++I)
- RemoveSubRegionBindings(B, *I, M);
+ for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end();
+ RI != RE; ++RI) {
+ if (!Callbacks.scan(RI.getData()))
+ return false;
+ }
- B = removeBinding(B, R);
+ return true;
+}
+
+RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B,
+ const SubRegion *R) {
+ BindingKey SRKey = BindingKey::Make(R, BindingKey::Default);
+ const MemRegion *ClusterHead = SRKey.getBaseRegion();
+ if (R == ClusterHead) {
+ // We can remove an entire cluster's bindings all in one go.
+ return RBFactory.remove(B, R);
+ }
+
+ if (SRKey.hasSymbolicOffset()) {
+ const SubRegion *Base = cast<SubRegion>(SRKey.getConcreteOffsetRegion());
+ B = removeSubRegionBindings(B, Base);
+ return addBinding(B, Base, BindingKey::Default, UnknownVal());
+ }
+
+ // This assumes the region being invalidated is char-aligned. This isn't
+ // true for bitfields, but since bitfields have no subregions they shouldn't
+ // be using this function anyway.
+ uint64_t Length = UINT64_MAX;
+
+ SVal Extent = R->getExtent(svalBuilder);
+ if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) {
+ const llvm::APSInt &ExtentInt = ExtentCI->getValue();
+ assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
+ // Extents are in bytes but region offsets are in bits. Be careful!
+ Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth();
+ }
+
+ const ClusterBindings *Cluster = B.lookup(ClusterHead);
+ if (!Cluster)
+ return B;
+
+ ClusterBindings Result = *Cluster;
+
+ // It is safe to iterate over the bindings as they are being changed
+ // because they are in an ImmutableMap.
+ for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end();
+ I != E; ++I) {
+ BindingKey NextKey = I.getKey();
+ if (NextKey.getRegion() == SRKey.getRegion()) {
+ if (NextKey.getOffset() > SRKey.getOffset() &&
+ NextKey.getOffset() - SRKey.getOffset() < Length) {
+ // Case 1: The next binding is inside the region we're invalidating.
+ // Remove it.
+ Result = CBFactory.remove(Result, NextKey);
+ } else if (NextKey.getOffset() == SRKey.getOffset()) {
+ // Case 2: The next binding is at the same offset as the region we're
+ // invalidating. In this case, we need to leave default bindings alone,
+ // since they may be providing a default value for a regions beyond what
+ // we're invalidating.
+ // FIXME: This is probably incorrect; consider invalidating an outer
+ // struct whose first field is bound to a LazyCompoundVal.
+ if (NextKey.isDirect())
+ Result = CBFactory.remove(Result, NextKey);
+ }
+ } else if (NextKey.hasSymbolicOffset()) {
+ const MemRegion *Base = NextKey.getConcreteOffsetRegion();
+ if (R->isSubRegionOf(Base)) {
+ // Case 3: The next key is symbolic and we just changed something within
+ // its concrete region. We don't know if the binding is still valid, so
+ // we'll be conservative and remove it.
+ if (NextKey.isDirect())
+ Result = CBFactory.remove(Result, NextKey);
+ } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
+ // Case 4: The next key is symbolic, but we changed a known
+ // super-region. In this case the binding is certainly no longer valid.
+ if (R == Base || BaseSR->isSubRegionOf(R))
+ Result = CBFactory.remove(Result, NextKey);
+ }
+ }
+ }
+
+ if (Result.isEmpty())
+ return RBFactory.remove(B, ClusterHead);
+ return RBFactory.add(B, ClusterHead, Result);
}
namespace {
@@ -621,7 +631,7 @@ public:
: ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals),
Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {}
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C);
void VisitBaseRegion(const MemRegion *baseR);
private:
@@ -646,26 +656,31 @@ void invalidateRegionsWorker::VisitBinding(SVal V) {
const MemRegion *LazyR = LCS->getRegion();
RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore());
+ // FIXME: This should not have to walk all bindings in the old store.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion());
- if (baseR && baseR->isSubRegionOf(LazyR))
- VisitBinding(RI.getData());
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ BindingKey K = CI.getKey();
+ if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) {
+ if (BaseR == LazyR)
+ VisitBinding(CI.getData());
+ else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR))
+ VisitBinding(CI.getData());
+ }
+ }
}
return;
}
}
-void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
- BindingKey *I, BindingKey *E) {
- for ( ; I != E; ++I) {
- // Get the old binding. Is it a region? If so, add it to the worklist.
- const BindingKey &K = *I;
- if (const SVal *V = RM.lookup(B, K))
- VisitBinding(*V);
+void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR,
+ const ClusterBindings &C) {
+ for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I)
+ VisitBinding(I.getData());
- B = RM.removeBinding(B, K);
- }
+ B = RM.removeCluster(B, BaseR);
}
void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
@@ -681,8 +696,22 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
BI != BE; ++BI) {
const VarRegion *VR = *BI;
const VarDecl *VD = VR->getDecl();
- if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage())
+ if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) {
AddToWorkList(VR);
+ }
+ else if (Loc::isLocType(VR->getValueType())) {
+ // Map the current bindings to a Store to retrieve the value
+ // of the binding. If that binding itself is a region, we should
+ // invalidate that region. This is because a block may capture
+ // a pointer value, but the thing pointed by that pointer may
+ // get invalidated.
+ Store store = B.getRootWithoutRetain();
+ SVal V = RM.getBinding(store, loc::MemRegionVal(VR));
+ if (const Loc *L = dyn_cast<Loc>(&V)) {
+ if (const MemRegion *LR = L->getAsRegion())
+ AddToWorkList(LR);
+ }
+ }
}
return;
}
@@ -771,7 +800,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
const Expr *Ex, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated) {
invalidateRegionsWorker W(*this, StateMgr,
RegionStoreManager::GetRegionBindings(store),
@@ -868,10 +897,22 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) {
return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx));
}
+// This mirrors Type::getCXXRecordDeclForPointerType(), but there doesn't
+// appear to be another need for this in the rest of the codebase.
+static const CXXRecordDecl *GetCXXRecordDeclForReferenceType(QualType Ty) {
+ if (const ReferenceType *RT = Ty->getAs<ReferenceType>())
+ if (const RecordType *RCT = RT->getPointeeType()->getAs<RecordType>())
+ return dyn_cast<CXXRecordDecl>(RCT->getDecl());
+ return 0;
+}
+
SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) {
const CXXRecordDecl *baseDecl;
+
if (baseType->isPointerType())
baseDecl = baseType->getCXXRecordDeclForPointerType();
+ else if (baseType->isReferenceType())
+ baseDecl = GetCXXRecordDeclForReferenceType(baseType);
else
baseDecl = baseType->getAsCXXRecordDecl();
@@ -894,7 +935,7 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base);
if (!baseRegVal)
return UnknownVal();
- const MemRegion *BaseRegion = baseRegVal->stripCasts();
+ const MemRegion *BaseRegion = baseRegVal->stripCasts(/*StripBases=*/false);
// Assume the derived class is a pointer or a reference to a CXX record.
derivedType = derivedType->getPointeeType();
@@ -917,23 +958,20 @@ SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
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 (!derivedType->isVoidType()) {
+ // Static upcasts are marked as DerivedToBase casts by Sema, so this will
+ // only happen when multiple or virtual inheritance is involved.
+ CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ if (SRDecl->isDerivedFrom(DerivedDecl, Paths)) {
+ SVal Result = loc::MemRegionVal(TSR);
+ const CXXBasePath &Path = *Paths.begin();
+ for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end();
+ I != E; ++I) {
+ Result = evalDerivedToBase(Result, I->Base->getType());
+ }
+ return Result;
+ }
}
if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR))
@@ -1036,8 +1074,12 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) {
if (RTy->isUnionType())
return UnknownVal();
- if (RTy->isArrayType())
- return getBindingForArray(store, R);
+ if (RTy->isArrayType()) {
+ if (RTy->isConstantArrayType())
+ return getBindingForArray(store, R);
+ else
+ return UnknownVal();
+ }
// FIXME: handle Vector types.
if (RTy->isVectorType())
@@ -1099,7 +1141,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) {
std::pair<Store, const MemRegion *>
RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
- const MemRegion *originalRegion) {
+ const MemRegion *originalRegion,
+ bool includeSuffix) {
if (originalRegion != R) {
if (Optional<SVal> OV = getDefaultBinding(B, R)) {
@@ -1121,9 +1164,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
const std::pair<Store, const MemRegion *> &X =
GetLazyBinding(B, FR->getSuperRegion(), originalRegion);
- if (X.second)
- return std::make_pair(X.first,
- MRMgr.getFieldRegionWithSuper(FR, X.second));
+ if (X.second) {
+ if (includeSuffix)
+ return std::make_pair(X.first,
+ MRMgr.getFieldRegionWithSuper(FR, X.second));
+ return X;
+ }
+
}
// C++ base object region is another kind of region that we should blast
// through to look for lazy compound value. It is like a field region.
@@ -1132,9 +1179,13 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
const std::pair<Store, const MemRegion *> &X =
GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion);
- if (X.second)
- return std::make_pair(X.first,
- MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, X.second));
+ if (X.second) {
+ if (includeSuffix)
+ return std::make_pair(X.first,
+ MRMgr.getCXXBaseObjectRegionWithSuper(baseReg,
+ X.second));
+ return X;
+ }
}
// The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is
@@ -1143,7 +1194,11 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R,
}
SVal RegionStoreManager::getBindingForElement(Store store,
- const ElementRegion* R) {
+ const ElementRegion* R) {
+ // We do not currently model bindings of the CompoundLiteralregion.
+ if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
+ return UnknownVal();
+
// Check if the region has a binding.
RegionBindings B = GetRegionBindings(store);
if (const Optional<SVal> &V = getDirectBinding(B, R))
@@ -1274,7 +1329,16 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store,
// At this point we have already checked in either getBindingForElement or
// getBindingForField if 'R' has a direct binding.
RegionBindings B = GetRegionBindings(store);
+
+ // Lazy binding?
+ Store lazyBindingStore = NULL;
+ const MemRegion *lazyBindingRegion = NULL;
+ llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R,
+ true);
+ if (lazyBindingRegion)
+ return getLazyBinding(lazyBindingRegion, lazyBindingStore);
+
// Record whether or not we see a symbolic index. That can completely
// be out of scope of our lookup.
bool hasSymbolicIndex = false;
@@ -1299,14 +1363,6 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store,
break;
}
- // Lazy binding?
- Store lazyBindingStore = NULL;
- const MemRegion *lazyBindingRegion = NULL;
- llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R);
-
- if (lazyBindingRegion)
- return getLazyBinding(lazyBindingRegion, lazyBindingStore);
-
if (R->hasStackNonParametersStorage()) {
if (isa<ElementRegion>(R)) {
// Currently we don't reason specially about Clang-style vectors. Check
@@ -1410,15 +1466,49 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) {
return svalBuilder.getRegionValueSymbolVal(R);
}
+static bool mayHaveLazyBinding(QualType Ty) {
+ return Ty->isArrayType() || Ty->isStructureOrClassType();
+}
+
SVal RegionStoreManager::getBindingForStruct(Store store,
const TypedValueRegion* R) {
- assert(R->getValueType()->isStructureOrClassType());
+ const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl();
+ if (RD->field_empty())
+ return UnknownVal();
+
+ // If we already have a lazy binding, don't create a new one,
+ // unless the first field might have a lazy binding of its own.
+ // (Right now we can't tell the difference.)
+ QualType FirstFieldType = RD->field_begin()->getType();
+ if (!mayHaveLazyBinding(FirstFieldType)) {
+ RegionBindings B = GetRegionBindings(store);
+ BindingKey K = BindingKey::Make(R, BindingKey::Default);
+ if (const nonloc::LazyCompoundVal *V =
+ dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+ return *V;
+ }
+ }
+
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
-SVal RegionStoreManager::getBindingForArray(Store store,
+SVal RegionStoreManager::getBindingForArray(Store store,
const TypedValueRegion * R) {
- assert(Ctx.getAsConstantArrayType(R->getValueType()));
+ const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType());
+ assert(Ty && "Only constant array types can have compound bindings.");
+
+ // If we already have a lazy binding, don't create a new one,
+ // unless the first element might have a lazy binding of its own.
+ // (Right now we can't tell the difference.)
+ if (!mayHaveLazyBinding(Ty->getElementType())) {
+ RegionBindings B = GetRegionBindings(store);
+ BindingKey K = BindingKey::Make(R, BindingKey::Default);
+ if (const nonloc::LazyCompoundVal *V =
+ dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) {
+ return *V;
+ }
+ }
+
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
@@ -1426,16 +1516,23 @@ bool RegionStoreManager::includedInBindings(Store store,
const MemRegion *region) const {
RegionBindings B = GetRegionBindings(store);
region = region->getBaseRegion();
-
- for (RegionBindings::iterator it = B.begin(), ei = B.end(); it != ei; ++it) {
- const BindingKey &K = it.getKey();
- if (region == K.getRegion())
- return true;
- const SVal &D = it.getData();
- if (const MemRegion *r = D.getAsRegion())
- if (r == region)
- return true;
+
+ // Quick path: if the base is the head of a cluster, the region is live.
+ if (B.lookup(region))
+ return true;
+
+ // Slow path: if the region is the VALUE of any binding, it is live.
+ for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) {
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const SVal &D = CI.getData();
+ if (const MemRegion *R = D.getAsRegion())
+ if (R->getBaseRegion() == region)
+ return true;
+ }
}
+
return false;
}
@@ -1461,24 +1558,15 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
// Check if the region is a struct region.
- if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R))
- if (TR->getValueType()->isStructureOrClassType())
+ if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType Ty = TR->getValueType();
+ if (Ty->isStructureOrClassType())
return BindStruct(store, TR, V);
-
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- if (ER->getIndex().isZeroConstant()) {
- if (const TypedValueRegion *superR =
- dyn_cast<TypedValueRegion>(ER->getSuperRegion())) {
- QualType superTy = superR->getValueType();
- // For now, just invalidate the fields of the struct/union/class.
- // This is for test rdar_test_7185607 in misc-ps-region-store.m.
- // FIXME: Precisely handle the fields of the record.
- if (superTy->isStructureOrClassType())
- return KillStruct(store, superR, UnknownVal());
- }
- }
+ if (Ty->isVectorType())
+ return BindVector(store, TR, V);
}
- else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
// Binding directly to a symbolic region should be treated as binding
// to element 0.
QualType T = SR->getSymbol()->getType(Ctx);
@@ -1492,10 +1580,13 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) {
R = GetElementZeroRegion(SR, T);
}
+ // Clear out bindings that may overlap with this binding.
+
// Perform the binding.
RegionBindings B = GetRegionBindings(store);
- return StoreRef(addBinding(B, R, BindingKey::Direct,
- V).getRootWithoutRetain(), *this);
+ B = removeSubRegionBindings(B, cast<SubRegion>(R));
+ BindingKey Key = BindingKey::Make(R, BindingKey::Direct);
+ return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this);
}
StoreRef RegionStoreManager::BindDecl(Store store, const VarRegion *VR,
@@ -1566,12 +1657,12 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R,
nonloc::LazyCompoundVal LCV =
cast<nonloc::LazyCompoundVal>(svalBuilder.
makeLazyCompoundVal(StoreRef(store, *this), S));
- return CopyLazyBindings(LCV, store, R);
+ return BindAggregate(store, R, LCV);
}
// Handle lazy compound values.
- if (nonloc::LazyCompoundVal *LCV = dyn_cast<nonloc::LazyCompoundVal>(&Init))
- return CopyLazyBindings(*LCV, store, R);
+ if (isa<nonloc::LazyCompoundVal>(Init))
+ return BindAggregate(store, R, Init);
// Remaining case: explicit compound values.
@@ -1607,6 +1698,46 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R,
return newStore;
}
+StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R,
+ SVal V) {
+ QualType T = R->getValueType();
+ assert(T->isVectorType());
+ const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
+
+ // Handle lazy compound values and symbolic values.
+ if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V))
+ return BindAggregate(store, R, V);
+
+ // We may get non-CompoundVal accidentally due to imprecise cast logic or
+ // that we are binding symbolic struct value. Kill the field values, and if
+ // the value is symbolic go and bind it as a "default" binding.
+ if (!isa<nonloc::CompoundVal>(V)) {
+ return BindAggregate(store, R, UnknownVal());
+ }
+
+ QualType ElemType = VT->getElementType();
+ nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
+ nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
+ unsigned index = 0, numElements = VT->getNumElements();
+ StoreRef newStore(store, *this);
+
+ for ( ; index != numElements ; ++index) {
+ if (VI == VE)
+ break;
+
+ NonLoc Idx = svalBuilder.makeArrayIndex(index);
+ const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx);
+
+ if (ElemType->isArrayType())
+ newStore = BindArray(newStore.getStore(), ER, *VI);
+ else if (ElemType->isStructureOrClassType())
+ newStore = BindStruct(newStore.getStore(), ER, *VI);
+ else
+ newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI);
+ }
+ return newStore;
+}
+
StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
SVal V) {
@@ -1622,17 +1753,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
if (!RD->isCompleteDefinition())
return StoreRef(store, *this);
- // Handle lazy compound values.
- if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V))
- return CopyLazyBindings(*LCV, store, R);
+ // Handle lazy compound values and symbolic values.
+ if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V))
+ return BindAggregate(store, R, V);
// We may get non-CompoundVal accidentally due to imprecise cast logic or
// that we are binding symbolic struct value. Kill the field values, and if
// the value is symbolic go and bind it as a "default" binding.
- if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) {
- SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal();
- return KillStruct(store, R, SV);
- }
+ if (V.isUnknown() || !isa<nonloc::CompoundVal>(V))
+ return BindAggregate(store, R, UnknownVal());
nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V);
nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
@@ -1646,10 +1775,10 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
break;
// Skip any unnamed bitfields to stay in sync with the initializers.
- if ((*FI)->isUnnamedBitfield())
+ if (FI->isUnnamedBitfield())
continue;
- QualType FTy = (*FI)->getType();
+ QualType FTy = FI->getType();
const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);
if (FTy->isArrayType())
@@ -1671,58 +1800,16 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
return newStore;
}
-StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R,
- SVal DefaultVal) {
- BindingKey key = BindingKey::Make(R, BindingKey::Default);
-
- // The BindingKey may be "invalid" if we cannot handle the region binding
- // explicitly. One example is something like array[index], where index
- // is a symbolic value. In such cases, we want to invalidate the entire
- // array, as the index assignment could have been to any element. In
- // the case of nested symbolic indices, we need to march up the region
- // hierarchy untile we reach a region whose binding we can reason about.
- const SubRegion *subReg = R;
-
- while (!key.isValid()) {
- if (const SubRegion *tmp = dyn_cast<SubRegion>(subReg->getSuperRegion())) {
- subReg = tmp;
- key = BindingKey::Make(tmp, BindingKey::Default);
- }
- else
- break;
- }
-
- // Remove the old bindings, using 'subReg' as the root of all regions
- // we will invalidate.
+StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R,
+ SVal Val) {
+ // Remove the old bindings, using 'R' as the root of all regions
+ // we will invalidate. Then add the new binding.
RegionBindings B = GetRegionBindings(store);
- OwningPtr<RegionStoreSubRegionMap>
- SubRegions(getRegionStoreSubRegionMap(store));
- RemoveSubRegionBindings(B, subReg, *SubRegions);
- // Set the default value of the struct region to "unknown".
- if (!key.isValid())
- return StoreRef(B.getRootWithoutRetain(), *this);
-
- return StoreRef(addBinding(B, key, DefaultVal).getRootWithoutRetain(), *this);
-}
-
-StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
- Store store,
- const TypedRegion *R) {
-
- // Nuke the old bindings stemming from R.
- RegionBindings B = GetRegionBindings(store);
+ B = removeSubRegionBindings(B, R);
+ B = addBinding(B, R, BindingKey::Default, Val);
- OwningPtr<RegionStoreSubRegionMap>
- SubRegions(getRegionStoreSubRegionMap(store));
-
- // B and DVM are updated after the call to RemoveSubRegionBindings.
- RemoveSubRegionBindings(B, R, *SubRegions.get());
-
- // Now copy the bindings. This amounts to just binding 'V' to 'R'. This
- // results in a zero-copy algorithm.
- return StoreRef(addBinding(B, R, BindingKey::Default,
- V).getRootWithoutRetain(), *this);
+ return StoreRef(B.getRootWithoutRetain(), *this);
}
//===----------------------------------------------------------------------===//
@@ -1732,9 +1819,14 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V,
RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K,
SVal V) {
- if (!K.isValid())
- return B;
- return RBFactory.add(B, K, V);
+ const MemRegion *Base = K.getBaseRegion();
+
+ const ClusterBindings *ExistingCluster = B.lookup(Base);
+ ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster
+ : CBFactory.getEmptyMap());
+
+ ClusterBindings NewCluster = CBFactory.add(Cluster, K, V);
+ return RBFactory.add(B, Base, NewCluster);
}
RegionBindings RegionStoreManager::addBinding(RegionBindings B,
@@ -1744,9 +1836,11 @@ RegionBindings RegionStoreManager::addBinding(RegionBindings B,
}
const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) {
- if (!K.isValid())
- return NULL;
- return B.lookup(K);
+ const ClusterBindings *Cluster = B.lookup(K.getBaseRegion());
+ if (!Cluster)
+ return 0;
+
+ return Cluster->lookup(K);
}
const SVal *RegionStoreManager::lookup(RegionBindings B,
@@ -1757,9 +1851,15 @@ const SVal *RegionStoreManager::lookup(RegionBindings B,
RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
BindingKey K) {
- if (!K.isValid())
+ const MemRegion *Base = K.getBaseRegion();
+ const ClusterBindings *Cluster = B.lookup(Base);
+ if (!Cluster)
return B;
- return RBFactory.remove(B, K);
+
+ ClusterBindings NewCluster = CBFactory.remove(*Cluster, K);
+ if (NewCluster.isEmpty())
+ return RBFactory.remove(B, Base);
+ return RBFactory.add(B, Base, NewCluster);
}
RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
@@ -1768,6 +1868,11 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
return removeBinding(B, BindingKey::Make(R, k));
}
+RegionBindings RegionStoreManager::removeCluster(RegionBindings B,
+ const MemRegion *Base) {
+ return RBFactory.remove(B, Base);
+}
+
//===----------------------------------------------------------------------===//
// State pruning.
//===----------------------------------------------------------------------===//
@@ -1789,8 +1894,8 @@ public:
SymReaper(symReaper), CurrentLCtx(LCtx) {}
// Called by ClusterAnalysis.
- void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C);
- void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E);
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C);
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings &C);
void VisitBindingKey(BindingKey K);
bool UpdatePostponed();
@@ -1799,18 +1904,18 @@ public:
}
void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
- RegionCluster &C) {
+ const ClusterBindings &C) {
if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) {
if (SymReaper.isLive(VR))
- AddToWorkList(baseR, C);
+ AddToWorkList(baseR, &C);
return;
}
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) {
if (SymReaper.isLive(SR->getSymbol()))
- AddToWorkList(SR, C);
+ AddToWorkList(SR, &C);
else
Postponed.push_back(SR);
@@ -1818,7 +1923,7 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
}
if (isa<NonStaticGlobalSpaceRegion>(baseR)) {
- AddToWorkList(baseR, C);
+ AddToWorkList(baseR, &C);
return;
}
@@ -1828,34 +1933,57 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
cast<StackArgumentsSpaceRegion>(TR->getSuperRegion());
const StackFrameContext *RegCtx = StackReg->getStackFrame();
if (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))
- AddToWorkList(TR, C);
+ AddToWorkList(TR, &C);
}
}
void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR,
- BindingKey *I, BindingKey *E) {
- for ( ; I != E; ++I)
- VisitBindingKey(*I);
+ const ClusterBindings &C) {
+ for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) {
+ VisitBindingKey(I.getKey());
+ VisitBinding(I.getData());
+ }
}
void removeDeadBindingsWorker::VisitBinding(SVal V) {
// Is it a LazyCompoundVal? All referenced regions are live as well.
if (const nonloc::LazyCompoundVal *LCS =
- dyn_cast<nonloc::LazyCompoundVal>(&V)) {
+ dyn_cast<nonloc::LazyCompoundVal>(&V)) {
const MemRegion *LazyR = LCS->getRegion();
RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore());
+
+ // FIXME: This should not have to walk all bindings in the old store.
for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){
- const SubRegion *baseR = dyn_cast<SubRegion>(RI.getKey().getRegion());
- if (baseR && baseR->isSubRegionOf(LazyR))
- VisitBinding(RI.getData());
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ BindingKey K = CI.getKey();
+ if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) {
+ if (BaseR == LazyR)
+ VisitBinding(CI.getData());
+ else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR))
+ VisitBinding(CI.getData());
+ }
+ }
}
+
return;
}
// If V is a region, then add it to the worklist.
- if (const MemRegion *R = V.getAsRegion())
+ if (const MemRegion *R = V.getAsRegion()) {
AddToWorkList(R);
+
+ // All regions captured by a block are also live.
+ if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end();
+ for ( ; I != E; ++I)
+ AddToWorkList(I.getCapturedRegion());
+ }
+ }
+
// Update the set of live symbols.
for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end();
@@ -1874,26 +2002,7 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) {
// should continue to track that symbol.
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R))
SymReaper.markLive(SymR->getSymbol());
-
- // For BlockDataRegions, enqueue the VarRegions for variables marked
- // with __block (passed-by-reference).
- // via BlockDeclRefExprs.
- if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) {
- for (BlockDataRegion::referenced_vars_iterator
- RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end();
- RI != RE; ++RI) {
- if ((*RI)->getDecl()->getAttr<BlocksAttr>())
- AddToWorkList(*RI);
- }
-
- // No possible data bindings on a BlockDataRegion.
- return;
- }
}
-
- // Visit the data binding for K.
- if (const SVal *V = RM.lookup(B, K))
- VisitBinding(*V);
}
bool removeDeadBindingsWorker::UpdatePostponed() {
@@ -1933,68 +2042,32 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
// as live. We now remove all the regions that are dead from the store
// as well as update DSymbols with the set symbols that are now dead.
for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- const BindingKey &K = I.getKey();
+ const MemRegion *Base = I.getKey();
// If the cluster has been visited, we know the region has been marked.
- if (W.isVisited(K.getRegion()))
+ if (W.isVisited(Base))
continue;
// Remove the dead entry.
- B = removeBinding(B, K);
+ B = removeCluster(B, Base);
- // Mark all non-live symbols that this binding references as dead.
- if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(K.getRegion()))
+ if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base))
SymReaper.maybeDead(SymR->getSymbol());
- SVal X = I.getData();
- SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
- for (; SI != SE; ++SI)
- SymReaper.maybeDead(*SI);
+ // Mark all non-live symbols that this binding references as dead.
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ SVal X = CI.getData();
+ SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end();
+ for (; SI != SE; ++SI)
+ SymReaper.maybeDead(*SI);
+ }
}
return StoreRef(B.getRootWithoutRetain(), *this);
}
-
-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>(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, callerCtx);
- store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
- ArgVal);
- }
- } else if (const CXXConstructExpr *CE =
- 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, callerCtx);
- store = Bind(store.getStore(),
- svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)),
- ArgVal);
- }
- } else
- assert(isa<CXXDestructorDecl>(calleeCtx->getDecl()));
-
- return store;
-}
-
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
@@ -2002,8 +2075,16 @@ StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state,
void RegionStoreManager::print(Store store, raw_ostream &OS,
const char* nl, const char *sep) {
RegionBindings B = GetRegionBindings(store);
- OS << "Store (direct and default bindings):" << nl;
+ OS << "Store (direct and default bindings), "
+ << (void*) B.getRootWithoutRetain()
+ << " :" << nl;
- for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
- OS << ' ' << I.getKey() << " : " << I.getData() << nl;
+ for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ OS << ' ' << CI.getKey() << " : " << CI.getData() << nl;
+ }
+ OS << nl;
+ }
}
diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 9e97f5e..d1936cd 100644
--- a/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
@@ -61,7 +62,6 @@ NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs,
NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
const SymExpr *rhs, QualType type) {
assert(lhs && rhs);
- assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true);
assert(!Loc::isLocType(type));
return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
}
@@ -149,6 +149,18 @@ SValBuilder::getConjuredSymbolVal(const Stmt *stmt,
return nonloc::SymbolVal(sym);
}
+DefinedOrUnknownSVal
+SValBuilder::getConjuredHeapSymbolVal(const Expr *E,
+ const LocationContext *LCtx,
+ unsigned VisitCount) {
+ QualType T = E->getType();
+ assert(Loc::isLocType(T));
+ assert(SymbolManager::canSymbolicate(T));
+
+ SymbolRef sym = SymMgr.getConjuredSymbol(E, LCtx, T, VisitCount);
+ return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym));
+}
+
DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
const MemRegion *region,
const Expr *expr, QualType type,
@@ -193,31 +205,48 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
return loc::MemRegionVal(BD);
}
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D,
+ const StackFrameContext *SFC) {
+ return loc::MemRegionVal(getRegionManager().
+ getCXXThisRegion(D->getThisType(getContext()), SFC));
+}
+
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
+ const StackFrameContext *SFC) {
+ const Type *T = D->getTypeForDecl();
+ QualType PT = getContext().getPointerType(QualType(T, 0));
+ return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
+}
+
//===----------------------------------------------------------------------===//
-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();
+SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
+ BinaryOperator::Opcode Op,
+ NonLoc LHS, NonLoc RHS,
+ QualType ResultTy) {
+ if (!State->isTainted(RHS) && !State->isTainted(LHS))
+ return UnknownVal();
+
+ const SymExpr *symLHS = LHS.getAsSymExpr();
+ const SymExpr *symRHS = RHS.getAsSymExpr();
+ // TODO: When the Max Complexity is reached, we should conjure a symbol
+ // instead of generating an Unknown value and propagate the taint info to it.
+ const unsigned MaxComp = 10000; // 100000 28X
+
+ if (symLHS && symRHS &&
+ (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
+ return makeNonLoc(symLHS, Op, symRHS, ResultTy);
+
+ if (symLHS && symLHS->computeComplexity() < MaxComp)
+ if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS))
return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
- }
- if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) {
- symRHS = RHS.getAsSymExpr();
+ if (symRHS && symRHS->computeComplexity() < MaxComp)
+ if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS))
return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
- }
- symLHS = LHS.getAsSymExpr();
- symRHS = RHS.getAsSymExpr();
- return makeNonLoc(symLHS, Op, symRHS, ResultTy);
- }
return UnknownVal();
}
@@ -324,7 +353,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
// Are we casting from an array to a pointer? If so just pass on
// the decayed value.
- if (castTy->isPointerType())
+ if (castTy->isPointerType() || castTy->isReferenceType())
return val;
// Are we casting from an array to an integer? If so, cast the decayed
@@ -340,9 +369,12 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
// Check for casts from a region to a specific type.
if (const MemRegion *R = val.getAsRegion()) {
+ // Handle other casts of locations to integers.
+ if (castTy->isIntegerType())
+ return evalCastFromLoc(loc::MemRegionVal(R), castTy);
+
// FIXME: We should handle the case where we strip off view layers to get
// to a desugared type.
-
if (!Loc::isLocType(castTy)) {
// FIXME: There can be gross cases where one casts the result of a function
// (that returns a pointer) to some other value that happens to fit
diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp
index b94aff4..8437f50 100644
--- a/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/lib/StaticAnalyzer/Core/SVals.cpp
@@ -133,9 +133,9 @@ const MemRegion *SVal::getAsRegion() const {
return 0;
}
-const MemRegion *loc::MemRegionVal::stripCasts() const {
+const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const {
const MemRegion *R = getRegion();
- return R ? R->StripCasts() : NULL;
+ return R ? R->StripCasts(StripBaseCasts) : NULL;
}
const void *nonloc::LazyCompoundVal::getStore() const {
@@ -309,22 +309,6 @@ void Loc::dumpToStream(raw_ostream &os) const {
case loc::MemRegionKind:
os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString();
break;
- case loc::ObjCPropRefKind: {
- const ObjCPropertyRefExpr *E = cast<loc::ObjCPropRef>(this)->getPropRefExpr();
- os << "objc-prop{";
- if (E->isSuperReceiver())
- os << "super.";
- else if (E->getBase())
- os << "<base>.";
-
- if (E->isImplicitProperty())
- os << E->getImplicitPropertyGetter()->getSelector().getAsString();
- else
- os << E->getExplicitProperty()->getName();
-
- os << "}";
- break;
- }
default:
llvm_unreachable("Pretty-printing not implemented for this Loc.");
}
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index a76a2da..5568f1c 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -71,9 +72,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond,
ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
Loc Cond, bool Assumption) {
-
- BasicValueFactory &BasicVals = state->getBasicVals();
-
switch (Cond.getSubKind()) {
default:
assert (false && "'Assume' not implemented for this Loc.");
@@ -88,7 +86,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
while (SubR) {
// FIXME: now we only find the first symbolic region.
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) {
- const llvm::APSInt &zero = BasicVals.getZeroWithPtrWidth();
+ const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth();
if (Assumption)
return assumeSymNE(state, SymR->getSymbol(), zero, zero);
else
@@ -134,12 +132,17 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
}
-ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(
- ProgramStateRef State,
- SymbolRef Sym,
- bool Assumption) {
- QualType T = State->getSymbolManager().getType(Sym);
- const llvm::APSInt &zero = State->getBasicVals().getValue(0, T);
+ProgramStateRef
+SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State,
+ SymbolRef Sym, bool Assumption) {
+ BasicValueFactory &BVF = getBasicVals();
+ QualType T = Sym->getType(BVF.getContext());
+
+ // None of the constraint solvers currently support non-integer types.
+ if (!T->isIntegerType())
+ return State;
+
+ const llvm::APSInt &zero = BVF.getValue(0, T);
if (Assumption)
return assumeSymNE(State, Sym, zero, zero);
else
@@ -158,8 +161,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
return assumeAuxForSymbol(state, sym, Assumption);
}
- BasicValueFactory &BasicVals = state->getBasicVals();
- SymbolManager &SymMgr = state->getSymbolManager();
+ BasicValueFactory &BasicVals = getBasicVals();
switch (Cond.getSubKind()) {
default:
@@ -184,7 +186,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
BinaryOperator::Opcode op = SE->getOpcode();
// Implicitly compare non-comparison expressions to 0.
if (!BinaryOperator::isComparisonOp(op)) {
- QualType T = SymMgr.getType(SE);
+ QualType T = SE->getType(BasicVals.getContext());
const llvm::APSInt &zero = BasicVals.getValue(0, T);
op = (Assumption ? BO_NE : BO_EQ);
return assumeSymRel(state, SE, op, zero);
@@ -209,33 +211,20 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state,
} // end switch
}
-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;
+static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) {
+ // Is it a "($sym+constant1)" expression?
+ if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
+ BinaryOperator::Opcode Op = SE->getOpcode();
+ if (Op == BO_Add || Op == BO_Sub) {
+ Sym = SE->getLHS();
+ Adjustment = APSIntType(Adjustment).convert(SE->getRHS());
+
+ // Don't forget to negate the adjustment if it's being subtracted.
+ // This should happen /after/ promotion, in case the value being
+ // subtracted is, say, CHAR_MIN, and the promoted type is 'int'.
+ if (Op == BO_Sub)
+ Adjustment = -Adjustment;
+ }
}
}
@@ -246,6 +235,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state,
assert(BinaryOperator::isComparisonOp(op) &&
"Non-comparison ops should be rewritten as comparisons to zero.");
+ BasicValueFactory &BVF = getBasicVals();
+ ASTContext &Ctx = BVF.getContext();
+
+ // Get the type used for calculating wraparound.
+ APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType(Ctx));
+
// 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
@@ -254,28 +249,12 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state,
// 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
- // comparisons should be performed using the Int's type, then mapped back to
- // the symbol's range of values.
- ProgramStateManager &StateMgr = state->getStateManager();
- ASTContext &Ctx = StateMgr.getContext();
-
- QualType T = Sym->getType(Ctx);
- assert(T->isIntegerType() || Loc::isLocType(T));
- unsigned bitwidth = Ctx.getTypeSize(T);
- bool isSymUnsigned
- = T->isUnsignedIntegerOrEnumerationType() || Loc::isLocType(T);
-
- // Convert the adjustment.
- Adjustment.setIsUnsigned(isSymUnsigned);
- Adjustment = Adjustment.extOrTrunc(bitwidth);
-
- // Convert the right-hand side integer.
- llvm::APSInt ConvertedInt(Int, isSymUnsigned);
- ConvertedInt = ConvertedInt.extOrTrunc(bitwidth);
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ computeAdjustment(Sym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int));
+ llvm::APSInt ConvertedInt = ComparisonType.convert(Int);
switch (op) {
default:
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
index e082d9d..088d70c 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
@@ -23,8 +23,10 @@ namespace ento {
class SimpleConstraintManager : public ConstraintManager {
SubEngine &SU;
+ BasicValueFactory &BVF;
public:
- SimpleConstraintManager(SubEngine &subengine) : SU(subengine) {}
+ SimpleConstraintManager(SubEngine &subengine, BasicValueFactory &BV)
+ : SU(subengine), BVF(BV) {}
virtual ~SimpleConstraintManager();
//===------------------------------------------------------------------===//
@@ -79,6 +81,8 @@ protected:
// Internal implementation.
//===------------------------------------------------------------------===//
+ BasicValueFactory &getBasicVals() const { return BVF; }
+
bool canReasonAbout(SVal X) const;
ProgramStateRef assumeAux(ProgramStateRef state,
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index d0558f1..ad58a07 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -106,9 +107,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
return UnknownVal();
llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue();
- i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() ||
- Loc::isLocType(castTy));
- i = i.extOrTrunc(Context.getTypeSize(castTy));
+ BasicVals.getAPSIntType(castTy).apply(i);
if (isLocType)
return makeIntLocVal(i);
@@ -139,9 +138,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) {
return makeLocAsInteger(val, BitWidth);
llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue();
- i.setIsUnsigned(castTy->isUnsignedIntegerOrEnumerationType() ||
- Loc::isLocType(castTy));
- i = i.extOrTrunc(BitWidth);
+ BasicVals.getAPSIntType(castTy).apply(i);
return makeIntVal(i);
}
@@ -272,14 +269,40 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy);
// If we reach this point, the expression cannot be simplified.
- // Make a SymbolVal for the entire expression.
- return makeNonLoc(LHS, op, RHS, resultTy);
+ // Make a SymbolVal for the entire expression, after converting the RHS.
+ const llvm::APSInt *ConvertedRHS = &RHS;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the symbolic value
+ // with the given constant.
+ // FIXME: This is an approximation of Sema::UsualArithmeticConversions.
+ ASTContext &Ctx = getContext();
+ QualType SymbolType = LHS->getType(Ctx);
+ uint64_t ValWidth = RHS.getBitWidth();
+ uint64_t TypeWidth = Ctx.getTypeSize(SymbolType);
+
+ if (ValWidth < TypeWidth) {
+ // If the value is too small, extend it.
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ } else if (ValWidth == TypeWidth) {
+ // If the value is signed but the symbol is unsigned, do the comparison
+ // in unsigned space. [C99 6.3.1.8]
+ // (For the opposite case, the value is already unsigned.)
+ if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType())
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ }
+ } else
+ ConvertedRHS = &BasicVals.Convert(resultTy, RHS);
+
+ return makeNonLoc(LHS, op, *ConvertedRHS, resultTy);
}
SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs,
QualType resultTy) {
+ NonLoc InputLHS = lhs;
+ NonLoc InputRHS = rhs;
+
// Handle trivial case where left-side and right-side are the same.
if (lhs == rhs)
switch (op) {
@@ -304,7 +327,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
while (1) {
switch (lhs.getSubKind()) {
default:
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ return makeSymExprValNN(state, op, lhs, rhs, resultTy);
case nonloc::LocAsIntegerKind: {
Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc();
switch (rhs.getSubKind()) {
@@ -315,8 +338,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
case nonloc::ConcreteIntKind: {
// Transform the integer into a location and compare.
llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue();
- i.setIsUnsigned(true);
- i = i.extOrTrunc(Context.getTypeSize(Context.VoidPtrTy));
+ BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
}
default:
@@ -327,86 +349,78 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
return makeTruthVal(true, resultTy);
default:
// This case also handles pointer arithmetic.
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
}
case nonloc::ConcreteIntKind: {
- const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
-
- // Is the RHS a symbol we can simplify?
- // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case.
- 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;
- if (BinaryOperator::isRelationalOp(op))
- rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant);
- else
- rhs_I = &BasicVals.Convert(resultTy, *Constant);
-
- rhs = nonloc::ConcreteInt(*rhs_I);
- }
+ llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue();
+
+ // If we're dealing with two known constants, just perform the operation.
+ if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) {
+ llvm::APSInt RHSValue = *KnownRHSValue;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the two values.
+ // FIXME: This is not correct. char + short will result in a promotion
+ // to int. Unfortunately we have lost types by this point.
+ APSIntType CompareType = std::max(APSIntType(LHSValue),
+ APSIntType(RHSValue));
+ CompareType.apply(LHSValue);
+ CompareType.apply(RHSValue);
+ } else if (!BinaryOperator::isShiftOp(op)) {
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ IntType.apply(LHSValue);
+ IntType.apply(RHSValue);
}
- }
- if (isa<nonloc::ConcreteInt>(rhs)) {
- return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs));
- } else {
- const llvm::APSInt& lhsValue = lhsInt.getValue();
-
- // Swap the left and right sides and flip the operator if doing so
- // allows us to better reason about the expression (this is a form
- // of expression canonicalization).
- // While we're at it, catch some special cases for non-commutative ops.
- NonLoc tmp = rhs;
- rhs = lhs;
- lhs = tmp;
+ const llvm::APSInt *Result =
+ BasicVals.evalAPSInt(op, LHSValue, RHSValue);
+ if (!Result)
+ return UndefinedVal();
- switch (op) {
- case BO_LT:
- case BO_GT:
- case BO_LE:
- case BO_GE:
- op = ReverseComparison(op);
- continue;
- case BO_EQ:
- case BO_NE:
- case BO_Add:
- case BO_Mul:
- case BO_And:
- case BO_Xor:
- case BO_Or:
- continue;
- case BO_Shr:
- if (lhsValue.isAllOnesValue() && lhsValue.isSigned())
- // At this point lhs and rhs have been swapped.
- return rhs;
- // FALL-THROUGH
- case BO_Shl:
- if (lhsValue == 0)
- // At this point lhs and rhs have been swapped.
- return rhs;
- return makeGenericVal(state, op, rhs, lhs, resultTy);
- default:
- return makeGenericVal(state, op, rhs, lhs, resultTy);
- }
+ return nonloc::ConcreteInt(*Result);
+ }
+
+ // Swap the left and right sides and flip the operator if doing so
+ // allows us to better reason about the expression (this is a form
+ // of expression canonicalization).
+ // While we're at it, catch some special cases for non-commutative ops.
+ switch (op) {
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ op = ReverseComparison(op);
+ // FALL-THROUGH
+ case BO_EQ:
+ case BO_NE:
+ case BO_Add:
+ case BO_Mul:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
+ std::swap(lhs, rhs);
+ continue;
+ case BO_Shr:
+ // (~0)>>a
+ if (LHSValue.isAllOnesValue() && LHSValue.isSigned())
+ return evalCastFromNonLoc(lhs, resultTy);
+ // FALL-THROUGH
+ case BO_Shl:
+ // 0<<a and 0>>a
+ if (LHSValue == 0)
+ return evalCastFromNonLoc(lhs, resultTy);
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
+ default:
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
case nonloc::SymbolValKind: {
- nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs);
+ // We only handle LHS as simple symbols or SymIntExprs.
+ SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol();
// 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);
+ if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) {
// Is this a logical not? (!x is represented as x == 0.)
if (op == BO_EQ && rhs.isZeroConstant()) {
@@ -452,95 +466,57 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
}
// 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);
- }
- }
-
- // Otherwise, make a SymbolVal out of the expression.
- return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy);
-
- // 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))
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) {
+ // If both the LHS and the current expression are additive,
+ // fold their constants and try again.
+ if (BinaryOperator::isAdditiveOp(op)) {
+ BinaryOperator::Opcode lop = symIntExpr->getOpcode();
+ if (BinaryOperator::isAdditiveOp(lop)) {
+ // Convert the two constants to a common type, then combine them.
+
+ // 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.
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS());
+ const llvm::APSInt &second = IntType.convert(*RHSValue);
+
+ const llvm::APSInt *newRHS;
+ if (lop == op)
+ newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
+ else
+ newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
+
+ assert(newRHS && "Invalid operation despite common type!");
+ rhs = nonloc::ConcreteInt(*newRHS);
+ lhs = nonloc::SymbolVal(symIntExpr->getLHS());
+ op = lop;
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);
- }
- }
+ // Otherwise, make a SymIntExpr out of the expression.
+ return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy);
}
- if (isa<nonloc::ConcreteInt>(rhs)) {
- return MakeSymIntVal(slhs->getSymbol(), op,
- cast<nonloc::ConcreteInt>(rhs).getValue(),
- resultTy);
+
+ } else if (isa<SymbolData>(Sym)) {
+ // Does the symbol simplify to a constant? If so, "fold" the constant
+ // by setting 'lhs' to a ConcreteInt and try again.
+ if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {
+ lhs = nonloc::ConcreteInt(*Constant);
+ continue;
}
- return makeGenericVal(state, op, lhs, rhs, resultTy);
+ // Is the RHS a constant?
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
+ return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
}
+
+ // Give up -- this is not a symbolic expression we can handle.
+ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
}
}
@@ -697,11 +673,18 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// regions, though.
return UnknownVal();
- // If both values wrap regions, see if they're from different base regions.
+ const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace();
+ const MemSpaceRegion *RightMS = RightMR->getMemorySpace();
+ const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion();
const MemRegion *LeftBase = LeftMR->getBaseRegion();
const MemRegion *RightBase = RightMR->getBaseRegion();
- if (LeftBase != RightBase &&
- !isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) {
+
+ // If the two regions are from different known memory spaces they cannot be
+ // equal. Also, assume that no symbolic region (whose memory space is
+ // unknown) is on the stack.
+ if (LeftMS != RightMS &&
+ ((LeftMS != UnknownMS && RightMS != UnknownMS) ||
+ (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) {
switch (op) {
default:
return UnknownVal();
@@ -712,24 +695,20 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef 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)) {
+ // If both values wrap regions, see if they're from different base regions.
+ // Note, heap base symbolic regions are assumed to not alias with
+ // each other; for example, we assume that malloc returns different address
+ // on each invocation.
+ if (LeftBase != RightBase &&
+ ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) ||
+ (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){
switch (op) {
- default:
- break;
- case BO_EQ:
- return makeTruthVal(false, resultTy);
- case BO_NE:
- return makeTruthVal(true, resultTy);
+ default:
+ return UnknownVal();
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
}
}
@@ -885,6 +864,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy);
}
}
+ return UnknownVal();
}
// We are dealing with pointer arithmetic.
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 11748ae..3af60a1 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclObjC.h"
@@ -23,10 +24,21 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr)
: svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
-StoreRef StoreManager::enterStackFrame(ProgramStateRef state,
- const LocationContext *callerCtx,
- const StackFrameContext *calleeCtx) {
- return StoreRef(state->getStore(), *this);
+StoreRef StoreManager::enterStackFrame(Store OldStore,
+ const CallEvent &Call,
+ const StackFrameContext *LCtx) {
+ StoreRef Store = StoreRef(OldStore, *this);
+
+ SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
+ Call.getInitialStackFrameContents(LCtx, InitialBindings);
+
+ for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(),
+ E = InitialBindings.end();
+ I != E; ++I) {
+ Store = Bind(Store.getStore(), I->first, I->second);
+ }
+
+ return Store;
}
const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base,
@@ -210,6 +222,17 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
llvm_unreachable("unreachable");
}
+SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) {
+ // Walk through the cast path to create nested CXXBaseRegions.
+ SVal Result = Derived;
+ for (CastExpr::path_const_iterator I = Cast->path_begin(),
+ E = Cast->path_end();
+ I != E; ++I) {
+ Result = evalDerivedToBase(Result, (*I)->getType());
+ }
+ return Result;
+}
+
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
@@ -357,6 +380,3 @@ bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
return true;
}
-
-void SubRegionMap::anchor() { }
-void SubRegionMap::Visitor::anchor() { }
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index adefb58..0bc192d 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -164,6 +164,13 @@ void SymExpr::symbol_iterator::expand() {
llvm_unreachable("unhandled expansion case");
}
+unsigned SymExpr::computeComplexity() const {
+ unsigned R = 0;
+ for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I)
+ R++;
+ return R;
+}
+
const SymbolRegionValue*
SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
llvm::FoldingSetNodeID profile;
@@ -501,6 +508,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return false;
return true;
}
+ // If no statement is provided, everything is this and parent contexts is live.
+ if (!Loc)
+ return true;
return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
@@ -510,6 +520,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
if (VarContext == CurrentContext) {
+ // If no statemetnt is provided, everything is live.
+ if (!Loc)
+ return true;
+
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;
diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
index fe912df..e5b8553 100644
--- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
@@ -42,6 +42,7 @@ public:
bool supportsLogicalOpControlFlow() const { return true; }
bool supportsAllBlockEdges() const { return true; }
virtual bool useVerboseDescription() const { return true; }
+ virtual bool supportsCrossFileDiagnostics() const { return true; }
};
} // end anonymous namespace
@@ -58,7 +59,9 @@ void TextPathDiagnostics::FlushDiagnosticsImpl(
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();
+
+ PathPieces FlatPath = D->path.flatten(/*ShouldFlattenMacros=*/true);
+ for (PathPieces::const_iterator I = FlatPath.begin(), E = FlatPath.end();
I != E; ++I) {
unsigned diagID =
Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note,
OpenPOWER on IntegriCloud