summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2011-10-20 21:14:49 +0000
committerdim <dim@FreeBSD.org>2011-10-20 21:14:49 +0000
commit3963a48221351c61c17fb3f382341ab04809a3d3 (patch)
treeee2483e98b09cac943dc93a6969d83ca737ff139 /lib/StaticAnalyzer
parent611ba3ea3300b71eb95dc4e45f20eee5dddd32e1 (diff)
downloadFreeBSD-src-3963a48221351c61c17fb3f382341ab04809a3d3.zip
FreeBSD-src-3963a48221351c61c17fb3f382341ab04809a3d3.tar.gz
Vendor import of clang release_30 branch r142614:
http://llvm.org/svn/llvm-project/cfe/branches/release_30@142614
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp32
-rw-r--r--lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp45
-rw-r--r--lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/CMakeLists.txt5
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp303
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp52
-rw-r--r--lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp68
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp28
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp103
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp12
-rw-r--r--lib/StaticAnalyzer/Checkers/Checkers.td40
-rw-r--r--lib/StaticAnalyzer/Checkers/ChrootChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Checkers/ClangCheckers.cpp32
-rw-r--r--lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp289
-rw-r--r--lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h29
-rw-r--r--lib/StaticAnalyzer/Checkers/ClangSACheckers.h1
-rw-r--r--lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp120
-rw-r--r--lib/StaticAnalyzer/Checkers/DebugCheckers.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp38
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp74
-rw-r--r--lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp632
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/Makefile4
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp140
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp268
-rw-r--r--lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp30
-rw-r--r--lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp73
-rw-r--r--lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp101
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp35
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp185
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp (renamed from lib/StaticAnalyzer/Core/CFRefCount.cpp)2855
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp33
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp58
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp33
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Core/AggExprVisitor.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp52
-rw-r--r--lib/StaticAnalyzer/Core/BasicConstraintManager.cpp179
-rw-r--r--lib/StaticAnalyzer/Core/BasicStore.cpp605
-rw-r--r--lib/StaticAnalyzer/Core/BasicValueFactory.cpp14
-rw-r--r--lib/StaticAnalyzer/Core/BlockCounter.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp580
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp746
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt16
-rw-r--r--lib/StaticAnalyzer/Core/Checker.cpp22
-rw-r--r--lib/StaticAnalyzer/Core/CheckerContext.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp136
-rw-r--r--lib/StaticAnalyzer/Core/CheckerRegistry.cpp149
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp239
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp76
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp31
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp1961
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp752
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (renamed from lib/StaticAnalyzer/Core/CXXExprEngine.cpp)87
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp253
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp279
-rw-r--r--lib/StaticAnalyzer/Core/FlatStore.cpp217
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp62
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp77
-rw-r--r--lib/StaticAnalyzer/Core/ObjCMessage.cpp52
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp225
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp79
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp (renamed from lib/StaticAnalyzer/Core/GRState.cpp)297
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp73
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp162
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp21
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp18
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp20
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.h26
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp43
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp28
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp153
-rw-r--r--lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp20
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp245
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.h6
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp108
-rw-r--r--lib/StaticAnalyzer/Frontend/FrontendActions.cpp5
-rw-r--r--lib/StaticAnalyzer/README.txt12
101 files changed, 7413 insertions, 6774 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp
index 8fc6d2a..dc524ba 100644
--- a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp
@@ -37,7 +37,7 @@ void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE,
QualType expectedResultTy = CE->getType();
// Fetch the signature of the called function.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal V = state->getSVal(CE);
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 983427a..cd977bf 100644
--- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -80,7 +80,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
- output << ND;
+ output << *ND;
}
else if (isa<BlockDecl>(D)) {
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
@@ -94,7 +94,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
<< (Eng.hasEmptyWorkList() ? "yes" : "no");
B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(),
- D->getLocation());
+ PathDiagnosticLocation(D, SM));
// Emit warning for each block we bailed out on
typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
@@ -106,7 +106,8 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
const CFGElement &CE = Exit->front();
if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE))
B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer "
- "stopped analyzing at this point", CS->getStmt()->getLocStart());
+ "stopped analyzing at this point",
+ PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index eb9665a..6935c5f 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -27,11 +27,12 @@ class ArrayBoundChecker :
public Checker<check::Location> {
mutable llvm::OwningPtr<BuiltinBug> BT;
public:
- void checkLocation(SVal l, bool isLoad, CheckerContext &C) const;
+ void checkLocation(SVal l, bool isLoad, const Stmt* S,
+ CheckerContext &C) const;
};
}
-void ArrayBoundChecker::checkLocation(SVal l, bool isLoad,
+void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
CheckerContext &C) const {
// Check for out of bound array element access.
const MemRegion *R = l.getAsRegion();
@@ -50,15 +51,15 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad,
if (Idx.isZeroConstant())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// Get the size of the array.
DefinedOrUnknownSVal NumElements
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
ER->getValueType());
- const GRState *StInBound = state->assumeInBound(Idx, NumElements, true);
- const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false);
+ const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true);
+ const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateSink(StOutBound);
if (!N)
@@ -73,10 +74,10 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad,
// reference is outside the range.
// Generate a report for this bug.
- RangedBugReport *report =
- new RangedBugReport(*BT, BT->getDescription(), N);
+ BugReport *report =
+ new BugReport(*BT, BT->getDescription(), N);
- report->addRange(C.getStmt()->getSourceRange());
+ report->addRange(LoadS->getSourceRange());
C.EmitReport(report);
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 65a6e63..6175028 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -30,11 +30,12 @@ class ArrayBoundCheckerV2 :
enum OOB_Kind { OOB_Precedes, OOB_Excedes };
- void reportOOB(CheckerContext &C, const GRState *errorState,
+ void reportOOB(CheckerContext &C, const ProgramState *errorState,
OOB_Kind kind) const;
public:
- void checkLocation(SVal l, bool isLoad, CheckerContext &C) const;
+ void checkLocation(SVal l, bool isLoad, const Stmt*S,
+ CheckerContext &C) const;
};
// FIXME: Eventually replace RegionRawOffset with this class.
@@ -53,12 +54,12 @@ public:
NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); }
const SubRegion *getRegion() const { return baseRegion; }
- static RegionRawOffsetV2 computeOffset(const GRState *state,
+ static RegionRawOffsetV2 computeOffset(const ProgramState *state,
SValBuilder &svalBuilder,
SVal location);
void dump() const;
- void dumpToStream(llvm::raw_ostream& os) const;
+ void dumpToStream(raw_ostream &os) const;
};
}
@@ -79,9 +80,10 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder,
}
void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
+ const Stmt* LoadS,
CheckerContext &checkerContext) const {
- // NOTE: Instead of using GRState::assumeInBound(), we are prototyping
+ // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
// some new logic here that reasons directly about memory region extents.
// Once that logic is more mature, we can bring it back to assumeInBound()
// for all clients to use.
@@ -90,8 +92,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
// memory access is within the extent of the base region. Since we
// have some flexibility in defining the base region, we can achieve
// various levels of conservatism in our buffer overflow checking.
- const GRState *state = checkerContext.getState();
- const GRState *originalState = state;
+ const ProgramState *state = checkerContext.getState();
+ const ProgramState *originalState = state;
SValBuilder &svalBuilder = checkerContext.getSValBuilder();
const RegionRawOffsetV2 &rawOffset =
@@ -116,7 +118,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
if (!lowerBoundToCheck)
return;
- const GRState *state_precedesLowerBound, *state_withinLowerBound;
+ const ProgramState *state_precedesLowerBound, *state_withinLowerBound;
llvm::tie(state_precedesLowerBound, state_withinLowerBound) =
state->assume(*lowerBoundToCheck);
@@ -148,7 +150,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
if (!upperboundToCheck)
break;
- const GRState *state_exceedsUpperBound, *state_withinUpperBound;
+ const ProgramState *state_exceedsUpperBound, *state_withinUpperBound;
llvm::tie(state_exceedsUpperBound, state_withinUpperBound) =
state->assume(*upperboundToCheck);
@@ -168,7 +170,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
}
void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
- const GRState *errorState,
+ const ProgramState *errorState,
OOB_Kind kind) const {
ExplodedNode *errorNode = checkerContext.generateSink(errorState);
@@ -187,14 +189,14 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
<< (kind == OOB_Precedes ? "(accessed memory precedes memory block)"
: "(access exceeds upper limit of memory block)");
- checkerContext.EmitReport(new RangedBugReport(*BT, os.str(), errorNode));
+ checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode));
}
void RegionRawOffsetV2::dump() const {
dumpToStream(llvm::errs());
}
-void RegionRawOffsetV2::dumpToStream(llvm::raw_ostream& os) const {
+void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
}
@@ -219,7 +221,7 @@ static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
// Scale a base value by a scaling factor, and return the scaled
// value as an SVal. Used by 'computeOffset'.
-static inline SVal scaleValue(const GRState *state,
+static inline SVal scaleValue(const ProgramState *state,
NonLoc baseVal, CharUnits scaling,
SValBuilder &sb) {
return sb.evalBinOpNN(state, BO_Mul, baseVal,
@@ -229,7 +231,7 @@ static inline SVal scaleValue(const GRState *state,
// Add an SVal to another, treating unknown and undefined values as
// summing to UnknownVal. Used by 'computeOffset'.
-static SVal addValue(const GRState *state, SVal x, SVal y,
+static SVal addValue(const ProgramState *state, SVal x, SVal y,
SValBuilder &svalBuilder) {
// We treat UnknownVals and UndefinedVals the same here because we
// only care about computing offsets.
@@ -243,7 +245,7 @@ static SVal addValue(const GRState *state, SVal x, SVal y,
/// Compute a raw byte offset from a base region. Used for array bounds
/// checking.
-RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const GRState *state,
+RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const ProgramState *state,
SValBuilder &svalBuilder,
SVal location)
{
diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
index d88a111..8296eb9 100644
--- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp
@@ -33,12 +33,12 @@ public:
void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// Check if the callee has a 'nonnull' attribute.
SVal X = state->getSVal(CE->getCallee());
- const FunctionDecl* FD = X.getAsFunctionDecl();
+ const FunctionDecl *FD = X.getAsFunctionDecl();
if (!FD)
return;
@@ -85,7 +85,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
}
ConstraintManager &CM = C.getConstraintManager();
- const GRState *stateNotNull, *stateNull;
+ const ProgramState *stateNotNull, *stateNull;
llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
if (stateNull && !stateNotNull) {
@@ -100,16 +100,15 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE,
BT.reset(new BugType("Argument with 'nonnull' attribute passed null",
"API"));
- EnhancedBugReport *R =
- new EnhancedBugReport(*BT,
- "Null pointer passed as an argument to a "
- "'nonnull' parameter", errorNode);
+ BugReport *R =
+ new BugReport(*BT, "Null pointer passed as an argument to a "
+ "'nonnull' parameter", errorNode);
// Highlight the range of the argument that was null.
const Expr *arg = *I;
R->addRange(arg->getSourceRange());
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg);
-
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode,
+ arg));
// Emit the bug report.
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 9fc8163..08cff0f 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -20,7 +20,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/AST/DeclObjC.h"
@@ -49,7 +50,7 @@ static const char* GetReceiverNameType(const ObjCMessage &msg) {
}
static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
- llvm::StringRef ClassName) {
+ StringRef ClassName) {
if (ID->getIdentifier()->getName() == ClassName)
return true;
@@ -92,7 +93,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C,
os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
<< msg.getSelector().getAsString() << "' cannot be nil";
- RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
+ BugReport *R = new BugReport(*BT, os.str(), N);
R->addRange(msg.getArgSourceRange(Arg));
C.EmitReport(R);
}
@@ -114,7 +115,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
// lexical comparisons.
std::string NameStr = S.getAsString();
- llvm::StringRef Name(NameStr);
+ StringRef Name(NameStr);
assert(!Name.empty());
// FIXME: Checking for initWithFormat: will not work in most cases
@@ -148,7 +149,7 @@ public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
private:
- void EmitError(const TypedRegion* R, const Expr* Ex,
+ void EmitError(const TypedRegion* R, const Expr *Ex,
uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
};
} // end anonymous namespace
@@ -194,7 +195,7 @@ namespace {
};
}
-static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
+static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
if (i < kCFNumberCharType)
@@ -248,10 +249,10 @@ static const char* GetCFNumberTypeStr(uint64_t i) {
void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
- const Expr* Callee = CE->getCallee();
- const GRState *state = C.getState();
+ const Expr *Callee = CE->getCallee();
+ const ProgramState *state = C.getState();
SVal CallV = state->getSVal(Callee);
- const FunctionDecl* FD = CallV.getAsFunctionDecl();
+ const FunctionDecl *FD = CallV.getAsFunctionDecl();
if (!FD)
return;
@@ -290,7 +291,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
if (!LV)
return;
- const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
+ const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
if (!R)
return;
@@ -335,7 +336,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
if (!BT)
BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
- RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
+ BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(CE->getArg(2)->getSourceRange());
C.EmitReport(report);
}
@@ -351,21 +352,21 @@ class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
mutable IdentifierInfo *Retain, *Release;
public:
CFRetainReleaseChecker(): Retain(0), Release(0) {}
- void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
};
} // end anonymous namespace
-void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
- CheckerContext& C) const {
+void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
// If the CallExpr doesn't have exactly 1 argument just give up checking.
if (CE->getNumArgs() != 1)
return;
// Get the function declaration of the callee.
- const GRState* state = C.getState();
+ const ProgramState *state = C.getState();
SVal X = state->getSVal(CE->getCallee());
- const FunctionDecl* FD = X.getAsFunctionDecl();
+ const FunctionDecl *FD = X.getAsFunctionDecl();
if (!FD)
return;
@@ -400,7 +401,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
// Are they equal?
- const GRState *stateTrue, *stateFalse;
+ const ProgramState *stateTrue, *stateFalse;
llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
if (stateTrue && !stateFalse) {
@@ -412,9 +413,9 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
? "Null pointer argument in call to CFRetain"
: "Null pointer argument in call to CFRelease";
- EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
+ BugReport *report = new BugReport(*BT, description, N);
report->addRange(Arg->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
C.EmitReport(report);
return;
}
@@ -471,7 +472,7 @@ void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
"of class '" << Class->getName()
<< "' and not the class directly";
- RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
+ BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(msg.getSourceRange());
C.EmitReport(report);
}
@@ -586,7 +587,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
// Verify that all arguments have Objective-C types.
llvm::Optional<ExplodedNode*> errorNode;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
QualType ArgTy = msg.getArgType(I);
@@ -629,7 +630,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
<< "' should be an Objective-C pointer type, not '"
<< ArgTy.getAsString() << "'";
- RangedBugReport *R = new RangedBugReport(*BT, os.str(),
+ BugReport *R = new BugReport(*BT, os.str(),
errorNode.getValue());
R->addRange(msg.getArgSourceRange(I));
C.EmitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index 12ac652..a57d031 100644
--- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -31,7 +31,7 @@ public:
bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
CheckerContext &C) const{
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
const FunctionDecl *FD = L.getAsFunctionDecl();
@@ -57,7 +57,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
// FIXME: Refactor into StoreManager itself?
MemRegionManager& RM = C.getStoreManager().getRegionManager();
const AllocaRegion* R =
- RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(),
+ RM.getAllocaRegion(CE, C.getCurrentBlockCount(),
C.getPredecessor()->getLocationContext());
// Set the extent of the region in bytes. This enables us to use the
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 8dc7f38..3e0d094 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -22,7 +22,7 @@ add_clang_library(clangStaticAnalyzerCheckers
CheckSecuritySyntaxOnly.cpp
CheckSizeofPointer.cpp
ChrootChecker.cpp
- ClangSACheckerProvider.cpp
+ ClangCheckers.cpp
DeadStoresChecker.cpp
DebugCheckers.cpp
DereferenceChecker.cpp
@@ -31,8 +31,10 @@ add_clang_library(clangStaticAnalyzerCheckers
IdempotentOperationChecker.cpp
IteratorsChecker.cpp
LLVMConventionsChecker.cpp
+ MacOSKeychainAPIChecker.cpp
MacOSXAPIChecker.cpp
MallocChecker.cpp
+ MallocOverflowSecurityChecker.cpp
NSAutoreleasePoolChecker.cpp
NSErrorChecker.cpp
NoReturnFunctionChecker.cpp
@@ -43,6 +45,7 @@ add_clang_library(clangStaticAnalyzerCheckers
PointerArithChecker.cpp
PointerSubChecker.cpp
PthreadLockChecker.cpp
+ RetainCountChecker.cpp
ReturnPointerRangeChecker.cpp
ReturnUndefChecker.cpp
StackAddrEscapeChecker.cpp
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index c5dac5d..1625219 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -1,4 +1,4 @@
-//= CStringChecker.h - Checks calls to C string functions ----------*- C++ -*-//
+//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//
//
// The LLVM Compiler Infrastructure
//
@@ -17,7 +17,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
@@ -40,14 +40,15 @@ public:
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
- void checkLiveSymbols(const GRState *state, SymbolReaper &SR) const;
+ void checkLiveSymbols(const ProgramState *state, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
- bool wantsRegionChangeUpdate(const GRState *state) const;
+ bool wantsRegionChangeUpdate(const ProgramState *state) const;
- const GRState *checkRegionChanges(const GRState *state,
- const StoreManager::InvalidatedSymbols *,
- const MemRegion * const *Begin,
- const MemRegion * const *End) const;
+ const ProgramState *
+ checkRegionChanges(const ProgramState *state,
+ const StoreManager::InvalidatedSymbols *,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions) const;
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
const CallExpr *) const;
@@ -57,8 +58,10 @@ public:
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
- const GRState *state,
- const Expr *Size, const Expr *Source, const Expr *Dest,
+ const ProgramState *state,
+ const Expr *Size,
+ const Expr *Source,
+ const Expr *Dest,
bool Restricted = false,
bool IsMempcpy = false) const;
@@ -66,14 +69,18 @@ public:
void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
- void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+ void evalstrLengthCommon(CheckerContext &C,
+ const CallExpr *CE,
bool IsStrnlen = false) const;
void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd,
- bool isBounded, bool isAppending) const;
+ void evalStrcpyCommon(CheckerContext &C,
+ const CallExpr *CE,
+ bool returnEnd,
+ bool isBounded,
+ bool isAppending) const;
void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
@@ -82,64 +89,85 @@ public:
void evalStrncmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
- bool isBounded = false, bool ignoreCase = false) const;
+ void evalStrcmpCommon(CheckerContext &C,
+ const CallExpr *CE,
+ bool isBounded = false,
+ bool ignoreCase = false) const;
// Utility methods
- std::pair<const GRState*, const GRState*>
+ std::pair<const ProgramState*, const ProgramState*>
static assumeZero(CheckerContext &C,
- const GRState *state, SVal V, QualType Ty);
+ const ProgramState *state, SVal V, QualType Ty);
- static const GRState *setCStringLength(const GRState *state,
- const MemRegion *MR, SVal strLength);
+ static const ProgramState *setCStringLength(const ProgramState *state,
+ const MemRegion *MR,
+ SVal strLength);
static SVal getCStringLengthForRegion(CheckerContext &C,
- const GRState *&state,
- const Expr *Ex, const MemRegion *MR,
+ const ProgramState *&state,
+ const Expr *Ex,
+ const MemRegion *MR,
bool hypothetical);
- SVal getCStringLength(CheckerContext &C, const GRState *&state,
- const Expr *Ex, SVal Buf,
+ SVal getCStringLength(CheckerContext &C,
+ const ProgramState *&state,
+ const Expr *Ex,
+ SVal Buf,
bool hypothetical = false) const;
const StringLiteral *getCStringLiteral(CheckerContext &C,
- const GRState *&state,
+ const ProgramState *&state,
const Expr *expr,
SVal val) const;
- static const GRState *InvalidateBuffer(CheckerContext &C,
- const GRState *state,
- const Expr *Ex, SVal V);
+ static const ProgramState *InvalidateBuffer(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *Ex, SVal V);
- static bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
+ static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR);
// Re-usable checks
- const GRState *checkNonNull(CheckerContext &C, const GRState *state,
- const Expr *S, SVal l) const;
- const GRState *CheckLocation(CheckerContext &C, const GRState *state,
- const Expr *S, SVal l,
- const char *message = NULL) const;
- const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state,
- const Expr *Size,
- const Expr *FirstBuf,
- const Expr *SecondBuf,
- const char *firstMessage = NULL,
- const char *secondMessage = NULL,
- bool WarnAboutSize = false) const;
- const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state,
- const Expr *Size, const Expr *Buf,
- const char *message = NULL,
- bool WarnAboutSize = false) const {
+ const ProgramState *checkNonNull(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *S,
+ SVal l) const;
+ const ProgramState *CheckLocation(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *S,
+ SVal l,
+ const char *message = NULL) const;
+ const ProgramState *CheckBufferAccess(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *Size,
+ const Expr *FirstBuf,
+ const Expr *SecondBuf,
+ const char *firstMessage = NULL,
+ const char *secondMessage = NULL,
+ bool WarnAboutSize = false) const;
+
+ const ProgramState *CheckBufferAccess(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *Size,
+ const Expr *Buf,
+ const char *message = NULL,
+ bool WarnAboutSize = false) const {
// This is a convenience override.
return CheckBufferAccess(C, state, Size, Buf, NULL, message, NULL,
WarnAboutSize);
}
- const GRState *CheckOverlap(CheckerContext &C, const GRState *state,
- const Expr *Size, const Expr *First,
- const Expr *Second) const;
- void emitOverlapBug(CheckerContext &C, const GRState *state,
- const Stmt *First, const Stmt *Second) const;
- const GRState *checkAdditionOverflow(CheckerContext &C, const GRState *state,
- NonLoc left, NonLoc right) const;
+ const ProgramState *CheckOverlap(CheckerContext &C,
+ const ProgramState *state,
+ const Expr *Size,
+ const Expr *First,
+ const Expr *Second) const;
+ void emitOverlapBug(CheckerContext &C,
+ const ProgramState *state,
+ const Stmt *First,
+ const Stmt *Second) const;
+
+ const ProgramState *checkAdditionOverflow(CheckerContext &C,
+ const ProgramState *state,
+ NonLoc left,
+ NonLoc right) const;
};
class CStringLength {
@@ -151,8 +179,8 @@ public:
namespace clang {
namespace ento {
template <>
- struct GRStateTrait<CStringLength>
- : public GRStatePartialTrait<CStringLength::EntryMap> {
+ struct ProgramStateTrait<CStringLength>
+ : public ProgramStatePartialTrait<CStringLength::EntryMap> {
static void *GDMIndex() { return CStringChecker::getTag(); }
};
}
@@ -162,26 +190,26 @@ namespace ento {
// Individual checks and utility methods.
//===----------------------------------------------------------------------===//
-std::pair<const GRState*, const GRState*>
-CStringChecker::assumeZero(CheckerContext &C, const GRState *state, SVal V,
+std::pair<const ProgramState*, const ProgramState*>
+CStringChecker::assumeZero(CheckerContext &C, const ProgramState *state, SVal V,
QualType Ty) {
DefinedSVal *val = dyn_cast<DefinedSVal>(&V);
if (!val)
- return std::pair<const GRState*, const GRState *>(state, state);
+ return std::pair<const ProgramState*, const ProgramState *>(state, state);
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty);
return state->assume(svalBuilder.evalEQ(state, *val, zero));
}
-const GRState *CStringChecker::checkNonNull(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::checkNonNull(CheckerContext &C,
+ const ProgramState *state,
const Expr *S, SVal l) const {
// If a previous check has failed, propagate the failure.
if (!state)
return NULL;
- const GRState *stateNull, *stateNonNull;
+ const ProgramState *stateNull, *stateNonNull;
llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
if (stateNull && !stateNonNull) {
@@ -200,10 +228,10 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C,
// Generate a report for this bug.
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get());
- EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
+ BugReport *report = new BugReport(*BT, os.str(), N);
report->addRange(S->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, S);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S));
C.EmitReport(report);
return NULL;
}
@@ -214,8 +242,8 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C,
}
// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
-const GRState *CStringChecker::CheckLocation(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::CheckLocation(CheckerContext &C,
+ const ProgramState *state,
const Expr *S, SVal l,
const char *warningMsg) const {
// If a previous check has failed, propagate the failure.
@@ -244,8 +272,8 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
// Get the index of the accessed element.
DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex());
- const GRState *StInBound = state->assumeInBound(Idx, Size, true);
- const GRState *StOutBound = state->assumeInBound(Idx, Size, false);
+ const ProgramState *StInBound = state->assumeInBound(Idx, Size, true);
+ const ProgramState *StOutBound = state->assumeInBound(Idx, Size, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateSink(StOutBound);
if (!N)
@@ -258,9 +286,9 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get());
// Generate a report for this bug.
- RangedBugReport *report;
+ BugReport *report;
if (warningMsg) {
- report = new RangedBugReport(*BT, warningMsg, N);
+ report = new BugReport(*BT, warningMsg, N);
} else {
assert(CurrentFunctionDescription);
assert(CurrentFunctionDescription[0] != '\0');
@@ -270,7 +298,7 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
os << (char)toupper(CurrentFunctionDescription[0])
<< &CurrentFunctionDescription[1]
<< " accesses out-of-bound array element";
- report = new RangedBugReport(*BT, os.str(), N);
+ report = new BugReport(*BT, os.str(), N);
}
// FIXME: It would be nice to eventually make this diagnostic more clear,
@@ -287,8 +315,8 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C,
return StInBound;
}
-const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C,
+ const ProgramState *state,
const Expr *Size,
const Expr *FirstBuf,
const Expr *SecondBuf,
@@ -359,8 +387,8 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C,
return state;
}
-const GRState *CStringChecker::CheckOverlap(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C,
+ const ProgramState *state,
const Expr *Size,
const Expr *First,
const Expr *Second) const {
@@ -372,7 +400,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C,
if (!state)
return NULL;
- const GRState *stateTrue, *stateFalse;
+ const ProgramState *stateTrue, *stateFalse;
// Get the buffer values and make sure they're known locations.
SVal firstVal = state->getSVal(First);
@@ -470,7 +498,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C,
return stateFalse;
}
-void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state,
+void CStringChecker::emitOverlapBug(CheckerContext &C, const ProgramState *state,
const Stmt *First, const Stmt *Second) const {
ExplodedNode *N = C.generateSink(state);
if (!N)
@@ -480,8 +508,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state,
BT_Overlap.reset(new BugType("Unix API", "Improper arguments"));
// Generate a report for this bug.
- RangedBugReport *report =
- new RangedBugReport(*BT_Overlap,
+ BugReport *report =
+ new BugReport(*BT_Overlap,
"Arguments must not be overlapping buffers", N);
report->addRange(First->getSourceRange());
report->addRange(Second->getSourceRange());
@@ -489,8 +517,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state,
C.EmitReport(report);
}
-const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C,
+ const ProgramState *state,
NonLoc left,
NonLoc right) const {
// If a previous check has failed, propagate the failure.
@@ -521,7 +549,7 @@ const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C,
SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
*maxMinusRightNL, cmpTy);
- const GRState *stateOverflow, *stateOkay;
+ const ProgramState *stateOverflow, *stateOkay;
llvm::tie(stateOverflow, stateOkay) =
state->assume(cast<DefinedOrUnknownSVal>(willOverflow));
@@ -557,7 +585,7 @@ const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C,
return state;
}
-const GRState *CStringChecker::setCStringLength(const GRState *state,
+const ProgramState *CStringChecker::setCStringLength(const ProgramState *state,
const MemRegion *MR,
SVal strLength) {
assert(!strLength.isUndef() && "Attempt to set an undefined string length");
@@ -598,7 +626,7 @@ const GRState *CStringChecker::setCStringLength(const GRState *state,
}
SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
- const GRState *&state,
+ const ProgramState *&state,
const Expr *Ex,
const MemRegion *MR,
bool hypothetical) {
@@ -610,7 +638,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
}
// Otherwise, get a new symbol and update the state.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
SValBuilder &svalBuilder = C.getSValBuilder();
QualType sizeTy = svalBuilder.getContext().getSizeType();
SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(),
@@ -622,7 +650,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
return strLength;
}
-SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
+SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&state,
const Expr *Ex, SVal Buf,
bool hypothetical) const {
const MemRegion *MR = Buf.getAsRegion();
@@ -644,7 +672,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
<< "', which is not a null-terminated string";
// Generate a report for this bug.
- EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString,
+ BugReport *report = new BugReport(*BT_NotCString,
os.str(), N);
report->addRange(Ex->getSourceRange());
@@ -705,7 +733,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
os << "not a null-terminated string";
// Generate a report for this bug.
- EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString,
+ BugReport *report = new BugReport(*BT_NotCString,
os.str(), N);
report->addRange(Ex->getSourceRange());
@@ -717,7 +745,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
}
const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
- const GRState *&state, const Expr *expr, SVal val) const {
+ const ProgramState *&state, const Expr *expr, SVal val) const {
// Get the memory region pointed to by the val.
const MemRegion *bufRegion = val.getAsRegion();
@@ -736,8 +764,8 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
return strRegion->getStringLiteral();
}
-const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
- const GRState *state,
+const ProgramState *CStringChecker::InvalidateBuffer(CheckerContext &C,
+ const ProgramState *state,
const Expr *E, SVal V) {
Loc *L = dyn_cast<Loc>(&V);
if (!L)
@@ -757,8 +785,8 @@ const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
}
// Invalidate this region.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
- return state->invalidateRegion(R, E, Count, NULL);
+ unsigned Count = C.getCurrentBlockCount();
+ return state->invalidateRegions(R, E, Count);
}
// If we have a non-region value by chance, just remove the binding.
@@ -767,17 +795,15 @@ const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
return state->unbindLoc(*L);
}
-bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
+bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR) {
- const TypedRegion *TR = dyn_cast<TypedRegion>(MR);
- if (!TR)
- return false;
+ const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR);
- switch (TR->getKind()) {
+ switch (MR->getKind()) {
case MemRegion::FunctionTextRegionKind: {
- const FunctionDecl *FD = cast<FunctionTextRegion>(TR)->getDecl();
+ const FunctionDecl *FD = cast<FunctionTextRegion>(MR)->getDecl();
if (FD)
- os << "the address of the function '" << FD << "'";
+ os << "the address of the function '" << *FD << '\'';
else
os << "the address of a function";
return true;
@@ -790,16 +816,16 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
return true;
case MemRegion::CXXThisRegionKind:
case MemRegion::CXXTempObjectRegionKind:
- os << "a C++ temp object of type " << TR->getValueType().getAsString();
+ os << "a C++ temp object of type " << TVR->getValueType().getAsString();
return true;
case MemRegion::VarRegionKind:
- os << "a variable of type" << TR->getValueType().getAsString();
+ os << "a variable of type" << TVR->getValueType().getAsString();
return true;
case MemRegion::FieldRegionKind:
- os << "a field of type " << TR->getValueType().getAsString();
+ os << "a field of type " << TVR->getValueType().getAsString();
return true;
case MemRegion::ObjCIvarRegionKind:
- os << "an instance variable of type " << TR->getValueType().getAsString();
+ os << "an instance variable of type " << TVR->getValueType().getAsString();
return true;
default:
return false;
@@ -812,7 +838,7 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
void CStringChecker::evalCopyCommon(CheckerContext &C,
const CallExpr *CE,
- const GRState *state,
+ const ProgramState *state,
const Expr *Size, const Expr *Dest,
const Expr *Source, bool Restricted,
bool IsMempcpy) const {
@@ -822,7 +848,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
SVal sizeVal = state->getSVal(Size);
QualType sizeTy = Size->getType();
- const GRState *stateZeroSize, *stateNonZeroSize;
+ const ProgramState *stateZeroSize, *stateNonZeroSize;
llvm::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, sizeVal, sizeTy);
@@ -887,7 +913,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
} else {
// If we don't know how much we copied, we can at least
// conjure a return value for later.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
SVal result =
C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count);
state = state->BindExpr(CE, result);
@@ -914,7 +940,7 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
const Expr *Dest = CE->getArg(0);
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true);
}
@@ -923,7 +949,7 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
// void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is a pointer to the byte following the last written byte.
const Expr *Dest = CE->getArg(0);
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
}
@@ -932,7 +958,7 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
// void *memmove(void *dst, const void *src, size_t n);
// The return value is the address of the destination buffer.
const Expr *Dest = CE->getArg(0);
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1));
}
@@ -951,14 +977,14 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
const Expr *Right = CE->getArg(1);
const Expr *Size = CE->getArg(2);
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
// See if the size argument is zero.
SVal sizeVal = state->getSVal(Size);
QualType sizeTy = Size->getType();
- const GRState *stateZeroSize, *stateNonZeroSize;
+ const ProgramState *stateZeroSize, *stateNonZeroSize;
llvm::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, sizeVal, sizeTy);
@@ -981,7 +1007,7 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
// See if they are the same.
DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
- const GRState *StSameBuf, *StNotSameBuf;
+ const ProgramState *StSameBuf, *StNotSameBuf;
llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
// If the two arguments might be the same buffer, we know the result is 0,
@@ -1002,7 +1028,7 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
state = CheckBufferAccess(C, state, Size, Left, Right);
if (state) {
// The return value is the comparison result, which we don't know.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, Count);
state = state->BindExpr(CE, CmpV);
C.addTransition(state);
@@ -1026,13 +1052,13 @@ void CStringChecker::evalstrnLength(CheckerContext &C,
void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
bool IsStrnlen) const {
CurrentFunctionDescription = "string length function";
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (IsStrnlen) {
const Expr *maxlenExpr = CE->getArg(1);
SVal maxlenVal = state->getSVal(maxlenExpr);
- const GRState *stateZeroSize, *stateNonZeroSize;
+ const ProgramState *stateZeroSize, *stateNonZeroSize;
llvm::tie(stateZeroSize, stateNonZeroSize) =
assumeZero(C, state, maxlenVal, maxlenExpr->getType());
@@ -1084,7 +1110,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal);
if (strLengthNL && maxlenValNL) {
- const GRState *stateStringTooLong, *stateStringNotTooLong;
+ const ProgramState *stateStringTooLong, *stateStringNotTooLong;
// Check if the strLength is greater than the maxlen.
llvm::tie(stateStringTooLong, stateStringNotTooLong) =
@@ -1108,7 +1134,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// no guarantee the full string length will actually be returned.
// All we know is the return value is the min of the string length
// and the limit. This is better than nothing.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count);
NonLoc *resultNL = cast<NonLoc>(&result);
@@ -1136,7 +1162,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// If we don't know the length of the string, conjure a return
// value, so it can be used in constraints, at least.
if (result.isUnknown()) {
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count);
}
}
@@ -1191,7 +1217,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
bool returnEnd, bool isBounded,
bool isAppending) const {
CurrentFunctionDescription = "string copy function";
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// Check that the destination is non-null.
const Expr *Dst = CE->getArg(0);
@@ -1241,7 +1267,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If we know both values, we might be able to figure out how much
// we're copying.
if (strLengthNL && lenValNL) {
- const GRState *stateSourceTooLong, *stateSourceNotTooLong;
+ const ProgramState *stateSourceTooLong, *stateSourceNotTooLong;
// Check if the max number to copy is less than the length of the src.
// If the bound is equal to the source length, strncpy won't null-
@@ -1480,7 +1506,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If this is a stpcpy-style copy, but we were unable to check for a buffer
// overflow, we still need a result. Conjure a return value.
if (returnEnd && Result.isUnknown()) {
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
Result = svalBuilder.getConjuredSymbolVal(NULL, CE, Count);
}
@@ -1514,7 +1540,7 @@ void CStringChecker::evalStrncasecmp(CheckerContext &C,
void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
bool isBounded, bool ignoreCase) const {
CurrentFunctionDescription = "string comparison function";
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// Check that the first string is non-null
const Expr *s1 = CE->getArg(0);
@@ -1549,7 +1575,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// See if they are the same.
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
- const GRState *StSameBuf, *StNotSameBuf;
+ const ProgramState *StSameBuf, *StNotSameBuf;
llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
// If the two arguments might be the same buffer, we know the result is 0,
@@ -1575,8 +1601,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
bool canComputeResult = false;
if (s1StrLiteral && s2StrLiteral) {
- llvm::StringRef s1StrRef = s1StrLiteral->getString();
- llvm::StringRef s2StrRef = s2StrLiteral->getString();
+ StringRef s1StrRef = s1StrLiteral->getString();
+ StringRef s2StrRef = s2StrLiteral->getString();
if (isBounded) {
// Get the max number of characters to compare.
@@ -1598,11 +1624,11 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
if (canComputeResult) {
// Real strcmp stops at null characters.
size_t s1Term = s1StrRef.find('\0');
- if (s1Term != llvm::StringRef::npos)
+ if (s1Term != StringRef::npos)
s1StrRef = s1StrRef.substr(0, s1Term);
size_t s2Term = s2StrRef.find('\0');
- if (s2Term != llvm::StringRef::npos)
+ if (s2Term != StringRef::npos)
s2StrRef = s2StrRef.substr(0, s2Term);
// Use StringRef's comparison methods to compute the actual result.
@@ -1624,7 +1650,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
if (!canComputeResult) {
// Conjure a symbolic value. It's the best we can do.
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ unsigned Count = C.getCurrentBlockCount();
SVal resultVal = svalBuilder.getConjuredSymbolVal(NULL, CE, Count);
state = state->BindExpr(CE, resultVal);
}
@@ -1640,7 +1666,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Get the callee. All the functions we care about are C functions
// with simple identifiers.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl();
@@ -1651,7 +1677,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
IdentifierInfo *II = FD->getIdentifier();
if (!II) // if no identifier, not a simple C function
return false;
- llvm::StringRef Name = II->getName();
+ StringRef Name = II->getName();
if (Name.startswith("__builtin_"))
Name = Name.substr(10);
@@ -1689,7 +1715,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
// Record string length for char a[] = "abc";
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
I != E; ++I) {
@@ -1723,16 +1749,16 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
C.addTransition(state);
}
-bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) const {
+bool CStringChecker::wantsRegionChangeUpdate(const ProgramState *state) const {
CStringLength::EntryMap Entries = state->get<CStringLength>();
return !Entries.isEmpty();
}
-const GRState *
-CStringChecker::checkRegionChanges(const GRState *state,
+const ProgramState *
+CStringChecker::checkRegionChanges(const ProgramState *state,
const StoreManager::InvalidatedSymbols *,
- const MemRegion * const *Begin,
- const MemRegion * const *End) const {
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions) const {
CStringLength::EntryMap Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return state;
@@ -1741,8 +1767,9 @@ CStringChecker::checkRegionChanges(const GRState *state,
llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
// First build sets for the changed regions and their super-regions.
- for ( ; Begin != End; ++Begin) {
- const MemRegion *MR = *Begin;
+ for (ArrayRef<const MemRegion *>::iterator
+ I = Regions.begin(), E = Regions.end(); I != E; ++I) {
+ const MemRegion *MR = *I;
Invalidated.insert(MR);
SuperRegions.insert(MR);
@@ -1779,7 +1806,7 @@ CStringChecker::checkRegionChanges(const GRState *state,
return state->set<CStringLength>(Entries);
}
-void CStringChecker::checkLiveSymbols(const GRState *state,
+void CStringChecker::checkLiveSymbols(const ProgramState *state,
SymbolReaper &SR) const {
// Mark all symbols in our string length map as valid.
CStringLength::EntryMap Entries = state->get<CStringLength>();
@@ -1799,7 +1826,7 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
if (!SR.hasDeadSymbols())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
CStringLength::EntryMap Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return;
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 6c3dfac..4db6ac0 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.h"
@@ -47,7 +48,8 @@ private:
void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg,
ExplodedNode *N) const;
- void HandleNilReceiver(CheckerContext &C, const GRState *state,
+ void HandleNilReceiver(CheckerContext &C,
+ const ProgramState *state,
ObjCMessage msg) const;
static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) {
@@ -63,9 +65,9 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
if (!N)
return;
- EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N);
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- bugreporter::GetCalleeExpr(N));
+ BugReport *R = new BugReport(*BT, BT->getName(), N);
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetCalleeExpr(N)));
C.EmitReport(R);
}
@@ -91,10 +93,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
LazyInit_BT(BT_desc, BT);
// Generate a report for this bug.
- EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N);
+ BugReport *R = new BugReport(*BT, BT->getName(), N);
R->addRange(argRange);
if (argEx)
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, argEx);
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx));
C.EmitReport(R);
}
return true;
@@ -105,7 +107,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
class FindUninitializedField {
public:
- llvm::SmallVector<const FieldDecl *, 10> FieldChain;
+ SmallVector<const FieldDecl *, 10> FieldChain;
private:
ASTContext &C;
StoreManager &StoreMgr;
@@ -116,7 +118,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
MemRegionManager &mrMgr, Store s)
: C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
- bool Find(const TypedRegion *R) {
+ bool Find(const TypedValueRegion *R) {
QualType T = R->getValueType();
if (const RecordType *RT = T->getAsStructureType()) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
@@ -157,23 +159,23 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
os << "Passed-by-value struct argument contains uninitialized data";
if (F.FieldChain.size() == 1)
- os << " (e.g., field: '" << F.FieldChain[0] << "')";
+ os << " (e.g., field: '" << *F.FieldChain[0] << "')";
else {
os << " (e.g., via the field chain: '";
bool first = true;
- for (llvm::SmallVectorImpl<const FieldDecl *>::iterator
+ for (SmallVectorImpl<const FieldDecl *>::iterator
DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
if (first)
first = false;
else
os << '.';
- os << *DI;
+ os << **DI;
}
os << "')";
}
// Generate a report for this bug.
- EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N);
+ BugReport *R = new BugReport(*BT, os.str(), N);
R->addRange(argRange);
// FIXME: enhance track back for uninitialized value for arbitrary
@@ -216,7 +218,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// FIXME: Handle 'super'?
if (const Expr *receiver = msg.getInstanceReceiver()) {
@@ -226,11 +228,11 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
if (!BT_msg_undef)
BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is "
"an uninitialized value"));
- EnhancedBugReport *R =
- new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
+ BugReport *R =
+ new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
R->addRange(receiver->getSourceRange());
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- receiver);
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ receiver));
C.EmitReport(R);
}
return;
@@ -238,7 +240,7 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
// Bifurcate the state into nil and non-nil ones.
DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
- const GRState *notNilState, *nilState;
+ const ProgramState *notNilState, *nilState;
llvm::tie(notNilState, nilState) = state->assume(receiverVal);
// Handle receiver must be nil.
@@ -271,11 +273,11 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
<< "' is nil and returns a value of type '"
<< msg.getType(C.getASTContext()).getAsString() << "' that will be garbage";
- EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N);
+ BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
if (const Expr *receiver = msg.getInstanceReceiver()) {
report->addRange(receiver->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- receiver);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ receiver));
}
C.EmitReport(report);
}
@@ -288,7 +290,7 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
}
void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
- const GRState *state,
+ const ProgramState *state,
ObjCMessage msg) const {
ASTContext &Ctx = C.getASTContext();
@@ -303,7 +305,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
// have the "use of undefined value" be smarter about where the
// undefined value came from.
if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){
- if (ExplodedNode* N = C.generateSink(state))
+ if (ExplodedNode *N = C.generateSink(state))
emitNilReceiverBug(C, msg, N);
return;
}
@@ -322,13 +324,13 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
if (voidPtrSize < returnTypeSize &&
- !(supportsNilWithFloatRet(Ctx.Target.getTriple()) &&
+ !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
(Ctx.FloatTy == CanRetTy ||
Ctx.DoubleTy == CanRetTy ||
Ctx.LongDoubleTy == CanRetTy ||
Ctx.LongLongTy == CanRetTy ||
Ctx.UnsignedLongLongTy == CanRetTy))) {
- if (ExplodedNode* N = C.generateSink(state))
+ if (ExplodedNode *N = C.generateSink(state))
emitNilReceiverBug(C, msg, N);
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 585a87d..84a9e6b 100644
--- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -44,7 +44,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
if (ToPointeeTy->isIncompleteType())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const MemRegion *R = state->getSVal(E).getAsRegion();
if (R == 0)
return;
@@ -72,7 +72,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
BT.reset(new BuiltinBug("Cast region with wrong size.",
"Cast a region whose size is not a multiple of the"
" destination type size."));
- RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(),
+ BugReport *R = new BugReport(*BT, BT->getDescription(),
errorNode);
R->addRange(CE->getSourceRange());
C.EmitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
index 3210b0a..c855210 100644
--- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -62,7 +62,7 @@ void CastToStructChecker::checkPreStmt(const CastExpr *CE,
"Casting a non-structure type to a structure type "
"and accessing a field can lead to memory access "
"errors or data corruption."));
- RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N);
+ BugReport *R = new BugReport(*BT,BT->getDescription(), N);
R->addRange(CE->getSourceRange());
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 0c693a0..c325bb1 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -27,9 +27,9 @@
using namespace clang;
using namespace ento;
-static bool scan_dealloc(Stmt* S, Selector Dealloc) {
+static bool scan_dealloc(Stmt *S, Selector Dealloc) {
- if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
+ if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
if (ME->getSelector() == Dealloc) {
switch (ME->getReceiverKind()) {
case ObjCMessageExpr::Instance: return false;
@@ -48,26 +48,26 @@ static bool scan_dealloc(Stmt* S, Selector Dealloc) {
return false;
}
-static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
- const ObjCPropertyDecl* PD,
+static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID,
+ const ObjCPropertyDecl *PD,
Selector Release,
IdentifierInfo* SelfII,
- ASTContext& Ctx) {
+ ASTContext &Ctx) {
// [mMyIvar release]
- if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
+ if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
if (ME->getSelector() == Release)
if (ME->getInstanceReceiver())
- if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
- if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver))
+ if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
+ if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver))
if (E->getDecl() == ID)
return true;
// [self setMyIvar:nil];
- if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
+ if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
if (ME->getInstanceReceiver())
- if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
- if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver))
+ if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
+ if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver))
if (E->getDecl()->getIdentifier() == SelfII)
if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
ME->getNumArgs() == 1 &&
@@ -78,7 +78,7 @@ static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
// self.myIvar = nil;
if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S))
if (BO->isAssignmentOp())
- if (ObjCPropertyRefExpr* PRE =
+ if (ObjCPropertyRefExpr *PRE =
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD)
if (BO->getRHS()->isNullPointerConstant(Ctx,
@@ -96,13 +96,13 @@ static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
return false;
}
-static void checkObjCDealloc(const ObjCImplementationDecl* D,
+static void checkObjCDealloc(const ObjCImplementationDecl *D,
const LangOptions& LOpts, BugReporter& BR) {
- assert (LOpts.getGCMode() != LangOptions::GCOnly);
+ assert (LOpts.getGC() != LangOptions::GCOnly);
- ASTContext& Ctx = BR.getContext();
- const ObjCInterfaceDecl* ID = D->getClassInterface();
+ ASTContext &Ctx = BR.getContext();
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
// Does the class contain any ivars that are pointers (or id<...>)?
// If not, skip the check entirely.
@@ -114,7 +114,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end();
I!=E; ++I) {
- ObjCIvarDecl* ID = *I;
+ ObjCIvarDecl *ID = *I;
QualType T = ID->getType();
if (!T->isObjCObjectPointerType() ||
@@ -154,7 +154,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
// Get the "dealloc" selector.
IdentifierInfo* II = &Ctx.Idents.get("dealloc");
Selector S = Ctx.Selectors.getSelector(0, &II);
- ObjCMethodDecl* MD = 0;
+ ObjCMethodDecl *MD = 0;
// Scan the instance methods for "dealloc".
for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
@@ -166,9 +166,12 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
}
}
+ PathDiagnosticLocation DLoc =
+ PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
+
if (!MD) { // No dealloc found.
- const char* name = LOpts.getGCMode() == LangOptions::NonGC
+ const char* name = LOpts.getGC() == LangOptions::NonGC
? "missing -dealloc"
: "missing -dealloc (Hybrid MM, non-GC)";
@@ -176,14 +179,14 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
llvm::raw_string_ostream os(buf);
os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method";
- BR.EmitBasicReport(name, os.str(), D->getLocStart());
+ BR.EmitBasicReport(name, os.str(), DLoc);
return;
}
// dealloc found. Scan for missing [super dealloc].
if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) {
- const char* name = LOpts.getGCMode() == LangOptions::NonGC
+ const char* name = LOpts.getGC() == LangOptions::NonGC
? "missing [super dealloc]"
: "missing [super dealloc] (Hybrid MM, non-GC)";
@@ -193,7 +196,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
<< "' does not send a 'dealloc' message to its super class"
" (missing [super dealloc])";
- BR.EmitBasicReport(name, os.str(), D->getLocStart());
+ BR.EmitBasicReport(name, os.str(), DLoc);
return;
}
@@ -213,7 +216,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
continue;
- ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl();
+ ObjCIvarDecl *ID = (*I)->getPropertyIvarDecl();
if (!ID)
continue;
@@ -221,13 +224,13 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars
continue;
- const ObjCPropertyDecl* PD = (*I)->getPropertyDecl();
+ const ObjCPropertyDecl *PD = (*I)->getPropertyDecl();
if (!PD)
continue;
// ivars cannot be set via read-only properties, so we'll skip them
if (PD->isReadOnly())
- continue;
+ continue;
// ivar must be released if and only if the kind of setter was not 'assign'
bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign;
@@ -240,24 +243,27 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D,
llvm::raw_string_ostream os(buf);
if (requiresRelease) {
- name = LOpts.getGCMode() == LangOptions::NonGC
+ name = LOpts.getGC() == LangOptions::NonGC
? "missing ivar release (leak)"
: "missing ivar release (Hybrid MM, non-GC)";
- os << "The '" << ID
+ os << "The '" << *ID
<< "' instance variable was retained by a synthesized property but "
"wasn't released in 'dealloc'";
} else {
- name = LOpts.getGCMode() == LangOptions::NonGC
+ name = LOpts.getGC() == LangOptions::NonGC
? "extra ivar release (use-after-release)"
: "extra ivar release (Hybrid MM, non-GC)";
- os << "The '" << ID
+ os << "The '" << *ID
<< "' instance variable was not retained by a synthesized property "
"but was released in 'dealloc'";
}
- BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation());
+ PathDiagnosticLocation SDLoc =
+ PathDiagnosticLocation::createBegin((*I), BR.getSourceManager());
+
+ BR.EmitBasicReport(name, category, os.str(), SDLoc);
}
}
}
@@ -272,7 +278,7 @@ class ObjCDeallocChecker : public Checker<
public:
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly)
+ if (mgr.getLangOptions().getGC() == LangOptions::GCOnly)
return;
checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR);
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index fec06a9..c076c1e3 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -28,7 +28,7 @@ using namespace clang;
using namespace ento;
static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
- ASTContext& C) {
+ ASTContext &C) {
// Right now don't compare the compatibility of pointers. That involves
// looking at subtyping relationships. FIXME: Future patch.
@@ -51,36 +51,40 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
llvm::raw_string_ostream os(sbuf);
os << "The Objective-C class '"
- << MethDerived->getClassInterface()
+ << *MethDerived->getClassInterface()
<< "', which is derived from class '"
- << MethAncestor->getClassInterface()
+ << *MethAncestor->getClassInterface()
<< "', defines the instance method '"
<< MethDerived->getSelector().getAsString()
<< "' whose return type is '"
<< ResDerived.getAsString()
<< "'. A method with the same name (same selector) is also defined in "
"class '"
- << MethAncestor->getClassInterface()
+ << *MethAncestor->getClassInterface()
<< "' and has a return type of '"
<< ResAncestor.getAsString()
<< "'. These two types are incompatible, and may result in undefined "
"behavior for clients of these classes.";
+ PathDiagnosticLocation MethDLoc =
+ PathDiagnosticLocation::createBegin(MethDerived,
+ BR.getSourceManager());
+
BR.EmitBasicReport("Incompatible instance method return type",
- os.str(), MethDerived->getLocStart());
+ os.str(), MethDLoc);
}
}
-static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID,
+static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
BugReporter& BR) {
- const ObjCInterfaceDecl* D = ID->getClassInterface();
- const ObjCInterfaceDecl* C = D->getSuperClass();
+ const ObjCInterfaceDecl *D = ID->getClassInterface();
+ const ObjCInterfaceDecl *C = D->getSuperClass();
if (!C)
return;
- ASTContext& Ctx = BR.getContext();
+ ASTContext &Ctx = BR.getContext();
// Build a DenseMap of the methods for quick querying.
typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
@@ -90,7 +94,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID,
for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(),
E=ID->instmeth_end(); I!=E; ++I) {
- ObjCMethodDecl* M = *I;
+ ObjCMethodDecl *M = *I;
IMeths[M->getSelector()] = M;
++NumMethods;
}
@@ -101,7 +105,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID,
for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(),
E=C->instmeth_end(); I!=E; ++I) {
- ObjCMethodDecl* M = *I;
+ ObjCMethodDecl *M = *I;
Selector S = M->getSelector();
MapTy::iterator MI = IMeths.find(S);
@@ -110,7 +114,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID,
continue;
--NumMethods;
- ObjCMethodDecl* MethDerived = MI->second;
+ ObjCMethodDecl *MethDerived = MI->second;
MI->second = 0;
CompareReturnTypes(MethDerived, M, BR, Ctx, ID);
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 53810ee..bf7ba18 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -12,18 +12,20 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/Basic/TargetInfo.h"
-#include "clang/AST/StmtVisitor.h"
-#include "llvm/Support/raw_ostream.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
static bool isArc4RandomAvailable(const ASTContext &Ctx) {
- const llvm::Triple &T = Ctx.Target.getTriple();
+ const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
return T.getVendor() == llvm::Triple::Apple ||
T.getOS() == llvm::Triple::FreeBSD ||
T.getOS() == llvm::Triple::NetBSD ||
@@ -34,14 +36,16 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) {
namespace {
class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR;
+ AnalysisContext* AC;
enum { num_setids = 6 };
IdentifierInfo *II_setid[num_setids];
const bool CheckRand;
public:
- WalkAST(BugReporter &br) : BR(br), II_setid(),
- CheckRand(isArc4RandomAvailable(BR.getContext())) {}
+ WalkAST(BugReporter &br, AnalysisContext* ac)
+ : BR(br), AC(ac), II_setid(),
+ CheckRand(isArc4RandomAvailable(BR.getContext())) {}
// Statement visitor methods.
void VisitCallExpr(CallExpr *CE);
@@ -52,7 +56,6 @@ public:
void VisitChildren(Stmt *S);
// Helpers.
- IdentifierInfo *getIdentifier(IdentifierInfo *& II, const char *str);
bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
typedef void (WalkAST::*FnCheck)(const CallExpr *,
@@ -67,22 +70,12 @@ public:
void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
void checkUncheckedReturnValue(CallExpr *CE);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
-// Helper methods.
-//===----------------------------------------------------------------------===//
-
-IdentifierInfo *WalkAST::getIdentifier(IdentifierInfo *& II, const char *str) {
- if (!II)
- II = &BR.getContext().Idents.get(str);
-
- return II;
-}
-
-//===----------------------------------------------------------------------===//
// AST walking.
//===----------------------------------------------------------------------===//
@@ -103,7 +96,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
IdentifierInfo *II = FD->getIdentifier();
if (!II) // if no identifier, not a simple C function
return;
- llvm::StringRef Name = II->getName();
+ StringRef Name = II->getName();
if (Name.startswith("__builtin_"))
Name = Name.substr(10);
@@ -124,6 +117,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
.Case("rand", &WalkAST::checkCall_rand)
.Case("rand_r", &WalkAST::checkCall_rand)
.Case("random", &WalkAST::checkCall_random)
+ .Case("vfork", &WalkAST::checkCall_vfork)
.Default(NULL);
// If the callee isn't defined, it is not of security concern.
@@ -247,7 +241,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
// referenced the compared variable.
const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
- llvm::SmallVector<SourceRange, 2> ranges;
+ SmallVector<SourceRange, 2> ranges;
llvm::SmallString<256> sbuf;
llvm::raw_svector_ostream os(sbuf);
@@ -259,8 +253,11 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
ranges.push_back(drInc->getSourceRange());
const char *bugType = "Floating point variable used as loop counter";
+
+ PathDiagnosticLocation FSLoc =
+ PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
BR.EmitBasicReport(bugType, "Security", os.str(),
- FS->getLocStart(), ranges.data(), ranges.size());
+ FSLoc, ranges.data(), ranges.size());
}
//===----------------------------------------------------------------------===//
@@ -290,11 +287,13 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential buffer overflow in call to 'gets'",
"Security",
"Call to function 'gets' is extremely insecure as it can "
"always result in a buffer overflow",
- CE->getLocStart(), &R, 1);
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -326,11 +325,13 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'",
"Security",
"The getpw() function is dangerous as it may overflow the "
"provided buffer. It is obsoleted by getpwuid().",
- CE->getLocStart(), &R, 1);
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -359,11 +360,13 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a waring.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'",
"Security",
"Call to function 'mktemp' is insecure as it always "
"creates or uses insecure temporary file. Use 'mkstemp' instead",
- CE->getLocStart(), &R, 1);
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -378,6 +381,8 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in "
"call 'strcpy'",
"Security",
@@ -385,7 +390,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
"provide bounding of the memory buffer. Replace "
"unbounded copy functions with analogous functions that "
"support length arguments such as 'strncpy'. CWE-119.",
- CE->getLocStart(), &R, 1);
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -400,6 +405,8 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in "
"call 'strcat'",
"Security",
@@ -407,7 +414,7 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
"provide bounding of the memory buffer. Replace "
"unbounded copy functions with analogous functions that "
"support length arguments such as 'strncat'. CWE-119.",
- CE->getLocStart(), &R, 1);
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -470,16 +477,18 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
llvm::SmallString<256> buf1;
llvm::raw_svector_ostream os1(buf1);
- os1 << '\'' << FD << "' is a poor random number generator";
+ os1 << '\'' << *FD << "' is a poor random number generator";
llvm::SmallString<256> buf2;
llvm::raw_svector_ostream os2(buf2);
- os2 << "Function '" << FD
+ os2 << "Function '" << *FD
<< "' is obsolete because it implements a poor random number generator."
<< " Use 'arc4random' instead";
SourceRange R = CE->getCallee()->getSourceRange();
- BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1);
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -502,11 +511,33 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("'random' is not a secure random number generator",
"Security",
"The 'random' function produces a sequence of values that "
"an adversary may be able to predict. Use 'arc4random' "
- "instead", CE->getLocStart(), &R, 1);
+ "instead", CELoc, &R, 1);
+}
+
+//===----------------------------------------------------------------------===//
+// Check: 'vfork' should not be used.
+// POS33-C: Do not use vfork().
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
+ // All calls to vfork() are insecure, issue a warning.
+ SourceRange R = CE->getCallee()->getSourceRange();
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport("Potential insecure implementation-specific behavior in "
+ "call 'vfork'",
+ "Security",
+ "Call to function 'vfork' is insecure as it can lead to "
+ "denial of service situations in the parent process. "
+ "Replace calls to vfork with calls to the safer "
+ "'posix_spawn' function",
+ CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -557,16 +588,18 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
// Issue a warning.
llvm::SmallString<256> buf1;
llvm::raw_svector_ostream os1(buf1);
- os1 << "Return value is not checked in call to '" << FD << '\'';
+ os1 << "Return value is not checked in call to '" << *FD << '\'';
llvm::SmallString<256> buf2;
llvm::raw_svector_ostream os2(buf2);
- os2 << "The return value from the call to '" << FD
- << "' is not checked. If an error occurs in '" << FD
+ os2 << "The return value from the call to '" << *FD
+ << "' is not checked. If an error occurs in '" << *FD
<< "', the following code may execute with unexpected privileges";
SourceRange R = CE->getCallee()->getSourceRange();
- BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1);
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1);
}
//===----------------------------------------------------------------------===//
@@ -578,7 +611,7 @@ class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- WalkAST walker(BR);
+ WalkAST walker(BR, mgr.getAnalysisContext(D));
walker.Visit(D->getBody());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
index abf53fd..469be05 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
@@ -13,9 +13,10 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
+#include "clang/AST/StmtVisitor.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
using namespace clang;
using namespace ento;
@@ -23,9 +24,10 @@ using namespace ento;
namespace {
class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR;
+ AnalysisContext* AC;
public:
- WalkAST(BugReporter &br) : BR(br) {}
+ WalkAST(BugReporter &br, AnalysisContext* ac) : BR(br), AC(ac) {}
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
void VisitStmt(Stmt *S) { VisitChildren(S); }
void VisitChildren(Stmt *S);
@@ -59,11 +61,13 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
return;
SourceRange R = ArgEx->getSourceRange();
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type",
"Logic",
"The code calls sizeof() on a pointer type. "
"This can produce an unexpected result.",
- E->getLocStart(), &R, 1);
+ ELoc, &R, 1);
}
}
@@ -76,7 +80,7 @@ class SizeofPointerChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- WalkAST walker(BR);
+ WalkAST walker(BR, mgr.getAnalysisContext(D));
walker.Visit(D->getBody());
}
};
diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td
index 2c196b5..d53e0b8 100644
--- a/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -10,41 +10,32 @@
include "clang/StaticAnalyzer/Checkers/CheckerBase.td"
//===----------------------------------------------------------------------===//
-// Groups.
-//===----------------------------------------------------------------------===//
-
-def AllExperimental : CheckerGroup<"all-experimental">;
-
-//===----------------------------------------------------------------------===//
// Packages.
//===----------------------------------------------------------------------===//
+def Experimental : Package<"experimental">;
+
def Core : Package<"core">;
def CoreBuiltin : Package<"builtin">, InPackage<Core>;
def CoreUninitialized : Package<"uninitialized">, InPackage<Core>;
-def CoreExperimental : Package<"experimental">, InPackage<Core>,
- InGroup<AllExperimental>, Hidden;
+def CoreExperimental : Package<"core">, InPackage<Experimental>, Hidden;
def Cplusplus : Package<"cplusplus">;
-def CplusplusExperimental : Package<"experimental">, InPackage<Cplusplus>,
- InGroup<AllExperimental>, Hidden;
+def CplusplusExperimental : Package<"cplusplus">, InPackage<Experimental>, Hidden;
def DeadCode : Package<"deadcode">;
-def DeadCodeExperimental : Package<"experimental">, InPackage<DeadCode>,
- InGroup<AllExperimental>, Hidden;
+def DeadCodeExperimental : Package<"deadcode">, InPackage<Experimental>, Hidden;
def Security : Package <"security">;
-def SecurityExperimental : Package<"experimental">, InPackage<Security>,
- InGroup<AllExperimental>, Hidden;
+def SecurityExperimental : Package<"security">, InPackage<Experimental>, Hidden;
def Unix : Package<"unix">;
-def UnixExperimental : Package<"experimental">, InPackage<Unix>,
- InGroup<AllExperimental>, Hidden;
+def UnixExperimental : Package<"unix">, InPackage<Experimental>, Hidden;
def OSX : Package<"osx">;
+def OSXExperimental : Package<"osx">, InPackage<Experimental>, Hidden;
def Cocoa : Package<"cocoa">, InPackage<OSX>;
-def CocoaExperimental : Package<"experimental">, InPackage<Cocoa>,
- InGroup<AllExperimental>, Hidden;
+def CocoaExperimental : Package<"cocoa">, InPackage<OSXExperimental>, Hidden;
def CoreFoundation : Package<"coreFoundation">, InPackage<OSX>;
def LLVM : Package<"llvm">;
@@ -220,6 +211,10 @@ def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,
HelpText<"Check for an out-of-bound pointer being returned to callers">,
DescFile<"ReturnPointerRangeChecker.cpp">;
+def MallocOverflowSecurityChecker : Checker<"MallocOverflow">,
+ HelpText<"Check for overflows in the arguments to malloc()">,
+ DescFile<"MallocOverflowSecurityChecker.cpp">;
+
} // end "security.experimental"
//===----------------------------------------------------------------------===//
@@ -274,6 +269,11 @@ def OSAtomicChecker : Checker<"AtomicCAS">,
HelpText<"Evaluate calls to OSAtomic functions">,
DescFile<"OSAtomicChecker.cpp">;
+def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">,
+ InPackage<OSX>,
+ HelpText<"Check for proper uses of Secure Keychain APIs">,
+ DescFile<"MacOSKeychainAPIChecker.cpp">;
+
} // end "macosx"
let ParentPackage = Cocoa in {
@@ -311,6 +311,10 @@ def NSErrorChecker : Checker<"NSError">,
HelpText<"Check usage of NSError** parameters">,
DescFile<"NSErrorChecker.cpp">;
+def RetainCountChecker : Checker<"RetainCount">,
+ HelpText<"Check for leaks and improper reference count management">,
+ DescFile<"RetainCountChecker.cpp">;
+
} // end "cocoa"
let ParentPackage = CocoaExperimental in {
diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 50b57d1..3c92381 100644
--- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -16,8 +16,8 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ImmutableMap.h"
using namespace clang;
@@ -62,7 +62,7 @@ private:
} // end anonymous namespace
bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
const FunctionDecl *FD = L.getAsFunctionDecl();
@@ -88,8 +88,8 @@ bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
}
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
- GRStateManager &Mgr = state->getStateManager();
+ const ProgramState *state = C.getState();
+ ProgramStateManager &Mgr = state->getStateManager();
// Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
// the GDM.
@@ -98,11 +98,11 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
}
void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
- GRStateManager &Mgr = state->getStateManager();
+ const ProgramState *state = C.getState();
+ ProgramStateManager &Mgr = state->getStateManager();
// If there are no jail state in the GDM, just return.
- const void* k = state->FindGDM(ChrootChecker::getTag());
+ const void *k = state->FindGDM(ChrootChecker::getTag());
if (!k)
return;
@@ -125,7 +125,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
// Check the jail state before any function call except chroot and chdir().
void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
const FunctionDecl *FD = L.getAsFunctionDecl();
@@ -143,7 +143,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
return;
// If jail state is ROOT_CHANGED, generate BugReport.
- void* const* k = state->FindGDM(ChrootChecker::getTag());
+ void *const* k = state->FindGDM(ChrootChecker::getTag());
if (k)
if (isRootChanged((intptr_t) *k))
if (ExplodedNode *N = C.generateNode()) {
diff --git a/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp b/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp
new file mode 100644
index 0000000..77a5a72
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp
@@ -0,0 +1,32 @@
+//===--- ClangCheckers.h - Provides builtin checkers ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/ClangCheckers.h"
+#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
+
+// FIXME: This is only necessary as long as there are checker registration
+// functions that do additional work besides mgr.registerChecker<CLASS>().
+// The only checkers that currently do this are:
+// - NSAutoreleasePoolChecker
+// - NSErrorChecker
+// - ObjCAtSyncChecker
+// It's probably worth including this information in Checkers.td to minimize
+// boilerplate code.
+#include "ClangSACheckers.h"
+
+using namespace clang;
+using namespace ento;
+
+void ento::registerBuiltinCheckers(CheckerRegistry &registry) {
+#define GET_CHECKERS
+#define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,GROUPINDEX,HIDDEN) \
+ registry.addChecker(register##CLASS, FULLNAME, HELPTEXT);
+#include "Checkers.inc"
+#undef GET_CHECKERS
+}
diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp
deleted file mode 100644
index 291f8e0..0000000
--- a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-//===--- ClangSACheckerProvider.cpp - Clang SA Checkers Provider ----------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines the CheckerProvider for the checkers defined in
-// libclangStaticAnalyzerCheckers.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ClangSACheckerProvider.h"
-#include "ClangSACheckers.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/CheckerProvider.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/ADT/DenseSet.h"
-#include "map"
-
-using namespace clang;
-using namespace ento;
-
-namespace {
-
-/// \brief Provider for all the checkers in libclangStaticAnalyzerCheckers.
-class ClangSACheckerProvider : public CheckerProvider {
-public:
- virtual void registerCheckers(CheckerManager &checkerMgr,
- CheckerOptInfo *checkOpts, unsigned numCheckOpts);
- virtual void printHelp(llvm::raw_ostream &OS);
-};
-
-}
-
-CheckerProvider *ento::createClangSACheckerProvider() {
- return new ClangSACheckerProvider();
-}
-
-namespace {
-
-struct StaticCheckerInfoRec {
- const char *FullName;
- void (*RegFunc)(CheckerManager &mgr);
- const char *HelpText;
- int GroupIndex;
- bool Hidden;
-};
-
-struct StaticPackageInfoRec {
- const char *FullName;
- int GroupIndex;
- bool Hidden;
-};
-
-struct StaticGroupInfoRec {
- const char *FullName;
-};
-
-} // end anonymous namespace.
-
-static const StaticPackageInfoRec StaticPackageInfo[] = {
-#define GET_PACKAGES
-#define PACKAGE(FULLNAME, GROUPINDEX, HIDDEN) \
- { FULLNAME, GROUPINDEX, HIDDEN },
-#include "Checkers.inc"
- { 0, -1, 0 }
-#undef PACKAGE
-#undef GET_PACKAGES
-};
-
-static const unsigned NumPackages = sizeof(StaticPackageInfo)
- / sizeof(StaticPackageInfoRec) - 1;
-
-static const StaticGroupInfoRec StaticGroupInfo[] = {
-#define GET_GROUPS
-#define GROUP(FULLNAME) \
- { FULLNAME },
-#include "Checkers.inc"
- { 0 }
-#undef GROUP
-#undef GET_GROUPS
-};
-
-static const unsigned NumGroups = sizeof(StaticGroupInfo)
- / sizeof(StaticGroupInfoRec) - 1;
-
-static const StaticCheckerInfoRec StaticCheckerInfo[] = {
-#define GET_CHECKERS
-#define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,GROUPINDEX,HIDDEN) \
- { FULLNAME, register##CLASS, HELPTEXT, GROUPINDEX, HIDDEN },
-#include "Checkers.inc"
- { 0, 0, 0, -1, 0}
-#undef CHECKER
-#undef GET_CHECKERS
-};
-
-static const unsigned NumCheckers = sizeof(StaticCheckerInfo)
- / sizeof(StaticCheckerInfoRec) - 1;
-
-namespace {
-
-struct CheckNameOption {
- const char *Name;
- const short *Members;
- const short *SubGroups;
- bool Hidden;
-};
-
-} // end anonymous namespace.
-
-#define GET_MEMBER_ARRAYS
-#include "Checkers.inc"
-#undef GET_MEMBER_ARRAYS
-
-// The table of check name options, sorted by name for fast binary lookup.
-static const CheckNameOption CheckNameTable[] = {
-#define GET_CHECKNAME_TABLE
-#include "Checkers.inc"
-#undef GET_CHECKNAME_TABLE
-};
-static const size_t
- CheckNameTableSize = sizeof(CheckNameTable) / sizeof(CheckNameTable[0]);
-
-static bool CheckNameOptionCompare(const CheckNameOption &LHS,
- const CheckNameOption &RHS) {
- return strcmp(LHS.Name, RHS.Name) < 0;
-}
-
-static void collectCheckers(const CheckNameOption *checkName,
- bool enable,
- llvm::DenseSet<const StaticCheckerInfoRec *> &checkers,
- bool collectHidden) {
- if (checkName->Hidden && !collectHidden)
- return;
-
- if (const short *member = checkName->Members) {
- if (enable) {
- for (; *member != -1; ++member)
- if (collectHidden || !StaticCheckerInfo[*member].Hidden)
- checkers.insert(&StaticCheckerInfo[*member]);
- } else {
- for (; *member != -1; ++member)
- checkers.erase(&StaticCheckerInfo[*member]);
- }
- }
-
- // Enable/disable all subgroups along with this one.
- if (const short *subGroups = checkName->SubGroups) {
- for (; *subGroups != -1; ++subGroups) {
- const CheckNameOption *sub = &CheckNameTable[*subGroups];
- collectCheckers(sub, enable, checkers, collectHidden && !sub->Hidden);
- }
- }
-}
-
-static void collectCheckers(CheckerOptInfo &opt,
- llvm::DenseSet<const StaticCheckerInfoRec *> &checkers) {
- const char *optName = opt.getName();
- CheckNameOption key = { optName, 0, 0, false };
- const CheckNameOption *found =
- std::lower_bound(CheckNameTable, CheckNameTable + CheckNameTableSize, key,
- CheckNameOptionCompare);
- if (found == CheckNameTable + CheckNameTableSize ||
- strcmp(found->Name, optName) != 0)
- return; // Check name not found.
-
- opt.claim();
- collectCheckers(found, opt.isEnabled(), checkers, /*collectHidden=*/true);
-}
-
-void ClangSACheckerProvider::registerCheckers(CheckerManager &checkerMgr,
- CheckerOptInfo *checkOpts, unsigned numCheckOpts) {
- llvm::DenseSet<const StaticCheckerInfoRec *> enabledCheckers;
- for (unsigned i = 0; i != numCheckOpts; ++i)
- collectCheckers(checkOpts[i], enabledCheckers);
- for (llvm::DenseSet<const StaticCheckerInfoRec *>::iterator
- I = enabledCheckers.begin(), E = enabledCheckers.end(); I != E; ++I) {
- (*I)->RegFunc(checkerMgr);
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Printing Help.
-//===----------------------------------------------------------------------===//
-
-static void printPackageOption(llvm::raw_ostream &OS) {
- // Find the maximum option length.
- unsigned OptionFieldWidth = 0;
- for (unsigned i = 0; i != NumPackages; ++i) {
- // Limit the amount of padding we are willing to give up for alignment.
- unsigned Length = strlen(StaticPackageInfo[i].FullName);
- if (Length <= 30)
- OptionFieldWidth = std::max(OptionFieldWidth, Length);
- }
-
- const unsigned InitialPad = 2;
- for (unsigned i = 0; i != NumPackages; ++i) {
- const StaticPackageInfoRec &package = StaticPackageInfo[i];
- const std::string &Option = package.FullName;
- int Pad = OptionFieldWidth - int(Option.size());
- OS.indent(InitialPad) << Option;
-
- if (package.GroupIndex != -1 || package.Hidden) {
- // Break on long option names.
- if (Pad < 0) {
- OS << "\n";
- Pad = OptionFieldWidth + InitialPad;
- }
- OS.indent(Pad + 1) << "[";
- if (package.GroupIndex != -1) {
- OS << "Group=" << StaticGroupInfo[package.GroupIndex].FullName;
- if (package.Hidden)
- OS << ", ";
- }
- if (package.Hidden)
- OS << "Hidden";
- OS << "]";
- }
-
- OS << "\n";
- }
-}
-
-typedef std::map<std::string, const StaticCheckerInfoRec *> SortedCheckers;
-
-static void printCheckerOption(llvm::raw_ostream &OS,SortedCheckers &checkers) {
- // Find the maximum option length.
- unsigned OptionFieldWidth = 0;
- for (SortedCheckers::iterator
- I = checkers.begin(), E = checkers.end(); I != E; ++I) {
- // Limit the amount of padding we are willing to give up for alignment.
- unsigned Length = strlen(I->second->FullName);
- if (Length <= 30)
- OptionFieldWidth = std::max(OptionFieldWidth, Length);
- }
-
- const unsigned InitialPad = 2;
- for (SortedCheckers::iterator
- I = checkers.begin(), E = checkers.end(); I != E; ++I) {
- const std::string &Option = I->first;
- const StaticCheckerInfoRec &checker = *I->second;
- int Pad = OptionFieldWidth - int(Option.size());
- OS.indent(InitialPad) << Option;
-
- // Break on long option names.
- if (Pad < 0) {
- OS << "\n";
- Pad = OptionFieldWidth + InitialPad;
- }
- OS.indent(Pad + 1) << checker.HelpText;
-
- if (checker.GroupIndex != -1 || checker.Hidden) {
- OS << " [";
- if (checker.GroupIndex != -1) {
- OS << "Group=" << StaticGroupInfo[checker.GroupIndex].FullName;
- if (checker.Hidden)
- OS << ", ";
- }
- if (checker.Hidden)
- OS << "Hidden";
- OS << "]";
- }
-
- OS << "\n";
- }
-}
-
-void ClangSACheckerProvider::printHelp(llvm::raw_ostream &OS) {
- OS << "USAGE: -analyzer-checker <CHECKER or PACKAGE or GROUP,...>\n";
-
- OS << "\nGROUPS:\n";
- for (unsigned i = 0; i != NumGroups; ++i)
- OS.indent(2) << StaticGroupInfo[i].FullName << "\n";
-
- OS << "\nPACKAGES:\n";
- printPackageOption(OS);
-
- OS << "\nCHECKERS:\n";
-
- // Sort checkers according to their full name.
- SortedCheckers checkers;
- for (unsigned i = 0; i != NumCheckers; ++i)
- checkers[StaticCheckerInfo[i].FullName] = &StaticCheckerInfo[i];
-
- printCheckerOption(OS, checkers);
-}
diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h
deleted file mode 100644
index f6c8011..0000000
--- a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===--- ClangSACheckerProvider.h - Clang SA Checkers Provider --*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines the entry point for creating the provider for the checkers defined
-// in libclangStaticAnalyzerCheckers.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H
-#define LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H
-
-namespace clang {
-
-namespace ento {
- class CheckerProvider;
-
-CheckerProvider *createClangSACheckerProvider();
-
-} // end ento namespace
-
-} // end clang namespace
-
-#endif
diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h
index 5524b0f..289ce8d 100644
--- a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h
+++ b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h
@@ -19,6 +19,7 @@ namespace clang {
namespace ento {
class CheckerManager;
+class CheckerRegistry;
#define GET_CHECKERS
#define CHECKER(FULLNAME,CLASS,CXXFILE,HELPTEXT,GROUPINDEX,HIDDEN) \
diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index ec2a88a..901af43 100644
--- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -50,7 +50,7 @@ void ReachableCode::computeReachableBlocks() {
if (!cfg.getNumBlockIDs())
return;
- llvm::SmallVector<const CFGBlock*, 10> worklist;
+ SmallVector<const CFGBlock*, 10> worklist;
worklist.push_back(&cfg.getEntry());
while (!worklist.empty()) {
@@ -68,12 +68,13 @@ void ReachableCode::computeReachableBlocks() {
}
namespace {
-class DeadStoreObs : public LiveVariables::ObserverTy {
+class DeadStoreObs : public LiveVariables::Observer {
const CFG &cfg;
ASTContext &Ctx;
BugReporter& BR;
+ AnalysisContext* AC;
ParentMap& Parents;
- llvm::SmallPtrSet<VarDecl*, 20> Escaped;
+ llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
llvm::OwningPtr<ReachableCode> reachableCode;
const CFGBlock *currentBlock;
@@ -81,14 +82,15 @@ class DeadStoreObs : public LiveVariables::ObserverTy {
public:
DeadStoreObs(const CFG &cfg, ASTContext &ctx,
- BugReporter& br, ParentMap& parents,
- llvm::SmallPtrSet<VarDecl*, 20> &escaped)
- : cfg(cfg), Ctx(ctx), BR(br), Parents(parents),
+ BugReporter& br, AnalysisContext* ac, ParentMap& parents,
+ llvm::SmallPtrSet<const VarDecl*, 20> &escaped)
+ : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents),
Escaped(escaped), currentBlock(0) {}
virtual ~DeadStoreObs() {}
- void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) {
+ void Report(const VarDecl *V, DeadStoreKind dsk,
+ PathDiagnosticLocation L, SourceRange R) {
if (Escaped.count(V))
return;
@@ -102,26 +104,25 @@ public:
if (!reachableCode->isReachable(currentBlock))
return;
- const std::string &name = V->getNameAsString();
-
- const char* BugType = 0;
- std::string msg;
+ llvm::SmallString<64> buf;
+ llvm::raw_svector_ostream os(buf);
+ const char *BugType = 0;
switch (dsk) {
default:
- assert(false && "Impossible dead store type.");
+ llvm_unreachable("Impossible dead store type.");
case DeadInit:
BugType = "Dead initialization";
- msg = "Value stored to '" + name +
- "' during its initialization is never read";
+ os << "Value stored to '" << *V
+ << "' during its initialization is never read";
break;
case DeadIncrement:
BugType = "Dead increment";
case Standard:
if (!BugType) BugType = "Dead assignment";
- msg = "Value stored to '" + name + "' is never read";
+ os << "Value stored to '" << *V << "' is never read";
break;
case Enclosing:
@@ -131,13 +132,12 @@ public:
return;
}
- BR.EmitBasicReport(BugType, "Dead store", msg, L, R);
+ BR.EmitBasicReport(BugType, "Dead store", os.str(), L, R);
}
- void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
+ void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
DeadStoreKind dsk,
- const LiveVariables::AnalysisDataTy& AD,
- const LiveVariables::ValTy& Live) {
+ const LiveVariables::LivenessValues &Live) {
if (!VD->hasLocalStorage())
return;
@@ -146,30 +146,32 @@ public:
if (VD->getType()->getAs<ReferenceType>())
return;
- if (!Live(VD, AD) &&
- !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>()))
- Report(VD, dsk, Ex->getSourceRange().getBegin(),
- Val->getSourceRange());
+ if (!Live.isLive(VD) &&
+ !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) {
+
+ PathDiagnosticLocation ExLoc =
+ PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
+ Report(VD, dsk, ExLoc, Val->getSourceRange());
+ }
}
- void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk,
- const LiveVariables::AnalysisDataTy& AD,
- const LiveVariables::ValTy& Live) {
- if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
- CheckVarDecl(VD, DR, Val, dsk, AD, Live);
+ void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
+ const LiveVariables::LivenessValues& Live) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ CheckVarDecl(VD, DR, Val, dsk, Live);
}
- bool isIncrement(VarDecl* VD, BinaryOperator* B) {
+ bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
if (B->isCompoundAssignmentOp())
return true;
- Expr* RHS = B->getRHS()->IgnoreParenCasts();
- BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
+ const Expr *RHS = B->getRHS()->IgnoreParenCasts();
+ const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
if (!BRHS)
return false;
- DeclRefExpr *DR;
+ const DeclRefExpr *DR;
if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
if (DR->getDecl() == VD)
@@ -182,9 +184,8 @@ public:
return false;
}
- virtual void ObserveStmt(Stmt* S, const CFGBlock *block,
- const LiveVariables::AnalysisDataTy& AD,
- const LiveVariables::ValTy& Live) {
+ virtual void observeStmt(const Stmt *S, const CFGBlock *block,
+ const LiveVariables::LivenessValues &Live) {
currentBlock = block;
@@ -194,10 +195,10 @@ public:
// Only cover dead stores from regular assignments. ++/-- dead stores
// have never flagged a real bug.
- if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
+ if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
if (!B->isAssignmentOp()) return; // Skip non-assignments.
- if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
+ if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
@@ -208,10 +209,10 @@ public:
return;
}
- Expr* RHS = B->getRHS()->IgnoreParenCasts();
+ Expr *RHS = B->getRHS()->IgnoreParenCasts();
// Special case: self-assignments. These are often used to shut up
// "unused variable" compiler warnings.
- if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS))
+ if (DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
return;
@@ -220,29 +221,29 @@ public:
? Enclosing
: (isIncrement(VD,B) ? DeadIncrement : Standard);
- CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live);
+ CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
}
}
- else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
+ else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
if (!U->isIncrementOp() || U->isPrefix())
return;
- Stmt *parent = Parents.getParentIgnoreParenCasts(U);
+ const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
if (!parent || !isa<ReturnStmt>(parent))
return;
- Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
+ const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
- if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
- CheckDeclRef(DR, U, DeadIncrement, AD, Live);
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
+ CheckDeclRef(DR, U, DeadIncrement, Live);
}
- else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
+ else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
// Iterate through the decls. Warn if any initializers are complex
// expressions that are not live (never used).
- for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
+ for (DeclStmt::const_decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
DI != DE; ++DI) {
- VarDecl* V = dyn_cast<VarDecl>(*DI);
+ VarDecl *V = dyn_cast<VarDecl>(*DI);
if (!V)
continue;
@@ -253,7 +254,7 @@ public:
if (V->getType()->getAs<ReferenceType>())
return;
- if (Expr* E = V->getInit()) {
+ if (Expr *E = V->getInit()) {
while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E))
E = exprClean->getSubExpr();
@@ -265,7 +266,7 @@ public:
// A dead initialization is a variable that is dead after it
// is initialized. We don't flag warnings for those variables
// marked 'unused'.
- if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) {
+ if (!Live.isLive(V) && V->getAttr<UnusedAttr>() == 0) {
// Special case: check for initializations with constants.
//
// e.g. : int x = 0;
@@ -296,7 +297,9 @@ public:
return;
}
- Report(V, DeadInit, V->getLocation(), E->getSourceRange());
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::create(V, BR.getSourceManager());
+ Report(V, DeadInit, Loc, E->getSourceRange());
}
}
}
@@ -318,15 +321,15 @@ public:
CFG& getCFG() { return *cfg; }
- llvm::SmallPtrSet<VarDecl*, 20> Escaped;
+ llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
void VisitUnaryOperator(UnaryOperator* U) {
// Check for '&'. Any VarDecl whose value has its address-taken we
// treat as escaped.
- Expr* E = U->getSubExpr()->IgnoreParenCasts();
+ Expr *E = U->getSubExpr()->IgnoreParenCasts();
if (U->getOpcode() == UO_AddrOf)
- if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E))
- if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
+ if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
Escaped.insert(VD);
return;
}
@@ -345,13 +348,14 @@ class DeadStoresChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- if (LiveVariables *L = mgr.getLiveVariables(D)) {
+ if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
CFG &cfg = *mgr.getCFG(D);
+ AnalysisContext *AC = mgr.getAnalysisContext(D);
ParentMap &pmap = mgr.getParentMap(D);
FindEscaped FS(&cfg);
FS.getCFG().VisitBlockStmts(FS);
- DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped);
- L->runOnAllBlocks(cfg, &A);
+ DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped);
+ L->runOnAllBlocks(A);
}
}
};
diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 486b7f7..d9d5694 100644
--- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -28,7 +28,7 @@ class LiveVariablesDumper : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
- if (LiveVariables* L = mgr.getLiveVariables(D)) {
+ if (LiveVariables* L = mgr.getAnalysis<LiveVariables>(D)) {
L->dumpBlockLiveness(mgr.getSourceManager());
}
}
diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index baaf8b3..eeda734 100644
--- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -29,16 +29,17 @@ class DereferenceChecker
mutable llvm::OwningPtr<BuiltinBug> BT_undef;
public:
- void checkLocation(SVal location, bool isLoad, CheckerContext &C) const;
+ void checkLocation(SVal location, bool isLoad, const Stmt* S,
+ CheckerContext &C) const;
- static void AddDerefSource(llvm::raw_ostream &os,
- llvm::SmallVectorImpl<SourceRange> &Ranges,
+ static void AddDerefSource(raw_ostream &os,
+ SmallVectorImpl<SourceRange> &Ranges,
const Expr *Ex, bool loadedFrom = false);
};
} // end anonymous namespace
-void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os,
- llvm::SmallVectorImpl<SourceRange> &Ranges,
+void DereferenceChecker::AddDerefSource(raw_ostream &os,
+ SmallVectorImpl<SourceRange> &Ranges,
const Expr *Ex,
bool loadedFrom) {
Ex = Ex->IgnoreParenLValueCasts();
@@ -65,7 +66,7 @@ void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os,
}
}
-void DereferenceChecker::checkLocation(SVal l, bool isLoad,
+void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
CheckerContext &C) const {
// Check for dereference of an undefined value.
if (l.isUndef()) {
@@ -73,10 +74,10 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad,
if (!BT_undef)
BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N);
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- bugreporter::GetDerefExpr(N));
+ BugReport *report =
+ new BugReport(*BT_undef, BT_undef->getDescription(), N);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetDerefExpr(N)));
C.EmitReport(report);
}
return;
@@ -88,9 +89,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad,
if (!isa<Loc>(location))
return;
- const Stmt *S = C.getStmt();
- const GRState *state = C.getState();
- const GRState *notNullState, *nullState;
+ const ProgramState *state = C.getState();
+ const ProgramState *notNullState, *nullState;
llvm::tie(notNullState, nullState) = state->assume(location);
// The explicit NULL case.
@@ -107,7 +107,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad,
BT_null.reset(new BuiltinBug("Dereference of null pointer"));
llvm::SmallString<100> buf;
- llvm::SmallVector<SourceRange, 2> Ranges;
+ SmallVector<SourceRange, 2> Ranges;
// Walk through lvalue casts to get the original expression
// that syntactically caused the load.
@@ -157,15 +157,15 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad,
break;
}
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_null,
+ BugReport *report =
+ new BugReport(*BT_null,
buf.empty() ? BT_null->getDescription():buf.str(),
N);
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- bugreporter::GetDerefExpr(N));
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetDerefExpr(N)));
- for (llvm::SmallVectorImpl<SourceRange>::iterator
+ for (SmallVectorImpl<SourceRange>::iterator
I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
report->addRange(*I);
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 07fb5aa..75b7cc4 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -52,7 +52,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
// Check for divide by zero.
ConstraintManager &CM = C.getConstraintManager();
- const GRState *stateNotZero, *stateZero;
+ const ProgramState *stateNotZero, *stateZero;
llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
if (stateZero && !stateNotZero) {
@@ -60,11 +60,11 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
if (!BT)
BT.reset(new BuiltinBug("Division by zero"));
- EnhancedBugReport *R =
- new EnhancedBugReport(*BT, BT->getDescription(), N);
+ BugReport *R =
+ new BugReport(*BT, BT->getDescription(), N);
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- bugreporter::GetDenomExpr(N));
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ bugreporter::GetDenomExpr(N)));
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index d699dee..531d87e 100644
--- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -44,7 +44,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
if (!T->isPointerType())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal RV = state->getSVal(B->getRHS());
@@ -57,7 +57,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
"Using a fixed address is not portable because that "
"address will probably not be valid in all "
"environments or platforms."));
- RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N);
+ BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getRHS()->getSourceRange());
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
index b0c07fc..5c257e5 100644
--- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp
@@ -96,10 +96,9 @@ private:
// Hash table and related data structures
struct BinaryOperatorData {
- BinaryOperatorData() : assumption(Possible), analysisContext(0) {}
+ BinaryOperatorData() : assumption(Possible) {}
Assumption assumption;
- AnalysisContext *analysisContext;
ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a
// BinaryOperator
};
@@ -118,7 +117,6 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B,
BinaryOperatorData &Data = hash[B];
Assumption &A = Data.assumption;
AnalysisContext *AC = C.getCurrentAnalysisContext();
- Data.analysisContext = AC;
// If we already have visited this node on a path that does not contain an
// idempotent operation, return immediately.
@@ -143,7 +141,7 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B,
|| containsNonLocalVarDecl(RHS);
}
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal LHSVal = state->getSVal(LHS);
SVal RHSVal = state->getSVal(RHS);
@@ -351,9 +349,14 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G,
// Unpack the hash contents
const BinaryOperatorData &Data = i->second;
const Assumption &A = Data.assumption;
- AnalysisContext *AC = Data.analysisContext;
const ExplodedNodeSet &ES = Data.explodedNodes;
+ // If there are no nodes accosted with the expression, nothing to report.
+ // FIXME: This is possible because the checker does part of processing in
+ // checkPreStmt and part in checkPostStmt.
+ if (ES.begin() == ES.end())
+ continue;
+
const BinaryOperator *B = i->first;
if (A == Impossible)
@@ -363,6 +366,8 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G,
// warning
if (Eng.hasWorkRemaining()) {
// If we can trace back
+ AnalysisContext *AC = (*ES.begin())->getLocationContext()
+ ->getAnalysisContext();
if (!pathWasCompletelyAnalyzed(AC,
AC->getCFGStmtMap()->getBlock(B),
Eng.getCoreEngine()))
@@ -407,18 +412,18 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G,
// Add a report for each ExplodedNode
for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) {
- EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I);
+ BugReport *report = new BugReport(*BT, os.str(), *I);
// Add source ranges and visitor hooks
if (LHSRelevant) {
const Expr *LHS = i->first->getLHS();
report->addRange(LHS->getSourceRange());
- report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS);
+ FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS);
}
if (RHSRelevant) {
const Expr *RHS = i->first->getRHS();
report->addRange(i->first->getRHS()->getSourceRange());
- report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS);
+ FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS);
}
BR.EmitReport(report);
diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp
index de6da4f..fbc57d3 100644
--- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp
@@ -20,7 +20,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
@@ -117,16 +117,28 @@ public:
CheckerContext &C) const;
private:
- const GRState *handleAssign(const GRState *state, const Expr *lexp,
- const Expr *rexp, const LocationContext *LC) const;
- const GRState *handleAssign(const GRState *state, const MemRegion *MR,
- const Expr *rexp, const LocationContext *LC) const;
- const GRState *invalidateIterators(const GRState *state, const MemRegion *MR,
- const MemberExpr *ME) const;
+ const ProgramState *handleAssign(const ProgramState *state,
+ const Expr *lexp,
+ const Expr *rexp,
+ const LocationContext *LC) const;
+
+ const ProgramState *handleAssign(const ProgramState *state,
+ const MemRegion *MR,
+ const Expr *rexp,
+ const LocationContext *LC) const;
+
+ const ProgramState *invalidateIterators(const ProgramState *state,
+ const MemRegion *MR,
+ const MemberExpr *ME) const;
+
void checkExpr(CheckerContext &C, const Expr *E) const;
+
void checkArgs(CheckerContext &C, const CallExpr *CE) const;
- const MemRegion *getRegion(const GRState *state, const Expr *E,
- const LocationContext *LC) const;
+
+ const MemRegion *getRegion(const ProgramState *state,
+ const Expr *E,
+ const LocationContext *LC) const;
+
const DeclRefExpr *getDeclRefExpr(const Expr *E) const;
};
@@ -139,8 +151,8 @@ public:
namespace clang {
namespace ento {
template <>
- struct GRStateTrait<IteratorState>
- : public GRStatePartialTrait<IteratorState::EntryMap> {
+ struct ProgramStateTrait<IteratorState>
+ : public ProgramStatePartialTrait<IteratorState::EntryMap> {
static void *GDMIndex() { return IteratorsChecker::getTag(); }
};
}
@@ -162,7 +174,7 @@ static RefKind getTemplateKind(const NamedDecl *td) {
|| nameSpace->getName() != "std")
return NoKind;
- llvm::StringRef name = td->getName();
+ StringRef name = td->getName();
return llvm::StringSwitch<RefKind>(name)
.Cases("vector", "deque", VectorKind)
.Default(NoKind);
@@ -215,7 +227,7 @@ static RefKind getTemplateKind(QualType T) {
// Iterate through our map and invalidate any iterators that were
// initialized fromt the specified instance MemRegion.
-const GRState *IteratorsChecker::invalidateIterators(const GRState *state,
+const ProgramState *IteratorsChecker::invalidateIterators(const ProgramState *state,
const MemRegion *MR, const MemberExpr *ME) const {
IteratorState::EntryMap Map = state->get<IteratorState>();
if (Map.isEmpty())
@@ -234,7 +246,7 @@ const GRState *IteratorsChecker::invalidateIterators(const GRState *state,
}
// Handle assigning to an iterator where we don't have the LValue MemRegion.
-const GRState *IteratorsChecker::handleAssign(const GRState *state,
+const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state,
const Expr *lexp, const Expr *rexp, const LocationContext *LC) const {
// Skip the cast if present.
if (const MaterializeTemporaryExpr *M
@@ -259,7 +271,7 @@ const GRState *IteratorsChecker::handleAssign(const GRState *state,
}
// handle assigning to an iterator
-const GRState *IteratorsChecker::handleAssign(const GRState *state,
+const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state,
const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const {
// Assume unknown until we find something definite.
state = state->set<IteratorState>(MR, RefState::getUnknown());
@@ -287,7 +299,7 @@ const GRState *IteratorsChecker::handleAssign(const GRState *state,
return state;
// Finally, see if it is one of the calls that will create
// a valid iterator and mark it if so, else mark as Unknown.
- llvm::StringRef mName = ME->getMemberDecl()->getName();
+ StringRef mName = ME->getMemberDecl()->getName();
if (llvm::StringSwitch<bool>(mName)
.Cases("begin", "insert", "erase", true).Default(false)) {
@@ -364,7 +376,7 @@ const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const {
}
// Get the MemRegion associated with the expresssion.
-const MemRegion *IteratorsChecker::getRegion(const GRState *state,
+const MemRegion *IteratorsChecker::getRegion(const ProgramState *state,
const Expr *E, const LocationContext *LC) const {
const DeclRefExpr *DRE = getDeclRefExpr(E);
if (!DRE)
@@ -382,7 +394,7 @@ const MemRegion *IteratorsChecker::getRegion(const GRState *state,
// use those nodes. We also cannot create multiple nodes at one ProgramPoint
// with the same tag.
void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const MemRegion *MR = getRegion(state, E,
C.getPredecessor()->getLocationContext());
if (!MR)
@@ -410,7 +422,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const {
"container to its container";
}
- EnhancedBugReport *R = new EnhancedBugReport(*BT_Invalid, msg, N);
+ BugReport *R = new BugReport(*BT_Invalid, msg, N);
R->addRange(getDeclRefExpr(E)->getSourceRange());
C.EmitReport(R);
}
@@ -422,7 +434,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const {
const_cast<IteratorsChecker*>(this)->BT_Undefined =
new BuiltinBug("Use of iterator that is not defined");
- EnhancedBugReport *R = new EnhancedBugReport(*BT_Undefined,
+ BugReport *R = new BugReport(*BT_Undefined,
BT_Undefined->getDescription(), N);
R->addRange(getDeclRefExpr(E)->getSourceRange());
C.EmitReport(R);
@@ -455,7 +467,7 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE,
CheckerContext &C) const
{
const LocationContext *LC = C.getPredecessor()->getLocationContext();
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
OverloadedOperatorKind Kind = OCE->getOperator();
if (Kind == OO_Equal) {
checkExpr(C, OCE->getArg(1));
@@ -491,8 +503,8 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE,
new BuiltinBug(
"Cannot compare iterators from different containers");
- EnhancedBugReport *R = new EnhancedBugReport(*BT_Incompatible,
- BT_Incompatible->getDescription(), N);
+ BugReport *R = new BugReport(*BT_Incompatible,
+ BT_Incompatible->getDescription(), N);
R->addRange(OCE->getSourceRange());
C.EmitReport(R);
}
@@ -505,14 +517,14 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE,
// uninitialized ones as Undefined.
void IteratorsChecker::checkPreStmt(const DeclStmt *DS,
CheckerContext &C) const {
- const Decl* D = *DS->decl_begin();
- const VarDecl* VD = dyn_cast<VarDecl>(D);
+ const Decl *D = *DS->decl_begin();
+ const VarDecl *VD = dyn_cast<VarDecl>(D);
// Only care about iterators.
if (getTemplateKind(VD->getType()) != VectorIteratorKind)
return;
// Get the MemRegion associated with the iterator and mark it as Undefined.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
Loc VarLoc = state->getLValue(VD, C.getPredecessor()->getLocationContext());
const MemRegion *MR = VarLoc.getAsRegion();
if (!MR)
@@ -520,7 +532,7 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS,
state = state->set<IteratorState>(MR, RefState::getUndefined());
// if there is an initializer, handle marking Valid if a proper initializer
- const Expr* InitEx = VD->getInit();
+ const Expr *InitEx = VD->getInit();
if (InitEx) {
// FIXME: This is too syntactic. Since 'InitEx' will be analyzed first
// it should resolve to an SVal that we can check for validity
@@ -544,8 +556,8 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS,
namespace { struct CalledReserved {}; }
namespace clang { namespace ento {
-template<> struct GRStateTrait<CalledReserved>
- : public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
+template<> struct ProgramStateTrait<CalledReserved>
+ : public ProgramStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
static void *GDMIndex() { static int index = 0; return &index; }
};
}}
@@ -571,8 +583,8 @@ void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE,
return;
// If we are calling a function that invalidates iterators, mark them
// appropriately by finding matching instances.
- const GRState *state = C.getState();
- llvm::StringRef mName = ME->getMemberDecl()->getName();
+ const ProgramState *state = C.getState();
+ StringRef mName = ME->getMemberDecl()->getName();
if (llvm::StringSwitch<bool>(mName)
.Cases("insert", "reserve", "push_back", true)
.Cases("erase", "pop_back", "clear", "resize", true)
diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
index 3d1b5e2..e398fcb 100644
--- a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -32,13 +32,13 @@ static bool IsLLVMStringRef(QualType T) {
if (!RT)
return false;
- return llvm::StringRef(QualType(RT, 0).getAsString()) ==
- "class llvm::StringRef";
+ return StringRef(QualType(RT, 0).getAsString()) ==
+ "class StringRef";
}
/// Check whether the declaration is semantically inside the top-level
/// namespace named by ns.
-static bool InNamespace(const Decl *D, llvm::StringRef NS) {
+static bool InNamespace(const Decl *D, StringRef NS) {
const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext());
if (!ND)
return false;
@@ -109,7 +109,7 @@ static bool IsSmallVector(QualType T) {
}
//===----------------------------------------------------------------------===//
-// CHECK: a llvm::StringRef should not be bound to a temporary std::string whose
+// CHECK: a StringRef should not be bound to a temporary std::string whose
// lifetime is shorter than the StringRef's.
//===----------------------------------------------------------------------===//
@@ -150,7 +150,7 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
return;
// Pattern match for:
- // llvm::StringRef x = call() (where call returns std::string)
+ // StringRef x = call() (where call returns std::string)
if (!IsLLVMStringRef(VD->getType()))
return;
ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init);
@@ -175,9 +175,10 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
// Okay, badness! Report an error.
const char *desc = "StringRef should not be bound to temporary "
"std::string that it outlives";
-
+ PathDiagnosticLocation VDLoc =
+ PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
BR.EmitBasicReport(desc, "LLVM Conventions", desc,
- VD->getLocStart(), Init->getSourceRange());
+ VDLoc, Init->getSourceRange());
}
//===----------------------------------------------------------------------===//
@@ -210,7 +211,7 @@ static bool IsPartOfAST(const CXXRecordDecl *R) {
namespace {
class ASTFieldVisitor {
- llvm::SmallVector<FieldDecl*, 10> FieldChain;
+ SmallVector<FieldDecl*, 10> FieldChain;
const CXXRecordDecl *Root;
BugReporter &BR;
public:
@@ -260,7 +261,7 @@ void ASTFieldVisitor::ReportError(QualType T) {
if (FieldChain.size() > 1) {
os << " via the following chain: ";
bool isFirst = true;
- for (llvm::SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(),
+ for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(),
E=FieldChain.end(); I!=E; ++I) {
if (!isFirst)
os << '.';
@@ -279,8 +280,10 @@ void ASTFieldVisitor::ReportError(QualType T) {
// just report warnings when we see an out-of-line method definition for a
// class, as that heuristic doesn't always work (the complete definition of
// the class may be in the header file, for example).
+ PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
+ FieldChain.front(), BR.getSourceManager());
BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions",
- os.str(), FieldChain.front()->getLocStart());
+ os.str(), L);
}
//===----------------------------------------------------------------------===//
@@ -294,7 +297,7 @@ class LLVMConventionsChecker : public Checker<
public:
void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr,
BugReporter &BR) const {
- if (R->isDefinition())
+ if (R->isCompleteDefinition())
CheckASTMemory(R, BR);
}
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
new file mode 100644
index 0000000..2607db8
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -0,0 +1,632 @@
+//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This checker flags misuses of KeyChainAPI. In particular, the password data
+// allocated/returned by SecKeychainItemCopyContent,
+// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
+// to be freed using a call to SecKeychainItemFreeContent.
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
+ check::PreStmt<ReturnStmt>,
+ check::PostStmt<CallExpr>,
+ check::EndPath,
+ check::DeadSymbols> {
+ mutable llvm::OwningPtr<BugType> BT;
+
+public:
+ /// AllocationState is a part of the checker specific state together with the
+ /// MemRegion corresponding to the allocated data.
+ struct AllocationState {
+ /// The index of the allocator function.
+ unsigned int AllocatorIdx;
+ SymbolRef Region;
+
+ AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
+ AllocatorIdx(Idx),
+ Region(R) {}
+
+ bool operator==(const AllocationState &X) const {
+ return (AllocatorIdx == X.AllocatorIdx &&
+ Region == X.Region);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(AllocatorIdx);
+ ID.AddPointer(Region);
+ }
+ };
+
+ void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
+
+private:
+ typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
+ typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec;
+
+ enum APIKind {
+ /// Denotes functions tracked by this checker.
+ ValidAPI = 0,
+ /// The functions commonly/mistakenly used in place of the given API.
+ ErrorAPI = 1,
+ /// The functions which may allocate the data. These are tracked to reduce
+ /// the false alarm rate.
+ PossibleAPI = 2
+ };
+ /// Stores the information about the allocator and deallocator functions -
+ /// these are the functions the checker is tracking.
+ struct ADFunctionInfo {
+ const char* Name;
+ unsigned int Param;
+ unsigned int DeallocatorIdx;
+ APIKind Kind;
+ };
+ static const unsigned InvalidIdx = 100000;
+ static const unsigned FunctionsToTrackSize = 8;
+ static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
+ /// The value, which represents no error return value for allocator functions.
+ static const unsigned NoErr = 0;
+
+ /// Given the function name, returns the index of the allocator/deallocator
+ /// function.
+ static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
+
+ inline void initBugType() const {
+ if (!BT)
+ BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API"));
+ }
+
+ void generateDeallocatorMismatchReport(const AllocationPair &AP,
+ const Expr *ArgExpr,
+ CheckerContext &C) const;
+
+ BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
+ ExplodedNode *N) const;
+
+ /// Check if RetSym evaluates to an error value in the current state.
+ bool definitelyReturnedError(SymbolRef RetSym,
+ const ProgramState *State,
+ SValBuilder &Builder,
+ bool noError = false) const;
+
+ /// Check if RetSym evaluates to a NoErr value in the current state.
+ bool definitelyDidnotReturnError(SymbolRef RetSym,
+ const ProgramState *State,
+ SValBuilder &Builder) const {
+ return definitelyReturnedError(RetSym, State, Builder, true);
+ }
+
+ /// The bug visitor which allows us to print extra diagnostics along the
+ /// BugReport path. For example, showing the allocation site of the leaked
+ /// region.
+ class SecKeychainBugVisitor : public BugReporterVisitor {
+ protected:
+ // The allocated region symbol tracked by the main analysis.
+ SymbolRef Sym;
+
+ public:
+ SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
+ virtual ~SecKeychainBugVisitor() {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Sym);
+ }
+
+ PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR);
+ };
+};
+}
+
+/// ProgramState traits to store the currently allocated (and not yet freed)
+/// symbols. This is a map from the allocated content symbol to the
+/// corresponding AllocationState.
+typedef llvm::ImmutableMap<SymbolRef,
+ MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy;
+
+namespace { struct AllocatedData {}; }
+namespace clang { namespace ento {
+template<> struct ProgramStateTrait<AllocatedData>
+ : public ProgramStatePartialTrait<AllocatedSetTy > {
+ static void *GDMIndex() { static int index = 0; return &index; }
+};
+}}
+
+static bool isEnclosingFunctionParam(const Expr *E) {
+ E = E->IgnoreParenCasts();
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ const ValueDecl *VD = DRE->getDecl();
+ if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
+ return true;
+ }
+ return false;
+}
+
+const MacOSKeychainAPIChecker::ADFunctionInfo
+ MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
+ {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0
+ {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1
+ {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2
+ {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3
+ {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4
+ {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5
+ {"free", 0, InvalidIdx, ErrorAPI}, // 6
+ {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7
+};
+
+unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
+ bool IsAllocator) {
+ for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
+ ADFunctionInfo FI = FunctionsToTrack[I];
+ if (FI.Name != Name)
+ continue;
+ // Make sure the function is of the right type (allocator vs deallocator).
+ if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
+ return InvalidIdx;
+ if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
+ return InvalidIdx;
+
+ return I;
+ }
+ // The function is not tracked.
+ return InvalidIdx;
+}
+
+static SymbolRef getSymbolForRegion(CheckerContext &C,
+ const MemRegion *R) {
+ // Implicit casts (ex: void* -> char*) can turn Symbolic region into element
+ // region, if that is the case, get the underlining region.
+ R = R->StripCasts();
+ if (!isa<SymbolicRegion>(R)) {
+ return 0;
+ }
+ return cast<SymbolicRegion>(R)->getSymbol();
+}
+
+static bool isBadDeallocationArgument(const MemRegion *Arg) {
+ if (isa<AllocaRegion>(Arg) ||
+ isa<BlockDataRegion>(Arg) ||
+ isa<TypedRegion>(Arg)) {
+ return true;
+ }
+ return false;
+}
+/// Given the address expression, retrieve the value it's pointing to. Assume
+/// that value is itself an address, and return the corresponding symbol.
+static SymbolRef getAsPointeeSymbol(const Expr *Expr,
+ CheckerContext &C) {
+ const ProgramState *State = C.getState();
+ SVal ArgV = State->getSVal(Expr);
+
+ if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) {
+ StoreManager& SM = C.getStoreManager();
+ const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion();
+ if (V)
+ return getSymbolForRegion(C, V);
+ }
+ return 0;
+}
+
+// When checking for error code, we need to consider the following cases:
+// 1) noErr / [0]
+// 2) someErr / [1, inf]
+// 3) unknown
+// If noError, returns true iff (1).
+// If !noError, returns true iff (2).
+bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym,
+ const ProgramState *State,
+ SValBuilder &Builder,
+ bool noError) const {
+ DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr,
+ Builder.getSymbolManager().getType(RetSym));
+ DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal,
+ nonloc::SymbolVal(RetSym));
+ const ProgramState *ErrState = State->assume(NoErr, noError);
+ if (ErrState == State) {
+ return true;
+ }
+
+ return false;
+}
+
+// Report deallocator mismatch. Remove the region from tracking - reporting a
+// missing free error after this one is redundant.
+void MacOSKeychainAPIChecker::
+ generateDeallocatorMismatchReport(const AllocationPair &AP,
+ const Expr *ArgExpr,
+ CheckerContext &C) const {
+ const ProgramState *State = C.getState();
+ State = State->remove<AllocatedData>(AP.first);
+ ExplodedNode *N = C.generateNode(State);
+
+ if (!N)
+ return;
+ initBugType();
+ llvm::SmallString<80> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ unsigned int PDeallocIdx =
+ FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
+
+ os << "Deallocator doesn't match the allocator: '"
+ << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
+ BugReport *Report = new BugReport(*BT, os.str(), N);
+ Report->addVisitor(new SecKeychainBugVisitor(AP.first));
+ Report->addRange(ArgExpr->getSourceRange());
+ C.EmitReport(Report);
+}
+
+void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const ProgramState *State = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = State->getSVal(Callee);
+ unsigned idx = InvalidIdx;
+
+ const FunctionDecl *funDecl = L.getAsFunctionDecl();
+ if (!funDecl)
+ return;
+ IdentifierInfo *funI = funDecl->getIdentifier();
+ if (!funI)
+ return;
+ StringRef funName = funI->getName();
+
+ // If it is a call to an allocator function, it could be a double allocation.
+ idx = getTrackedFunctionIndex(funName, true);
+ if (idx != InvalidIdx) {
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
+ if (const AllocationState *AS = State->get<AllocatedData>(V)) {
+ if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) {
+ // Remove the value from the state. The new symbol will be added for
+ // tracking when the second allocator is processed in checkPostStmt().
+ State = State->remove<AllocatedData>(V);
+ ExplodedNode *N = C.generateNode(State);
+ if (!N)
+ return;
+ initBugType();
+ llvm::SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
+ os << "Allocated data should be released before another call to "
+ << "the allocator: missing a call to '"
+ << FunctionsToTrack[DIdx].Name
+ << "'.";
+ BugReport *Report = new BugReport(*BT, os.str(), N);
+ Report->addVisitor(new SecKeychainBugVisitor(V));
+ Report->addRange(ArgExpr->getSourceRange());
+ C.EmitReport(Report);
+ }
+ }
+ return;
+ }
+
+ // Is it a call to one of deallocator functions?
+ idx = getTrackedFunctionIndex(funName, false);
+ if (idx == InvalidIdx)
+ return;
+
+ // Check the argument to the deallocator.
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ SVal ArgSVal = State->getSVal(ArgExpr);
+
+ // Undef is reported by another checker.
+ if (ArgSVal.isUndef())
+ return;
+
+ const MemRegion *Arg = ArgSVal.getAsRegion();
+ if (!Arg)
+ return;
+
+ SymbolRef ArgSM = getSymbolForRegion(C, Arg);
+ bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg);
+ // If the argument is coming from the heap, globals, or unknown, do not
+ // report it.
+ if (!ArgSM && !RegionArgIsBad)
+ return;
+
+ // Is the argument to the call being tracked?
+ const AllocationState *AS = State->get<AllocatedData>(ArgSM);
+ if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) {
+ return;
+ }
+ // If trying to free data which has not been allocated yet, report as a bug.
+ // TODO: We might want a more precise diagnostic for double free
+ // (that would involve tracking all the freed symbols in the checker state).
+ if (!AS || RegionArgIsBad) {
+ // It is possible that this is a false positive - the argument might
+ // have entered as an enclosing function parameter.
+ if (isEnclosingFunctionParam(ArgExpr))
+ return;
+
+ ExplodedNode *N = C.generateNode(State);
+ if (!N)
+ return;
+ initBugType();
+ BugReport *Report = new BugReport(*BT,
+ "Trying to free data which has not been allocated.", N);
+ Report->addRange(ArgExpr->getSourceRange());
+ C.EmitReport(Report);
+ return;
+ }
+
+ // Process functions which might deallocate.
+ if (FunctionsToTrack[idx].Kind == PossibleAPI) {
+
+ if (funName == "CFStringCreateWithBytesNoCopy") {
+ const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
+ // NULL ~ default deallocator, so warn.
+ if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
+ Expr::NPC_ValueDependentIsNotNull)) {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+ // One of the default allocators, so warn.
+ if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
+ StringRef DeallocatorName = DE->getFoundDecl()->getName();
+ if (DeallocatorName == "kCFAllocatorDefault" ||
+ DeallocatorName == "kCFAllocatorSystemDefault" ||
+ DeallocatorName == "kCFAllocatorMalloc") {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+ // If kCFAllocatorNull, which does not deallocate, we still have to
+ // find the deallocator. Otherwise, assume that the user had written a
+ // custom deallocator which does the right thing.
+ if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") {
+ State = State->remove<AllocatedData>(ArgSM);
+ C.addTransition(State);
+ return;
+ }
+ }
+ }
+ return;
+ }
+
+ // The call is deallocating a value we previously allocated, so remove it
+ // from the next state.
+ State = State->remove<AllocatedData>(ArgSM);
+
+ // Check if the proper deallocator is used.
+ unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
+ if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+
+ // If the return status is undefined or is error, report a bad call to free.
+ if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) {
+ ExplodedNode *N = C.generateNode(State);
+ if (!N)
+ return;
+ initBugType();
+ BugReport *Report = new BugReport(*BT,
+ "Call to free data when error was returned during allocation.", N);
+ Report->addVisitor(new SecKeychainBugVisitor(ArgSM));
+ Report->addRange(ArgExpr->getSourceRange());
+ C.EmitReport(Report);
+ return;
+ }
+
+ C.addTransition(State);
+}
+
+void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const ProgramState *State = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = State->getSVal(Callee);
+
+ const FunctionDecl *funDecl = L.getAsFunctionDecl();
+ if (!funDecl)
+ return;
+ IdentifierInfo *funI = funDecl->getIdentifier();
+ if (!funI)
+ return;
+ StringRef funName = funI->getName();
+
+ // If a value has been allocated, add it to the set for tracking.
+ unsigned idx = getTrackedFunctionIndex(funName, true);
+ if (idx == InvalidIdx)
+ return;
+
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ // If the argument entered as an enclosing function parameter, skip it to
+ // avoid false positives.
+ if (isEnclosingFunctionParam(ArgExpr))
+ return;
+
+ if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
+ // If the argument points to something that's not a symbolic region, it
+ // can be:
+ // - unknown (cannot reason about it)
+ // - undefined (already reported by other checker)
+ // - constant (null - should not be tracked,
+ // other constant will generate a compiler warning)
+ // - goto (should be reported by other checker)
+
+ // The call return value symbol should stay alive for as long as the
+ // allocated value symbol, since our diagnostics depend on the value
+ // returned by the call. Ex: Data should only be freed if noErr was
+ // returned during allocation.)
+ SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol();
+ C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
+
+ // Track the allocated value in the checker state.
+ State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
+ RetStatusSymbol));
+ assert(State);
+ C.addTransition(State);
+ }
+}
+
+void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ const Expr *retExpr = S->getRetValue();
+ if (!retExpr)
+ return;
+
+ // Check if the value is escaping through the return.
+ const ProgramState *state = C.getState();
+ const MemRegion *V = state->getSVal(retExpr).getAsRegion();
+ if (!V)
+ return;
+ state = state->remove<AllocatedData>(getSymbolForRegion(C, V));
+
+ // Proceed from the new state.
+ C.addTransition(state);
+}
+
+BugReport *MacOSKeychainAPIChecker::
+ generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
+ ExplodedNode *N) const {
+ const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
+ initBugType();
+ llvm::SmallString<70> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ os << "Allocated data is not released: missing a call to '"
+ << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
+ BugReport *Report = new BugReport(*BT, os.str(), N);
+ Report->addVisitor(new SecKeychainBugVisitor(AP.first));
+ Report->addRange(SourceRange());
+ return Report;
+}
+
+void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ const ProgramState *State = C.getState();
+ AllocatedSetTy ASet = State->get<AllocatedData>();
+ if (ASet.isEmpty())
+ return;
+
+ bool Changed = false;
+ AllocationPairVec Errors;
+ for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) {
+ if (SR.isLive(I->first))
+ continue;
+
+ Changed = true;
+ State = State->remove<AllocatedData>(I->first);
+ // If the allocated symbol is null or if the allocation call might have
+ // returned an error, do not report.
+ if (State->getSymVal(I->first) ||
+ definitelyReturnedError(I->second.Region, State, C.getSValBuilder()))
+ continue;
+ Errors.push_back(std::make_pair(I->first, &I->second));
+ }
+ if (!Changed)
+ return;
+
+ // Generate the new, cleaned up state.
+ ExplodedNode *N = C.generateNode(State);
+ if (!N)
+ return;
+
+ // Generate the error reports.
+ for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
+ I != E; ++I) {
+ C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
+ }
+}
+
+// TODO: Remove this after we ensure that checkDeadSymbols are always called.
+void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
+ ExprEngine &Eng) const {
+ const ProgramState *state = B.getState();
+ AllocatedSetTy AS = state->get<AllocatedData>();
+ if (AS.isEmpty())
+ return;
+
+ // Anything which has been allocated but not freed (nor escaped) will be
+ // found here, so report it.
+ bool Changed = false;
+ AllocationPairVec Errors;
+ for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) {
+ Changed = true;
+ state = state->remove<AllocatedData>(I->first);
+ // If the allocated symbol is null or if error code was returned at
+ // allocation, do not report.
+ if (state->getSymVal(I.getKey()) ||
+ definitelyReturnedError(I->second.Region, state,
+ Eng.getSValBuilder())) {
+ continue;
+ }
+ Errors.push_back(std::make_pair(I->first, &I->second));
+ }
+
+ // If no change, do not generate a new state.
+ if (!Changed)
+ return;
+
+ ExplodedNode *N = B.generateNode(state);
+ if (!N)
+ return;
+
+ // Generate the error reports.
+ for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
+ I != E; ++I) {
+ Eng.getBugReporter().EmitReport(
+ generateAllocatedDataNotReleasedReport(*I, N));
+ }
+}
+
+
+PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
+ const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
+ if (!AS)
+ return 0;
+ const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym);
+ if (ASPrev)
+ return 0;
+
+ // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
+ // allocation site.
+ const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation())
+ .getStmt());
+ const FunctionDecl *funDecl = CE->getDirectCallee();
+ assert(funDecl && "We do not support indirect function calls as of now.");
+ StringRef funName = funDecl->getName();
+
+ // Get the expression of the corresponding argument.
+ unsigned Idx = getTrackedFunctionIndex(funName, true);
+ assert(Idx != InvalidIdx && "This should be a call to an allocator.");
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
+ PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
+ N->getLocationContext());
+ return new PathDiagnosticEventPiece(Pos, "Data is allocated here.");
+}
+
+void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MacOSKeychainAPIChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index f8d076b..88d492e 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -20,7 +20,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
@@ -56,7 +56,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
// Check if the first argument is stack allocated. If so, issue a warning
// because that's likely to be bad news.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
return;
@@ -81,7 +81,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
os << " Perhaps you intended to declare the variable as 'static'?";
- RangedBugReport *report = new RangedBugReport(*BT_dispatchOnce, os.str(), N);
+ BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.EmitReport(report);
}
@@ -94,7 +94,7 @@ void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
// FIXME: This sort of logic is common to several checkers, including
// UnixAPIChecker, PthreadLockChecker, and CStringChecker. Should refactor.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl();
diff --git a/lib/StaticAnalyzer/Checkers/Makefile b/lib/StaticAnalyzer/Checkers/Makefile
index 97f4642..2582908 100644
--- a/lib/StaticAnalyzer/Checkers/Makefile
+++ b/lib/StaticAnalyzer/Checkers/Makefile
@@ -19,6 +19,6 @@ TABLEGEN_INC_FILES_COMMON = 1
include $(CLANG_LEVEL)/Makefile
-$(ObjDir)/Checkers.inc.tmp : Checkers.td $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include/clang/StaticAnalyzer/Checkers/CheckerBase.td $(TBLGEN) $(ObjDir)/.dir
+$(ObjDir)/Checkers.inc.tmp : Checkers.td $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include/clang/StaticAnalyzer/Checkers/CheckerBase.td $(CLANG_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building Clang SA Checkers tables with tblgen"
- $(Verb) $(TableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $<
+ $(Verb) $(ClangTableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $<
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 9100215..5631802 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -17,8 +17,8 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ImmutableMap.h"
using namespace clang;
@@ -80,35 +80,37 @@ public:
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
- const GRState *evalAssume(const GRState *state, SVal Cond,
+ const ProgramState *evalAssume(const ProgramState *state, SVal Cond,
bool Assumption) const;
- void checkLocation(SVal l, bool isLoad, CheckerContext &C) const;
- void checkBind(SVal location, SVal val, CheckerContext &C) const;
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+ void checkBind(SVal location, SVal val, const Stmt*S,
+ CheckerContext &C) const;
private:
static void MallocMem(CheckerContext &C, const CallExpr *CE);
static void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att);
- static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
- const GRState *state) {
+ const ProgramState *state) {
return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state);
}
- static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE,
SVal SizeEx, SVal Init,
- const GRState *state);
+ const ProgramState *state);
void FreeMem(CheckerContext &C, const CallExpr *CE) const;
void FreeMemAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att) const;
- const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state, unsigned Num, bool Hold) const;
+ const ProgramState *FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ const ProgramState *state, unsigned Num, bool Hold) const;
void ReallocMem(CheckerContext &C, const CallExpr *CE) const;
static void CallocMem(CheckerContext &C, const CallExpr *CE);
- static bool SummarizeValue(llvm::raw_ostream& os, SVal V);
- static bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR);
+ static bool SummarizeValue(raw_ostream &os, SVal V);
+ static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) const;
};
} // end anonymous namespace
@@ -118,15 +120,15 @@ typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy;
namespace clang {
namespace ento {
template <>
- struct GRStateTrait<RegionState>
- : public GRStatePartialTrait<RegionStateTy> {
+ struct ProgramStateTrait<RegionState>
+ : public ProgramStatePartialTrait<RegionStateTy> {
static void *GDMIndex() { static int x; return &x; }
};
}
}
bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
@@ -193,7 +195,7 @@ bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
}
void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
- const GRState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(),
+ const ProgramState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(),
C.getState());
C.addTransition(state);
}
@@ -205,21 +207,21 @@ void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- const GRState *state =
+ const ProgramState *state =
MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
C.addTransition(state);
return;
}
- const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(),
+ const ProgramState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(),
C.getState());
C.addTransition(state);
}
-const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
+const ProgramState *MallocChecker::MallocMemAux(CheckerContext &C,
const CallExpr *CE,
SVal Size, SVal Init,
- const GRState *state) {
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ const ProgramState *state) {
+ unsigned Count = C.getCurrentBlockCount();
SValBuilder &svalBuilder = C.getSValBuilder();
// Set the return value.
@@ -247,7 +249,7 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C,
}
void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false);
+ const ProgramState *state = FreeMemAux(C, CE, C.getState(), 0, false);
if (state)
C.addTransition(state);
@@ -260,15 +262,15 @@ void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE,
for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
I != E; ++I) {
- const GRState *state = FreeMemAux(C, CE, C.getState(), *I,
+ const ProgramState *state = FreeMemAux(C, CE, C.getState(), *I,
Att->getOwnKind() == OwnershipAttr::Holds);
if (state)
C.addTransition(state);
}
}
-const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
- const GRState *state, unsigned Num,
+const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ const ProgramState *state, unsigned Num,
bool Hold) const {
const Expr *ArgExpr = CE->getArg(Num);
SVal ArgVal = state->getSVal(ArgExpr);
@@ -281,7 +283,7 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
// FIXME: Technically using 'Assume' here can result in a path
// bifurcation. In such cases we need to return two states, not just one.
- const GRState *notNullState, *nullState;
+ const ProgramState *notNullState, *nullState;
llvm::tie(notNullState, nullState) = state->assume(location);
// The explicit NULL case, no operation is performed.
@@ -364,7 +366,7 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
return notNullState->set<RegionState>(Sym, RefState::getReleased(CE));
}
-bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) {
+bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
if (nonloc::ConcreteInt *IntVal = dyn_cast<nonloc::ConcreteInt>(&V))
os << "an integer (" << IntVal->getValue() << ")";
else if (loc::ConcreteInt *ConstAddr = dyn_cast<loc::ConcreteInt>(&V))
@@ -377,13 +379,13 @@ bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) {
return true;
}
-bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os,
+bool MallocChecker::SummarizeRegion(raw_ostream &os,
const MemRegion *MR) {
switch (MR->getKind()) {
case MemRegion::FunctionTextRegionKind: {
const FunctionDecl *FD = cast<FunctionTextRegion>(MR)->getDecl();
if (FD)
- os << "the address of the function '" << FD << "'";
+ os << "the address of the function '" << *FD << '\'';
else
os << "the address of a function";
return true;
@@ -484,14 +486,14 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
os << "not memory allocated by malloc()";
}
- EnhancedBugReport *R = new EnhancedBugReport(*BT_BadFree, os.str(), N);
+ BugReport *R = new BugReport(*BT_BadFree, os.str(), N);
R->addRange(range);
C.EmitReport(R);
}
}
void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *arg0Expr = CE->getArg(0);
DefinedOrUnknownSVal arg0Val
= cast<DefinedOrUnknownSVal>(state->getSVal(arg0Expr));
@@ -517,7 +519,7 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
// If the ptr is NULL and the size is not 0, the call is equivalent to
// malloc(size).
- const GRState *stateEqual = state->assume(PtrEQ, true);
+ const ProgramState *stateEqual = state->assume(PtrEQ, true);
if (stateEqual && state->assume(SizeZero, false)) {
// Hack: set the NULL symbolic region to released to suppress false warning.
// In the future we should add more states for allocated regions, e.g.,
@@ -527,28 +529,25 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
if (Sym)
stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE));
- const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
+ const ProgramState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
UndefinedVal(), stateEqual);
C.addTransition(stateMalloc);
}
- if (const GRState *stateNotEqual = state->assume(PtrEQ, false)) {
+ if (const ProgramState *stateNotEqual = state->assume(PtrEQ, false)) {
// If the size is 0, free the memory.
- if (const GRState *stateSizeZero = stateNotEqual->assume(SizeZero, true))
- if (const GRState *stateFree =
+ if (const ProgramState *stateSizeZero = stateNotEqual->assume(SizeZero, true))
+ if (const ProgramState *stateFree =
FreeMemAux(C, CE, stateSizeZero, 0, false)) {
- // Add the state transition to set input pointer argument to be free.
- C.addTransition(stateFree);
-
- // Bind the return value to UndefinedVal because it is now free.
- C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true));
+ // Bind the return value to NULL because it is now free.
+ C.addTransition(stateFree->BindExpr(CE, svalBuilder.makeNull(), true));
}
- if (const GRState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false))
- if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero,
+ if (const ProgramState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false))
+ if (const ProgramState *stateFree = FreeMemAux(C, CE, stateSizeNotZero,
0, false)) {
// FIXME: We should copy the content of the original buffer.
- const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
+ const ProgramState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
UnknownVal(), stateFree);
C.addTransition(stateRealloc);
}
@@ -556,7 +555,7 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
}
void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
SVal count = state->getSVal(CE->getArg(0));
@@ -574,33 +573,40 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
if (!SymReaper.hasDeadSymbols())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
RegionStateTy RS = state->get<RegionState>();
RegionStateTy::Factory &F = state->get_context<RegionState>();
+ bool generateReport = false;
+
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
if (SymReaper.isDead(I->first)) {
- if (I->second.isAllocated()) {
- if (ExplodedNode *N = C.generateNode()) {
- if (!BT_Leak)
- BT_Leak.reset(new BuiltinBug("Memory leak",
- "Allocated memory never released. Potential memory leak."));
- // FIXME: where it is allocated.
- BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
- C.EmitReport(R);
- }
- }
+ if (I->second.isAllocated())
+ generateReport = true;
// Remove the dead symbol from the map.
RS = F.remove(RS, I->first);
+
}
}
- C.generateNode(state->set<RegionState>(RS));
+
+ ExplodedNode *N = C.generateNode(state->set<RegionState>(RS));
+
+ // FIXME: This does not handle when we have multiple leaks at a single
+ // place.
+ if (N && generateReport) {
+ if (!BT_Leak)
+ BT_Leak.reset(new BuiltinBug("Memory leak",
+ "Allocated memory never released. Potential memory leak."));
+ // FIXME: where it is allocated.
+ BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
+ C.EmitReport(R);
+ }
}
void MallocChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
- const GRState *state = B.getState();
+ const ProgramState *state = B.getState();
RegionStateTy M = state->get<RegionState>();
for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) {
@@ -623,7 +629,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
if (!retExpr)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SymbolRef Sym = state->getSVal(retExpr).getAsSymbol();
if (!Sym)
@@ -640,7 +646,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
C.addTransition(state);
}
-const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond,
+const ProgramState *MallocChecker::evalAssume(const ProgramState *state, SVal Cond,
bool Assumption) const {
// If a symblic region is assumed to NULL, set its state to AllocateFailed.
// FIXME: should also check symbols assumed to non-null.
@@ -657,7 +663,8 @@ const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond,
}
// Check if the location is a freed symbolic region.
-void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const {
+void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const {
SymbolRef Sym = l.getLocSymbolInBase();
if (Sym) {
const RefState *RS = C.getState()->get<RegionState>(Sym);
@@ -675,13 +682,14 @@ void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const {
}
}
-void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const {
+void MallocChecker::checkBind(SVal location, SVal val,
+ const Stmt *BindS, CheckerContext &C) const {
// The PreVisitBind implements the same algorithm as already used by the
// Objective C ownership checker: if the pointer escaped from this scope by
// assignment, let it go. However, assigning to fields of a stack-storage
// structure does not transfer ownership.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location);
// Check for null dereferences.
@@ -694,7 +702,7 @@ void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const {
if (Sym) {
if (const RefState *RS = state->get<RegionState>(Sym)) {
// If ptr is NULL, no operation is performed.
- const GRState *notNullState, *nullState;
+ const ProgramState *notNullState, *nullState;
llvm::tie(notNullState, nullState) = state->assume(l);
// Generate a transition for 'nullState' to record the assumption
@@ -724,7 +732,7 @@ void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const {
// We no longer own this pointer.
notNullState =
notNullState->set<RegionState>(Sym,
- RefState::getRelinquished(C.getStmt()));
+ RefState::getRelinquished(BindS));
}
while (false);
}
diff --git a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
new file mode 100644
index 0000000..cf5501a
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -0,0 +1,268 @@
+// MallocOverflowSecurityChecker.cpp - Check for malloc overflows -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker detects a common memory allocation security flaw.
+// Suppose 'unsigned int n' comes from an untrusted source. If the
+// code looks like 'malloc (n * 4)', and an attacker can make 'n' be
+// say MAX_UINT/4+2, then instead of allocating the correct 'n' 4-byte
+// elements, this will actually allocate only two because of overflow.
+// Then when the rest of the program attempts to store values past the
+// second element, these values will actually overwrite other items in
+// the heap, probably allowing the attacker to execute arbitrary code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct MallocOverflowCheck {
+ const BinaryOperator *mulop;
+ const Expr *variable;
+
+ MallocOverflowCheck (const BinaryOperator *m, const Expr *v)
+ : mulop(m), variable (v)
+ {}
+};
+
+class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
+ BugReporter &BR) const;
+
+ void CheckMallocArgument(
+ llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Expr *TheArgument, ASTContext &Context) const;
+
+ void OutputPossibleOverflows(
+ llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Decl *D, BugReporter &BR, AnalysisManager &mgr) const;
+
+};
+} // end anonymous namespace
+
+void MallocOverflowSecurityChecker::CheckMallocArgument(
+ llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Expr *TheArgument,
+ ASTContext &Context) const {
+
+ /* Look for a linear combination with a single variable, and at least
+ one multiplication.
+ Reject anything that applies to the variable: an explicit cast,
+ conditional expression, an operation that could reduce the range
+ of the result, or anything too complicated :-). */
+ const Expr * e = TheArgument;
+ const BinaryOperator * mulop = NULL;
+
+ for (;;) {
+ e = e->IgnoreParenImpCasts();
+ if (isa<BinaryOperator>(e)) {
+ const BinaryOperator * binop = dyn_cast<BinaryOperator>(e);
+ BinaryOperatorKind opc = binop->getOpcode();
+ // TODO: ignore multiplications by 1, reject if multiplied by 0.
+ if (mulop == NULL && opc == BO_Mul)
+ mulop = binop;
+ if (opc != BO_Mul && opc != BO_Add && opc != BO_Sub && opc != BO_Shl)
+ return;
+
+ const Expr *lhs = binop->getLHS();
+ const Expr *rhs = binop->getRHS();
+ if (rhs->isEvaluatable(Context))
+ e = lhs;
+ else if ((opc == BO_Add || opc == BO_Mul)
+ && lhs->isEvaluatable(Context))
+ e = rhs;
+ else
+ return;
+ }
+ else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e))
+ break;
+ else
+ return;
+ }
+
+ if (mulop == NULL)
+ return;
+
+ // We've found the right structure of malloc argument, now save
+ // the data so when the body of the function is completely available
+ // we can check for comparisons.
+
+ // TODO: Could push this into the innermost scope where 'e' is
+ // defined, rather than the whole function.
+ PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e));
+}
+
+namespace {
+// A worker class for OutputPossibleOverflows.
+class CheckOverflowOps :
+ public EvaluatedExprVisitor<CheckOverflowOps> {
+public:
+ typedef llvm::SmallVectorImpl<MallocOverflowCheck> theVecType;
+
+private:
+ theVecType &toScanFor;
+ ASTContext &Context;
+
+ bool isIntZeroExpr(const Expr *E) const {
+ if (!E->getType()->isIntegralOrEnumerationType())
+ return false;
+ llvm::APSInt Result;
+ if (E->EvaluateAsInt(Result, Context))
+ return Result == 0;
+ return false;
+ }
+
+ void CheckExpr(const Expr *E_p) {
+ const Expr *E = E_p->IgnoreParenImpCasts();
+
+ theVecType::iterator i = toScanFor.end();
+ theVecType::iterator e = toScanFor.begin();
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) {
+ const Decl * EdreD = DR->getDecl();
+ while (i != e) {
+ --i;
+ if (const DeclRefExpr *DR_i = dyn_cast<DeclRefExpr>(i->variable)) {
+ if (DR_i->getDecl() == EdreD)
+ i = toScanFor.erase(i);
+ }
+ }
+ }
+ else if (isa<MemberExpr>(E)) {
+ // No points-to analysis, just look at the member
+ const Decl * EmeMD = dyn_cast<MemberExpr>(E)->getMemberDecl();
+ while (i != e) {
+ --i;
+ if (isa<MemberExpr>(i->variable)) {
+ if (dyn_cast<MemberExpr>(i->variable)->getMemberDecl() == EmeMD)
+ i = toScanFor.erase (i);
+ }
+ }
+ }
+ }
+
+ public:
+ void VisitBinaryOperator(BinaryOperator *E) {
+ if (E->isComparisonOp()) {
+ const Expr * lhs = E->getLHS();
+ const Expr * rhs = E->getRHS();
+ // Ignore comparisons against zero, since they generally don't
+ // protect against an overflow.
+ if (!isIntZeroExpr(lhs) && ! isIntZeroExpr(rhs)) {
+ CheckExpr(lhs);
+ CheckExpr(rhs);
+ }
+ }
+ EvaluatedExprVisitor<CheckOverflowOps>::VisitBinaryOperator(E);
+ }
+
+ /* We specifically ignore loop conditions, because they're typically
+ not error checks. */
+ void VisitWhileStmt(WhileStmt *S) {
+ return this->Visit(S->getBody());
+ }
+ void VisitForStmt(ForStmt *S) {
+ return this->Visit(S->getBody());
+ }
+ void VisitDoStmt(DoStmt *S) {
+ return this->Visit(S->getBody());
+ }
+
+ CheckOverflowOps(theVecType &v, ASTContext &ctx)
+ : EvaluatedExprVisitor<CheckOverflowOps>(ctx),
+ toScanFor(v), Context(ctx)
+ { }
+ };
+}
+
+// OutputPossibleOverflows - We've found a possible overflow earlier,
+// now check whether Body might contain a comparison which might be
+// preventing the overflow.
+// This doesn't do flow analysis, range analysis, or points-to analysis; it's
+// just a dumb "is there a comparison" scan. The aim here is to
+// detect the most blatent cases of overflow and educate the
+// programmer.
+void MallocOverflowSecurityChecker::OutputPossibleOverflows(
+ llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Decl *D, BugReporter &BR, AnalysisManager &mgr) const {
+ // By far the most common case: nothing to check.
+ if (PossibleMallocOverflows.empty())
+ return;
+
+ // Delete any possible overflows which have a comparison.
+ CheckOverflowOps c(PossibleMallocOverflows, BR.getContext());
+ c.Visit(mgr.getAnalysisContext(D)->getBody());
+
+ // Output warnings for all overflows that are left.
+ for (CheckOverflowOps::theVecType::iterator
+ i = PossibleMallocOverflows.begin(),
+ e = PossibleMallocOverflows.end();
+ i != e;
+ ++i) {
+ SourceRange R = i->mulop->getSourceRange();
+ BR.EmitBasicReport("MallocOverflowSecurityChecker",
+ "the computation of the size of the memory allocation may overflow",
+ PathDiagnosticLocation::createOperatorLoc(i->mulop,
+ BR.getSourceManager()),
+ &R, 1);
+ }
+}
+
+void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &mgr,
+ BugReporter &BR) const {
+
+ CFG *cfg = mgr.getCFG(D);
+ if (!cfg)
+ return;
+
+ // A list of variables referenced in possibly overflowing malloc operands.
+ llvm::SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows;
+
+ for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) {
+ CFGBlock *block = *it;
+ for (CFGBlock::iterator bi = block->begin(), be = block->end();
+ bi != be; ++bi) {
+ if (const CFGStmt *CS = bi->getAs<CFGStmt>()) {
+ if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
+ // Get the callee.
+ const FunctionDecl *FD = TheCall->getDirectCallee();
+
+ if (!FD)
+ return;
+
+ // Get the name of the callee. If it's a builtin, strip off the prefix.
+ IdentifierInfo *FnInfo = FD->getIdentifier();
+ if (!FnInfo)
+ return;
+
+ if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) {
+ if (TheCall->getNumArgs() == 1)
+ CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0),
+ mgr.getASTContext());
+ }
+ }
+ }
+ }
+ }
+
+ OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr);
+}
+
+void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MallocOverflowSecurityChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index f11db64..7f74a7d 100644
--- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -9,7 +9,7 @@
//
// This file defines a NSAutoreleasePoolChecker, a small checker that warns
// about subpar uses of NSAutoreleasePool. Note that while the check itself
-// (in it's current form) could be written as a flow-insensitive check, in
+// (in its current form) could be written as a flow-insensitive check, in
// can be potentially enhanced in the future with flow-sensitive information.
// It is also a good example of the CheckerVisitor interface.
//
@@ -18,9 +18,10 @@
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Decl.h"
@@ -53,7 +54,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg,
if (!PT)
return;
- const ObjCInterfaceDecl* OD = PT->getInterfaceDecl();
+ const ObjCInterfaceDecl *OD = PT->getInterfaceDecl();
if (!OD)
return;
if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool"))
@@ -66,14 +67,18 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg,
return;
SourceRange R = msg.getSourceRange();
-
+ BugReporter &BR = C.getBugReporter();
+ const LocationContext *LC = C.getPredecessor()->getLocationContext();
+ const SourceManager &SM = BR.getSourceManager();
+ const Expr *E = msg.getMsgOrPropExpr();
+ PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(E, SM, LC);
C.getBugReporter().EmitBasicReport("Use -drain instead of -release",
"API Upgrade (Apple)",
"Use -drain instead of -release when using NSAutoreleasePool "
- "and garbage collection", R.getBegin(), &R, 1);
+ "and garbage collection", L, &R, 1);
}
void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
- if (mgr.getLangOptions().getGCMode() != LangOptions::NonGC)
+ if (mgr.getLangOptions().getGC() != LangOptions::NonGC)
mgr.registerChecker<NSAutoreleasePoolChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index a51d8e0..5678998 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -19,7 +19,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Decl.h"
@@ -60,7 +60,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
II = &D->getASTContext().Idents.get("NSError");
bool hasNSError = false;
- for (ObjCMethodDecl::param_iterator
+ for (ObjCMethodDecl::param_const_iterator
I = D->param_begin(), E = D->param_end(); I != E; ++I) {
if (IsNSError((*I)->getType(), II)) {
hasNSError = true;
@@ -72,8 +72,10 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
const char *err = "Method accepting NSError** "
"should have a non-void return value to indicate whether or not an "
"error occurred";
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(D, BR.getSourceManager());
BR.EmitBasicReport("Bad return type when passing NSError**",
- "Coding conventions (Apple)", err, D->getLocation());
+ "Coding conventions (Apple)", err, L);
}
}
@@ -118,8 +120,10 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
const char *err = "Function accepting CFErrorRef* "
"should have a non-void return value to indicate whether or not an "
"error occurred";
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(D, BR.getSourceManager());
BR.EmitBasicReport("Bad return type when passing CFErrorRef*",
- "Coding conventions (Apple)", err, D->getLocation());
+ "Coding conventions (Apple)", err, L);
}
}
@@ -153,7 +157,8 @@ public:
NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0),
ShouldCheckNSError(0), ShouldCheckCFError(0) { }
- void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const;
+ void checkLocation(SVal loc, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
void checkEvent(ImplicitNullDerefEvent event) const;
};
}
@@ -166,18 +171,18 @@ typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
namespace clang {
namespace ento {
template <>
- struct GRStateTrait<NSErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {
+ struct ProgramStateTrait<NSErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> {
static void *GDMIndex() { static int index = 0; return &index; }
};
template <>
- struct GRStateTrait<CFErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {
+ struct ProgramStateTrait<CFErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> {
static void *GDMIndex() { static int index = 0; return &index; }
};
}
}
template <typename T>
-static bool hasFlag(SVal val, const GRState *state) {
+static bool hasFlag(SVal val, const ProgramState *state) {
if (SymbolRef sym = val.getAsSymbol())
if (const unsigned *attachedFlags = state->get<T>(sym))
return *attachedFlags;
@@ -185,7 +190,7 @@ static bool hasFlag(SVal val, const GRState *state) {
}
template <typename T>
-static void setFlag(const GRState *state, SVal val, CheckerContext &C) {
+static void setFlag(const ProgramState *state, SVal val, CheckerContext &C) {
// We tag the symbol that the SVal wraps.
if (SymbolRef sym = val.getAsSymbol())
C.addTransition(state->set<T>(sym, true));
@@ -207,6 +212,7 @@ static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
}
void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
+ const Stmt *S,
CheckerContext &C) const {
if (!isLoad)
return;
@@ -214,7 +220,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
return;
ASTContext &Ctx = C.getASTContext();
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
// SVal so that we can later check it when handling the
@@ -247,7 +253,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
return;
SVal loc = event.Location;
- const GRState *state = event.SinkNode->getState();
+ const ProgramState *state = event.SinkNode->getState();
BugReporter &BR = *event.BR;
bool isNSError = hasFlag<NSErrorOut>(loc, state);
@@ -277,7 +283,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
bug = new NSErrorDerefBug();
else
bug = new CFErrorDerefBug();
- EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(),
+ BugReport *report = new BugReport(*bug, os.str(),
event.SinkNode);
BR.EmitReport(report);
}
diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index 2d0af9c..81f1924 100644
--- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -16,23 +16,27 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "llvm/ADT/StringSwitch.h"
+#include <cstdarg>
using namespace clang;
using namespace ento;
namespace {
-class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr> > {
+class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>,
+ check::PostObjCMessage > {
public:
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const;
};
}
void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
@@ -50,7 +54,7 @@ void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
// Here are a few hardwired ones. If this takes too long, we can
// potentially cache these results.
BuildSinks
- = llvm::StringSwitch<bool>(llvm::StringRef(II->getName()))
+ = llvm::StringSwitch<bool>(StringRef(II->getName()))
.Case("exit", true)
.Case("panic", true)
.Case("error", true)
@@ -73,9 +77,70 @@ void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
}
if (BuildSinks)
- C.generateSink(CE);
+ C.generateSink();
}
+static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) {
+ va_list argp;
+ va_start(argp, Sel);
+
+ unsigned Slot = 0;
+ const char *Arg;
+ while ((Arg = va_arg(argp, const char *))) {
+ if (!Sel->getNameForSlot(Slot).equals(Arg))
+ break; // still need to va_end!
+ ++Slot;
+ }
+
+ va_end(argp);
+
+ // We only succeeded if we made it to the end of the argument list.
+ return (Arg == NULL);
+}
+
+void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg,
+ CheckerContext &C) const {
+ // HACK: This entire check is to handle two messages in the Cocoa frameworks:
+ // -[NSAssertionHandler
+ // handleFailureInMethod:object:file:lineNumber:description:]
+ // -[NSAssertionHandler
+ // handleFailureInFunction:file:lineNumber:description:]
+ // Eventually these should be annotated with __attribute__((noreturn)).
+ // Because ObjC messages use dynamic dispatch, it is not generally safe to
+ // assume certain methods can't return. In cases where it is definitely valid,
+ // see if you can mark the methods noreturn or analyzer_noreturn instead of
+ // adding more explicit checks to this method.
+
+ if (!Msg.isInstanceMessage())
+ return;
+
+ const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
+ if (!Receiver)
+ return;
+ if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
+ return;
+
+ Selector Sel = Msg.getSelector();
+ switch (Sel.getNumArgs()) {
+ default:
+ return;
+ case 4:
+ if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file",
+ "lineNumber", "description", NULL))
+ return;
+ break;
+ case 5:
+ if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file",
+ "lineNumber", "description", NULL))
+ return;
+ break;
+ }
+
+ // If we got here, it's one of the messages we care about.
+ C.generateSink();
+}
+
+
void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<NoReturnFunctionChecker>();
}
diff --git a/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp
index 7262bc3..f426265 100644
--- a/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp
@@ -22,22 +22,32 @@ using namespace ento;
namespace {
-class OSAtomicChecker : public Checker<eval::Call> {
+class OSAtomicChecker : public Checker<eval::InlineCall> {
public:
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool inlineCall(const CallExpr *CE, ExprEngine &Eng,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) const;
private:
- static bool evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE);
+ bool evalOSAtomicCompareAndSwap(const CallExpr *CE,
+ ExprEngine &Eng,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) const;
+
+ ExplodedNode *generateNode(const ProgramState *State,
+ ExplodedNode *Pred, const CallExpr *Statement,
+ StmtNodeBuilder &B, ExplodedNodeSet &Dst) const;
};
-
}
-bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
- const GRState *state = C.getState();
+bool OSAtomicChecker::inlineCall(const CallExpr *CE,
+ ExprEngine &Eng,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) const {
+ const ProgramState *state = Pred->getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
- const FunctionDecl* FD = L.getAsFunctionDecl();
+ const FunctionDecl *FD = L.getAsFunctionDecl();
if (!FD)
return false;
@@ -45,24 +55,38 @@ bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
if (!II)
return false;
- llvm::StringRef FName(II->getName());
+ StringRef FName(II->getName());
// Check for compare and swap.
if (FName.startswith("OSAtomicCompareAndSwap") ||
FName.startswith("objc_atomicCompareAndSwap"))
- return evalOSAtomicCompareAndSwap(C, CE);
+ return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst);
// FIXME: Other atomics.
return false;
}
-bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
- const CallExpr *CE) {
+ExplodedNode *OSAtomicChecker::generateNode(const ProgramState *State,
+ ExplodedNode *Pred,
+ const CallExpr *Statement,
+ StmtNodeBuilder &B,
+ ExplodedNodeSet &Dst) const {
+ ExplodedNode *N = B.generateNode(Statement, State, Pred, this);
+ if (N)
+ Dst.Add(N);
+ return N;
+}
+
+bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE,
+ ExprEngine &Eng,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) const {
// Not enough arguments to match OSAtomicCompareAndSwap?
if (CE->getNumArgs() != 3)
return false;
- ASTContext &Ctx = C.getASTContext();
+ StmtNodeBuilder &Builder = Eng.getBuilder();
+ ASTContext &Ctx = Eng.getContext();
const Expr *oldValueExpr = CE->getArg(0);
QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
@@ -87,15 +111,11 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
if (theValueTypePointee != newValueType)
return false;
- static unsigned magic_load = 0;
- static unsigned magic_store = 0;
-
- const void *OSAtomicLoadTag = &magic_load;
- const void *OSAtomicStoreTag = &magic_store;
-
+ static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load");
+ static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store");
+
// Load 'theValue'.
- ExprEngine &Engine = C.getEngine();
- const GRState *state = C.getState();
+ const ProgramState *state = Pred->getState();
ExplodedNodeSet Tmp;
SVal location = state->getSVal(theValueExpr);
// Here we should use the value type of the region as the load type, because
@@ -106,19 +126,19 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
// LoadTy specifying can be omitted. But we put it here to emphasize the
// semantics.
QualType LoadTy;
- if (const TypedRegion *TR =
- dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
+ if (const TypedValueRegion *TR =
+ dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
LoadTy = TR->getValueType();
}
- Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(),
- state, location, OSAtomicLoadTag, LoadTy);
+ Eng.evalLoad(Tmp, theValueExpr, Pred,
+ state, location, &OSAtomicLoadTag, LoadTy);
if (Tmp.empty()) {
// If no nodes were generated, other checkers must generated sinks. But
// since the builder state was restored, we set it manually to prevent
// auto transition.
// FIXME: there should be a better approach.
- C.getNodeBuilder().BuildSinks = true;
+ Builder.BuildSinks = true;
return true;
}
@@ -126,7 +146,7 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
I != E; ++I) {
ExplodedNode *N = *I;
- const GRState *stateLoad = N->getState();
+ const ProgramState *stateLoad = N->getState();
// Use direct bindings from the environment since we are forcing a load
// from a location that the Environment would typically not be used
@@ -145,12 +165,13 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
DefinedOrUnknownSVal oldValueVal =
cast<DefinedOrUnknownSVal>(oldValueVal_untested);
- SValBuilder &svalBuilder = Engine.getSValBuilder();
+ SValBuilder &svalBuilder = Eng.getSValBuilder();
// Perform the comparison.
- DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal);
+ DefinedOrUnknownSVal Cmp =
+ svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal);
- const GRState *stateEqual = stateLoad->assume(Cmp, true);
+ const ProgramState *stateEqual = stateLoad->assume(Cmp, true);
// Were they equal?
if (stateEqual) {
@@ -159,20 +180,20 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
SVal val = stateEqual->getSVal(newValueExpr);
// Handle implicit value casts.
- if (const TypedRegion *R =
- dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
+ if (const TypedValueRegion *R =
+ dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType());
}
- Engine.evalStore(TmpStore, NULL, theValueExpr, N,
- stateEqual, location, val, OSAtomicStoreTag);
+ Eng.evalStore(TmpStore, NULL, theValueExpr, N,
+ stateEqual, location, val, &OSAtomicStoreTag);
if (TmpStore.empty()) {
// If no nodes were generated, other checkers must generated sinks. But
// since the builder state was restored, we set it manually to prevent
// auto transition.
// FIXME: there should be a better approach.
- C.getNodeBuilder().BuildSinks = true;
+ Builder.BuildSinks = true;
return true;
}
@@ -180,24 +201,24 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C,
for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
E2 = TmpStore.end(); I2 != E2; ++I2) {
ExplodedNode *predNew = *I2;
- const GRState *stateNew = predNew->getState();
+ const ProgramState *stateNew = predNew->getState();
// Check for 'void' return type if we have a bogus function prototype.
SVal Res = UnknownVal();
QualType T = CE->getType();
if (!T->isVoidType())
- Res = Engine.getSValBuilder().makeTruthVal(true, T);
- C.generateNode(stateNew->BindExpr(CE, Res), predNew);
+ Res = Eng.getSValBuilder().makeTruthVal(true, T);
+ generateNode(stateNew->BindExpr(CE, Res), predNew, CE, Builder, Dst);
}
}
// Were they not equal?
- if (const GRState *stateNotEqual = stateLoad->assume(Cmp, false)) {
+ if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) {
// Check for 'void' return type if we have a bogus function prototype.
SVal Res = UnknownVal();
QualType T = CE->getType();
if (!T->isVoidType())
- Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType());
- C.generateNode(stateNotEqual->BindExpr(CE, Res), N);
+ Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType());
+ generateNode(stateNotEqual->BindExpr(CE, Res), N, CE, Builder, Dst);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index a118049..3e4e07b 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -38,7 +38,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
CheckerContext &C) const {
const Expr *Ex = S->getSynchExpr();
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal V = state->getSVal(Ex);
// Uninitialized value used for the mutex?
@@ -47,9 +47,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
if (!BT_undef)
BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex "
"for @synchronized"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N);
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex);
+ BugReport *report =
+ new BugReport(*BT_undef, BT_undef->getDescription(), N);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex));
C.EmitReport(report);
}
return;
@@ -59,7 +59,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
return;
// Check for null mutexes.
- const GRState *notNullState, *nullState;
+ const ProgramState *notNullState, *nullState;
llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V));
if (nullState) {
@@ -70,10 +70,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
if (!BT_null)
BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() "
"(no synchronization will occur)"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_null, BT_null->getDescription(), N);
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- Ex);
+ BugReport *report =
+ new BugReport(*BT_null, BT_null->getDescription(), N);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex));
C.EmitReport(report);
return;
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 4c05867..2fb9944 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -50,7 +50,8 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/ParentMap.h"
@@ -76,7 +77,8 @@ public:
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkLocation(SVal location, bool isLoad, CheckerContext &C) const;
+ void checkLocation(SVal location, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
};
} // end anonymous namespace
@@ -109,11 +111,11 @@ namespace { struct PreCallSelfFlags {}; }
namespace clang {
namespace ento {
template<>
- struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> {
- static void* GDMIndex() { static int index = 0; return &index; }
+ struct ProgramStateTrait<SelfFlag> : public ProgramStatePartialTrait<SelfFlag> {
+ static void *GDMIndex() { static int index = 0; return &index; }
};
template <>
- struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> {
+ struct ProgramStateTrait<CalledInit> : public ProgramStatePartialTrait<bool> {
static void *GDMIndex() { static int index = 0; return &index; }
};
@@ -122,13 +124,13 @@ namespace ento {
/// object before the call so we can assign them to the new object that 'self'
/// points to after the call.
template <>
- struct GRStateTrait<PreCallSelfFlags> : public GRStatePartialTrait<unsigned> {
+ struct ProgramStateTrait<PreCallSelfFlags> : public ProgramStatePartialTrait<unsigned> {
static void *GDMIndex() { static int index = 0; return &index; }
};
}
}
-static SelfFlagEnum getSelfFlags(SVal val, const GRState *state) {
+static SelfFlagEnum getSelfFlags(SVal val, const ProgramState *state) {
if (SymbolRef sym = val.getAsSymbol())
if (const unsigned *attachedFlags = state->get<SelfFlag>(sym))
return (SelfFlagEnum)*attachedFlags;
@@ -139,7 +141,7 @@ static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {
return getSelfFlags(val, C.getState());
}
-static void addSelfFlag(const GRState *state, SVal val,
+static void addSelfFlag(const ProgramState *state, SVal val,
SelfFlagEnum flag, CheckerContext &C) {
// We tag the symbol that the SVal wraps.
if (SymbolRef sym = val.getAsSymbol())
@@ -179,8 +181,8 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C,
if (!N)
return;
- EnhancedBugReport *report =
- new EnhancedBugReport(*new InitSelfBug(), errorStr, N);
+ BugReport *report =
+ new BugReport(*new InitSelfBug(), errorStr, N);
C.EmitReport(report);
}
@@ -197,7 +199,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg,
if (isInitMessage(msg)) {
// Tag the return value as the result of an initializer.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
// FIXME this really should be context sensitive, where we record
// the current stack frame (for IPA). Also, we need to clean this
@@ -257,7 +259,7 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
for (CallExpr::const_arg_iterator
I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) {
SVal argV = state->getSVal(*I);
@@ -275,7 +277,7 @@ void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE,
void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
for (CallExpr::const_arg_iterator
I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) {
SVal argV = state->getSVal(*I);
@@ -294,10 +296,11 @@ void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE,
}
void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
+ const Stmt *S,
CheckerContext &C) const {
// Tag the result of a load from 'self' so that we can easily know that the
// value is the object that 'self' points to.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (isSelfVar(location, C))
addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C);
}
@@ -315,9 +318,9 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
// self = [super init] applies only to NSObject subclasses.
// For instance, NSProxy doesn't implement -init.
- ASTContext& Ctx = MD->getASTContext();
+ ASTContext &Ctx = MD->getASTContext();
IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
- ObjCInterfaceDecl* ID = MD->getClassInterface()->getSuperClass();
+ ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass();
for ( ; ID ; ID = ID->getSuperClass()) {
IdentifierInfo *II = ID->getIdentifier();
diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index d78e5ce..bbc262f 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -29,7 +29,7 @@ using namespace ento;
enum IVarState { Unused, Used };
typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
-static void Scan(IvarUsageMap& M, const Stmt* S) {
+static void Scan(IvarUsageMap& M, const Stmt *S) {
if (!S)
return;
@@ -51,11 +51,11 @@ static void Scan(IvarUsageMap& M, const Stmt* S) {
Scan(M, *I);
}
-static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) {
+static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
if (!D)
return;
- const ObjCIvarDecl* ID = D->getPropertyIvarDecl();
+ const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
if (!ID)
return;
@@ -65,7 +65,7 @@ static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) {
I->second = Used;
}
-static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) {
+static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
// Scan the methods for accesses.
for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(),
E = D->instmeth_end(); I!=E; ++I)
@@ -102,14 +102,14 @@ static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
BugReporter &BR) {
- const ObjCInterfaceDecl* ID = D->getClassInterface();
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
IvarUsageMap M;
// Iterate over the ivars.
for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(),
E=ID->ivar_end(); I!=E; ++I) {
- const ObjCIvarDecl* ID = *I;
+ const ObjCIvarDecl *ID = *I;
// Ignore ivars that...
// (a) aren't private
@@ -155,12 +155,14 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
if (I->second == Unused) {
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- os << "Instance variable '" << I->first << "' in class '" << ID
+ os << "Instance variable '" << *I->first << "' in class '" << *ID
<< "' is never used by the methods in its @implementation "
"(although it may be used by category methods).";
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(I->first, BR.getSourceManager());
BR.EmitBasicReport("Unused instance variable", "Optimization",
- os.str(), I->first->getLocation());
+ os.str(), L);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index 7c21acc..202522b 100644
--- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -36,7 +36,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B,
if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal LV = state->getSVal(B->getLHS());
SVal RV = state->getSVal(B->getRHS());
@@ -56,7 +56,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B,
"Pointer arithmetic done on non-array variables "
"means reliance on memory layout, which is "
"dangerous."));
- RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N);
+ BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 16ede20..924c7f2 100644
--- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -39,7 +39,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
if (B->getOpcode() != BO_Sub)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal LV = state->getSVal(B->getLHS());
SVal RV = state->getSVal(B->getRHS());
@@ -64,7 +64,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
BT.reset(new BuiltinBug("Pointer subtraction",
"Subtraction of two pointers that do not point to "
"the same memory chunk may cause incorrect result."));
- RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N);
+ BugReport *R = new BugReport(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 74199bb..c02b5b1 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -1,4 +1,4 @@
-//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===//
+//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually
-// this shouldn't be registered with ExprEngineInternalChecks.
+// This defines PthreadLockChecker, a simple lock -> unlock checker.
+// Also handles XNU locks, which behave similarly enough to share code.
//
//===----------------------------------------------------------------------===//
@@ -16,25 +16,29 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
-#include "llvm/ADT/ImmutableSet.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/ImmutableList.h"
using namespace clang;
using namespace ento;
namespace {
-class PthreadLockChecker
- : public Checker< check::PostStmt<CallExpr> > {
+class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
+ mutable llvm::OwningPtr<BugType> BT_doublelock;
+ mutable llvm::OwningPtr<BugType> BT_lor;
+ enum LockingSemantics {
+ NotApplicable = 0,
+ PthreadSemantics,
+ XNUSemantics
+ };
public:
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void AcquireLock(CheckerContext &C, const CallExpr *CE,
- SVal lock, bool isTryLock) const;
+ void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
+ bool isTryLock, enum LockingSemantics semantics) const;
- void ReleaseLock(CheckerContext &C, const CallExpr *CE,
- SVal lock) const;
-
+ void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
};
} // end anonymous namespace
@@ -42,80 +46,117 @@ public:
namespace { class LockSet {}; }
namespace clang {
namespace ento {
-template <> struct GRStateTrait<LockSet> :
- public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
- static void* GDMIndex() { static int x = 0; return &x; }
+template <> struct ProgramStateTrait<LockSet> :
+ public ProgramStatePartialTrait<llvm::ImmutableList<const MemRegion*> > {
+ static void *GDMIndex() { static int x = 0; return &x; }
};
-} // end GR namespace
+} // end of ento (ProgramState) namespace
} // end clang namespace
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
- const FunctionTextRegion *R =
- dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
-
- if (!R)
+ const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl();
+
+ if (!FD)
return;
-
- IdentifierInfo *II = R->getDecl()->getIdentifier();
+
+ // Get the name of the callee.
+ IdentifierInfo *II = FD->getIdentifier();
if (!II) // if no identifier, not a simple C function
return;
- llvm::StringRef FName = II->getName();
-
- if (FName == "pthread_mutex_lock") {
- if (CE->getNumArgs() != 1)
- return;
- AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false);
- }
- else if (FName == "pthread_mutex_trylock") {
- if (CE->getNumArgs() != 1)
- return;
- AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true);
- }
- else if (FName == "pthread_mutex_unlock") {
- if (CE->getNumArgs() != 1)
- return;
+ StringRef FName = II->getName();
+
+ if (CE->getNumArgs() != 1)
+ return;
+
+ if (FName == "pthread_mutex_lock" ||
+ FName == "pthread_rwlock_rdlock" ||
+ FName == "pthread_rwlock_wrlock")
+ AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false, PthreadSemantics);
+ else if (FName == "lck_mtx_lock" ||
+ FName == "lck_rw_lock_exclusive" ||
+ FName == "lck_rw_lock_shared")
+ AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false, XNUSemantics);
+ else if (FName == "pthread_mutex_trylock" ||
+ FName == "pthread_rwlock_tryrdlock" ||
+ FName == "pthread_rwlock_tryrwlock")
+ AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true, PthreadSemantics);
+ else if (FName == "lck_mtx_try_lock" ||
+ FName == "lck_rw_try_lock_exclusive" ||
+ FName == "lck_rw_try_lock_shared")
+ AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true, XNUSemantics);
+ else if (FName == "pthread_mutex_unlock" ||
+ FName == "pthread_rwlock_unlock" ||
+ FName == "lck_mtx_unlock" ||
+ FName == "lck_rw_done")
ReleaseLock(C, CE, state->getSVal(CE->getArg(0)));
- }
}
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
- SVal lock, bool isTryLock) const {
+ SVal lock, bool isTryLock,
+ enum LockingSemantics semantics) const {
const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal X = state->getSVal(CE);
if (X.isUnknownOrUndef())
return;
DefinedSVal retVal = cast<DefinedSVal>(X);
- const GRState *lockSucc = state;
-
+
+ if (state->contains<LockSet>(lockR)) {
+ if (!BT_doublelock)
+ BT_doublelock.reset(new BugType("Double locking", "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *report = new BugReport(*BT_doublelock,
+ "This lock has already "
+ "been acquired", N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.EmitReport(report);
+ return;
+ }
+
+ const ProgramState *lockSucc = state;
if (isTryLock) {
- // Bifurcate the state, and allow a mode where the lock acquisition fails.
- const GRState *lockFail;
- llvm::tie(lockFail, lockSucc) = state->assume(retVal);
+ // Bifurcate the state, and allow a mode where the lock acquisition fails.
+ const ProgramState *lockFail;
+ switch (semantics) {
+ case PthreadSemantics:
+ llvm::tie(lockFail, lockSucc) = state->assume(retVal);
+ break;
+ case XNUSemantics:
+ llvm::tie(lockSucc, lockFail) = state->assume(retVal);
+ break;
+ default:
+ llvm_unreachable("Unknown tryLock locking semantics");
+ break;
+ }
assert(lockFail && lockSucc);
- C.addTransition(C.generateNode(CE, lockFail));
- }
- else {
- // Assume that the return value was 0.
+ C.addTransition(lockFail);
+
+ } else if (semantics == PthreadSemantics) {
+ // Assume that the return value was 0.
lockSucc = state->assume(retVal, false);
assert(lockSucc);
+
+ } else {
+ // XNU locking semantics return void on non-try locks
+ assert((semantics == XNUSemantics) && "Unknown locking semantics");
+ lockSucc = state;
}
- // Record that the lock was acquired.
+ // Record that the lock was acquired.
lockSucc = lockSucc->add<LockSet>(lockR);
-
- C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) :
- C.getPredecessor());
+ C.addTransition(lockSucc);
}
void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
@@ -125,19 +166,37 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
if (!lockR)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
+ llvm::ImmutableList<const MemRegion*> LS = state->get<LockSet>();
- // Record that the lock was released.
- // FIXME: Handle unlocking locks that were never acquired. This may
- // require IPA for wrappers.
- const GRState *unlockState = state->remove<LockSet>(lockR);
-
- if (state == unlockState)
+ // FIXME: Better analysis requires IPA for wrappers.
+ // FIXME: check for double unlocks
+ if (LS.isEmpty())
return;
- C.addTransition(C.generateNode(CE, unlockState));
+ const MemRegion *firstLockR = LS.getHead();
+ if (firstLockR != lockR) {
+ if (!BT_lor)
+ BT_lor.reset(new BugType("Lock order reversal", "Lock checker"));
+ ExplodedNode *N = C.generateSink();
+ if (!N)
+ return;
+ BugReport *report = new BugReport(*BT_lor,
+ "This was not the most "
+ "recently acquired lock. "
+ "Possible lock order "
+ "reversal", N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.EmitReport(report);
+ return;
+ }
+
+ // Record that the lock was released.
+ state = state->set<LockSet>(LS.getTail());
+ C.addTransition(state);
}
+
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
}
diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index bf53029..93e0fe5 100644
--- a/lib/StaticAnalyzer/Core/CFRefCount.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -1,4 +1,4 @@
-// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
+//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--//
//
// The LLVM Compiler Infrastructure
//
@@ -7,111 +7,56 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines the methods for CFRefCount, which implements
-// a reference count checker for Core Foundation (Mac OS X).
+// This file defines the methods for RetainCountChecker, which implements
+// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "ClangSACheckers.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
-#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
-#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
-#include <stdarg.h>
+#include <cstdarg>
using namespace clang;
using namespace ento;
-using llvm::StringRef;
using llvm::StrInStrNoCase;
namespace {
-class InstanceReceiver {
- ObjCMessage Msg;
- const LocationContext *LC;
-public:
- InstanceReceiver() : LC(0) { }
- InstanceReceiver(const ObjCMessage &msg,
- const LocationContext *lc = 0) : Msg(msg), LC(lc) {}
-
- bool isValid() const {
- return Msg.isValid() && Msg.isInstanceMessage();
- }
- operator bool() const {
- return isValid();
- }
-
- SVal getSValAsScalarOrLoc(const GRState *state) {
- assert(isValid());
- // We have an expression for the receiver? Fetch the value
- // of that expression.
- if (const Expr *Ex = Msg.getInstanceReceiver())
- return state->getSValAsScalarOrLoc(Ex);
-
- // Otherwise we are sending a message to super. In this case the
- // object reference is the same as 'self'.
- if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl())
- return state->getSVal(state->getRegion(SelfDecl, LC));
-
- return UnknownVal();
- }
-
- SourceRange getSourceRange() const {
- assert(isValid());
- if (const Expr *Ex = Msg.getInstanceReceiver())
- return Ex->getSourceRange();
-
- // Otherwise we are sending a message to super.
- SourceLocation L = Msg.getSuperLoc();
- assert(L.isValid());
- return SourceRange(L, L);
- }
-};
-}
-
-static const ObjCMethodDecl*
-ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) {
- const ObjCInterfaceDecl *ID = MD->getClassInterface();
-
- return MD->isInstanceMethod()
- ? ID->lookupInstanceMethod(MD->getSelector())
- : ID->lookupClassMethod(MD->getSelector());
-}
-
-namespace {
+/// Wrapper around different kinds of node builder, so that helper functions
+/// can have a common interface.
class GenericNodeBuilderRefCount {
- StmtNodeBuilder *SNB;
- const Stmt *S;
- const void *tag;
+ CheckerContext *C;
+ const ProgramPointTag *tag;
EndOfFunctionNodeBuilder *ENB;
public:
- GenericNodeBuilderRefCount(StmtNodeBuilder &snb, const Stmt *s,
- const void *t)
- : SNB(&snb), S(s), tag(t), ENB(0) {}
+ GenericNodeBuilderRefCount(CheckerContext &c,
+ const ProgramPointTag *t)
+ : C(&c), tag(t), ENB(0) {}
GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &enb)
- : SNB(0), S(0), tag(0), ENB(&enb) {}
+ : C(0), tag(0), ENB(&enb) {}
- ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) {
- if (SNB)
- return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag),
- state, Pred);
+ ExplodedNode *MakeNode(const ProgramState *state, ExplodedNode *Pred) {
+ if (C)
+ return C->generateNode(state, Pred, tag, false);
assert(ENB);
return ENB->generateNode(state, Pred);
@@ -125,9 +70,9 @@ public:
/// ArgEffect is used to summarize a function/method call's effect on a
/// particular argument.
-enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing,
+enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg,
DecRefBridgedTransfered,
- DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,
+ IncRefMsg, IncRef, MakeCollectable, MayEscape,
NewAutoreleasePool, SelfOwn, StopTracking };
namespace llvm {
@@ -148,9 +93,8 @@ namespace {
/// respect to its return value.
class RetEffect {
public:
- enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
+ enum Kind { NoRet, OwnedSymbol, OwnedAllocatedSymbol,
NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol,
- ReceiverAlias,
OwnedWhenTrackedReceiver };
enum ObjKind { CF, ObjC, AnyObj };
@@ -158,36 +102,27 @@ public:
private:
Kind K;
ObjKind O;
- unsigned index;
- RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {}
- RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {}
+ RetEffect(Kind k, ObjKind o = AnyObj) : K(k), O(o) {}
public:
Kind getKind() const { return K; }
ObjKind getObjKind() const { return O; }
- unsigned getIndex() const {
- assert(getKind() == Alias);
- return index;
- }
-
bool isOwned() const {
return K == OwnedSymbol || K == OwnedAllocatedSymbol ||
K == OwnedWhenTrackedReceiver;
}
+ bool operator==(const RetEffect &Other) const {
+ return K == Other.K && O == Other.O;
+ }
+
static RetEffect MakeOwnedWhenTrackedReceiver() {
return RetEffect(OwnedWhenTrackedReceiver, ObjC);
}
- static RetEffect MakeAlias(unsigned Idx) {
- return RetEffect(Alias, Idx);
- }
- static RetEffect MakeReceiverAlias() {
- return RetEffect(ReceiverAlias);
- }
static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) {
return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o);
}
@@ -314,15 +249,15 @@ public:
ID.Add(T);
}
- void print(llvm::raw_ostream& Out) const;
+ void print(raw_ostream &Out) const;
};
-void RefVal::print(llvm::raw_ostream& Out) const {
+void RefVal::print(raw_ostream &Out) const {
if (!T.isNull())
- Out << "Tracked Type:" << T.getAsString() << '\n';
+ Out << "Tracked " << T.getAsString() << '/';
switch (getKind()) {
- default: assert(false);
+ default: llvm_unreachable("Invalid RefVal kind");
case Owned: {
Out << "Owned";
unsigned cnt = getCount();
@@ -406,18 +341,19 @@ typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings;
namespace clang {
namespace ento {
- template<>
- struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> {
- static void* GDMIndex() {
- static int RefBIndex = 0;
- return &RefBIndex;
- }
- };
+template<>
+struct ProgramStateTrait<RefBindings>
+ : public ProgramStatePartialTrait<RefBindings> {
+ static void *GDMIndex() {
+ static int RefBIndex = 0;
+ return &RefBIndex;
+ }
+};
}
}
//===----------------------------------------------------------------------===//
-// Summaries
+// Function/Method behavior summaries.
//===----------------------------------------------------------------------===//
namespace {
@@ -436,19 +372,13 @@ class RetainSummary {
ArgEffect Receiver;
/// Ret - The effect on the return value. Used to indicate if the
- /// function/method call returns a new tracked symbol, returns an
- /// alias of one of the arguments in the call, and so on.
+ /// function/method call returns a new tracked symbol.
RetEffect Ret;
- /// EndPath - Indicates that execution of this method/function should
- /// terminate the simulation of a path.
- bool EndPath;
-
public:
RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff,
- ArgEffect ReceiverEff, bool endpath = false)
- : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R),
- EndPath(endpath) {}
+ ArgEffect ReceiverEff)
+ : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {}
/// getArg - Return the argument effect on the argument specified by
/// idx (starting from 0).
@@ -474,10 +404,6 @@ public:
/// setRetEffect - Set the effect of the return value of the call.
void setRetEffect(RetEffect E) { Ret = E; }
- /// isEndPath - Returns true if executing the given method/function should
- /// terminate the path.
- bool isEndPath() const { return EndPath; }
-
/// Sets the effect on the receiver of the message.
void setReceiverEffect(ArgEffect e) { Receiver = e; }
@@ -485,6 +411,14 @@ public:
/// getReceiverEffect - Returns the effect on the receiver of the call.
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
ArgEffect getReceiverEffect() const { return Receiver; }
+
+ /// Test if two retain summaries are identical. Note that merely equivalent
+ /// summaries are not necessarily identical (for example, if an explicit
+ /// argument effect matches the default effect).
+ bool operator==(const RetainSummary &Other) const {
+ return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect &&
+ Receiver == Other.Receiver && Ret == Other.Ret;
+ }
};
} // end anonymous namespace
@@ -500,10 +434,10 @@ public:
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
: II(ii), S(s) {}
- ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s)
+ ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s)
: II(d ? d->getIdentifier() : 0), S(s) {}
- ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s)
+ ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s)
: II(d ? d->getIdentifier() : ii), S(s) {}
ObjCSummaryKey(Selector s)
@@ -547,19 +481,19 @@ struct isPodLike<ObjCSummaryKey> { static const bool value = true; };
namespace {
class ObjCSummaryCache {
- typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy;
+ typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy;
MapTy M;
public:
ObjCSummaryCache() {}
- RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName,
+ const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
Selector S) {
// Lookup the method using the decl for the class @interface. If we
// have no decl, lookup using the class name.
return D ? find(D, S) : find(ClsName, S);
}
- RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) {
+ const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
@@ -574,7 +508,7 @@ public:
// generate initial summaries without having to worry about NSObject
// being declared.
// FIXME: We may change this at some point.
- for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) {
+ for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) {
if ((I = M.find(ObjCSummaryKey(C, S))) != M.end())
break;
@@ -584,12 +518,12 @@ public:
// Cache the summary with original key to make the next lookup faster
// and return the iterator.
- RetainSummary *Summ = I->second;
+ const RetainSummary *Summ = I->second;
M[K] = Summ;
return Summ;
}
- RetainSummary* find(IdentifierInfo* II, Selector S) {
+ const RetainSummary * find(IdentifierInfo* II, Selector S) {
// FIXME: Class method lookup. Right now we dont' have a good way
// of going between IdentifierInfo* and the class hierarchy.
MapTy::iterator I = M.find(ObjCSummaryKey(II, S));
@@ -600,11 +534,11 @@ public:
return I == M.end() ? NULL : I->second;
}
- RetainSummary*& operator[](ObjCSummaryKey K) {
+ const RetainSummary *& operator[](ObjCSummaryKey K) {
return M[K];
}
- RetainSummary*& operator[](Selector S) {
+ const RetainSummary *& operator[](Selector S) {
return M[ ObjCSummaryKey(S) ];
}
};
@@ -621,7 +555,7 @@ class RetainSummaryManager {
// Typedefs.
//==-----------------------------------------------------------------==//
- typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*>
+ typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *>
FuncSummariesTy;
typedef ObjCSummaryCache ObjCMethodSummariesTy;
@@ -631,11 +565,7 @@ class RetainSummaryManager {
//==-----------------------------------------------------------------==//
/// Ctx - The ASTContext object for the analyzed ASTs.
- ASTContext& Ctx;
-
- /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier
- /// "CFDictionaryCreate".
- IdentifierInfo* CFDictionaryCreateII;
+ ASTContext &Ctx;
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
@@ -672,7 +602,7 @@ class RetainSummaryManager {
RetEffect ObjCInitRetE;
RetainSummary DefaultSummary;
- RetainSummary* StopSummary;
+ const RetainSummary *StopSummary;
//==-----------------------------------------------------------------==//
// Methods.
@@ -687,30 +617,28 @@ class RetainSummaryManager {
public:
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
- RetainSummary *getDefaultSummary() {
- RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
- return new (Summ) RetainSummary(DefaultSummary);
+ const RetainSummary *getDefaultSummary() {
+ return &DefaultSummary;
}
+
+ const RetainSummary * getUnarySummary(const FunctionType* FT,
+ UnaryFuncKind func);
- RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func);
-
- RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD);
- RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD);
- RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD,
- StringRef FName);
+ const RetainSummary * getCFSummaryCreateRule(const FunctionDecl *FD);
+ const RetainSummary * getCFSummaryGetRule(const FunctionDecl *FD);
+ const RetainSummary * getCFCreateGetRuleSummary(const FunctionDecl *FD);
- RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff,
- ArgEffect ReceiverEff = DoNothing,
- ArgEffect DefaultEff = MayEscape,
- bool isEndPath = false);
+ const RetainSummary * getPersistentSummary(ArgEffects AE, RetEffect RetEff,
+ ArgEffect ReceiverEff = DoNothing,
+ ArgEffect DefaultEff = MayEscape);
- RetainSummary* getPersistentSummary(RetEffect RE,
- ArgEffect ReceiverEff = DoNothing,
- ArgEffect DefaultEff = MayEscape) {
+ const RetainSummary * getPersistentSummary(RetEffect RE,
+ ArgEffect ReceiverEff = DoNothing,
+ ArgEffect DefaultEff = MayEscape) {
return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff);
}
- RetainSummary *getPersistentStopSummary() {
+ const RetainSummary *getPersistentStopSummary() {
if (StopSummary)
return StopSummary;
@@ -720,35 +648,35 @@ public:
return StopSummary;
}
- RetainSummary *getInitMethodSummary(QualType RetTy);
+ const RetainSummary *getInitMethodSummary(QualType RetTy);
void InitializeClassMethodSummaries();
void InitializeMethodSummaries();
private:
- void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) {
+ void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) {
ObjCClassMethodSummaries[S] = Summ;
}
- void addNSObjectMethSummary(Selector S, RetainSummary *Summ) {
+ void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) {
ObjCMethodSummaries[S] = Summ;
}
void addClassMethSummary(const char* Cls, const char* nullaryName,
- RetainSummary *Summ) {
+ const RetainSummary *Summ) {
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
Selector S = GetNullarySelector(nullaryName, Ctx);
ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
void addInstMethSummary(const char* Cls, const char* nullaryName,
- RetainSummary *Summ) {
+ const RetainSummary *Summ) {
IdentifierInfo* ClsII = &Ctx.Idents.get(Cls);
Selector S = GetNullarySelector(nullaryName, Ctx);
ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
Selector generateSelector(va_list argp) {
- llvm::SmallVector<IdentifierInfo*, 10> II;
+ SmallVector<IdentifierInfo*, 10> II;
while (const char* s = va_arg(argp, const char*))
II.push_back(&Ctx.Idents.get(s));
@@ -757,47 +685,36 @@ private:
}
void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries,
- RetainSummary* Summ, va_list argp) {
+ const RetainSummary * Summ, va_list argp) {
Selector S = generateSelector(argp);
Summaries[ObjCSummaryKey(ClsII, S)] = Summ;
}
- void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) {
+ void addInstMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
va_end(argp);
}
- void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) {
+ void addClsMethSummary(const char* Cls, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp);
va_end(argp);
}
- void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) {
+ void addClsMethSummary(IdentifierInfo *II, const RetainSummary * Summ, ...) {
va_list argp;
va_start(argp, Summ);
addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp);
va_end(argp);
}
- void addPanicSummary(const char* Cls, ...) {
- RetainSummary* Summ = getPersistentSummary(AF.getEmptyMap(),
- RetEffect::MakeNoRet(),
- DoNothing, DoNothing, true);
- va_list argp;
- va_start (argp, Cls);
- addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp);
- va_end(argp);
- }
-
public:
- RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC)
+ RetainSummaryManager(ASTContext &ctx, bool gcenabled, bool usesARC)
: Ctx(ctx),
- CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
GCEnabled(gcenabled),
ARCEnabled(usesARC),
AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
@@ -819,31 +736,31 @@ public:
InitializeMethodSummaries();
}
- ~RetainSummaryManager();
-
- RetainSummary* getSummary(const FunctionDecl* FD);
+ const RetainSummary * getSummary(const FunctionDecl *FD);
- RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
- const GRState *state,
- const LocationContext *LC);
+ const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
+ const ProgramState *state,
+ const LocationContext *LC);
- RetainSummary* getInstanceMethodSummary(const ObjCMessage &msg,
- const ObjCInterfaceDecl* ID) {
+ const RetainSummary * getInstanceMethodSummary(const ObjCMessage &msg,
+ const ObjCInterfaceDecl *ID) {
return getInstanceMethodSummary(msg.getSelector(), 0,
ID, msg.getMethodDecl(), msg.getType(Ctx));
}
- RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl* ID,
- const ObjCMethodDecl *MD,
- QualType RetTy);
+ const RetainSummary * getInstanceMethodSummary(Selector S,
+ IdentifierInfo *ClsName,
+ const ObjCInterfaceDecl *ID,
+ const ObjCMethodDecl *MD,
+ QualType RetTy);
- RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl *ID,
- const ObjCMethodDecl *MD,
- QualType RetTy);
+ const RetainSummary *getClassMethodSummary(Selector S,
+ IdentifierInfo *ClsName,
+ const ObjCInterfaceDecl *ID,
+ const ObjCMethodDecl *MD,
+ QualType RetTy);
- RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
+ const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
const ObjCInterfaceDecl *Class = 0;
if (!msg.isInstanceMessage())
Class = msg.getReceiverInterface();
@@ -856,30 +773,26 @@ public:
/// getMethodSummary - This version of getMethodSummary is used to query
/// the summary for the current method being analyzed.
- RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
+ const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
// FIXME: Eventually this should be unneeded.
const ObjCInterfaceDecl *ID = MD->getClassInterface();
Selector S = MD->getSelector();
IdentifierInfo *ClsName = ID->getIdentifier();
QualType ResultTy = MD->getResultType();
- // Resolve the method decl last.
- if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD))
- MD = InterfaceMD;
-
if (MD->isInstanceMethod())
return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy);
else
return getClassMethodSummary(S, ClsName, ID, MD, ResultTy);
}
- RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD,
- Selector S, QualType RetTy);
+ const RetainSummary * getCommonMethodSummary(const ObjCMethodDecl *MD,
+ Selector S, QualType RetTy);
- void updateSummaryFromAnnotations(RetainSummary &Summ,
+ void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD);
- void updateSummaryFromAnnotations(RetainSummary &Summ,
+ void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD);
bool isGCEnabled() const { return GCEnabled; }
@@ -888,35 +801,69 @@ public:
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
- RetainSummary *copySummary(RetainSummary *OldSumm) {
- RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
+ const RetainSummary *copySummary(const RetainSummary *OldSumm) {
+ RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(*OldSumm);
return Summ;
}
};
+// Used to avoid allocating long-term (BPAlloc'd) memory for default retain
+// summaries. If a function or method looks like it has a default summary, but
+// it has annotations, the annotations are added to the stack-based template
+// and then copied into managed memory.
+class RetainSummaryTemplate {
+ RetainSummaryManager &Manager;
+ const RetainSummary *&RealSummary;
+ const RetainSummary *BaseSummary;
+ RetainSummary ScratchSummary;
+ bool Accessed;
+public:
+ RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base,
+ RetainSummaryManager &manager)
+ : Manager(manager),
+ RealSummary(real),
+ BaseSummary(&base),
+ ScratchSummary(base),
+ Accessed(false) {}
+
+ ~RetainSummaryTemplate() {
+ if (Accessed)
+ RealSummary = Manager.copySummary(&ScratchSummary);
+ else if (!RealSummary)
+ RealSummary = BaseSummary;
+ }
+
+ RetainSummary &operator*() {
+ Accessed = true;
+ return ScratchSummary;
+ }
+
+ RetainSummary *operator->() {
+ Accessed = true;
+ return &ScratchSummary;
+ }
+};
+
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Implementation of checker data structures.
//===----------------------------------------------------------------------===//
-RetainSummaryManager::~RetainSummaryManager() {}
-
ArgEffects RetainSummaryManager::getArgEffects() {
ArgEffects AE = ScratchArgs;
ScratchArgs = AF.getEmptyMap();
return AE;
}
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff,
ArgEffect ReceiverEff,
- ArgEffect DefaultEff,
- bool isEndPath) {
+ ArgEffect DefaultEff) {
// Create the summary and return it.
- RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
- new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath);
+ RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
+ new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff);
return Summ;
}
@@ -924,22 +871,28 @@ RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff,
// Summary creation for functions (largely uses of Core Foundation).
//===----------------------------------------------------------------------===//
-static bool isRetain(const FunctionDecl* FD, StringRef FName) {
+static bool isRetain(const FunctionDecl *FD, StringRef FName) {
return FName.endswith("Retain");
}
-static bool isRelease(const FunctionDecl* FD, StringRef FName) {
+static bool isRelease(const FunctionDecl *FD, StringRef FName) {
return FName.endswith("Release");
}
-RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
+static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
+ // FIXME: Remove FunctionDecl parameter.
+ // FIXME: Is it really okay if MakeCollectable isn't a suffix?
+ return FName.find("MakeCollectable") != StringRef::npos;
+}
+
+const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
if (I != FuncSummaries.end())
return I->second;
// No summary? Generate one.
- RetainSummary *S = 0;
+ const RetainSummary *S = 0;
do {
// We generate "stop" summaries for implicitly defined functions.
@@ -1054,10 +1007,10 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
if (cocoa::isRefType(RetTy, "CF", FName)) {
if (isRetain(FD, FName))
S = getUnarySummary(FT, cfretain);
- else if (FName.find("MakeCollectable") != StringRef::npos)
+ else if (isMakeCollectable(FD, FName))
S = getUnarySummary(FT, cfmakecollectable);
else
- S = getCFCreateGetRuleSummary(FD, FName);
+ S = getCFCreateGetRuleSummary(FD);
break;
}
@@ -1067,7 +1020,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
if (isRetain(FD, FName))
S = getUnarySummary(FT, cfretain);
else
- S = getCFCreateGetRuleSummary(FD, FName);
+ S = getCFCreateGetRuleSummary(FD);
break;
}
@@ -1076,7 +1029,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
if (cocoa::isRefType(RetTy, "DADisk") ||
cocoa::isRefType(RetTy, "DADissenter") ||
cocoa::isRefType(RetTy, "DASessionRef")) {
- S = getCFCreateGetRuleSummary(FD, FName);
+ S = getCFCreateGetRuleSummary(FD);
break;
}
@@ -1121,27 +1074,22 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
}
while (0);
- if (!S)
- S = getDefaultSummary();
-
// Annotations override defaults.
- assert(S);
- updateSummaryFromAnnotations(*S, FD);
+ updateSummaryFromAnnotations(S, FD);
FuncSummaries[FD] = S;
return S;
}
-RetainSummary*
-RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD,
- StringRef FName) {
- if (coreFoundation::followsCreateRule(FName))
+const RetainSummary *
+RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
+ if (coreFoundation::followsCreateRule(FD))
return getCFSummaryCreateRule(FD);
return getCFSummaryGetRule(FD);
}
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getUnarySummary(const FunctionType* FT,
UnaryFuncKind func) {
@@ -1153,44 +1101,27 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT,
assert (ScratchArgs.isEmpty());
+ ArgEffect Effect;
switch (func) {
- case cfretain: {
- ScratchArgs = AF.add(ScratchArgs, 0, IncRef);
- return getPersistentSummary(RetEffect::MakeAlias(0),
- DoNothing, DoNothing);
- }
-
- case cfrelease: {
- ScratchArgs = AF.add(ScratchArgs, 0, DecRef);
- return getPersistentSummary(RetEffect::MakeNoRet(),
- DoNothing, DoNothing);
- }
-
- case cfmakecollectable: {
- ScratchArgs = AF.add(ScratchArgs, 0, MakeCollectable);
- return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing);
- }
-
- default:
- assert (false && "Not a supported unary function.");
- return getDefaultSummary();
+ case cfretain: Effect = IncRef; break;
+ case cfrelease: Effect = DecRef; break;
+ case cfmakecollectable: Effect = MakeCollectable; break;
+ default: llvm_unreachable("Not a supported unary function.");
}
+
+ ScratchArgs = AF.add(ScratchArgs, 0, Effect);
+ return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
}
-RetainSummary*
-RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl* FD) {
+const RetainSummary *
+RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
assert (ScratchArgs.isEmpty());
- if (FD->getIdentifier() == CFDictionaryCreateII) {
- ScratchArgs = AF.add(ScratchArgs, 1, DoNothingByRef);
- ScratchArgs = AF.add(ScratchArgs, 2, DoNothingByRef);
- }
-
return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
}
-RetainSummary*
-RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) {
+const RetainSummary *
+RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
assert (ScratchArgs.isEmpty());
return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF),
DoNothing, DoNothing);
@@ -1200,7 +1131,7 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) {
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
assert(ScratchArgs.isEmpty());
// 'init' methods conceptually return a newly allocated object and claim
@@ -1213,22 +1144,24 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
}
void
-RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD) {
if (!FD)
return;
+ RetainSummaryTemplate Template(Summ, DefaultSummary, *this);
+
// Effects on the parameters.
unsigned parm_idx = 0;
for (FunctionDecl::param_const_iterator pi = FD->param_begin(),
pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
if (pd->getAttr<NSConsumedAttr>()) {
- if (!GCEnabled)
- Summ.addArg(AF, parm_idx, DecRef);
- }
- else if(pd->getAttr<CFConsumedAttr>()) {
- Summ.addArg(AF, parm_idx, DecRef);
+ if (!GCEnabled) {
+ Template->addArg(AF, parm_idx, DecRef);
+ }
+ } else if (pd->getAttr<CFConsumedAttr>()) {
+ Template->addArg(AF, parm_idx, DecRef);
}
}
@@ -1237,83 +1170,84 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
// Determine if there is a special return effect for this method.
if (cocoa::isCocoaObjectRef(RetTy)) {
if (FD->getAttr<NSReturnsRetainedAttr>()) {
- Summ.setRetEffect(ObjCAllocRetE);
+ Template->setRetEffect(ObjCAllocRetE);
}
else if (FD->getAttr<CFReturnsRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
+ Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
}
else if (FD->getAttr<NSReturnsNotRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
+ Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
}
else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
+ Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
}
- }
- else if (RetTy->getAs<PointerType>()) {
+ } else if (RetTy->getAs<PointerType>()) {
if (FD->getAttr<CFReturnsRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
+ Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
}
else if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
+ Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
}
}
}
void
-RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
- const ObjCMethodDecl *MD) {
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
+ const ObjCMethodDecl *MD) {
if (!MD)
return;
+ RetainSummaryTemplate Template(Summ, DefaultSummary, *this);
+
bool isTrackedLoc = false;
// Effects on the receiver.
if (MD->getAttr<NSConsumesSelfAttr>()) {
if (!GCEnabled)
- Summ.setReceiverEffect(DecRefMsg);
+ Template->setReceiverEffect(DecRefMsg);
}
// Effects on the parameters.
unsigned parm_idx = 0;
- for (ObjCMethodDecl::param_iterator pi=MD->param_begin(), pe=MD->param_end();
+ for (ObjCMethodDecl::param_const_iterator
+ pi=MD->param_begin(), pe=MD->param_end();
pi != pe; ++pi, ++parm_idx) {
const ParmVarDecl *pd = *pi;
if (pd->getAttr<NSConsumedAttr>()) {
if (!GCEnabled)
- Summ.addArg(AF, parm_idx, DecRef);
+ Template->addArg(AF, parm_idx, DecRef);
}
else if(pd->getAttr<CFConsumedAttr>()) {
- Summ.addArg(AF, parm_idx, DecRef);
+ Template->addArg(AF, parm_idx, DecRef);
}
}
// Determine if there is a special return effect for this method.
if (cocoa::isCocoaObjectRef(MD->getResultType())) {
if (MD->getAttr<NSReturnsRetainedAttr>()) {
- Summ.setRetEffect(ObjCAllocRetE);
+ Template->setRetEffect(ObjCAllocRetE);
return;
}
if (MD->getAttr<NSReturnsNotRetainedAttr>()) {
- Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
+ Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC));
return;
}
isTrackedLoc = true;
- }
-
- if (!isTrackedLoc)
+ } else {
isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL;
+ }
if (isTrackedLoc) {
if (MD->getAttr<CFReturnsRetainedAttr>())
- Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
+ Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true));
else if (MD->getAttr<CFReturnsNotRetainedAttr>())
- Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
+ Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF));
}
}
-RetainSummary*
-RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
+const RetainSummary *
+RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD,
Selector S, QualType RetTy) {
if (MD) {
@@ -1322,9 +1256,9 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
// Delegates are a frequent form of false positives with the retain
// count checker.
unsigned i = 0;
- for (ObjCMethodDecl::param_iterator I = MD->param_begin(),
+ for (ObjCMethodDecl::param_const_iterator I = MD->param_begin(),
E = MD->param_end(); I != E; ++I, ++i)
- if (ParmVarDecl *PD = *I) {
+ if (const ParmVarDecl *PD = *I) {
QualType Ty = Ctx.getCanonicalType(PD->getType());
if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy)
ScratchArgs = AF.add(ScratchArgs, i, StopTracking);
@@ -1370,15 +1304,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape);
}
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
- const GRState *state,
+ const ProgramState *state,
const LocationContext *LC) {
// We need the type-information of the tracked receiver object
// Retrieve it from the state.
const Expr *Receiver = msg.getInstanceReceiver();
- const ObjCInterfaceDecl* ID = 0;
+ const ObjCInterfaceDecl *ID = 0;
// FIXME: Is this really working as expected? There are cases where
// we just use the 'ID' from the message expression.
@@ -1410,19 +1344,18 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
// FIXME: The receiver could be a reference to a class, meaning that
// we should use the class method.
- RetainSummary *Summ = getInstanceMethodSummary(msg, ID);
- return Summ ? Summ : getDefaultSummary();
+ return getInstanceMethodSummary(msg, ID);
}
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getInstanceMethodSummary(Selector S,
IdentifierInfo *ClsName,
- const ObjCInterfaceDecl* ID,
+ const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy) {
// Look up a summary in our summary cache.
- RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S);
+ const RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S);
if (!Summ) {
assert(ScratchArgs.isEmpty());
@@ -1434,7 +1367,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
Summ = getCommonMethodSummary(MD, S, RetTy);
// Annotations override defaults.
- updateSummaryFromAnnotations(*Summ, MD);
+ updateSummaryFromAnnotations(Summ, MD);
// Memoize the summary.
ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
@@ -1443,19 +1376,21 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
return Summ;
}
-RetainSummary*
+const RetainSummary *
RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy) {
assert(ClsName && "Class name must be specified.");
- RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S);
+ const RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S);
if (!Summ) {
Summ = getCommonMethodSummary(MD, S, RetTy);
+
// Annotations override defaults.
- updateSummaryFromAnnotations(*Summ, MD);
+ updateSummaryFromAnnotations(Summ, MD);
+
// Memoize the summary.
ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
}
@@ -1465,8 +1400,6 @@ RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
void RetainSummaryManager::InitializeClassMethodSummaries() {
assert(ScratchArgs.isEmpty());
- RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE);
-
// Create the [NSAssertionHandler currentHander] summary.
addClassMethSummary("NSAssertionHandler", "currentHandler",
getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC)));
@@ -1482,7 +1415,8 @@ void RetainSummaryManager::InitializeClassMethodSummaries() {
// used for delegates that can release the object. When we have better
// inter-procedural analysis we can potentially do something better. This
// workaround is to remove false positives.
- Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
+ const RetainSummary *Summ =
+ getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
"afterDelay", NULL);
@@ -1506,7 +1440,7 @@ void RetainSummaryManager::InitializeMethodSummaries() {
// Create the "init" selector. It just acts as a pass-through for the
// receiver.
- RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
+ const RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg);
addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
// awakeAfterUsingCoder: behaves basically like an 'init' method. It
@@ -1515,35 +1449,34 @@ void RetainSummaryManager::InitializeMethodSummaries() {
InitSumm);
// The next methods are allocators.
- RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
- RetainSummary *CFAllocSumm =
+ const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE);
+ const RetainSummary *CFAllocSumm =
getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true));
// Create the "retain" selector.
- RetEffect E = RetEffect::MakeReceiverAlias();
- RetainSummary *Summ = getPersistentSummary(E, IncRefMsg);
+ RetEffect NoRet = RetEffect::MakeNoRet();
+ const RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg);
addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
// Create the "release" selector.
- Summ = getPersistentSummary(E, DecRefMsg);
+ Summ = getPersistentSummary(NoRet, DecRefMsg);
addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
// Create the "drain" selector.
- Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
+ Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef);
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
// Create the -dealloc summary.
- Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc);
+ Summ = getPersistentSummary(NoRet, Dealloc);
addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
// Create the "autorelease" selector.
- Summ = getPersistentSummary(E, Autorelease);
+ Summ = getPersistentSummary(NoRet, Autorelease);
addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
// Specially handle NSAutoreleasePool.
addInstMethSummary("NSAutoreleasePool", "init",
- getPersistentSummary(RetEffect::MakeReceiverAlias(),
- NewAutoreleasePool));
+ getPersistentSummary(NoRet, NewAutoreleasePool));
// For NSWindow, allocated objects are (initially) self-owned.
// FIXME: For now we opt for false negatives with NSWindow, as these objects
@@ -1551,7 +1484,7 @@ void RetainSummaryManager::InitializeMethodSummaries() {
// Thus, we need to track an NSWindow's display status.
// This is tracked in <rdar://problem/6062711>.
// See also http://llvm.org/bugs/show_bug.cgi?id=3714.
- RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
+ const RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(),
StopTracking,
StopTracking);
@@ -1583,13 +1516,6 @@ void RetainSummaryManager::InitializeMethodSummaries() {
// exit a method.
addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
- // Create NSAssertionHandler summaries.
- addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file",
- "lineNumber", "description", NULL);
-
- addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object",
- "file", "lineNumber", "description", NULL);
-
// Create summaries QCRenderer/QCView -createSnapShotImageOfType:
addInstMethSummary("QCRenderer", AllocSumm,
"createSnapshotImageOfType", NULL);
@@ -1623,26 +1549,27 @@ namespace { class AutoreleaseStack {}; }
namespace clang {
namespace ento {
-template<> struct GRStateTrait<AutoreleaseStack>
- : public GRStatePartialTrait<ARStack> {
- static inline void* GDMIndex() { return &AutoRBIndex; }
+template<> struct ProgramStateTrait<AutoreleaseStack>
+ : public ProgramStatePartialTrait<ARStack> {
+ static inline void *GDMIndex() { return &AutoRBIndex; }
};
-template<> struct GRStateTrait<AutoreleasePoolContents>
- : public GRStatePartialTrait<ARPoolContents> {
- static inline void* GDMIndex() { return &AutoRCIndex; }
+template<> struct ProgramStateTrait<AutoreleasePoolContents>
+ : public ProgramStatePartialTrait<ARPoolContents> {
+ static inline void *GDMIndex() { return &AutoRCIndex; }
};
} // end GR namespace
} // end clang namespace
-static SymbolRef GetCurrentAutoreleasePool(const GRState* state) {
+static SymbolRef GetCurrentAutoreleasePool(const ProgramState *state) {
ARStack stack = state->get<AutoreleaseStack>();
return stack.isEmpty() ? SymbolRef() : stack.getHead();
}
-static const GRState * SendAutorelease(const GRState *state,
- ARCounts::Factory &F, SymbolRef sym) {
-
+static const ProgramState *
+SendAutorelease(const ProgramState *state,
+ ARCounts::Factory &F,
+ SymbolRef sym) {
SymbolRef pool = GetCurrentAutoreleasePool(state);
const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool);
ARCounts newCnts(0);
@@ -1658,196 +1585,11 @@ static const GRState * SendAutorelease(const GRState *state,
}
//===----------------------------------------------------------------------===//
-// Transfer functions.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
-class CFRefCount : public TransferFuncs {
-public:
- class BindingsPrinter : public GRState::Printer {
- public:
- virtual void Print(llvm::raw_ostream& Out, const GRState* state,
- const char* nl, const char* sep);
- };
-
- typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*>
- SummaryLogTy;
-
- RetainSummaryManager Summaries;
- SummaryLogTy SummaryLog;
- const LangOptions& LOpts;
- ARCounts::Factory ARCountFactory;
-
- BugType *useAfterRelease, *releaseNotOwned;
- BugType *deallocGC, *deallocNotOwned;
- BugType *leakWithinFunction, *leakAtReturn;
- BugType *overAutorelease;
- BugType *returnNotOwnedForOwned;
- BugReporter *BR;
-
- const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E,
- RefVal::Kind& hasErr);
-
- void ProcessNonLeakError(ExplodedNodeSet& Dst,
- StmtNodeBuilder& Builder,
- const Expr* NodeExpr, SourceRange ErrorRange,
- ExplodedNode* Pred,
- const GRState* St,
- RefVal::Kind hasErr, SymbolRef Sym);
-
- const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V,
- llvm::SmallVectorImpl<SymbolRef> &Leaked);
-
- ExplodedNode* ProcessLeaks(const GRState * state,
- llvm::SmallVectorImpl<SymbolRef> &Leaked,
- GenericNodeBuilderRefCount &Builder,
- ExprEngine &Eng,
- ExplodedNode *Pred = 0);
-
-public:
- CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
- : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount),
- LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
- deallocGC(0), deallocNotOwned(0),
- leakWithinFunction(0), leakAtReturn(0), overAutorelease(0),
- returnNotOwnedForOwned(0), BR(0) {}
-
- virtual ~CFRefCount() {}
-
- void RegisterChecks(ExprEngine &Eng);
-
- virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) {
- Printers.push_back(new BindingsPrinter());
- }
-
- bool isGCEnabled() const { return Summaries.isGCEnabled(); }
- bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); }
-
- const LangOptions& getLangOptions() const { return LOpts; }
-
- const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const {
- SummaryLogTy::const_iterator I = SummaryLog.find(N);
- return I == SummaryLog.end() ? 0 : I->second;
- }
-
- // Calls.
-
- void evalSummary(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- const Expr* Ex,
- const CallOrObjCMessage &callOrMsg,
- InstanceReceiver Receiver,
- const RetainSummary& Summ,
- const MemRegion *Callee,
- ExplodedNode* Pred, const GRState *state);
-
- virtual void evalCall(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- const CallExpr* CE, SVal L,
- ExplodedNode* Pred);
-
-
- virtual void evalObjCMessage(ExplodedNodeSet& Dst,
- ExprEngine& Engine,
- StmtNodeBuilder& Builder,
- ObjCMessage msg,
- ExplodedNode* Pred,
- const GRState *state);
- // Stores.
- virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val);
-
- // End-of-path.
-
- virtual void evalEndPath(ExprEngine& Engine,
- EndOfFunctionNodeBuilder& Builder);
-
- virtual void evalDeadSymbols(ExplodedNodeSet& Dst,
- ExprEngine& Engine,
- StmtNodeBuilder& Builder,
- ExplodedNode* Pred,
- const GRState* state,
- SymbolReaper& SymReaper);
-
- std::pair<ExplodedNode*, const GRState *>
- HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilderRefCount Bd,
- ExplodedNode* Pred, ExprEngine &Eng,
- SymbolRef Sym, RefVal V, bool &stop);
- // Return statements.
-
- virtual void evalReturn(ExplodedNodeSet& Dst,
- ExprEngine& Engine,
- StmtNodeBuilder& Builder,
- const ReturnStmt* S,
- ExplodedNode* Pred);
-
- void evalReturnWithRetEffect(ExplodedNodeSet& Dst,
- ExprEngine& Engine,
- StmtNodeBuilder& Builder,
- const ReturnStmt* S,
- ExplodedNode* Pred,
- RetEffect RE, RefVal X,
- SymbolRef Sym, const GRState *state);
-
-
- // Assumptions.
-
- virtual const GRState *evalAssume(const GRState* state, SVal condition,
- bool assumption);
-};
-
-} // end anonymous namespace
-
-static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym,
- const GRState *state) {
- Out << ' ';
- if (Sym)
- Out << Sym->getSymbolID();
- else
- Out << "<pool>";
- Out << ":{";
-
- // Get the contents of the pool.
- if (const ARCounts *cnts = state->get<AutoreleasePoolContents>(Sym))
- for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J)
- Out << '(' << J.getKey() << ',' << J.getData() << ')';
-
- Out << '}';
-}
-
-void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out,
- const GRState* state,
- const char* nl, const char* sep) {
-
- RefBindings B = state->get<RefBindings>();
-
- if (!B.isEmpty())
- Out << sep << nl;
-
- for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
- Out << (*I).first << " : ";
- (*I).second.print(Out);
- Out << nl;
- }
-
- // Print the autorelease stack.
- Out << sep << nl << "AR pool stack:";
- ARStack stack = state->get<AutoreleaseStack>();
-
- PrintPool(Out, SymbolRef(), state); // Print the caller's pool.
- for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I)
- PrintPool(Out, *I, state);
-
- Out << nl;
-}
-
-//===----------------------------------------------------------------------===//
// Error reporting.
//===----------------------------------------------------------------------===//
-
namespace {
+ typedef llvm::DenseMap<const ExplodedNode *, const RetainSummary *>
+ SummaryLogTy;
//===-------------===//
// Bug Descriptions. //
@@ -1855,35 +1597,30 @@ namespace {
class CFRefBug : public BugType {
protected:
- CFRefCount& TF;
-
- CFRefBug(CFRefCount* tf, llvm::StringRef name)
- : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {}
+ CFRefBug(StringRef name)
+ : BugType(name, "Memory (Core Foundation/Objective-C)") {}
public:
- CFRefCount& getTF() { return TF; }
-
// FIXME: Eventually remove.
- virtual const char* getDescription() const = 0;
+ virtual const char *getDescription() const = 0;
virtual bool isLeak() const { return false; }
};
class UseAfterRelease : public CFRefBug {
public:
- UseAfterRelease(CFRefCount* tf)
- : CFRefBug(tf, "Use-after-release") {}
+ UseAfterRelease() : CFRefBug("Use-after-release") {}
- const char* getDescription() const {
+ const char *getDescription() const {
return "Reference-counted object is used after it is released";
}
};
class BadRelease : public CFRefBug {
public:
- BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {}
+ BadRelease() : CFRefBug("Bad release") {}
- const char* getDescription() const {
+ const char *getDescription() const {
return "Incorrect decrement of the reference count of an object that is "
"not owned at this point by the caller";
}
@@ -1891,8 +1628,8 @@ namespace {
class DeallocGC : public CFRefBug {
public:
- DeallocGC(CFRefCount *tf)
- : CFRefBug(tf, "-dealloc called while using garbage collection") {}
+ DeallocGC()
+ : CFRefBug("-dealloc called while using garbage collection") {}
const char *getDescription() const {
return "-dealloc called while using garbage collection";
@@ -1901,8 +1638,8 @@ namespace {
class DeallocNotOwned : public CFRefBug {
public:
- DeallocNotOwned(CFRefCount *tf)
- : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {}
+ DeallocNotOwned()
+ : CFRefBug("-dealloc sent to non-exclusively owned object") {}
const char *getDescription() const {
return "-dealloc sent to object that may be referenced elsewhere";
@@ -1911,8 +1648,8 @@ namespace {
class OverAutorelease : public CFRefBug {
public:
- OverAutorelease(CFRefCount *tf) :
- CFRefBug(tf, "Object sent -autorelease too many times") {}
+ OverAutorelease()
+ : CFRefBug("Object sent -autorelease too many times") {}
const char *getDescription() const {
return "Object sent -autorelease too many times";
@@ -1921,8 +1658,8 @@ namespace {
class ReturnedNotOwnedForOwned : public CFRefBug {
public:
- ReturnedNotOwnedForOwned(CFRefCount *tf) :
- CFRefBug(tf, "Method should return an owned object") {}
+ ReturnedNotOwnedForOwned()
+ : CFRefBug("Method should return an owned object") {}
const char *getDescription() const {
return "Object with a +0 retain count returned to caller where a +1 "
@@ -1933,141 +1670,170 @@ namespace {
class Leak : public CFRefBug {
const bool isReturn;
protected:
- Leak(CFRefCount* tf, llvm::StringRef name, bool isRet)
- : CFRefBug(tf, name), isReturn(isRet) {}
+ Leak(StringRef name, bool isRet)
+ : CFRefBug(name), isReturn(isRet) {
+ // Leaks should not be reported if they are post-dominated by a sink.
+ setSuppressOnSink(true);
+ }
public:
- const char* getDescription() const { return ""; }
+ const char *getDescription() const { return ""; }
bool isLeak() const { return true; }
};
class LeakAtReturn : public Leak {
public:
- LeakAtReturn(CFRefCount* tf, llvm::StringRef name)
- : Leak(tf, name, true) {}
+ LeakAtReturn(StringRef name)
+ : Leak(name, true) {}
};
class LeakWithinFunction : public Leak {
public:
- LeakWithinFunction(CFRefCount* tf, llvm::StringRef name)
- : Leak(tf, name, false) {}
+ LeakWithinFunction(StringRef name)
+ : Leak(name, false) {}
};
//===---------===//
// Bug Reports. //
//===---------===//
- class CFRefReport : public RangedBugReport {
+ class CFRefReportVisitor : public BugReporterVisitor {
protected:
SymbolRef Sym;
- const CFRefCount &TF;
+ const SummaryLogTy &SummaryLog;
+ bool GCEnabled;
+
public:
- CFRefReport(CFRefBug& D, const CFRefCount &tf,
- ExplodedNode *n, SymbolRef sym)
- : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {}
+ CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log)
+ : Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {}
- CFRefReport(CFRefBug& D, const CFRefCount &tf,
- ExplodedNode *n, SymbolRef sym, llvm::StringRef endText)
- : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {}
-
- virtual ~CFRefReport() {}
-
- CFRefBug& getBugType() const {
- return (CFRefBug&) RangedBugReport::getBugType();
+ virtual void Profile(llvm::FoldingSetNodeID &ID) const {
+ static int x = 0;
+ ID.AddPointer(&x);
+ ID.AddPointer(Sym);
}
- virtual std::pair<ranges_iterator, ranges_iterator> getRanges() const {
- if (!getBugType().isLeak())
- return RangedBugReport::getRanges();
- else
- return std::make_pair(ranges_iterator(), ranges_iterator());
- }
+ virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR);
- SymbolRef getSymbol() const { return Sym; }
+ virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ BugReport &BR);
+ };
- PathDiagnosticPiece* getEndPath(BugReporterContext& BRC,
- const ExplodedNode* N);
+ class CFRefLeakReportVisitor : public CFRefReportVisitor {
+ public:
+ CFRefLeakReportVisitor(SymbolRef sym, bool GCEnabled,
+ const SummaryLogTy &log)
+ : CFRefReportVisitor(sym, GCEnabled, log) {}
- std::pair<const char**,const char**> getExtraDescriptiveText();
+ PathDiagnosticPiece *getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ BugReport &BR);
+ };
+
+ class CFRefReport : public BugReport {
+ void addGCModeDescription(const LangOptions &LOpts, bool GCEnabled);
- PathDiagnosticPiece* VisitNode(const ExplodedNode* N,
- const ExplodedNode* PrevN,
- BugReporterContext& BRC);
+ public:
+ CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
+ const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
+ bool registerVisitor = true)
+ : BugReport(D, D.getDescription(), n) {
+ if (registerVisitor)
+ addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log));
+ addGCModeDescription(LOpts, GCEnabled);
+ }
+
+ CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
+ const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
+ StringRef endText)
+ : BugReport(D, D.getDescription(), endText, n) {
+ addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log));
+ addGCModeDescription(LOpts, GCEnabled);
+ }
+
+ virtual std::pair<ranges_iterator, ranges_iterator> getRanges() {
+ const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType());
+ if (!BugTy.isLeak())
+ return BugReport::getRanges();
+ else
+ return std::make_pair(ranges_iterator(), ranges_iterator());
+ }
};
class CFRefLeakReport : public CFRefReport {
- SourceLocation AllocSite;
const MemRegion* AllocBinding;
- public:
- CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
- ExplodedNode *n, SymbolRef sym,
- ExprEngine& Eng);
- PathDiagnosticPiece* getEndPath(BugReporterContext& BRC,
- const ExplodedNode* N);
+ public:
+ CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled,
+ const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
+ ExprEngine &Eng);
- SourceLocation getLocation() const { return AllocSite; }
+ PathDiagnosticLocation getLocation(const SourceManager &SM) const {
+ assert(Location.isValid());
+ return Location;
+ }
};
} // end anonymous namespace
-
-
-static const char* Msgs[] = {
- // GC only
- "Code is compiled to only use garbage collection",
- // No GC.
- "Code is compiled to use reference counts",
- // Hybrid, with GC.
- "Code is compiled to use either garbage collection (GC) or reference counts"
- " (non-GC). The bug occurs with GC enabled",
- // Hybrid, without GC
- "Code is compiled to use either garbage collection (GC) or reference counts"
- " (non-GC). The bug occurs in non-GC mode"
-};
-
-std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() {
- CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF();
-
- switch (TF.getLangOptions().getGCMode()) {
- default:
- assert(false);
-
- case LangOptions::GCOnly:
- assert (TF.isGCEnabled());
- return std::make_pair(&Msgs[0], &Msgs[0]+1);
-
- case LangOptions::NonGC:
- assert (!TF.isGCEnabled());
- return std::make_pair(&Msgs[1], &Msgs[1]+1);
-
- case LangOptions::HybridGC:
- if (TF.isGCEnabled())
- return std::make_pair(&Msgs[2], &Msgs[2]+1);
- else
- return std::make_pair(&Msgs[3], &Msgs[3]+1);
+void CFRefReport::addGCModeDescription(const LangOptions &LOpts,
+ bool GCEnabled) {
+ const char *GCModeDescription = 0;
+
+ switch (LOpts.getGC()) {
+ case LangOptions::GCOnly:
+ assert(GCEnabled);
+ GCModeDescription = "Code is compiled to only use garbage collection";
+ break;
+
+ case LangOptions::NonGC:
+ assert(!GCEnabled);
+ GCModeDescription = "Code is compiled to use reference counts";
+ break;
+
+ case LangOptions::HybridGC:
+ if (GCEnabled) {
+ GCModeDescription = "Code is compiled to use either garbage collection "
+ "(GC) or reference counts (non-GC). The bug occurs "
+ "with GC enabled";
+ break;
+ } else {
+ GCModeDescription = "Code is compiled to use either garbage collection "
+ "(GC) or reference counts (non-GC). The bug occurs "
+ "in non-GC mode";
+ break;
+ }
}
+
+ assert(GCModeDescription && "invalid/unknown GC mode");
+ addExtraText(GCModeDescription);
}
-static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V,
+// FIXME: This should be a method on SmallVector.
+static inline bool contains(const SmallVectorImpl<ArgEffect>& V,
ArgEffect X) {
- for (llvm::SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end();
+ for (SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end();
I!=E; ++I)
if (*I == X) return true;
return false;
}
-PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
- const ExplodedNode* PrevN,
- BugReporterContext& BRC) {
+PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
- if (!isa<PostStmt>(N->getLocation()))
+ if (!isa<StmtPoint>(N->getLocation()))
return NULL;
// Check if the type state has changed.
- const GRState *PrevSt = PrevN->getState();
- const GRState *CurrSt = N->getState();
+ const ProgramState *PrevSt = PrevN->getState();
+ const ProgramState *CurrSt = N->getState();
const RefVal* CurrT = CurrSt->get<RefBindings>(Sym);
if (!CurrT) return NULL;
@@ -2083,13 +1849,13 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
// This is the allocation site since the previous node had no bindings
// for this symbol.
if (!PrevT) {
- const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
+ const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
// Get the name of the callee (if it is available).
SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee());
- if (const FunctionDecl* FD = X.getAsFunctionDecl())
- os << "Call to function '" << FD << '\'';
+ if (const FunctionDecl *FD = X.getAsFunctionDecl())
+ os << "Call to function '" << *FD << '\'';
else
os << "function call";
}
@@ -2110,7 +1876,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
if (CurrV.isOwned()) {
os << "+1 retain count";
- if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {
+ if (GCEnabled) {
assert(CurrV.getObjKind() == RetEffect::CF);
os << ". "
"Core Foundation objects are not automatically garbage collected.";
@@ -2121,19 +1887,20 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
os << "+0 retain count";
}
- PathDiagnosticLocation Pos(S, BRC.getSourceManager());
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
return new PathDiagnosticEventPiece(Pos, os.str());
}
// Gather up the effects that were performed on the object at this
// program point
- llvm::SmallVector<ArgEffect, 2> AEffects;
+ SmallVector<ArgEffect, 2> AEffects;
- if (const RetainSummary *Summ =
- TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) {
+ const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N);
+ if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) {
// We only have summaries attached to nodes after evaluating CallExpr and
// ObjCMessageExprs.
- const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
+ const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
// Iterate through the parameter expressions and see if the symbol
@@ -2166,7 +1933,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
RefVal PrevV = *PrevT;
// Specially handle -dealloc.
- if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) {
+ if (!GCEnabled && contains(AEffects, Dealloc)) {
// Determine if the object's reference count was pushed to zero.
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
// We may not have transitioned to 'release' if we hit an error.
@@ -2181,16 +1948,15 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
// Specially handle CFMakeCollectable and friends.
if (contains(AEffects, MakeCollectable)) {
// Get the name of the function.
- const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
+ const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee());
- const FunctionDecl* FD = X.getAsFunctionDecl();
- const std::string& FName = FD->getNameAsString();
+ const FunctionDecl *FD = X.getAsFunctionDecl();
- if (TF.isGCEnabled()) {
+ if (GCEnabled) {
// Determine if the object's reference count was pushed to zero.
assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
- os << "In GC mode a call to '" << FName
+ os << "In GC mode a call to '" << *FD
<< "' decrements an object's retain count and registers the "
"object with the garbage collector. ";
@@ -2205,7 +1971,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
<< '.';
}
else
- os << "When GC is not enabled a call to '" << FName
+ os << "When GC is not enabled a call to '" << *FD
<< "' has no effect on its argument.";
// Nothing more to say.
@@ -2237,7 +2003,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
os << " The object now has a +" << Count << " retain count.";
if (PrevV.getKind() == RefVal::Released) {
- assert(TF.isGCEnabled() && CurrV.getCount() > 0);
+ assert(GCEnabled && CurrV.getCount() > 0);
os << " The object is not eligible for garbage collection until the "
"retain count reaches 0 again.";
}
@@ -2262,11 +2028,11 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
}
// Emit any remaining diagnostics for the argument effects (if any).
- for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
+ for (SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
E=AEffects.end(); I != E; ++I) {
// A bunch of things have alternate behavior under GC.
- if (TF.isGCEnabled())
+ if (GCEnabled)
switch (*I) {
default: break;
case Autorelease:
@@ -2285,15 +2051,16 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
if (os.str().empty())
return 0; // We have nothing to say!
- const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
- PathDiagnosticLocation Pos(S, BRC.getSourceManager());
- PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str());
+ const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt();
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ PathDiagnosticPiece *P = new PathDiagnosticEventPiece(Pos, os.str());
// Add the range by scanning the children of the statement for any bindings
// to Sym.
for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end();
I!=E; ++I)
- if (const Expr* Exp = dyn_cast_or_null<Expr>(*I))
+ if (const Expr *Exp = dyn_cast_or_null<Expr>(*I))
if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) {
P->addRange(Exp->getSourceRange());
break;
@@ -2335,16 +2102,16 @@ namespace {
}
static std::pair<const ExplodedNode*,const MemRegion*>
-GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N,
+GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
SymbolRef Sym) {
// Find both first node that referred to the tracked symbol and the
// memory location that value was store to.
- const ExplodedNode* Last = N;
+ const ExplodedNode *Last = N;
const MemRegion* FirstBinding = 0;
while (N) {
- const GRState* St = N->getState();
+ const ProgramState *St = N->getState();
RefBindings B = St->get<RefBindings>();
if (!B.lookup(Sym))
@@ -2362,17 +2129,19 @@ GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N,
}
PathDiagnosticPiece*
-CFRefReport::getEndPath(BugReporterContext& BRC,
- const ExplodedNode* EndN) {
+CFRefReportVisitor::getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndN,
+ BugReport &BR) {
// Tell the BugReporterContext to report cases when the tracked symbol is
// assigned to different variables, etc.
BRC.addNotableSymbol(Sym);
- return RangedBugReport::getEndPath(BRC, EndN);
+ return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
}
PathDiagnosticPiece*
-CFRefLeakReport::getEndPath(BugReporterContext& BRC,
- const ExplodedNode* EndN){
+CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndN,
+ BugReport &BR) {
// Tell the BugReporterContext to report cases when the tracked symbol is
// assigned to different variables, etc.
@@ -2381,41 +2150,19 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
// We are reporting a leak. Walk up the graph to get to the first node where
// the symbol appeared, and also get the first VarDecl that tracked object
// is stored to.
- const ExplodedNode* AllocNode = 0;
+ const ExplodedNode *AllocNode = 0;
const MemRegion* FirstBinding = 0;
llvm::tie(AllocNode, FirstBinding) =
GetAllocationSite(BRC.getStateManager(), EndN, Sym);
- SourceManager& SMgr = BRC.getSourceManager();
+ SourceManager& SM = BRC.getSourceManager();
// Compute an actual location for the leak. Sometimes a leak doesn't
// occur at an actual statement (e.g., transition between blocks; end
// of function) so we need to walk the graph and compute a real location.
- const ExplodedNode* LeakN = EndN;
- PathDiagnosticLocation L;
-
- while (LeakN) {
- ProgramPoint P = LeakN->getLocation();
-
- if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
- L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr);
- break;
- }
- else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
- if (const Stmt* Term = BE->getSrc()->getTerminator()) {
- L = PathDiagnosticLocation(Term->getLocStart(), SMgr);
- break;
- }
- }
-
- LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin());
- }
-
- if (!L.isValid()) {
- const Decl &D = EndN->getCodeDecl();
- L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr);
- }
+ const ExplodedNode *LeakN = EndN;
+ PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
@@ -2454,7 +2201,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
}
}
else if (RV->getKind() == RefVal::ErrorGCLeakReturned) {
- ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
+ ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl());
os << " and returned from method '" << MD.getSelector().getAsString()
<< "' is potentially leaked when using garbage collection. Callers "
"of this method do not expect a returned object with a +1 retain "
@@ -2468,10 +2215,11 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
return new PathDiagnosticEventPiece(L, os.str());
}
-CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
- ExplodedNode *n,
- SymbolRef sym, ExprEngine& Eng)
-: CFRefReport(D, tf, n, sym) {
+CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
+ bool GCEnabled, const SummaryLogTy &Log,
+ ExplodedNode *n, SymbolRef sym,
+ ExprEngine &Eng)
+: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) {
// Most bug reports are cached at the location where they occurred.
// With leaks, we want to unique them by the location where they were
@@ -2481,41 +2229,418 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
// Note that this is *not* the trimmed graph; we are guaranteed, however,
// that all ancestor nodes that represent the allocation site have the
// same SourceLocation.
- const ExplodedNode* AllocNode = 0;
+ const ExplodedNode *AllocNode = 0;
+
+ const SourceManager& SMgr = Eng.getContext().getSourceManager();
llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
- GetAllocationSite(Eng.getStateManager(), getErrorNode(), getSymbol());
+ GetAllocationSite(Eng.getStateManager(), getErrorNode(), sym);
// Get the SourceLocation for the allocation site.
ProgramPoint P = AllocNode->getLocation();
- AllocSite = cast<PostStmt>(P).getStmt()->getLocStart();
-
+ const Stmt *AllocStmt = cast<PostStmt>(P).getStmt();
+ Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
+ n->getLocationContext());
// Fill in the description of the bug.
Description.clear();
llvm::raw_string_ostream os(Description);
- SourceManager& SMgr = Eng.getContext().getSourceManager();
- unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite);
+ unsigned AllocLine = SMgr.getExpansionLineNumber(AllocStmt->getLocStart());
os << "Potential leak ";
- if (tf.isGCEnabled()) {
+ if (GCEnabled)
os << "(when using garbage collection) ";
- }
os << "of an object allocated on line " << AllocLine;
// FIXME: AllocBinding doesn't get populated for RegionStore yet.
if (AllocBinding)
os << " and stored into '" << AllocBinding->getString() << '\'';
+
+ addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log));
}
//===----------------------------------------------------------------------===//
// Main checker logic.
//===----------------------------------------------------------------------===//
+namespace {
+class RetainCountChecker
+ : public Checker< check::Bind,
+ check::DeadSymbols,
+ check::EndAnalysis,
+ check::EndPath,
+ check::PostStmt<BlockExpr>,
+ check::PostStmt<CastExpr>,
+ check::PostStmt<CallExpr>,
+ check::PostStmt<CXXConstructExpr>,
+ check::PostObjCMessage,
+ check::PreStmt<ReturnStmt>,
+ check::RegionChanges,
+ eval::Assume,
+ eval::Call > {
+ mutable llvm::OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned;
+ mutable llvm::OwningPtr<CFRefBug> deallocGC, deallocNotOwned;
+ mutable llvm::OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned;
+ mutable llvm::OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn;
+ mutable llvm::OwningPtr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC;
+
+ typedef llvm::DenseMap<SymbolRef, const SimpleProgramPointTag *> SymbolTagMap;
+
+ // This map is only used to ensure proper deletion of any allocated tags.
+ mutable SymbolTagMap DeadSymbolTags;
+
+ mutable llvm::OwningPtr<RetainSummaryManager> Summaries;
+ mutable llvm::OwningPtr<RetainSummaryManager> SummariesGC;
+
+ mutable ARCounts::Factory ARCountFactory;
+
+ mutable SummaryLogTy SummaryLog;
+ mutable bool ShouldResetSummaryLog;
+
+public:
+ RetainCountChecker() : ShouldResetSummaryLog(false) {}
+
+ virtual ~RetainCountChecker() {
+ DeleteContainerSeconds(DeadSymbolTags);
+ }
+
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const {
+ // FIXME: This is a hack to make sure the summary log gets cleared between
+ // analyses of different code bodies.
+ //
+ // Why is this necessary? Because a checker's lifetime is tied to a
+ // translation unit, but an ExplodedGraph's lifetime is just a code body.
+ // Once in a blue moon, a new ExplodedNode will have the same address as an
+ // old one with an associated summary, and the bug report visitor gets very
+ // confused. (To make things worse, the summary lifetime is currently also
+ // tied to a code body, so we get a crash instead of incorrect results.)
+ //
+ // Why is this a bad solution? Because if the lifetime of the ExplodedGraph
+ // changes, things will start going wrong again. Really the lifetime of this
+ // log needs to be tied to either the specific nodes in it or the entire
+ // ExplodedGraph, not to a specific part of the code being analyzed.
+ //
+ // (Also, having stateful local data means that the same checker can't be
+ // used from multiple threads, but a lot of checkers have incorrect
+ // assumptions about that anyway. So that wasn't a priority at the time of
+ // this fix.)
+ //
+ // This happens at the end of analysis, but bug reports are emitted /after/
+ // this point. So we can't just clear the summary log now. Instead, we mark
+ // that the next time we access the summary log, it should be cleared.
+
+ // If we never reset the summary log during /this/ code body analysis,
+ // there were no new summaries. There might still have been summaries from
+ // the /last/ analysis, so clear them out to make sure the bug report
+ // visitors don't get confused.
+ if (ShouldResetSummaryLog)
+ SummaryLog.clear();
+
+ ShouldResetSummaryLog = !SummaryLog.empty();
+ }
+
+ CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts,
+ bool GCEnabled) const {
+ if (GCEnabled) {
+ if (!leakWithinFunctionGC)
+ leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when "
+ "using garbage "
+ "collection"));
+ return leakWithinFunctionGC.get();
+ } else {
+ if (!leakWithinFunction) {
+ if (LOpts.getGC() == LangOptions::HybridGC) {
+ leakWithinFunction.reset(new LeakWithinFunction("Leak of object when "
+ "not using garbage "
+ "collection (GC) in "
+ "dual GC/non-GC "
+ "code"));
+ } else {
+ leakWithinFunction.reset(new LeakWithinFunction("Leak"));
+ }
+ }
+ return leakWithinFunction.get();
+ }
+ }
+
+ CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
+ if (GCEnabled) {
+ if (!leakAtReturnGC)
+ leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when "
+ "using garbage collection"));
+ return leakAtReturnGC.get();
+ } else {
+ if (!leakAtReturn) {
+ if (LOpts.getGC() == LangOptions::HybridGC) {
+ leakAtReturn.reset(new LeakAtReturn("Leak of returned object when "
+ "not using garbage collection "
+ "(GC) in dual GC/non-GC code"));
+ } else {
+ leakAtReturn.reset(new LeakAtReturn("Leak of returned object"));
+ }
+ }
+ return leakAtReturn.get();
+ }
+ }
+
+ RetainSummaryManager &getSummaryManager(ASTContext &Ctx,
+ bool GCEnabled) const {
+ // FIXME: We don't support ARC being turned on and off during one analysis.
+ // (nor, for that matter, do we support changing ASTContexts)
+ bool ARCEnabled = (bool)Ctx.getLangOptions().ObjCAutoRefCount;
+ if (GCEnabled) {
+ if (!SummariesGC)
+ SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled));
+ else
+ assert(SummariesGC->isARCEnabled() == ARCEnabled);
+ return *SummariesGC;
+ } else {
+ if (!Summaries)
+ Summaries.reset(new RetainSummaryManager(Ctx, false, ARCEnabled));
+ else
+ assert(Summaries->isARCEnabled() == ARCEnabled);
+ return *Summaries;
+ }
+ }
+
+ RetainSummaryManager &getSummaryManager(CheckerContext &C) const {
+ return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled());
+ }
+
+ void printState(raw_ostream &Out, const ProgramState *State,
+ const char *NL, const char *Sep) const;
+
+ void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
+ void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
+
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
+ void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
+ CheckerContext &C) const;
+
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+
+ const ProgramState *evalAssume(const ProgramState *state, SVal Cond,
+ bool Assumption) const;
+
+ const ProgramState *
+ checkRegionChanges(const ProgramState *state,
+ const StoreManager::InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions) const;
+
+ bool wantsRegionChangeUpdate(const ProgramState *state) const {
+ return true;
+ }
+
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
+ ExplodedNode *Pred, RetEffect RE, RefVal X,
+ SymbolRef Sym, const ProgramState *state) const;
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void checkEndPath(EndOfFunctionNodeBuilder &Builder, ExprEngine &Eng) const;
+
+ const ProgramState *updateSymbol(const ProgramState *state, SymbolRef sym,
+ RefVal V, ArgEffect E, RefVal::Kind &hasErr,
+ CheckerContext &C) const;
+
+ void processNonLeakError(const ProgramState *St, SourceRange ErrorRange,
+ RefVal::Kind ErrorKind, SymbolRef Sym,
+ CheckerContext &C) const;
+
+ const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
+
+ const ProgramState *handleSymbolDeath(const ProgramState *state,
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const;
+
+ std::pair<ExplodedNode *, const ProgramState *>
+ handleAutoreleaseCounts(const ProgramState *state,
+ GenericNodeBuilderRefCount Bd, ExplodedNode *Pred,
+ ExprEngine &Eng, SymbolRef Sym, RefVal V) const;
+
+ ExplodedNode *processLeaks(const ProgramState *state,
+ SmallVectorImpl<SymbolRef> &Leaked,
+ GenericNodeBuilderRefCount &Builder,
+ ExprEngine &Eng,
+ ExplodedNode *Pred = 0) const;
+};
+} // end anonymous namespace
+
+namespace {
+class StopTrackingCallback : public SymbolVisitor {
+ const ProgramState *state;
+public:
+ StopTrackingCallback(const ProgramState *st) : state(st) {}
+ const ProgramState *getState() const { return state; }
+
+ bool VisitSymbol(SymbolRef sym) {
+ state = state->remove<RefBindings>(sym);
+ return true;
+ }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Handle statements that may have an effect on refcounts.
+//===----------------------------------------------------------------------===//
+
+void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
+ CheckerContext &C) const {
+
+ // Scan the BlockDecRefExprs for any object the retain count checker
+ // may be tracking.
+ if (!BE->getBlockDecl()->hasCaptures())
+ return;
+
+ const ProgramState *state = C.getState();
+ const BlockDataRegion *R =
+ cast<BlockDataRegion>(state->getSVal(BE).getAsRegion());
+
+ BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
+ E = R->referenced_vars_end();
+
+ if (I == E)
+ return;
+
+ // FIXME: For now we invalidate the tracking of all symbols passed to blocks
+ // via captured variables, even though captured variables result in a copy
+ // and in implicit increment/decrement of a retain count.
+ SmallVector<const MemRegion*, 10> Regions;
+ const LocationContext *LC = C.getPredecessor()->getLocationContext();
+ MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+
+ for ( ; I != E; ++I) {
+ const VarRegion *VR = *I;
+ if (VR->getSuperRegion() == R) {
+ VR = MemMgr.getVarRegion(VR->getDecl(), LC);
+ }
+ Regions.push_back(VR);
+ }
+
+ state =
+ state->scanReachableSymbols<StopTrackingCallback>(Regions.data(),
+ Regions.data() + Regions.size()).getState();
+ C.addTransition(state);
+}
+
+void RetainCountChecker::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
+ if (!BE)
+ return;
+
+ ArgEffect AE = IncRef;
+
+ switch (BE->getBridgeKind()) {
+ case clang::OBC_Bridge:
+ // Do nothing.
+ return;
+ case clang::OBC_BridgeRetained:
+ AE = IncRef;
+ break;
+ case clang::OBC_BridgeTransfer:
+ AE = DecRefBridgedTransfered;
+ break;
+ }
+
+ const ProgramState *state = C.getState();
+ SymbolRef Sym = state->getSVal(CE).getAsLocSymbol();
+ if (!Sym)
+ return;
+ const RefVal* T = state->get<RefBindings>(Sym);
+ if (!T)
+ return;
+
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ state = updateSymbol(state, Sym, *T, AE, hasErr, C);
+
+ if (hasErr) {
+ // FIXME: If we get an error during a bridge cast, should we report it?
+ // Should we assert that there is no error?
+ return;
+ }
+
+ C.generateNode(state);
+}
+
+void RetainCountChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ // Get the callee.
+ const ProgramState *state = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = state->getSVal(Callee);
+
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const RetainSummary *Summ = 0;
+
+ // FIXME: Better support for blocks. For now we stop tracking anything
+ // that is passed to blocks.
+ // FIXME: Need to handle variables that are "captured" by the block.
+ if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
+ Summ = Summaries.getPersistentStopSummary();
+ } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) {
+ Summ = Summaries.getSummary(FD);
+ } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
+ if (const CXXMethodDecl *MD = me->getMethodDecl())
+ Summ = Summaries.getSummary(MD);
+ }
+
+ if (!Summ)
+ Summ = Summaries.getDefaultSummary();
+
+ checkSummary(*Summ, CallOrObjCMessage(CE, state), C);
+}
+
+void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
+ CheckerContext &C) const {
+ const CXXConstructorDecl *Ctor = CE->getConstructor();
+ if (!Ctor)
+ return;
+
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const RetainSummary *Summ = Summaries.getSummary(Ctor);
+
+ // If we didn't get a summary, this constructor doesn't affect retain counts.
+ if (!Summ)
+ return;
+
+ const ProgramState *state = C.getState();
+ checkSummary(*Summ, CallOrObjCMessage(CE, state), C);
+}
+
+void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
+ CheckerContext &C) const {
+ const ProgramState *state = C.getState();
+ ExplodedNode *Pred = C.getPredecessor();
+
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+
+ const RetainSummary *Summ;
+ if (Msg.isInstanceMessage()) {
+ const LocationContext *LC = Pred->getLocationContext();
+ Summ = Summaries.getInstanceMethodSummary(Msg, state, LC);
+ } else {
+ Summ = Summaries.getClassMethodSummary(Msg);
+ }
+
+ // If we didn't get a summary, this message doesn't affect retain counts.
+ if (!Summ)
+ return;
+
+ checkSummary(*Summ, CallOrObjCMessage(Msg, state), C);
+}
+
/// GetReturnType - Used to get the return type of a message expression or
/// function call with the intention of affixing that type to a tracked symbol.
/// While the the return type can be queried directly from RetEx, when
/// invoking class methods we augment to the return type to be that of
/// a pointer to the class (as opposed it just being id).
-static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) {
+// FIXME: We may be able to do this with related result types instead.
+// This function is probably overestimating.
+static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
QualType RetTy = RetE->getType();
// If RetE is not a message expression just return its type.
// If RetE is a message expression, return its types if it is something
@@ -2536,167 +2661,43 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) {
return RetTy;
}
-
-// HACK: Symbols that have ref-count state that are referenced directly
-// (not as structure or array elements, or via bindings) by an argument
-// should not have their ref-count state stripped after we have
-// done an invalidation pass.
-//
-// FIXME: This is a global to currently share between CFRefCount and
-// RetainReleaseChecker. Eventually all functionality in CFRefCount should
-// be migrated to RetainReleaseChecker, and we can make this a non-global.
-llvm::DenseSet<SymbolRef> WhitelistedSymbols;
-namespace {
-struct ResetWhiteList {
- ResetWhiteList() {}
- ~ResetWhiteList() { WhitelistedSymbols.clear(); }
-};
-}
-
-void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- const Expr* Ex,
- const CallOrObjCMessage &callOrMsg,
- InstanceReceiver Receiver,
- const RetainSummary& Summ,
- const MemRegion *Callee,
- ExplodedNode* Pred, const GRState *state) {
+void RetainCountChecker::checkSummary(const RetainSummary &Summ,
+ const CallOrObjCMessage &CallOrMsg,
+ CheckerContext &C) const {
+ const ProgramState *state = C.getState();
// Evaluate the effect of the arguments.
RefVal::Kind hasErr = (RefVal::Kind) 0;
SourceRange ErrorRange;
SymbolRef ErrorSym = 0;
- llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate;
-
- // Use RAII to make sure the whitelist is properly cleared.
- ResetWhiteList resetWhiteList;
+ for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
+ SVal V = CallOrMsg.getArgSVal(idx);
- // Invalidate all instance variables of the receiver of a message.
- // FIXME: We should be able to do better with inter-procedural analysis.
- if (Receiver) {
- SVal V = Receiver.getSValAsScalarOrLoc(state);
if (SymbolRef Sym = V.getAsLocSymbol()) {
- if (state->get<RefBindings>(Sym))
- WhitelistedSymbols.insert(Sym);
- }
- if (const MemRegion *region = V.getAsRegion())
- RegionsToInvalidate.push_back(region);
- }
-
- // 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 (callOrMsg.isCXXCall()) {
- if (const MemRegion *callee = callOrMsg.getCXXCallee().getAsRegion())
- RegionsToInvalidate.push_back(callee);
- }
-
- for (unsigned idx = 0, e = callOrMsg.getNumArgs(); idx != e; ++idx) {
- SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx);
- SymbolRef Sym = V.getAsLocSymbol();
-
- if (Sym)
- if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) {
- WhitelistedSymbols.insert(Sym);
- state = Update(state, Sym, *T, Summ.getArg(idx), hasErr);
+ if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) {
+ state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C);
if (hasErr) {
- ErrorRange = callOrMsg.getArgSourceRange(idx);
+ ErrorRange = CallOrMsg.getArgSourceRange(idx);
ErrorSym = Sym;
break;
}
}
-
- tryAgain:
- if (isa<Loc>(V)) {
- if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) {
- if (Summ.getArg(idx) == DoNothingByRef)
- continue;
-
- // Invalidate the value of the variable passed by reference.
- const MemRegion *R = MR->getRegion();
-
- // Are we dealing with an ElementRegion? If the element type is
- // a basic integer type (e.g., char, int) and the underying 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
- // approriately 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);
- continue;
- }
- 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));
- }
}
- else if (isa<nonloc::LocAsInteger>(V)) {
- // If we are passing a location wrapped as an integer, unwrap it and
- // invalidate the values referred by the location.
- V = cast<nonloc::LocAsInteger>(V).getLoc();
- goto tryAgain;
- }
- }
-
- // Block calls result in all captured values passed-via-reference to be
- // invalidated.
- if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee)) {
- RegionsToInvalidate.push_back(BR);
}
- // Invalidate regions we designed for invalidation use the batch invalidation
- // API.
-
- // 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 = Builder.getCurrentBlockCount();
- StoreManager::InvalidatedSymbols IS;
-
- // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
- // global variables.
- // NOTE: RetainReleaseChecker handles the actual invalidation of symbols.
- state =
- state->invalidateRegions(RegionsToInvalidate.data(),
- RegionsToInvalidate.data() +
- RegionsToInvalidate.size(),
- Ex, Count, &IS,
- /* invalidateGlobals = */
- Eng.doesInvalidateGlobals(callOrMsg));
-
// Evaluate the effect on the message receiver.
- if (!ErrorRange.isValid() && Receiver) {
- SymbolRef Sym = Receiver.getSValAsScalarOrLoc(state).getAsLocSymbol();
- if (Sym) {
- if (const RefVal* T = state->get<RefBindings>(Sym)) {
- state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr);
+ bool ReceiverIsTracked = false;
+ if (!hasErr && CallOrMsg.isObjCMessage()) {
+ const LocationContext *LC = C.getPredecessor()->getLocationContext();
+ SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC);
+ if (SymbolRef Sym = Receiver.getAsLocSymbol()) {
+ if (const RefVal *T = state->get<RefBindings>(Sym)) {
+ ReceiverIsTracked = true;
+ state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
+ hasErr, C);
if (hasErr) {
- ErrorRange = Receiver.getSourceRange();
+ ErrorRange = CallOrMsg.getReceiverSourceRange();
ErrorSym = Sym;
}
}
@@ -2705,8 +2706,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
// Process any errors.
if (hasErr) {
- ProcessNonLeakError(Dst, Builder, Ex, ErrorRange, Pred, state,
- hasErr, ErrorSym);
+ processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C);
return;
}
@@ -2714,75 +2714,34 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
RetEffect RE = Summ.getRetEffect();
if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
- bool found = false;
- if (Receiver) {
- SVal V = Receiver.getSValAsScalarOrLoc(state);
- if (SymbolRef Sym = V.getAsLocSymbol())
- if (state->get<RefBindings>(Sym)) {
- found = true;
- RE = Summaries.getObjAllocRetEffect();
- }
- } // FIXME: Otherwise, this is a send-to-super instance message.
- if (!found)
+ if (ReceiverIsTracked)
+ RE = getSummaryManager(C).getObjAllocRetEffect();
+ else
RE = RetEffect::MakeNoRet();
}
switch (RE.getKind()) {
default:
- assert (false && "Unhandled RetEffect."); break;
-
- case RetEffect::NoRet: {
- // Make up a symbol for the return value (not reference counted).
- // FIXME: Most of this logic is not specific to the retain/release
- // checker.
-
- // FIXME: We eventually should handle structs and other compound types
- // that are returned by value.
-
- // Use the result type from callOrMsg as it automatically adjusts
- // for methods/functions that return references.
- QualType resultTy = callOrMsg.getResultType(Eng.getContext());
- if (Loc::isLocType(resultTy) ||
- (resultTy->isIntegerType() && resultTy->isScalarType())) {
- unsigned Count = Builder.getCurrentBlockCount();
- SValBuilder &svalBuilder = Eng.getSValBuilder();
- SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, resultTy, Count);
- state = state->BindExpr(Ex, X, false);
- }
-
- break;
- }
-
- case RetEffect::Alias: {
- unsigned idx = RE.getIndex();
- assert (idx < callOrMsg.getNumArgs());
- SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx);
- state = state->BindExpr(Ex, V, false);
- break;
- }
+ llvm_unreachable("Unhandled RetEffect."); break;
- case RetEffect::ReceiverAlias: {
- assert(Receiver);
- SVal V = Receiver.getSValAsScalarOrLoc(state);
- state = state->BindExpr(Ex, V, false);
+ case RetEffect::NoRet:
+ // No work necessary.
break;
- }
case RetEffect::OwnedAllocatedSymbol:
case RetEffect::OwnedSymbol: {
- unsigned Count = Builder.getCurrentBlockCount();
- SValBuilder &svalBuilder = Eng.getSValBuilder();
- SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count);
+ SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr()).getAsSymbol();
+ if (!Sym)
+ break;
// Use the result type from callOrMsg as it automatically adjusts
- // for methods/functions that return references.
- QualType resultTy = callOrMsg.getResultType(Eng.getContext());
+ // for methods/functions that return references.
+ QualType ResultTy = CallOrMsg.getResultType(C.getASTContext());
state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
- resultTy));
- state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false);
+ ResultTy));
// FIXME: Add a flag to the checker where allocations are assumed to
- // *not fail.
+ // *not* fail. (The code below is out-of-date, though.)
#if 0
if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) {
bool isFeasible;
@@ -2797,151 +2756,324 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
case RetEffect::GCNotOwnedSymbol:
case RetEffect::ARCNotOwnedSymbol:
case RetEffect::NotOwnedSymbol: {
- unsigned Count = Builder.getCurrentBlockCount();
- SValBuilder &svalBuilder = Eng.getSValBuilder();
- SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count);
- QualType RetT = GetReturnType(Ex, svalBuilder.getContext());
+ const Expr *Ex = CallOrMsg.getOriginExpr();
+ SymbolRef Sym = state->getSVal(Ex).getAsSymbol();
+ if (!Sym)
+ break;
+
+ // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *.
+ QualType ResultTy = GetReturnType(Ex, C.getASTContext());
state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),
- RetT));
- state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false);
+ ResultTy));
break;
}
}
- // Generate a sink node if we are at the end of a path.
- ExplodedNode *NewNode =
- Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state)
- : Builder.MakeNode(Dst, Ex, Pred, state);
+ // This check is actually necessary; otherwise the statement builder thinks
+ // we've hit a previously-found path.
+ // Normally addTransition takes care of this, but we want the node pointer.
+ ExplodedNode *NewNode;
+ if (state == C.getState()) {
+ NewNode = C.getPredecessor();
+ } else {
+ NewNode = C.generateNode(state);
+ }
- // Annotate the edge with summary we used.
- if (NewNode) SummaryLog[NewNode] = &Summ;
+ // Annotate the node with summary we used.
+ if (NewNode) {
+ // FIXME: This is ugly. See checkEndAnalysis for why it's necessary.
+ if (ShouldResetSummaryLog) {
+ SummaryLog.clear();
+ ShouldResetSummaryLog = false;
+ }
+ SummaryLog[NewNode] = &Summ;
+ }
}
-void CFRefCount::evalCall(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- const CallExpr* CE, SVal L,
- ExplodedNode* Pred) {
-
- RetainSummary *Summ = 0;
+const ProgramState *
+RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym,
+ RefVal V, ArgEffect E, RefVal::Kind &hasErr,
+ CheckerContext &C) const {
+ // In GC mode [... release] and [... retain] do nothing.
+ // In ARC mode they shouldn't exist at all, but we just ignore them.
+ bool IgnoreRetainMsg = C.isObjCGCEnabled();
+ if (!IgnoreRetainMsg)
+ IgnoreRetainMsg = (bool)C.getASTContext().getLangOptions().ObjCAutoRefCount;
- // FIXME: Better support for blocks. For now we stop tracking anything
- // that is passed to blocks.
- // FIXME: Need to handle variables that are "captured" by the block.
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- Summ = Summaries.getPersistentStopSummary();
- }
- else if (const FunctionDecl* FD = L.getAsFunctionDecl()) {
- Summ = Summaries.getSummary(FD);
+ switch (E) {
+ default: break;
+ case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break;
+ case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break;
+ case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break;
+ case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing :
+ NewAutoreleasePool; break;
}
- else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- if (const CXXMethodDecl *MD = me->getMethodDecl())
- Summ = Summaries.getSummary(MD);
- else
- Summ = Summaries.getDefaultSummary();
+
+ // Handle all use-after-releases.
+ if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) {
+ V = V ^ RefVal::ErrorUseAfterRelease;
+ hasErr = V.getKind();
+ return state->set<RefBindings>(sym, V);
}
- else
- Summ = Summaries.getDefaultSummary();
- assert(Summ);
- evalSummary(Dst, Eng, Builder, CE,
- CallOrObjCMessage(CE, Builder.GetState(Pred)),
- InstanceReceiver(), *Summ,L.getAsRegion(),
- Pred, Builder.GetState(Pred));
-}
+ switch (E) {
+ case DecRefMsg:
+ case IncRefMsg:
+ case MakeCollectable:
+ llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted");
+ return state;
-void CFRefCount::evalObjCMessage(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- ObjCMessage msg,
- ExplodedNode* Pred,
- const GRState *state) {
- RetainSummary *Summ =
- msg.isInstanceMessage()
- ? Summaries.getInstanceMethodSummary(msg, state,Pred->getLocationContext())
- : Summaries.getClassMethodSummary(msg);
-
- assert(Summ && "RetainSummary is null");
- evalSummary(Dst, Eng, Builder, msg.getOriginExpr(),
- CallOrObjCMessage(msg, Builder.GetState(Pred)),
- InstanceReceiver(msg, Pred->getLocationContext()), *Summ, NULL,
- Pred, state);
-}
+ case Dealloc:
+ // Any use of -dealloc in GC is *bad*.
+ if (C.isObjCGCEnabled()) {
+ V = V ^ RefVal::ErrorDeallocGC;
+ hasErr = V.getKind();
+ break;
+ }
-namespace {
-class StopTrackingCallback : public SymbolVisitor {
- const GRState *state;
-public:
- StopTrackingCallback(const GRState *st) : state(st) {}
- const GRState *getState() const { return state; }
+ switch (V.getKind()) {
+ default:
+ llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
+ break;
+ case RefVal::Owned:
+ // The object immediately transitions to the released state.
+ V = V ^ RefVal::Released;
+ V.clearCounts();
+ return state->set<RefBindings>(sym, V);
+ case RefVal::NotOwned:
+ V = V ^ RefVal::ErrorDeallocNotOwned;
+ hasErr = V.getKind();
+ break;
+ }
+ break;
- bool VisitSymbol(SymbolRef sym) {
- state = state->remove<RefBindings>(sym);
- return true;
- }
-};
-} // end anonymous namespace
+ case NewAutoreleasePool:
+ assert(!C.isObjCGCEnabled());
+ return state->add<AutoreleaseStack>(sym);
+ case MayEscape:
+ if (V.getKind() == RefVal::Owned) {
+ V = V ^ RefVal::NotOwned;
+ break;
+ }
-void CFRefCount::evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) {
- // Are we storing to something that causes the value to "escape"?
- bool escapes = false;
+ // Fall-through.
- // A value escapes in three possible cases (this may change):
- //
- // (1) we are binding to something that is not a memory region.
- // (2) we are binding to a memregion that does not have stack storage
- // (3) we are binding to a memregion with stack storage that the store
- // does not understand.
- const GRState *state = B.getState();
+ case DoNothing:
+ return state;
- if (!isa<loc::MemRegionVal>(location))
- escapes = true;
- else {
- const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion();
- escapes = !R->hasStackStorage();
+ case Autorelease:
+ if (C.isObjCGCEnabled())
+ return state;
- if (!escapes) {
- // To test (3), generate a new state with the binding removed. If it is
- // the same state, then it escapes (since the store cannot represent
- // the binding).
- escapes = (state == (state->bindLoc(cast<Loc>(location), UnknownVal())));
- }
+ // Update the autorelease counts.
+ state = SendAutorelease(state, ARCountFactory, sym);
+ V = V.autorelease();
+ break;
+
+ case StopTracking:
+ return state->remove<RefBindings>(sym);
+
+ case IncRef:
+ switch (V.getKind()) {
+ default:
+ llvm_unreachable("Invalid RefVal state for a retain.");
+ break;
+ case RefVal::Owned:
+ case RefVal::NotOwned:
+ V = V + 1;
+ break;
+ case RefVal::Released:
+ // Non-GC cases are handled above.
+ assert(C.isObjCGCEnabled());
+ V = (V ^ RefVal::Owned) + 1;
+ break;
+ }
+ break;
+
+ case SelfOwn:
+ V = V ^ RefVal::NotOwned;
+ // Fall-through.
+ case DecRef:
+ case DecRefBridgedTransfered:
+ switch (V.getKind()) {
+ default:
+ // case 'RefVal::Released' handled above.
+ llvm_unreachable("Invalid RefVal state for a release.");
+ break;
+
+ case RefVal::Owned:
+ assert(V.getCount() > 0);
+ if (V.getCount() == 1)
+ V = V ^ (E == DecRefBridgedTransfered ?
+ RefVal::NotOwned : RefVal::Released);
+ V = V - 1;
+ break;
+
+ case RefVal::NotOwned:
+ if (V.getCount() > 0)
+ V = V - 1;
+ else {
+ V = V ^ RefVal::ErrorReleaseNotOwned;
+ hasErr = V.getKind();
+ }
+ break;
+
+ case RefVal::Released:
+ // Non-GC cases are handled above.
+ assert(C.isObjCGCEnabled());
+ V = V ^ RefVal::ErrorUseAfterRelease;
+ hasErr = V.getKind();
+ break;
+ }
+ break;
}
+ return state->set<RefBindings>(sym, V);
+}
- // If our store can represent the binding and we aren't storing to something
- // that doesn't have local storage then just return and have the simulation
- // state continue as is.
- if (!escapes)
+void RetainCountChecker::processNonLeakError(const ProgramState *St,
+ SourceRange ErrorRange,
+ RefVal::Kind ErrorKind,
+ SymbolRef Sym,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.generateSink(St);
+ if (!N)
+ return;
+
+ CFRefBug *BT;
+ switch (ErrorKind) {
+ default:
+ llvm_unreachable("Unhandled error.");
return;
+ case RefVal::ErrorUseAfterRelease:
+ if (!useAfterRelease)
+ useAfterRelease.reset(new UseAfterRelease());
+ BT = &*useAfterRelease;
+ break;
+ case RefVal::ErrorReleaseNotOwned:
+ if (!releaseNotOwned)
+ releaseNotOwned.reset(new BadRelease());
+ BT = &*releaseNotOwned;
+ break;
+ case RefVal::ErrorDeallocGC:
+ if (!deallocGC)
+ deallocGC.reset(new DeallocGC());
+ BT = &*deallocGC;
+ break;
+ case RefVal::ErrorDeallocNotOwned:
+ if (!deallocNotOwned)
+ deallocNotOwned.reset(new DeallocNotOwned());
+ BT = &*deallocNotOwned;
+ break;
+ }
- // Otherwise, find all symbols referenced by 'val' that we are tracking
- // and stop tracking them.
- B.MakeNode(state->scanReachableSymbols<StopTrackingCallback>(val).getState());
+ assert(BT);
+ CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOptions(),
+ C.isObjCGCEnabled(), SummaryLog,
+ N, Sym);
+ report->addRange(ErrorRange);
+ C.EmitReport(report);
}
- // Return statements.
+//===----------------------------------------------------------------------===//
+// Handle the return values of retain-count-related functions.
+//===----------------------------------------------------------------------===//
+
+bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
+ // Get the callee. We're only interested in simple C functions.
+ const ProgramState *state = C.getState();
+ const Expr *Callee = CE->getCallee();
+ SVal L = state->getSVal(Callee);
-void CFRefCount::evalReturn(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- const ReturnStmt* S,
- ExplodedNode* Pred) {
+ const FunctionDecl *FD = L.getAsFunctionDecl();
+ if (!FD)
+ return false;
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return false;
+
+ // For now, we're only handling the functions that return aliases of their
+ // arguments: CFRetain and CFMakeCollectable (and their families).
+ // Eventually we should add other functions we can model entirely,
+ // such as CFRelease, which don't invalidate their arguments or globals.
+ if (CE->getNumArgs() != 1)
+ return false;
+
+ // Get the name of the function.
+ StringRef FName = II->getName();
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ // See if it's one of the specific functions we know how to eval.
+ bool canEval = false;
+
+ QualType ResultTy = FD->getResultType();
+ if (ResultTy->isObjCIdType()) {
+ // Handle: id NSMakeCollectable(CFTypeRef)
+ canEval = II->isStr("NSMakeCollectable");
+ } else if (ResultTy->isPointerType()) {
+ // Handle: (CF|CG)Retain
+ // CFMakeCollectable
+ // It's okay to be a little sloppy here (CGMakeCollectable doesn't exist).
+ if (cocoa::isRefType(ResultTy, "CF", FName) ||
+ cocoa::isRefType(ResultTy, "CG", FName)) {
+ canEval = isRetain(FD, FName) || isMakeCollectable(FD, FName);
+ }
+ }
+
+ if (!canEval)
+ return false;
+
+ // Bind the return value.
+ SVal RetVal = state->getSVal(CE->getArg(0));
+ if (RetVal.isUnknown()) {
+ // If the receiver is unknown, conjure a return value.
+ SValBuilder &SVB = C.getSValBuilder();
+ unsigned Count = C.getCurrentBlockCount();
+ SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count);
+ }
+ state = state->BindExpr(CE, RetVal, false);
+
+ // FIXME: This should not be necessary, but otherwise the argument seems to be
+ // considered alive during the next statement.
+ if (const MemRegion *ArgRegion = RetVal.getAsRegion()) {
+ // Save the refcount status of the argument.
+ SymbolRef Sym = RetVal.getAsLocSymbol();
+ RefBindings::data_type *Binding = 0;
+ if (Sym)
+ Binding = state->get<RefBindings>(Sym);
+
+ // Invalidate the argument region.
+ unsigned Count = C.getCurrentBlockCount();
+ state = state->invalidateRegions(ArgRegion, CE, Count);
+
+ // Restore the refcount status of the argument.
+ if (Binding)
+ state = state->set<RefBindings>(Sym, *Binding);
+ }
+
+ C.addTransition(state);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Handle return statements.
+//===----------------------------------------------------------------------===//
- const Expr* RetE = S->getRetValue();
+void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ const Expr *RetE = S->getRetValue();
if (!RetE)
return;
- const GRState *state = Builder.GetState(Pred);
+ const ProgramState *state = C.getState();
SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol();
-
if (!Sym)
return;
// Get the reference count binding (if any).
- const RefVal* T = state->get<RefBindings>(Sym);
-
+ const RefVal *T = state->get<RefBindings>(Sym);
if (!T)
return;
@@ -2951,7 +3083,7 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst,
switch (X.getKind()) {
case RefVal::Owned: {
unsigned cnt = X.getCount();
- assert (cnt > 0);
+ assert(cnt > 0);
X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
break;
@@ -2975,21 +3107,25 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst,
// Update the binding.
state = state->set<RefBindings>(Sym, X);
- Pred = Builder.MakeNode(Dst, S, Pred, state);
+ ExplodedNode *Pred = C.generateNode(state);
+
+ // At this point we have updated the state properly.
+ // Everything after this is merely checking to see if the return value has
+ // been over- or under-retained.
// Did we cache out?
if (!Pred)
return;
// Update the autorelease counts.
- static unsigned autoreleasetag = 0;
- GenericNodeBuilderRefCount Bd(Builder, S, &autoreleasetag);
- bool stop = false;
- llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym,
- X, stop);
+ static SimpleProgramPointTag
+ AutoreleaseTag("RetainCountChecker : Autorelease");
+ GenericNodeBuilderRefCount Bd(C, &AutoreleaseTag);
+ llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred,
+ C.getEngine(), Sym, X);
// Did we cache out?
- if (!Pred || stop)
+ if (!Pred)
return;
// Get the updated binding.
@@ -2998,36 +3134,37 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst,
X = *T;
// Consult the summary of the enclosing method.
- Decl const *CD = &Pred->getCodeDecl();
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const Decl *CD = &Pred->getCodeDecl();
- if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
- const RetainSummary &Summ = *Summaries.getMethodSummary(MD);
- return evalReturnWithRetEffect(Dst, Eng, Builder, S,
- Pred, Summ.getRetEffect(), X,
- Sym, state);
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
+ // Unlike regular functions, /all/ ObjC methods are assumed to always
+ // follow Cocoa retain-count conventions, not just those with special
+ // names or attributes.
+ const RetainSummary *Summ = Summaries.getMethodSummary(MD);
+ RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
+ checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
}
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
if (!isa<CXXMethodDecl>(FD))
if (const RetainSummary *Summ = Summaries.getSummary(FD))
- return evalReturnWithRetEffect(Dst, Eng, Builder, S,
- Pred, Summ->getRetEffect(), X,
- Sym, state);
+ checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X,
+ Sym, state);
}
}
-void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst,
- ExprEngine &Eng,
- StmtNodeBuilder &Builder,
- const ReturnStmt *S,
- ExplodedNode *Pred,
- RetEffect RE, RefVal X,
- SymbolRef Sym, const GRState *state) {
+void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
+ CheckerContext &C,
+ ExplodedNode *Pred,
+ RetEffect RE, RefVal X,
+ SymbolRef Sym,
+ const ProgramState *state) const {
// Any leaks or other errors?
if (X.isReturnedOwned() && X.getCount() == 0) {
if (RE.getKind() != RetEffect::NoRet) {
bool hasError = false;
- if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) {
+ if (C.isObjCGCEnabled() && RE.getObjKind() == RetEffect::ObjC) {
// Things are more complicated with garbage collection. If the
// returned object is suppose to be an Objective-C object, we have
// a leak (as the caller expects a GC'ed object) because no
@@ -3045,46 +3182,88 @@ void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst,
if (hasError) {
// Generate an error node.
- static int ReturnOwnLeakTag = 0;
state = state->set<RefBindings>(Sym, X);
- ExplodedNode *N =
- Builder.generateNode(PostStmt(S, Pred->getLocationContext(),
- &ReturnOwnLeakTag), state, Pred);
+
+ static SimpleProgramPointTag
+ ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak");
+ ExplodedNode *N = C.generateNode(state, Pred, &ReturnOwnLeakTag);
if (N) {
+ const LangOptions &LOpts = C.getASTContext().getLangOptions();
+ bool GCEnabled = C.isObjCGCEnabled();
CFRefReport *report =
- new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this,
- N, Sym, Eng);
- BR->EmitReport(report);
+ new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled),
+ LOpts, GCEnabled, SummaryLog,
+ N, Sym, C.getEngine());
+ C.EmitReport(report);
}
}
}
- return;
- }
-
- if (X.isReturnedNotOwned()) {
+ } else if (X.isReturnedNotOwned()) {
if (RE.isOwned()) {
// Trying to return a not owned object to a caller expecting an
// owned object.
-
- static int ReturnNotOwnedForOwnedTag = 0;
state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
- if (ExplodedNode *N =
- Builder.generateNode(PostStmt(S, Pred->getLocationContext(),
- &ReturnNotOwnedForOwnedTag),
- state, Pred)) {
- CFRefReport *report =
- new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned),
- *this, N, Sym);
- BR->EmitReport(report);
+
+ static SimpleProgramPointTag
+ ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned");
+ ExplodedNode *N = C.generateNode(state, Pred, &ReturnNotOwnedTag);
+ if (N) {
+ if (!returnNotOwnedForOwned)
+ returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned());
+
+ CFRefReport *report =
+ new CFRefReport(*returnNotOwnedForOwned,
+ C.getASTContext().getLangOptions(),
+ C.isObjCGCEnabled(), SummaryLog, N, Sym);
+ C.EmitReport(report);
}
}
}
}
-// Assumptions.
+//===----------------------------------------------------------------------===//
+// Check various ways a symbol can be invalidated.
+//===----------------------------------------------------------------------===//
+
+void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
+ CheckerContext &C) const {
+ // Are we storing to something that causes the value to "escape"?
+ bool escapes = true;
+
+ // A value escapes in three possible cases (this may change):
+ //
+ // (1) we are binding to something that is not a memory region.
+ // (2) we are binding to a memregion that does not have stack storage
+ // (3) we are binding to a memregion with stack storage that the store
+ // does not understand.
+ const ProgramState *state = C.getState();
+
+ if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) {
+ escapes = !regionLoc->getRegion()->hasStackStorage();
+
+ if (!escapes) {
+ // To test (3), generate a new state with the binding added. If it is
+ // the same state, then it escapes (since the store cannot represent
+ // the binding).
+ escapes = (state == (state->bindLoc(*regionLoc, val)));
+ }
+ }
+
+ // If our store can represent the binding and we aren't storing to something
+ // that doesn't have local storage then just return and have the simulation
+ // state continue as is.
+ if (!escapes)
+ return;
+
+ // Otherwise, find all symbols referenced by 'val' that we are tracking
+ // and stop tracking them.
+ state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
+ C.addTransition(state);
+}
-const GRState* CFRefCount::evalAssume(const GRState *state,
- SVal Cond, bool Assumption) {
+const ProgramState *RetainCountChecker::evalAssume(const ProgramState *state,
+ SVal Cond,
+ bool Assumption) const {
// FIXME: We may add to the interface of evalAssume the list of symbols
// whose assumptions have changed. For now we just iterate through the
@@ -3098,9 +3277,9 @@ const GRState* CFRefCount::evalAssume(const GRState *state,
return state;
bool changed = false;
- RefBindings::Factory& RefBFactory = state->get_context<RefBindings>();
+ RefBindings::Factory &RefBFactory = state->get_context<RefBindings>();
- for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
+ for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
// Check if the symbol is null (or equal to any constant).
// If this is the case, stop tracking the symbol.
if (state->getSymVal(I.getKey())) {
@@ -3115,161 +3294,48 @@ const GRState* CFRefCount::evalAssume(const GRState *state,
return state;
}
-const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
- RefVal V, ArgEffect E,
- RefVal::Kind& hasErr) {
-
- // In GC mode [... release] and [... retain] do nothing.
- switch (E) {
- default: break;
- case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break;
- case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break;
- case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;
- case NewAutoreleasePool: E = isGCEnabled() ? DoNothing :
- NewAutoreleasePool; break;
- }
+const ProgramState *
+RetainCountChecker::checkRegionChanges(const ProgramState *state,
+ const StoreManager::InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions) const {
+ if (!invalidated)
+ return state;
- // Handle all use-after-releases.
- if (!isGCEnabled() && V.getKind() == RefVal::Released) {
- V = V ^ RefVal::ErrorUseAfterRelease;
- hasErr = V.getKind();
- return state->set<RefBindings>(sym, V);
+ llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
+ for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
+ E = ExplicitRegions.end(); I != E; ++I) {
+ if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>())
+ WhitelistedSymbols.insert(SR->getSymbol());
}
- switch (E) {
- case DecRefMsg:
- case IncRefMsg:
- case MakeCollectable:
- assert(false &&
- "DecRefMsg/IncRefMsg/MakeCollectable already transformed");
- return state;
-
- case Dealloc:
- // Any use of -dealloc in GC is *bad*.
- if (isGCEnabled()) {
- V = V ^ RefVal::ErrorDeallocGC;
- hasErr = V.getKind();
- break;
- }
-
- switch (V.getKind()) {
- default:
- assert(false && "Invalid case.");
- case RefVal::Owned:
- // The object immediately transitions to the released state.
- V = V ^ RefVal::Released;
- V.clearCounts();
- return state->set<RefBindings>(sym, V);
- case RefVal::NotOwned:
- V = V ^ RefVal::ErrorDeallocNotOwned;
- hasErr = V.getKind();
- break;
- }
- break;
-
- case NewAutoreleasePool:
- assert(!isGCEnabled());
- return state->add<AutoreleaseStack>(sym);
-
- case MayEscape:
- if (V.getKind() == RefVal::Owned) {
- V = V ^ RefVal::NotOwned;
- break;
- }
-
- // Fall-through.
-
- case DoNothingByRef:
- case DoNothing:
- return state;
-
- case Autorelease:
- if (isGCEnabled())
- return state;
-
- // Update the autorelease counts.
- state = SendAutorelease(state, ARCountFactory, sym);
- V = V.autorelease();
- break;
-
- case StopTracking:
- return state->remove<RefBindings>(sym);
-
- case IncRef:
- switch (V.getKind()) {
- default:
- assert(false);
-
- case RefVal::Owned:
- case RefVal::NotOwned:
- V = V + 1;
- break;
- case RefVal::Released:
- // Non-GC cases are handled above.
- assert(isGCEnabled());
- V = (V ^ RefVal::Owned) + 1;
- break;
- }
- break;
-
- case SelfOwn:
- V = V ^ RefVal::NotOwned;
- // Fall-through.
- case DecRef:
- case DecRefBridgedTransfered:
- switch (V.getKind()) {
- default:
- // case 'RefVal::Released' handled above.
- assert (false);
-
- case RefVal::Owned:
- assert(V.getCount() > 0);
- if (V.getCount() == 1)
- V = V ^ (E == DecRefBridgedTransfered ?
- RefVal::NotOwned : RefVal::Released);
- V = V - 1;
- break;
-
- case RefVal::NotOwned:
- if (V.getCount() > 0)
- V = V - 1;
- else {
- V = V ^ RefVal::ErrorReleaseNotOwned;
- hasErr = V.getKind();
- }
- break;
-
- case RefVal::Released:
- // Non-GC cases are handled above.
- assert(isGCEnabled());
- V = V ^ RefVal::ErrorUseAfterRelease;
- hasErr = V.getKind();
- break;
- }
- break;
+ for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(),
+ E = invalidated->end(); I!=E; ++I) {
+ SymbolRef sym = *I;
+ if (WhitelistedSymbols.count(sym))
+ continue;
+ // Remove any existing reference-count binding.
+ state = state->remove<RefBindings>(sym);
}
- return state->set<RefBindings>(sym, V);
+ return state;
}
//===----------------------------------------------------------------------===//
// Handle dead symbols and end-of-path.
//===----------------------------------------------------------------------===//
-std::pair<ExplodedNode*, const GRState *>
-CFRefCount::HandleAutoreleaseCounts(const GRState * state,
- GenericNodeBuilderRefCount Bd,
- ExplodedNode* Pred,
- ExprEngine &Eng,
- SymbolRef Sym, RefVal V, bool &stop) {
-
+std::pair<ExplodedNode *, const ProgramState *>
+RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state,
+ GenericNodeBuilderRefCount Bd,
+ ExplodedNode *Pred, ExprEngine &Eng,
+ SymbolRef Sym, RefVal V) const {
unsigned ACnt = V.getAutoreleaseCount();
- stop = false;
// No autorelease counts? Nothing to be done.
if (!ACnt)
return std::make_pair(Pred, state);
- assert(!isGCEnabled() && "Autorelease counts in GC mode?");
+ assert(!Eng.isObjCGCEnabled() && "Autorelease counts in GC mode?");
unsigned Cnt = V.getCount();
// FIXME: Handle sending 'autorelease' to already released object.
@@ -3284,48 +3350,54 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state,
V = V ^ RefVal::ReturnedNotOwned;
else
V = V ^ RefVal::NotOwned;
- }
- else {
+ } else {
V.setCount(Cnt - ACnt);
V.setAutoreleaseCount(0);
}
state = state->set<RefBindings>(Sym, V);
ExplodedNode *N = Bd.MakeNode(state, Pred);
- stop = (N == 0);
+ if (N == 0)
+ state = 0;
return std::make_pair(N, state);
}
// Woah! More autorelease counts then retain counts left.
// Emit hard error.
- stop = true;
V = V ^ RefVal::ErrorOverAutorelease;
state = state->set<RefBindings>(Sym, V);
if (ExplodedNode *N = Bd.MakeNode(state, Pred)) {
N->markAsSink();
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
+ llvm::SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
os << "Object over-autoreleased: object was sent -autorelease ";
if (V.getAutoreleaseCount() > 1)
os << V.getAutoreleaseCount() << " times ";
os << "but the object has a +" << V.getCount() << " retain count";
+ if (!overAutorelease)
+ overAutorelease.reset(new OverAutorelease());
+
+ const LangOptions &LOpts = Eng.getContext().getLangOptions();
CFRefReport *report =
- new CFRefReport(*static_cast<CFRefBug*>(overAutorelease),
- *this, N, Sym, os.str());
- BR->EmitReport(report);
+ new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false,
+ SummaryLog, N, Sym, os.str());
+ Eng.getBugReporter().EmitReport(report);
}
- return std::make_pair((ExplodedNode*)0, state);
+ return std::make_pair((ExplodedNode *)0, (const ProgramState *)0);
}
-const GRState *
-CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V,
- llvm::SmallVectorImpl<SymbolRef> &Leaked) {
-
- bool hasLeak = V.isOwned() ||
- ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
+const ProgramState *
+RetainCountChecker::handleSymbolDeath(const ProgramState *state,
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const {
+ bool hasLeak = false;
+ if (V.isOwned())
+ hasLeak = true;
+ else if (V.isNotOwned() || V.isReturnedOwned())
+ hasLeak = (V.getCount() > 0);
if (!hasLeak)
return state->remove<RefBindings>(sid);
@@ -3334,13 +3406,11 @@ CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V,
return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
}
-ExplodedNode*
-CFRefCount::ProcessLeaks(const GRState * state,
- llvm::SmallVectorImpl<SymbolRef> &Leaked,
- GenericNodeBuilderRefCount &Builder,
- ExprEngine& Eng,
- ExplodedNode *Pred) {
-
+ExplodedNode *
+RetainCountChecker::processLeaks(const ProgramState *state,
+ SmallVectorImpl<SymbolRef> &Leaked,
+ GenericNodeBuilderRefCount &Builder,
+ ExprEngine &Eng, ExplodedNode *Pred) const {
if (Leaked.empty())
return Pred;
@@ -3348,85 +3418,94 @@ CFRefCount::ProcessLeaks(const GRState * state,
ExplodedNode *N = Builder.MakeNode(state, Pred);
if (N) {
- for (llvm::SmallVectorImpl<SymbolRef>::iterator
+ for (SmallVectorImpl<SymbolRef>::iterator
I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
- CFRefBug *BT = static_cast<CFRefBug*>(Pred ? leakWithinFunction
- : leakAtReturn);
+ const LangOptions &LOpts = Eng.getContext().getLangOptions();
+ bool GCEnabled = Eng.isObjCGCEnabled();
+ CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled)
+ : getLeakAtReturnBug(LOpts, GCEnabled);
assert(BT && "BugType not initialized.");
- CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng);
- BR->EmitReport(report);
+
+ CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled,
+ SummaryLog, N, *I, Eng);
+ Eng.getBugReporter().EmitReport(report);
}
}
return N;
}
-void CFRefCount::evalEndPath(ExprEngine& Eng,
- EndOfFunctionNodeBuilder& Builder) {
-
- const GRState *state = Builder.getState();
+void RetainCountChecker::checkEndPath(EndOfFunctionNodeBuilder &Builder,
+ ExprEngine &Eng) const {
+ const ProgramState *state = Builder.getState();
GenericNodeBuilderRefCount Bd(Builder);
RefBindings B = state->get<RefBindings>();
- ExplodedNode *Pred = 0;
+ ExplodedNode *Pred = Builder.getPredecessor();
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- bool stop = false;
- llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng,
- (*I).first,
- (*I).second, stop);
-
- if (stop)
+ llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng,
+ I->first, I->second);
+ if (!state)
return;
}
B = state->get<RefBindings>();
- llvm::SmallVector<SymbolRef, 10> Leaked;
+ SmallVector<SymbolRef, 10> Leaked;
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
- state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked);
+ state = handleSymbolDeath(state, I->first, I->second, Leaked);
- ProcessLeaks(state, Leaked, Bd, Eng, Pred);
+ processLeaks(state, Leaked, Bd, Eng, Pred);
+}
+
+const ProgramPointTag *
+RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const {
+ const SimpleProgramPointTag *&tag = DeadSymbolTags[sym];
+ if (!tag) {
+ llvm::SmallString<64> buf;
+ llvm::raw_svector_ostream out(buf);
+ out << "RetainCountChecker : Dead Symbol : " << sym->getSymbolID();
+ tag = new SimpleProgramPointTag(out.str());
+ }
+ return tag;
}
-void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst,
- ExprEngine& Eng,
- StmtNodeBuilder& Builder,
- ExplodedNode* Pred,
- const GRState* state,
- SymbolReaper& SymReaper) {
- const Stmt *S = Builder.getStmt();
+void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ExprEngine &Eng = C.getEngine();
+ ExplodedNode *Pred = C.getPredecessor();
+
+ const ProgramState *state = C.getState();
RefBindings B = state->get<RefBindings>();
// Update counts from autorelease pools
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
E = SymReaper.dead_end(); I != E; ++I) {
SymbolRef Sym = *I;
- if (const RefVal* T = B.lookup(Sym)){
+ if (const RefVal *T = B.lookup(Sym)){
// Use the symbol as the tag.
// FIXME: This might not be as unique as we would like.
- GenericNodeBuilderRefCount Bd(Builder, S, Sym);
- bool stop = false;
- llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng,
- Sym, *T, stop);
- if (stop)
+ GenericNodeBuilderRefCount Bd(C, getDeadSymbolTag(Sym));
+ llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng,
+ Sym, *T);
+ if (!state)
return;
}
}
B = state->get<RefBindings>();
- llvm::SmallVector<SymbolRef, 10> Leaked;
+ SmallVector<SymbolRef, 10> Leaked;
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
E = SymReaper.dead_end(); I != E; ++I) {
- if (const RefVal* T = B.lookup(*I))
- state = HandleSymbolDeath(state, *I, *T, Leaked);
+ if (const RefVal *T = B.lookup(*I))
+ state = handleSymbolDeath(state, *I, *T, Leaked);
}
- static unsigned LeakPPTag = 0;
{
- GenericNodeBuilderRefCount Bd(Builder, S, &LeakPPTag);
- Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred);
+ GenericNodeBuilderRefCount Bd(C, this);
+ Pred = processLeaks(state, Leaked, Bd, Eng, Pred);
}
// Did we cache out?
@@ -3434,256 +3513,76 @@ void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst,
return;
// Now generate a new node that nukes the old bindings.
- RefBindings::Factory& F = state->get_context<RefBindings>();
+ RefBindings::Factory &F = state->get_context<RefBindings>();
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
- E = SymReaper.dead_end(); I!=E; ++I) B = F.remove(B, *I);
+ E = SymReaper.dead_end(); I != E; ++I)
+ B = F.remove(B, *I);
state = state->set<RefBindings>(B);
- Builder.MakeNode(Dst, S, Pred, state);
-}
-
-void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
- StmtNodeBuilder& Builder,
- const Expr* NodeExpr,
- SourceRange ErrorRange,
- ExplodedNode* Pred,
- const GRState* St,
- RefVal::Kind hasErr, SymbolRef Sym) {
- Builder.BuildSinks = true;
- ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
-
- if (!N)
- return;
-
- CFRefBug *BT = 0;
-
- switch (hasErr) {
- default:
- assert(false && "Unhandled error.");
- return;
- case RefVal::ErrorUseAfterRelease:
- BT = static_cast<CFRefBug*>(useAfterRelease);
- break;
- case RefVal::ErrorReleaseNotOwned:
- BT = static_cast<CFRefBug*>(releaseNotOwned);
- break;
- case RefVal::ErrorDeallocGC:
- BT = static_cast<CFRefBug*>(deallocGC);
- break;
- case RefVal::ErrorDeallocNotOwned:
- BT = static_cast<CFRefBug*>(deallocNotOwned);
- break;
- }
-
- CFRefReport *report = new CFRefReport(*BT, *this, N, Sym);
- report->addRange(ErrorRange);
- BR->EmitReport(report);
+ C.generateNode(state, Pred);
}
//===----------------------------------------------------------------------===//
-// Pieces of the retain/release checker implemented using a CheckerVisitor.
-// More pieces of the retain/release checker will be migrated to this interface
-// (ideally, all of it some day).
+// Debug printing of refcount bindings and autorelease pools.
//===----------------------------------------------------------------------===//
-namespace {
-class RetainReleaseChecker
- : public Checker< check::PostStmt<BlockExpr>,
- check::PostStmt<CastExpr>,
- check::RegionChanges > {
-public:
- bool wantsRegionUpdate;
-
- RetainReleaseChecker() : wantsRegionUpdate(true) {}
-
-
- void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
-
- void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
-
- const GRState *checkRegionChanges(const GRState *state,
- const StoreManager::InvalidatedSymbols *invalidated,
- const MemRegion * const *begin,
- const MemRegion * const *end) const;
-
- bool wantsRegionChangeUpdate(const GRState *state) const {
- return wantsRegionUpdate;
- }
-};
-} // end anonymous namespace
+static void PrintPool(raw_ostream &Out, SymbolRef Sym,
+ const ProgramState *State) {
+ Out << ' ';
+ if (Sym)
+ Out << Sym->getSymbolID();
+ else
+ Out << "<pool>";
+ Out << ":{";
-const GRState *
-RetainReleaseChecker::checkRegionChanges(const GRState *state,
- const StoreManager::InvalidatedSymbols *invalidated,
- const MemRegion * const *begin,
- const MemRegion * const *end) const {
- if (!invalidated)
- return state;
+ // Get the contents of the pool.
+ if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym))
+ for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I)
+ Out << '(' << I.getKey() << ',' << I.getData() << ')';
- for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(),
- E = invalidated->end(); I!=E; ++I) {
- SymbolRef sym = *I;
- if (WhitelistedSymbols.count(sym))
- continue;
- // Remove any existing reference-count binding.
- state = state->remove<RefBindings>(sym);
- }
- return state;
+ Out << '}';
}
-void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,
- CheckerContext &C) const {
-
- // Scan the BlockDecRefExprs for any object the retain/release checker
- // may be tracking.
- if (!BE->getBlockDecl()->hasCaptures())
- return;
+static bool UsesAutorelease(const ProgramState *state) {
+ // A state uses autorelease if it allocated an autorelease pool or if it has
+ // objects in the caller's autorelease pool.
+ return !state->get<AutoreleaseStack>().isEmpty() ||
+ state->get<AutoreleasePoolContents>(SymbolRef());
+}
- const GRState *state = C.getState();
- const BlockDataRegion *R =
- cast<BlockDataRegion>(state->getSVal(BE).getAsRegion());
+void RetainCountChecker::printState(raw_ostream &Out, const ProgramState *State,
+ const char *NL, const char *Sep) const {
- BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
- E = R->referenced_vars_end();
+ RefBindings B = State->get<RefBindings>();
- if (I == E)
- return;
-
- // FIXME: For now we invalidate the tracking of all symbols passed to blocks
- // via captured variables, even though captured variables result in a copy
- // and in implicit increment/decrement of a retain count.
- llvm::SmallVector<const MemRegion*, 10> Regions;
- const LocationContext *LC = C.getPredecessor()->getLocationContext();
- MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+ if (!B.isEmpty())
+ Out << Sep << NL;
- for ( ; I != E; ++I) {
- const VarRegion *VR = *I;
- if (VR->getSuperRegion() == R) {
- VR = MemMgr.getVarRegion(VR->getDecl(), LC);
- }
- Regions.push_back(VR);
+ for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ Out << I->first << " : ";
+ I->second.print(Out);
+ Out << NL;
}
- state =
- state->scanReachableSymbols<StopTrackingCallback>(Regions.data(),
- Regions.data() + Regions.size()).getState();
- C.addTransition(state);
-}
+ // Print the autorelease stack.
+ if (UsesAutorelease(State)) {
+ Out << Sep << NL << "AR pool stack:";
+ ARStack Stack = State->get<AutoreleaseStack>();
-void RetainReleaseChecker::checkPostStmt(const CastExpr *CE,
- CheckerContext &C) const {
- const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
- if (!BE)
- return;
-
- ArgEffect AE = IncRef;
-
- switch (BE->getBridgeKind()) {
- case clang::OBC_Bridge:
- // Do nothing.
- return;
- case clang::OBC_BridgeRetained:
- AE = IncRef;
- break;
- case clang::OBC_BridgeTransfer:
- AE = DecRefBridgedTransfered;
- break;
- }
-
- const GRState *state = C.getState();
- SymbolRef Sym = state->getSVal(CE).getAsLocSymbol();
- if (!Sym)
- return;
- const RefVal* T = state->get<RefBindings>(Sym);
- if (!T)
- return;
+ PrintPool(Out, SymbolRef(), State); // Print the caller's pool.
+ for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I)
+ PrintPool(Out, *I, State);
- // This is gross. Once the checker and CFRefCount are unified,
- // this will go away.
- CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF());
- RefVal::Kind hasErr = (RefVal::Kind) 0;
- state = cf.Update(state, Sym, *T, AE, hasErr);
-
- if (hasErr) {
-
- return;
+ Out << NL;
}
-
- C.generateNode(state);
}
//===----------------------------------------------------------------------===//
-// Transfer function creation for external clients.
+// Checker registration.
//===----------------------------------------------------------------------===//
-void CFRefCount::RegisterChecks(ExprEngine& Eng) {
- BugReporter &BR = Eng.getBugReporter();
-
- useAfterRelease = new UseAfterRelease(this);
- BR.Register(useAfterRelease);
-
- releaseNotOwned = new BadRelease(this);
- BR.Register(releaseNotOwned);
-
- deallocGC = new DeallocGC(this);
- BR.Register(deallocGC);
-
- deallocNotOwned = new DeallocNotOwned(this);
- BR.Register(deallocNotOwned);
-
- overAutorelease = new OverAutorelease(this);
- BR.Register(overAutorelease);
-
- returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this);
- BR.Register(returnNotOwnedForOwned);
-
- // First register "return" leaks.
- const char* name = 0;
-
- if (isGCEnabled())
- name = "Leak of returned object when using garbage collection";
- else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
- name = "Leak of returned object when not using garbage collection (GC) in "
- "dual GC/non-GC code";
- else {
- assert(getLangOptions().getGCMode() == LangOptions::NonGC);
- name = "Leak of returned object";
- }
-
- // Leaks should not be reported if they are post-dominated by a sink.
- leakAtReturn = new LeakAtReturn(this, name);
- leakAtReturn->setSuppressOnSink(true);
- BR.Register(leakAtReturn);
-
- // Second, register leaks within a function/method.
- if (isGCEnabled())
- name = "Leak of object when using garbage collection";
- else if (getLangOptions().getGCMode() == LangOptions::HybridGC)
- name = "Leak of object when not using garbage collection (GC) in "
- "dual GC/non-GC code";
- else {
- assert(getLangOptions().getGCMode() == LangOptions::NonGC);
- name = "Leak";
- }
-
- // Leaks should not be reported if they are post-dominated by sinks.
- leakWithinFunction = new LeakWithinFunction(this, name);
- leakWithinFunction->setSuppressOnSink(true);
- BR.Register(leakWithinFunction);
-
- // Save the reference to the BugReporter.
- this->BR = &BR;
-
- // Register the RetainReleaseChecker with the ExprEngine object.
- // Functionality in CFRefCount will be migrated to RetainReleaseChecker
- // over time.
- // FIXME: HACK! Remove TransferFuncs and turn all of CFRefCount into fully
- // using the checker mechanism.
- Eng.getCheckerManager().registerChecker<RetainReleaseChecker>();
+void ento::registerRetainCountChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<RetainCountChecker>();
}
-TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
- const LangOptions& lopts) {
- return new CFRefCount(Ctx, GCEnabled, lopts);
-}
diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 1729b25..e761bff 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -33,7 +33,7 @@ public:
void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *RetE = RS->getRetValue();
if (!RetE)
@@ -58,8 +58,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
= C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
ER->getValueType());
- const GRState *StInBound = state->assumeInBound(Idx, NumElements, true);
- const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false);
+ const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true);
+ const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateSink(StOutBound);
@@ -78,8 +78,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
// reference is outside the range.
// Generate a report for this bug.
- RangedBugReport *report =
- new RangedBugReport(*BT, BT->getDescription(), N);
+ BugReport *report =
+ new BugReport(*BT, BT->getDescription(), N);
report->addRange(RetE->getSourceRange());
C.EmitReport(report);
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 7c215b7..e8c8d90 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -50,11 +50,11 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
BT.reset(new BuiltinBug("Garbage return value",
"Undefined or garbage value returned to caller"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT, BT->getDescription(), N);
+ BugReport *report =
+ new BugReport(*BT, BT->getDescription(), N);
report->addRange(RetE->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE));
C.EmitReport(report);
}
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 73ce359..91c4b96 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -17,7 +17,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
@@ -35,12 +35,12 @@ public:
private:
void EmitStackError(CheckerContext &C, const MemRegion *R,
const Expr *RetE) const;
- static SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R,
+ static SourceRange GenName(raw_ostream &os, const MemRegion *R,
SourceManager &SM);
};
}
-SourceRange StackAddrEscapeChecker::GenName(llvm::raw_ostream &os,
+SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os,
const MemRegion *R,
SourceManager &SM) {
// Get the base region, stripping away fields and elements.
@@ -50,34 +50,39 @@ SourceRange StackAddrEscapeChecker::GenName(llvm::raw_ostream &os,
// Check if the region is a compound literal.
if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
- const CompoundLiteralExpr* CL = CR->getLiteralExpr();
+ const CompoundLiteralExpr *CL = CR->getLiteralExpr();
os << "stack memory associated with a compound literal "
"declared on line "
- << SM.getInstantiationLineNumber(CL->getLocStart())
+ << SM.getExpansionLineNumber(CL->getLocStart())
<< " returned to caller";
range = CL->getSourceRange();
}
else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
- const Expr* ARE = AR->getExpr();
+ const Expr *ARE = AR->getExpr();
SourceLocation L = ARE->getLocStart();
range = ARE->getSourceRange();
os << "stack memory allocated by call to alloca() on line "
- << SM.getInstantiationLineNumber(L);
+ << SM.getExpansionLineNumber(L);
}
else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
const BlockDecl *BD = BR->getCodeRegion()->getDecl();
SourceLocation L = BD->getLocStart();
range = BD->getSourceRange();
os << "stack-allocated block declared on line "
- << SM.getInstantiationLineNumber(L);
+ << SM.getExpansionLineNumber(L);
}
else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << "stack memory associated with local variable '"
<< VR->getString() << '\'';
range = VR->getDecl()->getSourceRange();
}
+ else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
+ os << "stack memory associated with temporary object of type '"
+ << TOR->getValueType().getAsString() << '\'';
+ range = TOR->getExpr()->getSourceRange();
+ }
else {
- assert(false && "Invalid region in ReturnStackAddressChecker.");
+ llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
}
return range;
@@ -99,7 +104,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *
llvm::raw_svector_ostream os(buf);
SourceRange range = GenName(os, R, C.getSourceManager());
os << " returned to caller";
- RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N);
+ BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
report->addRange(RetE->getSourceRange());
if (range.isValid())
report->addRange(range);
@@ -134,7 +139,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
- const GRState *state = B.getState();
+ const ProgramState *state = B.getState();
// Iterate over all bindings to global variables and see if it contains
// a memory region in the stack space.
@@ -143,7 +148,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng;
const StackFrameContext *CurSFC;
public:
- llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;
+ SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;
CallBack(ExprEngine &Eng, const LocationContext *LCtx)
: Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {}
@@ -202,9 +207,9 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
Eng.getContext().getSourceManager());
os << " is still referred to by the global variable '";
const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
- os << VR->getDecl()->getNameAsString()
+ os << *VR->getDecl()
<< "' upon returning to the caller. This will be a dangling reference";
- RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N);
+ BugReport *report = new BugReport(*BT_stackleak, os.str(), N);
if (range.isValid())
report->addRange(range);
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 711c672..1d14e9e 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -16,8 +16,8 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -96,9 +96,9 @@ private:
void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
- const GRState *CheckNullStream(SVal SV, const GRState *state,
+ const ProgramState *CheckNullStream(SVal SV, const ProgramState *state,
CheckerContext &C) const;
- const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state,
+ const ProgramState *CheckDoubleClose(const CallExpr *CE, const ProgramState *state,
CheckerContext &C) const;
};
@@ -107,15 +107,15 @@ private:
namespace clang {
namespace ento {
template <>
- struct GRStateTrait<StreamState>
- : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
+ struct ProgramStateTrait<StreamState>
+ : public ProgramStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
static void *GDMIndex() { static int x; return &x; }
};
}
}
bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
const FunctionDecl *FD = L.getAsFunctionDecl();
@@ -221,8 +221,8 @@ void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
}
void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
- unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ const ProgramState *state = C.getState();
+ unsigned Count = C.getCurrentBlockCount();
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedSVal RetVal =
cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count));
@@ -231,7 +231,7 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
ConstraintManager &CM = C.getConstraintManager();
// Bifurcate the state into two: one with a valid FILE* pointer, the other
// with a NULL.
- const GRState *stateNotNull, *stateNull;
+ const ProgramState *stateNotNull, *stateNull;
llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
if (SymbolRef Sym = RetVal.getAsSymbol()) {
@@ -247,25 +247,25 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
}
void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = CheckDoubleClose(CE, C.getState(), C);
+ const ProgramState *state = CheckDoubleClose(CE, C.getState(), C);
if (state)
C.addTransition(state);
}
void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C))
return;
}
void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C))
return;
}
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C)))
return;
// Check the legality of the 'whence' argument of 'fseek'.
@@ -291,61 +291,61 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
}
void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C))
return;
}
-const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state,
+const ProgramState *StreamChecker::CheckNullStream(SVal SV, const ProgramState *state,
CheckerContext &C) const {
const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV);
if (!DV)
return 0;
ConstraintManager &CM = C.getConstraintManager();
- const GRState *stateNotNull, *stateNull;
+ const ProgramState *stateNotNull, *stateNull;
llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
if (!stateNotNull && stateNull) {
@@ -361,8 +361,8 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state,
return stateNotNull;
}
-const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
- const GRState *state,
+const ProgramState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
+ const ProgramState *state,
CheckerContext &C) const {
SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol();
if (!Sym)
@@ -399,7 +399,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
E = SymReaper.dead_end(); I != E; ++I) {
SymbolRef Sym = *I;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const StreamState *SS = state->get<StreamState>(Sym);
if (!SS)
return;
@@ -420,7 +420,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void StreamChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
- const GRState *state = B.getState();
+ const ProgramState *state = B.getState();
typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
SymMap M = state->get<StreamState>();
@@ -445,7 +445,7 @@ void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
if (!RetE)
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SymbolRef Sym = state->getSVal(RetE).getAsSymbol();
if (!Sym)
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 1fb1815..b860b34 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -27,26 +27,25 @@ class UndefBranchChecker : public Checker<check::BranchCondition> {
mutable llvm::OwningPtr<BuiltinBug> BT;
struct FindUndefExpr {
- GRStateManager& VM;
- const GRState* St;
+ const ProgramState *St;
- FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {}
+ FindUndefExpr(const ProgramState *S) : St(S) {}
- const Expr* FindExpr(const Expr* Ex) {
+ const Expr *FindExpr(const Expr *Ex) {
if (!MatchesCriteria(Ex))
return 0;
for (Stmt::const_child_iterator I = Ex->child_begin(),
E = Ex->child_end();I!=E;++I)
- if (const Expr* ExI = dyn_cast_or_null<Expr>(*I)) {
- const Expr* E2 = FindExpr(ExI);
+ if (const Expr *ExI = dyn_cast_or_null<Expr>(*I)) {
+ const Expr *E2 = FindExpr(ExI);
if (E2) return E2;
}
return Ex;
}
- bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); }
+ bool MatchesCriteria(const Expr *Ex) { return St->getSVal(Ex).isUndef(); }
};
public:
@@ -59,10 +58,10 @@ public:
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
BranchNodeBuilder &Builder,
ExprEngine &Eng) const {
- const GRState *state = Builder.getState();
+ const ProgramState *state = Builder.getState();
SVal X = state->getSVal(Condition);
if (X.isUndef()) {
- ExplodedNode *N = Builder.generateNode(state, true);
+ ExplodedNode *N = Builder.generateNode(Condition, state);
if (N) {
N->markAsSink();
if (!BT)
@@ -74,33 +73,31 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
// branch condition." We do a recursive walk of the condition's
// subexpressions and roughly look for the most nested subexpression
// that binds to Undefined. We then highlight that expression's range.
- BlockEdge B = cast<BlockEdge>(N->getLocation());
- const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition());
- assert (Ex && "Block must have a terminator.");
// Get the predecessor node and check if is a PostStmt with the Stmt
// being the terminator condition. We want to inspect the state
// of that node instead because it will contain main information about
// the subexpressions.
- assert (!N->pred_empty());
// Note: any predecessor will do. They should have identical state,
// since all the BlockEdge did was act as an error sink since the value
// had to already be undefined.
+ assert (!N->pred_empty());
+ const Expr *Ex = cast<Expr>(Condition);
ExplodedNode *PrevN = *N->pred_begin();
ProgramPoint P = PrevN->getLocation();
- const GRState* St = N->getState();
+ const ProgramState *St = N->getState();
- if (PostStmt* PS = dyn_cast<PostStmt>(&P))
+ if (PostStmt *PS = dyn_cast<PostStmt>(&P))
if (PS->getStmt() == Ex)
St = PrevN->getState();
- FindUndefExpr FindIt(Eng.getStateManager(), St);
+ FindUndefExpr FindIt(St);
Ex = FindIt.FindExpr(Ex);
// Emit the bug report.
- EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N);
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex);
+ BugReport *R = new BugReport(*BT, BT->getDescription(), N);
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex));
R->addRange(Ex->getSourceRange());
Eng.getBugReporter().EmitReport(R);
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 69958d1..2aebed9 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -55,7 +55,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
if (!BE->getBlockDecl()->hasCaptures())
return;
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const BlockDataRegion *R =
cast<BlockDataRegion>(state->getSVal(BE).getAsRegion());
@@ -74,8 +74,9 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
// Get the VarRegion associated with VD in the local stack frame.
const LocationContext *LC = C.getPredecessor()->getLocationContext();
VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC);
+ SVal VRVal = state->getSVal(VR);
- if (state->getSVal(VR).isUndef())
+ if (VRVal.isUndef())
if (ExplodedNode *N = C.generateSink()) {
if (!BT)
BT.reset(new BuiltinBug("uninitialized variable captured by block"));
@@ -87,10 +88,10 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
os << "Variable '" << VD->getName()
<< "' is uninitialized when captured by block";
- EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N);
+ BugReport *R = new BugReport(*BT, os.str(), N);
if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
R->addRange(Ex->getSourceRange());
- R->addVisitorCreator(bugreporter::registerFindLastStore, VR);
+ R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR));
// need location of block
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 7fa3804..7ae9668 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -35,7 +35,7 @@ public:
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
CheckerContext &C) const {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (state->getSVal(B).isUndef()) {
// Generate an error node.
ExplodedNode *N = C.generateSink();
@@ -71,13 +71,13 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
<< BinaryOperator::getOpcodeStr(B->getOpcode())
<< "' expression is undefined";
}
- EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N);
+ BugReport *report = new BugReport(*BT, OS.str(), N);
if (Ex) {
report->addRange(Ex->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex));
}
else
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B));
C.EmitReport(report);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index e51ab20..bb6831b 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -40,10 +40,10 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
BT.reset(new BuiltinBug("Array subscript is undefined"));
// Generate a report for this bug.
- EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N);
+ BugReport *R = new BugReport(*BT, BT->getName(), N);
R->addRange(A->getIdx()->getSourceRange());
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- A->getIdx());
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ A->getIdx()));
C.EmitReport(R);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 28806e3..5ca4a9f 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -27,11 +27,13 @@ class UndefinedAssignmentChecker
mutable llvm::OwningPtr<BugType> BT;
public:
- void checkBind(SVal location, SVal val, CheckerContext &C) const;
+ void checkBind(SVal location, SVal val, const Stmt *S,
+ CheckerContext &C) const;
};
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
+ const Stmt *StoreE,
CheckerContext &C) const {
if (!val.isUndef())
return;
@@ -49,11 +51,10 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
// Generate a report for this bug.
const Expr *ex = 0;
- const Stmt *StoreE = C.getStmt();
while (StoreE) {
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
if (B->isCompoundAssignmentOp()) {
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (state->getSVal(B->getLHS()).isUndef()) {
str = "The left expression of the compound assignment is an "
"uninitialized value. The computed value will also be garbage";
@@ -67,17 +68,17 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
}
if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
- const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
ex = VD->getInit();
}
break;
}
- EnhancedBugReport *R = new EnhancedBugReport(*BT, str, N);
+ BugReport *R = new BugReport(*BT, str, N);
if (ex) {
R->addRange(ex->getSourceRange());
- R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex);
+ R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex));
}
C.EmitReport(R);
}
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 0ecc391..cec286d 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -62,7 +62,8 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
// The definition of O_CREAT is platform specific. We need a better way
// of querying this information from the checking environment.
if (!Val_O_CREAT.hasValue()) {
- if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple)
+ if (C.getASTContext().getTargetInfo().getTriple().getVendor()
+ == llvm::Triple::Apple)
Val_O_CREAT = 0x0200;
else {
// FIXME: We need a more general way of getting the O_CREAT value.
@@ -73,7 +74,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
}
// Look at the 'oflags' argument for the O_CREAT flag.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
if (CE->getNumArgs() < 2) {
// The frontend should issue a warning for this case, so this is a sanity
@@ -101,7 +102,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
// Check if maskedFlags is non-zero.
- const GRState *trueState, *falseState;
+ const ProgramState *trueState, *falseState;
llvm::tie(trueState, falseState) = state->assume(maskedFlags);
// Only emit an error if the value of 'maskedFlags' is properly
@@ -116,8 +117,8 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
LazyInitialize(BT_open, "Improper use of 'open'");
- RangedBugReport *report =
- new RangedBugReport(*BT_open,
+ BugReport *report =
+ new BugReport(*BT_open,
"Call to 'open' requires a third argument when "
"the 'O_CREAT' flag is set", N);
report->addRange(oflagsEx->getSourceRange());
@@ -140,7 +141,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
// Check if the first argument is stack allocated. If so, issue a warning
// because that's likely to be bad news.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
return;
@@ -163,7 +164,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
- RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N);
+ BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.EmitReport(report);
}
@@ -182,13 +183,13 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
return;
// Check if the allocation size is 0.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
SVal argVal = state->getSVal(CE->getArg(0));
if (argVal.isUnknownOrUndef())
return;
- const GRState *trueState, *falseState;
+ const ProgramState *trueState, *falseState;
llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal));
// Is the value perfectly constrained to zero?
@@ -202,12 +203,12 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes");
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation"
+ BugReport *report =
+ new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation"
" size of 0 bytes", N);
report->addRange(CE->getArg(0)->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
- CE->getArg(0));
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
+ CE->getArg(0)));
C.EmitReport(report);
return;
}
@@ -225,7 +226,7 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
// Get the callee. All the functions we care about are C functions
// with simple identifiers.
- const GRState *state = C.getState();
+ const ProgramState *state = C.getState();
const Expr *Callee = CE->getCallee();
const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl();
diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index b540bce..459ee65 100644
--- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -60,11 +60,12 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
CFG *C = 0;
ParentMap *PM = 0;
+ const LocationContext *LC = 0;
// Iterate over ExplodedGraph
for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
I != E; ++I) {
const ProgramPoint &P = I->getLocation();
- const LocationContext *LC = P.getLocationContext();
+ LC = P.getLocationContext();
// Save the CFG if we don't have it already
if (!C)
@@ -111,22 +112,30 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
// FIXME: This should be extended to include other unreachable markers,
// such as llvm_unreachable.
if (!CB->empty()) {
- CFGElement First = CB->front();
- if (const CFGStmt *S = First.getAs<CFGStmt>()) {
- if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
- if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)
- continue;
- }
+ bool foundUnreachable = false;
+ for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end();
+ ci != ce; ++ci) {
+ if (const CFGStmt *S = (*ci).getAs<CFGStmt>())
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
+ if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) {
+ foundUnreachable = true;
+ break;
+ }
+ }
}
+ if (foundUnreachable)
+ continue;
}
// We found a block that wasn't covered - find the statement to report
SourceRange SR;
+ PathDiagnosticLocation DL;
SourceLocation SL;
if (const Stmt *S = getUnreachableStmt(CB)) {
SR = S->getSourceRange();
- SL = S->getLocStart();
- if (SR.isInvalid() || SL.isInvalid())
+ DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC);
+ SL = DL.asLocation();
+ if (SR.isInvalid() || !SL.isValid())
continue;
}
else
@@ -138,7 +147,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
continue;
B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never"
- " executed", SL, SR);
+ " executed", DL, SR);
}
}
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 875dce2..b34b97c 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -48,8 +48,8 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
return;
// FIXME: Handle multi-dimensional VLAs.
- const Expr* SE = VLA->getSizeExpr();
- const GRState *state = C.getState();
+ const Expr *SE = VLA->getSizeExpr();
+ const ProgramState *state = C.getState();
SVal sizeV = state->getSVal(SE);
if (sizeV.isUndef()) {
@@ -62,10 +62,10 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
BT_undef.reset(new BuiltinBug("Declared variable-length array (VLA) "
"uses a garbage value as its size"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_undef, BT_undef->getName(), N);
+ BugReport *report =
+ new BugReport(*BT_undef, BT_undef->getName(), N);
report->addRange(SE->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE));
C.EmitReport(report);
return;
}
@@ -78,19 +78,19 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
// Check if the size is zero.
DefinedSVal sizeD = cast<DefinedSVal>(sizeV);
- const GRState *stateNotZero, *stateZero;
+ const ProgramState *stateNotZero, *stateZero;
llvm::tie(stateNotZero, stateZero) = state->assume(sizeD);
if (stateZero && !stateNotZero) {
- ExplodedNode* N = C.generateSink(stateZero);
+ ExplodedNode *N = C.generateSink(stateZero);
if (!BT_zero)
BT_zero.reset(new BuiltinBug("Declared variable-length array (VLA) has "
"zero size"));
- EnhancedBugReport *report =
- new EnhancedBugReport(*BT_zero, BT_zero->getName(), N);
+ BugReport *report =
+ new BugReport(*BT_zero, BT_zero->getName(), N);
report->addRange(SE->getSourceRange());
- report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE);
+ report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE));
C.EmitReport(report);
return;
}
diff --git a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp
index 901190d..0936d61 100644
--- a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp
+++ b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp
@@ -46,7 +46,7 @@ public:
void AggExprVisitor::VisitCastExpr(CastExpr *E) {
switch (E->getCastKind()) {
default:
- assert(0 && "Unhandled cast kind");
+ llvm_unreachable("Unhandled cast kind");
case CK_NoOp:
case CK_ConstructorConversion:
case CK_UserDefinedConversion:
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 5f4f83c..17ec70d 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -14,6 +14,58 @@
using namespace clang;
using namespace ento;
+AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
+ const LangOptions &lang,
+ PathDiagnosticConsumer *pd,
+ StoreManagerCreator storemgr,
+ ConstraintManagerCreator constraintmgr,
+ CheckerManager *checkerMgr,
+ idx::Indexer *idxer,
+ unsigned maxnodes, unsigned maxvisit,
+ bool vizdot, bool vizubi,
+ AnalysisPurgeMode purge,
+ bool eager, bool trim,
+ bool inlinecall, bool useUnoptimizedCFG,
+ bool addImplicitDtors, bool addInitializers,
+ bool eagerlyTrimEGraph)
+ : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
+ Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd),
+ CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
+ CheckerMgr(checkerMgr), Idxer(idxer),
+ AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
+ VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
+ EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
+ EagerlyTrimEGraph(eagerlyTrimEGraph)
+{
+ AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
+}
+
+AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
+ AnalysisManager &ParentAM)
+ : AnaCtxMgr(ParentAM.AnaCtxMgr.getUseUnoptimizedCFG(),
+ ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors,
+ ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers),
+ Ctx(ctx), Diags(diags),
+ LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()),
+ CreateStoreMgr(ParentAM.CreateStoreMgr),
+ CreateConstraintMgr(ParentAM.CreateConstraintMgr),
+ CheckerMgr(ParentAM.CheckerMgr),
+ Idxer(ParentAM.Idxer),
+ AScope(ScopeDecl),
+ MaxNodes(ParentAM.MaxNodes),
+ MaxVisit(ParentAM.MaxVisit),
+ VisualizeEGDot(ParentAM.VisualizeEGDot),
+ VisualizeEGUbi(ParentAM.VisualizeEGUbi),
+ PurgeDead(ParentAM.PurgeDead),
+ EagerlyAssume(ParentAM.EagerlyAssume),
+ TrimGraph(ParentAM.TrimGraph),
+ InlineCall(ParentAM.InlineCall),
+ EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph)
+{
+ AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
+}
+
+
AnalysisContext *
AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) {
idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D),
diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
index 3050ca3..6c748b6 100644
--- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp
@@ -8,14 +8,13 @@
//===----------------------------------------------------------------------===//
//
// This file defines BasicConstraintManager, a class that tracks simple
-// equality and inequality constraints on symbolic values of GRState.
+// equality and inequality constraints on symbolic values of ProgramState.
//
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -25,7 +24,7 @@ using namespace ento;
namespace { class ConstNotEq {}; }
namespace { class ConstEq {}; }
-typedef llvm::ImmutableMap<SymbolRef,GRState::IntSetTy> ConstNotEqTy;
+typedef llvm::ImmutableMap<SymbolRef,ProgramState::IntSetTy> ConstNotEqTy;
typedef llvm::ImmutableMap<SymbolRef,const llvm::APSInt*> ConstEqTy;
static int ConstEqIndex = 0;
@@ -34,13 +33,14 @@ static int ConstNotEqIndex = 0;
namespace clang {
namespace ento {
template<>
-struct GRStateTrait<ConstNotEq> : public GRStatePartialTrait<ConstNotEqTy> {
- static inline void* GDMIndex() { return &ConstNotEqIndex; }
+struct ProgramStateTrait<ConstNotEq> :
+ public ProgramStatePartialTrait<ConstNotEqTy> {
+ static inline void *GDMIndex() { return &ConstNotEqIndex; }
};
template<>
-struct GRStateTrait<ConstEq> : public GRStatePartialTrait<ConstEqTy> {
- static inline void* GDMIndex() { return &ConstEqIndex; }
+struct ProgramStateTrait<ConstEq> : public ProgramStatePartialTrait<ConstEqTy> {
+ static inline void *GDMIndex() { return &ConstEqIndex; }
};
}
}
@@ -50,62 +50,81 @@ namespace {
// constants and integer variables.
class BasicConstraintManager
: public SimpleConstraintManager {
- GRState::IntSetTy::Factory ISetFactory;
+ ProgramState::IntSetTy::Factory ISetFactory;
public:
- BasicConstraintManager(GRStateManager &statemgr, SubEngine &subengine)
+ BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine)
: SimpleConstraintManager(subengine),
ISetFactory(statemgr.getAllocator()) {}
- const GRState *assumeSymNE(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState *assumeSymEQ(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState *assumeSymLT(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState *assumeSymGT(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState *assumeSymGE(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState *assumeSymLE(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V,
- const llvm::APSInt& Adjustment);
-
- const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V);
-
- const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V);
-
- const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const;
- bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V)
- const;
- bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V)
- const;
-
- const GRState* removeDeadBindings(const GRState* state, SymbolReaper& SymReaper);
-
- void print(const GRState* state, llvm::raw_ostream& Out,
- const char* nl, const char *sep);
+ const ProgramState *assumeSymNE(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *assumeSymEQ(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *assumeSymLT(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *assumeSymGT(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *assumeSymGE(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *assumeSymLE(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V,
+ const llvm::APSInt& Adjustment);
+
+ const ProgramState *AddEQ(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V);
+
+ const ProgramState *AddNE(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V);
+
+ const llvm::APSInt* getSymVal(const ProgramState *state,
+ SymbolRef sym) const;
+
+ bool isNotEqual(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V) const;
+
+ bool isEqual(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V) const;
+
+ const ProgramState *removeDeadBindings(const ProgramState *state,
+ SymbolReaper& SymReaper);
+
+ void print(const ProgramState *state,
+ raw_ostream &Out,
+ const char* nl,
+ const char *sep);
};
} // end anonymous namespace
-ConstraintManager* ento::CreateBasicConstraintManager(GRStateManager& statemgr,
- SubEngine &subengine) {
+ConstraintManager*
+ento::CreateBasicConstraintManager(ProgramStateManager& statemgr,
+ SubEngine &subengine) {
return new BasicConstraintManager(statemgr, subengine);
}
-
-const GRState*
-BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymNE(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
// First, determine if sym == X, where X+Adjustment != V.
@@ -124,8 +143,9 @@ BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym,
return AddNE(state, sym, Adjusted);
}
-const GRState*
-BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymEQ(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
// First, determine if sym == X, where X+Adjustment != V.
@@ -145,8 +165,9 @@ BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym,
}
// The logic for these will be handled in another ConstraintManager.
-const GRState*
-BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymLT(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
// Is 'V' the smallest possible value?
@@ -159,8 +180,9 @@ BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym,
return assumeSymNE(state, sym, V, Adjustment);
}
-const GRState*
-BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymGT(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) {
// Is 'V' the largest possible value?
@@ -173,8 +195,9 @@ BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym,
return assumeSymNE(state, sym, V, Adjustment);
}
-const GRState*
-BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymGE(const ProgramState *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).
@@ -201,8 +224,9 @@ BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym,
return state;
}
-const GRState*
-BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym,
+const ProgramState*
+BasicConstraintManager::assumeSymLE(const ProgramState *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).
@@ -229,18 +253,20 @@ BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym,
return state;
}
-const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym,
+const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt& V) {
// Create a new state with the old binding replaced.
return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V));
}
-const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym,
- const llvm::APSInt& V) {
+const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state,
+ SymbolRef sym,
+ const llvm::APSInt& V) {
// First, retrieve the NE-set associated with the given symbol.
ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym);
- GRState::IntSetTy S = T ? *T : ISetFactory.getEmptySet();
+ ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet();
// Now add V to the NE set.
S = ISetFactory.add(S, &state->getBasicVals().getValue(V));
@@ -249,13 +275,14 @@ const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym
return state->set<ConstNotEq>(sym, S);
}
-const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state,
+const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state,
SymbolRef sym) const {
const ConstEqTy::data_type* T = state->get<ConstEq>(sym);
return T ? *T : NULL;
}
-bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym,
+bool BasicConstraintManager::isNotEqual(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt& V) const {
// Retrieve the NE-set associated with the given symbol.
@@ -265,7 +292,8 @@ bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym,
return T ? T->contains(&state->getBasicVals().getValue(V)) : false;
}
-bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym,
+bool BasicConstraintManager::isEqual(const ProgramState *state,
+ SymbolRef sym,
const llvm::APSInt& V) const {
// Retrieve the EQ-set associated with the given symbol.
const ConstEqTy::data_type* T = state->get<ConstEq>(sym);
@@ -275,8 +303,8 @@ bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym,
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
-const GRState*
-BasicConstraintManager::removeDeadBindings(const GRState* state,
+const ProgramState*
+BasicConstraintManager::removeDeadBindings(const ProgramState *state,
SymbolReaper& SymReaper) {
ConstEqTy CE = state->get<ConstEq>();
@@ -301,7 +329,8 @@ BasicConstraintManager::removeDeadBindings(const GRState* state,
return state->set<ConstNotEq>(CNE);
}
-void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out,
+void BasicConstraintManager::print(const ProgramState *state,
+ raw_ostream &Out,
const char* nl, const char *sep) {
// Print equality constraints.
@@ -324,7 +353,7 @@ void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out,
Out << nl << " $" << I.getKey() << " : ";
bool isFirst = true;
- GRState::IntSetTy::iterator J = I.getData().begin(),
+ ProgramState::IntSetTy::iterator J = I.getData().begin(),
EJ = I.getData().end();
for ( ; J != EJ; ++J) {
diff --git a/lib/StaticAnalyzer/Core/BasicStore.cpp b/lib/StaticAnalyzer/Core/BasicStore.cpp
deleted file mode 100644
index 7c9f45a..0000000
--- a/lib/StaticAnalyzer/Core/BasicStore.cpp
+++ /dev/null
@@ -1,605 +0,0 @@
-//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defined the BasicStore and BasicStoreManager classes.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ExprObjC.h"
-#include "clang/Analysis/Analyses/LiveVariables.h"
-#include "clang/Analysis/AnalysisContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "llvm/ADT/ImmutableMap.h"
-
-using namespace clang;
-using namespace ento;
-
-typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy;
-
-namespace {
-
-class BasicStoreSubRegionMap : public SubRegionMap {
-public:
- BasicStoreSubRegionMap() {}
-
- bool iterSubRegions(const MemRegion* R, Visitor& V) const {
- return true; // Do nothing. No subregions.
- }
-};
-
-class BasicStoreManager : public StoreManager {
- BindingsTy::Factory VBFactory;
-public:
- BasicStoreManager(GRStateManager& mgr)
- : StoreManager(mgr), VBFactory(mgr.getAllocator()) {}
-
- ~BasicStoreManager() {}
-
- SubRegionMap *getSubRegionMap(Store store) {
- return new BasicStoreSubRegionMap();
- }
-
- SVal Retrieve(Store store, Loc loc, QualType T = QualType());
-
- StoreRef invalidateRegion(Store store, const MemRegion *R, const Expr *E,
- unsigned Count, InvalidatedSymbols &IS);
-
- StoreRef invalidateRegions(Store store, const MemRegion * const *Begin,
- const MemRegion * const *End, const Expr *E,
- unsigned Count, InvalidatedSymbols &IS,
- bool invalidateGlobals,
- InvalidatedRegions *Regions);
-
- StoreRef scanForIvars(Stmt *B, const Decl* SelfDecl,
- const MemRegion *SelfRegion, Store St);
-
- StoreRef Bind(Store St, Loc loc, SVal V);
- StoreRef Remove(Store St, Loc loc);
- StoreRef getInitialStore(const LocationContext *InitLoc);
-
- StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr*,
- const LocationContext*, SVal val) {
- return StoreRef(store, *this);
- }
-
- /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit
- /// conversions between arrays and pointers.
- SVal ArrayToPointer(Loc Array) { return Array; }
-
- /// removeDeadBindings - Scans a BasicStore of 'state' for dead values.
- /// It updatees the GRState object in place with the values removed.
- StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
- llvm::SmallVectorImpl<const MemRegion*>& RegionRoots);
-
- void iterBindings(Store store, BindingsHandler& f);
-
- StoreRef BindDecl(Store store, const VarRegion *VR, SVal InitVal) {
- return BindDeclInternal(store, VR, &InitVal);
- }
-
- StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR) {
- return BindDeclInternal(store, VR, 0);
- }
-
- StoreRef BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal);
-
- static inline BindingsTy GetBindings(Store store) {
- return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store));
- }
-
- void print(Store store, llvm::raw_ostream& Out, const char* nl,
- const char *sep);
-
-private:
- SVal LazyRetrieve(Store store, const TypedRegion *R);
-};
-
-} // end anonymous namespace
-
-
-StoreManager* ento::CreateBasicStoreManager(GRStateManager& StMgr) {
- return new BasicStoreManager(StMgr);
-}
-
-static bool isHigherOrderRawPtr(QualType T, ASTContext &C) {
- bool foundPointer = false;
- while (1) {
- const PointerType *PT = T->getAs<PointerType>();
- if (!PT) {
- if (!foundPointer)
- return false;
-
- // intptr_t* or intptr_t**, etc?
- if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy))
- return true;
-
- QualType X = C.getCanonicalType(T).getUnqualifiedType();
- return X == C.VoidTy;
- }
-
- foundPointer = true;
- T = PT->getPointeeType();
- }
-}
-
-SVal BasicStoreManager::LazyRetrieve(Store store, const TypedRegion *R) {
- const VarRegion *VR = dyn_cast<VarRegion>(R);
- if (!VR)
- return UnknownVal();
-
- const VarDecl *VD = VR->getDecl();
- QualType T = VD->getType();
-
- // Only handle simple types that we can symbolicate.
- if (!SymbolManager::canSymbolicate(T) || !T->isScalarType())
- return UnknownVal();
-
- // Globals and parameters start with symbolic values.
- // Local variables initially are undefined.
-
- // Non-static globals may have had their values reset by invalidateRegions.
- const MemSpaceRegion *MS = VR->getMemorySpace();
- if (isa<NonStaticGlobalSpaceRegion>(MS)) {
- BindingsTy B = GetBindings(store);
- // FIXME: Copy-and-pasted from RegionStore.cpp.
- if (BindingsTy::data_type *Val = B.lookup(MS)) {
- if (SymbolRef parentSym = Val->getAsSymbol())
- return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
-
- if (Val->isZeroConstant())
- return svalBuilder.makeZeroVal(T);
-
- if (Val->isUnknownOrUndef())
- return *Val;
-
- assert(0 && "Unknown default value.");
- }
- }
-
- if (VR->hasGlobalsOrParametersStorage() ||
- isa<UnknownSpaceRegion>(VR->getMemorySpace()))
- return svalBuilder.getRegionValueSymbolVal(R);
-
- return UndefinedVal();
-}
-
-SVal BasicStoreManager::Retrieve(Store store, Loc loc, QualType T) {
- if (isa<UnknownVal>(loc))
- return UnknownVal();
-
- assert(!isa<UndefinedVal>(loc));
-
- switch (loc.getSubKind()) {
-
- case loc::MemRegionKind: {
- const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion();
-
- if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) ||
- isa<CXXThisRegion>(R)))
- return UnknownVal();
-
- BindingsTy B = GetBindings(store);
- BindingsTy::data_type *Val = B.lookup(R);
- const TypedRegion *TR = cast<TypedRegion>(R);
-
- if (Val)
- return CastRetrievedVal(*Val, TR, T);
-
- SVal V = LazyRetrieve(store, TR);
- return V.isUnknownOrUndef() ? V : CastRetrievedVal(V, TR, T);
- }
-
- case loc::ObjCPropRefKind:
- case loc::ConcreteIntKind:
- // Support direct accesses to memory. It's up to individual checkers
- // to flag an error.
- return UnknownVal();
-
- default:
- assert (false && "Invalid Loc.");
- break;
- }
-
- return UnknownVal();
-}
-
-StoreRef BasicStoreManager::Bind(Store store, Loc loc, SVal V) {
- if (isa<loc::ConcreteInt>(loc))
- return StoreRef(store, *this);
-
- const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion();
-
- // Special case: a default symbol assigned to the NonStaticGlobalsSpaceRegion
- // that is used to derive other symbols.
- if (isa<NonStaticGlobalSpaceRegion>(R)) {
- BindingsTy B = GetBindings(store);
- return StoreRef(VBFactory.add(B, R, V).getRoot(), *this);
- }
-
- // Special case: handle store of pointer values (Loc) to pointers via
- // a cast to intXX_t*, void*, etc. This is needed to handle
- // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier.
- if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V))
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // FIXME: Should check for index 0.
- QualType T = ER->getLocationType();
-
- if (isHigherOrderRawPtr(T, Ctx))
- R = ER->getSuperRegion();
- }
-
- if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || isa<CXXThisRegion>(R)))
- return StoreRef(store, *this);
-
- const TypedRegion *TyR = cast<TypedRegion>(R);
-
- // Do not bind to arrays. We need to explicitly check for this so that
- // we do not encounter any weirdness of trying to load/store from arrays.
- if (TyR->isBoundable() && TyR->getValueType()->isArrayType())
- return StoreRef(store, *this);
-
- if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) {
- // Only convert 'V' to a location iff the underlying region type
- // is a location as well.
- // FIXME: We are allowing a store of an arbitrary location to
- // a pointer. We may wish to flag a type error here if the types
- // are incompatible. This may also cause lots of breakage
- // elsewhere. Food for thought.
- if (TyR->isBoundable() && Loc::isLocType(TyR->getValueType()))
- V = X->getLoc();
- }
-
- BindingsTy B = GetBindings(store);
- return StoreRef(V.isUnknown()
- ? VBFactory.remove(B, R).getRoot()
- : VBFactory.add(B, R, V).getRoot(), *this);
-}
-
-StoreRef BasicStoreManager::Remove(Store store, Loc loc) {
- switch (loc.getSubKind()) {
- case loc::MemRegionKind: {
- const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion();
-
- if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) ||
- isa<CXXThisRegion>(R)))
- return StoreRef(store, *this);
-
- return StoreRef(VBFactory.remove(GetBindings(store), R).getRoot(), *this);
- }
- default:
- assert ("Remove for given Loc type not yet implemented.");
- return StoreRef(store, *this);
- }
-}
-
-StoreRef BasicStoreManager::removeDeadBindings(Store store,
- const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
- llvm::SmallVectorImpl<const MemRegion*>& RegionRoots)
-{
- BindingsTy B = GetBindings(store);
- typedef SVal::symbol_iterator symbol_iterator;
-
- // Iterate over the variable bindings.
- for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) {
- if (const VarRegion *VR = dyn_cast<VarRegion>(I.getKey())) {
- if (SymReaper.isLive(VR))
- RegionRoots.push_back(VR);
- else
- continue;
- }
- else if (isa<ObjCIvarRegion>(I.getKey()) ||
- isa<NonStaticGlobalSpaceRegion>(I.getKey()) ||
- isa<CXXThisRegion>(I.getKey()))
- RegionRoots.push_back(I.getKey());
- else
- continue;
-
- // Mark the bindings in the data as live.
- SVal X = I.getData();
- for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI)
- SymReaper.markLive(*SI);
- }
-
- // Scan for live variables and live symbols.
- llvm::SmallPtrSet<const MemRegion*, 10> Marked;
-
- while (!RegionRoots.empty()) {
- const MemRegion* MR = RegionRoots.back();
- RegionRoots.pop_back();
-
- while (MR) {
- if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(MR)) {
- SymReaper.markLive(SymR->getSymbol());
- break;
- }
- else if (isa<VarRegion>(MR) || isa<ObjCIvarRegion>(MR) ||
- isa<NonStaticGlobalSpaceRegion>(MR) || isa<CXXThisRegion>(MR)) {
- if (Marked.count(MR))
- break;
-
- Marked.insert(MR);
- SVal X = Retrieve(store, loc::MemRegionVal(MR));
-
- // FIXME: We need to handle symbols nested in region definitions.
- for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI)
- SymReaper.markLive(*SI);
-
- if (!isa<loc::MemRegionVal>(X))
- break;
-
- const loc::MemRegionVal& LVD = cast<loc::MemRegionVal>(X);
- RegionRoots.push_back(LVD.getRegion());
- break;
- }
- else if (const SubRegion* R = dyn_cast<SubRegion>(MR))
- MR = R->getSuperRegion();
- else
- break;
- }
- }
-
- // Remove dead variable bindings.
- StoreRef newStore(store, *this);
- for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) {
- const MemRegion* R = I.getKey();
-
- if (!Marked.count(R)) {
- newStore = Remove(newStore.getStore(), svalBuilder.makeLoc(R));
- SVal X = I.getData();
-
- for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI)
- SymReaper.maybeDead(*SI);
- }
- }
-
- return newStore;
-}
-
-StoreRef BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl,
- const MemRegion *SelfRegion,
- Store St) {
-
- StoreRef newStore(St, *this);
-
- for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end();
- CI != CE; ++CI) {
-
- if (!*CI)
- continue;
-
- // Check if the statement is an ivar reference. We only
- // care about self.ivar.
- if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(*CI)) {
- const Expr *Base = IV->getBase()->IgnoreParenCasts();
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Base)) {
- if (DR->getDecl() == SelfDecl) {
- const ObjCIvarRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(),
- SelfRegion);
- SVal X = svalBuilder.getRegionValueSymbolVal(IVR);
- newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(IVR), X);
- }
- }
- }
- else
- newStore = scanForIvars(*CI, SelfDecl, SelfRegion, newStore.getStore());
- }
-
- return newStore;
-}
-
-StoreRef BasicStoreManager::getInitialStore(const LocationContext *InitLoc) {
- // The LiveVariables information already has a compilation of all VarDecls
- // used in the function. Iterate through this set, and "symbolicate"
- // any VarDecl whose value originally comes from outside the function.
- typedef LiveVariables::AnalysisDataTy LVDataTy;
- LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData();
- StoreRef St(VBFactory.getEmptyMap().getRoot(), *this);
-
- for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) {
- const NamedDecl* ND = I->first;
-
- // Handle implicit parameters.
- if (const ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) {
- const Decl& CD = *InitLoc->getDecl();
- if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) {
- if (MD->getSelfDecl() == PD) {
- // FIXME: Add type constraints (when they become available) to
- // SelfRegion? (i.e., it implements MD->getClassInterface()).
- const VarRegion *VR = MRMgr.getVarRegion(PD, InitLoc);
- const MemRegion *SelfRegion =
- svalBuilder.getRegionValueSymbolVal(VR).getAsRegion();
- assert(SelfRegion);
- St = Bind(St.getStore(), svalBuilder.makeLoc(VR),
- loc::MemRegionVal(SelfRegion));
- // Scan the method for ivar references. While this requires an
- // entire AST scan, the cost should not be high in practice.
- St = scanForIvars(MD->getBody(), PD, SelfRegion, St.getStore());
- }
- }
- }
- }
-
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(InitLoc->getDecl())) {
- // For C++ non-static member variables, add a symbolic region for 'this' in
- // the initial stack frame.
- if (MD->isInstance()) {
- QualType ThisT = MD->getThisType(StateMgr.getContext());
- MemRegionManager &RegMgr = svalBuilder.getRegionManager();
- const CXXThisRegion *ThisR = RegMgr.getCXXThisRegion(ThisT, InitLoc);
- SVal ThisV = svalBuilder.getRegionValueSymbolVal(ThisR);
- St = Bind(St.getStore(), svalBuilder.makeLoc(ThisR), ThisV);
- }
- }
-
- return St;
-}
-
-StoreRef BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR,
- SVal* InitVal) {
-
- BasicValueFactory& BasicVals = StateMgr.getBasicVals();
- const VarDecl *VD = VR->getDecl();
- StoreRef newStore(store, *this);
-
- // BasicStore does not model arrays and structs.
- if (VD->getType()->isArrayType() || VD->getType()->isStructureOrClassType())
- return newStore;
-
- if (VD->hasGlobalStorage()) {
- // Handle variables with global storage: extern, static, PrivateExtern.
-
- // FIXME:: static variables may have an initializer, but the second time a
- // function is called those values may not be current. Currently, a function
- // will not be called more than once.
-
- // Static global variables should not be visited here.
- assert(!(VD->getStorageClass() == SC_Static &&
- VD->isFileVarDecl()));
-
- // Process static variables.
- if (VD->getStorageClass() == SC_Static) {
- // C99: 6.7.8 Initialization
- // If an object that has static storage duration is not initialized
- // explicitly, then:
- // -if it has pointer type, it is initialized to a null pointer;
- // -if it has arithmetic type, it is initialized to (positive or
- // unsigned) zero;
- if (!InitVal) {
- QualType T = VD->getType();
- if (Loc::isLocType(T))
- newStore = Bind(store, loc::MemRegionVal(VR),
- loc::ConcreteInt(BasicVals.getValue(0, T)));
- else if (T->isIntegerType() && T->isScalarType())
- newStore = Bind(store, loc::MemRegionVal(VR),
- nonloc::ConcreteInt(BasicVals.getValue(0, T)));
- } else {
- newStore = Bind(store, loc::MemRegionVal(VR), *InitVal);
- }
- }
- } else {
- // Process local scalar variables.
- QualType T = VD->getType();
- // BasicStore only supports scalars.
- if ((T->isScalarType() || T->isReferenceType()) &&
- svalBuilder.getSymbolManager().canSymbolicate(T)) {
- SVal V = InitVal ? *InitVal : UndefinedVal();
- newStore = Bind(store, loc::MemRegionVal(VR), V);
- }
- }
-
- return newStore;
-}
-
-void BasicStoreManager::print(Store store, llvm::raw_ostream& Out,
- const char* nl, const char *sep) {
-
- BindingsTy B = GetBindings(store);
- Out << "Variables:" << nl;
-
- bool isFirst = true;
-
- for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) {
- if (isFirst)
- isFirst = false;
- else
- Out << nl;
-
- Out << ' ' << I.getKey() << " : " << I.getData();
- }
-}
-
-
-void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) {
- BindingsTy B = GetBindings(store);
-
- for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I)
- if (!f.HandleBinding(*this, store, I.getKey(), I.getData()))
- return;
-
-}
-
-StoreManager::BindingsHandler::~BindingsHandler() {}
-
-//===----------------------------------------------------------------------===//
-// Binding invalidation.
-//===----------------------------------------------------------------------===//
-
-
-StoreRef BasicStoreManager::invalidateRegions(Store store,
- const MemRegion * const *I,
- const MemRegion * const *End,
- const Expr *E, unsigned Count,
- InvalidatedSymbols &IS,
- bool invalidateGlobals,
- InvalidatedRegions *Regions) {
- StoreRef newStore(store, *this);
-
- if (invalidateGlobals) {
- BindingsTy B = GetBindings(store);
- for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) {
- const MemRegion *R = I.getKey();
- if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()))
- newStore = invalidateRegion(newStore.getStore(), R, E, Count, IS);
- }
- }
-
- for ( ; I != End ; ++I) {
- const MemRegion *R = *I;
- // Don't invalidate globals twice.
- if (invalidateGlobals) {
- if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()))
- continue;
- }
- newStore = invalidateRegion(newStore.getStore(), *I, E, Count, IS);
- if (Regions)
- Regions->push_back(R);
- }
-
- // FIXME: This is copy-and-paste from RegionStore.cpp.
- if (invalidateGlobals) {
- // Bind the non-static globals memory space to a new symbol that we will
- // use to derive the bindings for all non-static globals.
- const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion();
- SVal V =
- svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, E,
- /* symbol type, doesn't matter */ Ctx.IntTy,
- Count);
-
- newStore = Bind(newStore.getStore(), loc::MemRegionVal(GS), V);
- if (Regions)
- Regions->push_back(GS);
- }
-
- return newStore;
-}
-
-
-StoreRef BasicStoreManager::invalidateRegion(Store store,
- const MemRegion *R,
- const Expr *E,
- unsigned Count,
- InvalidatedSymbols &IS) {
- R = R->StripCasts();
-
- if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R)))
- return StoreRef(store, *this);
-
- BindingsTy B = GetBindings(store);
- if (BindingsTy::data_type *Val = B.lookup(R)) {
- if (SymbolRef Sym = Val->getAsSymbol())
- IS.insert(Sym);
- }
-
- QualType T = cast<TypedRegion>(R)->getValueType();
- SVal V = svalBuilder.getConjuredSymbolVal(R, E, T, Count);
- return Bind(store, loc::MemRegionVal(R), V);
-}
diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 0ed4ff1..fe96700 100644
--- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -27,7 +27,7 @@ void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T,
void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID,
const StoreRef &store,
- const TypedRegion *region) {
+ const TypedValueRegion *region) {
ID.AddPointer(store.getStore());
ID.AddPointer(region);
}
@@ -70,7 +70,7 @@ BasicValueFactory::~BasicValueFactory() {
const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) {
llvm::FoldingSetNodeID ID;
- void* InsertPos;
+ void *InsertPos;
typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy;
X.Profile(ID);
@@ -113,7 +113,7 @@ BasicValueFactory::getCompoundValData(QualType T,
llvm::FoldingSetNodeID ID;
CompoundValData::Profile(ID, T, Vals);
- void* InsertPos;
+ void *InsertPos;
CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
@@ -128,10 +128,10 @@ BasicValueFactory::getCompoundValData(QualType T,
const LazyCompoundValData*
BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
- const TypedRegion *region) {
+ const TypedValueRegion *region) {
llvm::FoldingSetNodeID ID;
LazyCompoundValData::Profile(ID, store, region);
- void* InsertPos;
+ void *InsertPos;
LazyCompoundValData *D =
LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
@@ -243,7 +243,7 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
if (!PersistentSVals) PersistentSVals = new PersistentSValsTy();
llvm::FoldingSetNodeID ID;
- void* InsertPos;
+ void *InsertPos;
V.Profile(ID);
ID.AddPointer((void*) Data);
@@ -268,7 +268,7 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) {
if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy();
llvm::FoldingSetNodeID ID;
- void* InsertPos;
+ void *InsertPos;
V1.Profile(ID);
V2.Profile(ID);
diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp
index ed52b6b..74d761e 100644
--- a/lib/StaticAnalyzer/Core/BlockCounter.cpp
+++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp
@@ -48,11 +48,11 @@ public:
typedef llvm::ImmutableMap<CountKey, unsigned> CountMap;
-static inline CountMap GetMap(void* D) {
+static inline CountMap GetMap(void *D) {
return CountMap(static_cast<CountMap::TreeTy*>(D));
}
-static inline CountMap::Factory& GetFactory(void* F) {
+static inline CountMap::Factory& GetFactory(void *F) {
return *static_cast<CountMap::Factory*>(F);
}
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index 8b5d383..fbbdb04 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -33,52 +33,31 @@ using namespace clang;
using namespace ento;
BugReporterVisitor::~BugReporterVisitor() {}
-BugReporterContext::~BugReporterContext() {
- for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I)
- if ((*I)->isOwnedByReporterContext()) delete *I;
-}
-
-void BugReporterContext::addVisitor(BugReporterVisitor* visitor) {
- if (!visitor)
- return;
-
- llvm::FoldingSetNodeID ID;
- visitor->Profile(ID);
- void *InsertPos;
-
- if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
- delete visitor;
- return;
- }
-
- CallbacksSet.InsertNode(visitor, InsertPos);
- Callbacks = F.add(visitor, Callbacks);
-}
//===----------------------------------------------------------------------===//
// Helper routines for walking the ExplodedGraph and fetching statements.
//===----------------------------------------------------------------------===//
-static inline const Stmt* GetStmt(const ProgramPoint &P) {
+static inline const Stmt *GetStmt(const ProgramPoint &P) {
if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P))
return SP->getStmt();
- else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P))
+ else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P))
return BE->getSrc()->getTerminator();
return 0;
}
static inline const ExplodedNode*
-GetPredecessorNode(const ExplodedNode* N) {
+GetPredecessorNode(const ExplodedNode *N) {
return N->pred_empty() ? NULL : *(N->pred_begin());
}
static inline const ExplodedNode*
-GetSuccessorNode(const ExplodedNode* N) {
+GetSuccessorNode(const ExplodedNode *N) {
return N->succ_empty() ? NULL : *(N->succ_begin());
}
-static const Stmt* GetPreviousStmt(const ExplodedNode* N) {
+static const Stmt *GetPreviousStmt(const ExplodedNode *N) {
for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N))
if (const Stmt *S = GetStmt(N->getLocation()))
return S;
@@ -86,7 +65,7 @@ static const Stmt* GetPreviousStmt(const ExplodedNode* N) {
return 0;
}
-static const Stmt* GetNextStmt(const ExplodedNode* N) {
+static const Stmt *GetNextStmt(const ExplodedNode *N) {
for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N))
if (const Stmt *S = GetStmt(N->getLocation())) {
// Check if the statement is '?' or '&&'/'||'. These are "merges",
@@ -104,11 +83,6 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) {
default:
break;
}
-
- // Some expressions don't have locations.
- if (S->getLocStart().isInvalid())
- continue;
-
return S;
}
@@ -116,7 +90,7 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) {
}
static inline const Stmt*
-GetCurrentOrPreviousStmt(const ExplodedNode* N) {
+GetCurrentOrPreviousStmt(const ExplodedNode *N) {
if (const Stmt *S = GetStmt(N->getLocation()))
return S;
@@ -124,7 +98,7 @@ GetCurrentOrPreviousStmt(const ExplodedNode* N) {
}
static inline const Stmt*
-GetCurrentOrNextStmt(const ExplodedNode* N) {
+GetCurrentOrNextStmt(const ExplodedNode *N) {
if (const Stmt *S = GetStmt(N->getLocation()))
return S;
@@ -145,7 +119,7 @@ public:
NodeMapClosure(NodeBackMap *m) : M(*m) {}
~NodeMapClosure() {}
- const ExplodedNode* getOriginalNode(const ExplodedNode* N) {
+ const ExplodedNode *getOriginalNode(const ExplodedNode *N) {
NodeBackMap::iterator I = M.find(N);
return I == M.end() ? 0 : I->second;
}
@@ -153,25 +127,29 @@ public:
class PathDiagnosticBuilder : public BugReporterContext {
BugReport *R;
- PathDiagnosticClient *PDC;
+ PathDiagnosticConsumer *PDC;
llvm::OwningPtr<ParentMap> PM;
NodeMapClosure NMC;
public:
PathDiagnosticBuilder(GRBugReporter &br,
BugReport *r, NodeBackMap *Backmap,
- PathDiagnosticClient *pdc)
+ PathDiagnosticConsumer *pdc)
: BugReporterContext(br),
- R(r), PDC(pdc), NMC(Backmap) {
- addVisitor(R);
- }
+ R(r), PDC(pdc), NMC(Backmap) {}
- PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N);
+ PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
- PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os,
- const ExplodedNode* N);
+ PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os,
+ const ExplodedNode *N);
+
+ BugReport *getBugReport() { return R; }
Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
+ const LocationContext* getLocationContext() {
+ return R->getErrorNode()->getLocationContext();
+ }
+
ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); }
const Stmt *getParent(const Stmt *S) {
@@ -182,8 +160,8 @@ public:
PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
- PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const {
- return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive;
+ PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const {
+ return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive;
}
bool supportsLogicalOpControlFlow() const {
@@ -193,17 +171,17 @@ public:
} // end anonymous namespace
PathDiagnosticLocation
-PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) {
+PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
if (const Stmt *S = GetNextStmt(N))
- return PathDiagnosticLocation(S, getSourceManager());
+ return PathDiagnosticLocation(S, getSourceManager(), getLocationContext());
- return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(),
- getSourceManager());
+ return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(),
+ getSourceManager());
}
PathDiagnosticLocation
-PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os,
- const ExplodedNode* N) {
+PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
+ const ExplodedNode *N) {
// Slow, but probably doesn't matter.
if (os.str().empty())
@@ -213,7 +191,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os,
if (Loc.asStmt())
os << "Execution continues on line "
- << getSourceManager().getInstantiationLineNumber(Loc.asLocation())
+ << getSourceManager().getExpansionLineNumber(Loc.asLocation())
<< '.';
else {
os << "Execution jumps to the end of the ";
@@ -253,9 +231,10 @@ static bool IsNested(const Stmt *S, ParentMap &PM) {
PathDiagnosticLocation
PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
- assert(S && "Null Stmt* passed to getEnclosingStmtLocation");
+ assert(S && "Null Stmt *passed to getEnclosingStmtLocation");
ParentMap &P = getParentMap();
SourceManager &SMgr = getSourceManager();
+ const LocationContext *LC = getLocationContext();
while (IsNested(S, P)) {
const Stmt *Parent = P.getParentIgnoreParens(S);
@@ -267,44 +246,44 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
case Stmt::BinaryOperatorClass: {
const BinaryOperator *B = cast<BinaryOperator>(Parent);
if (B->isLogicalOp())
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
break;
}
case Stmt::CompoundStmtClass:
case Stmt::StmtExprClass:
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
case Stmt::ChooseExprClass:
// Similar to '?' if we are referring to condition, just have the edge
// point to the entire choose expression.
if (cast<ChooseExpr>(Parent)->getCond() == S)
- return PathDiagnosticLocation(Parent, SMgr);
+ return PathDiagnosticLocation(Parent, SMgr, LC);
else
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass:
// For '?', if we are referring to condition, just have the edge point
// to the entire '?' expression.
if (cast<AbstractConditionalOperator>(Parent)->getCond() == S)
- return PathDiagnosticLocation(Parent, SMgr);
+ return PathDiagnosticLocation(Parent, SMgr, LC);
else
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
case Stmt::DoStmtClass:
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
case Stmt::ForStmtClass:
if (cast<ForStmt>(Parent)->getBody() == S)
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
break;
case Stmt::IfStmtClass:
if (cast<IfStmt>(Parent)->getCond() != S)
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
break;
case Stmt::ObjCForCollectionStmtClass:
if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S)
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
break;
case Stmt::WhileStmtClass:
if (cast<WhileStmt>(Parent)->getCond() != S)
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
break;
default:
break;
@@ -322,7 +301,7 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
switch (Parent->getStmtClass()) {
case Stmt::ForStmtClass:
case Stmt::ObjCForCollectionStmtClass:
- return PathDiagnosticLocation(Parent, SMgr);
+ return PathDiagnosticLocation(Parent, SMgr, LC);
default:
break;
}
@@ -335,20 +314,20 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
if (const ForStmt *FS =
dyn_cast_or_null<ForStmt>(P.getParentIgnoreParens(S))) {
if (FS->getInit() == S)
- return PathDiagnosticLocation(FS, SMgr);
+ return PathDiagnosticLocation(FS, SMgr, LC);
}
}
- return PathDiagnosticLocation(S, SMgr);
+ return PathDiagnosticLocation(S, SMgr, LC);
}
//===----------------------------------------------------------------------===//
// ScanNotableSymbols: closure-like callback for scanning Store bindings.
//===----------------------------------------------------------------------===//
-static const VarDecl*
-GetMostRecentVarDeclBinding(const ExplodedNode* N,
- GRStateManager& VMgr, SVal X) {
+static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N,
+ ProgramStateManager& VMgr,
+ SVal X) {
for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) {
@@ -357,7 +336,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N,
if (!isa<PostStmt>(P))
continue;
- const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt());
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt());
if (!DR)
continue;
@@ -367,7 +346,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N,
if (X != Y)
continue;
- const VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl());
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (!VD)
continue;
@@ -383,19 +362,29 @@ class NotableSymbolHandler
: public StoreManager::BindingsHandler {
SymbolRef Sym;
- const GRState* PrevSt;
- const Stmt* S;
- GRStateManager& VMgr;
- const ExplodedNode* Pred;
+ const ProgramState *PrevSt;
+ const Stmt *S;
+ ProgramStateManager& VMgr;
+ const ExplodedNode *Pred;
PathDiagnostic& PD;
BugReporter& BR;
public:
- NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s,
- GRStateManager& vmgr, const ExplodedNode* pred,
- PathDiagnostic& pd, BugReporter& br)
- : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {}
+ NotableSymbolHandler(SymbolRef sym,
+ const ProgramState *prevst,
+ const Stmt *s,
+ ProgramStateManager& vmgr,
+ const ExplodedNode *pred,
+ PathDiagnostic& pd,
+ BugReporter& br)
+ : Sym(sym),
+ PrevSt(prevst),
+ S(s),
+ VMgr(vmgr),
+ Pred(pred),
+ PD(pd),
+ BR(br) {}
bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R,
SVal V) {
@@ -422,14 +411,14 @@ public:
return true;
// What variable did we assign to?
- DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts());
+ DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts());
if (!DR)
return true;
VD = dyn_cast<VarDecl>(DR->getDecl());
}
- else if (const DeclStmt* DS = dyn_cast<DeclStmt>(S)) {
+ else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
// FIXME: Eventually CFGs won't have DeclStmts. Right now we
// assume that each DeclStmt has a single Decl. This invariant
// holds by construction in the CFG.
@@ -440,19 +429,20 @@ public:
return true;
// What is the most recently referenced variable with this binding?
- const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V);
+ const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V);
if (!MostRecent)
return true;
// Create the diagnostic.
- FullSourceLoc L(S->getLocStart(), BR.getSourceManager());
-
if (Loc::isLocType(VD->getType())) {
- std::string msg = "'" + std::string(VD->getNameAsString()) +
- "' now aliases '" + MostRecent->getNameAsString() + "'";
-
- PD.push_front(new PathDiagnosticEventPiece(L, msg));
+ llvm::SmallString<64> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << '\'' << *VD << "' now aliases '" << *MostRecent << '\'';
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createBegin(S, BR.getSourceManager(),
+ Pred->getLocationContext());
+ PD.push_front(new PathDiagnosticEventPiece(L, os.str()));
}
return true;
@@ -460,20 +450,20 @@ public:
};
}
-static void HandleNotableSymbol(const ExplodedNode* N,
- const Stmt* S,
+static void HandleNotableSymbol(const ExplodedNode *N,
+ const Stmt *S,
SymbolRef Sym, BugReporter& BR,
PathDiagnostic& PD) {
- const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin();
- const GRState* PrevSt = Pred ? Pred->getState() : 0;
+ const ExplodedNode *Pred = N->pred_empty() ? 0 : *N->pred_begin();
+ const ProgramState *PrevSt = Pred ? Pred->getState() : 0;
if (!PrevSt)
return;
// Look at the region bindings of the current state that map to the
// specified symbol. Are any of them not in the previous state?
- GRStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager();
+ ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager();
NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR);
cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H);
}
@@ -483,13 +473,13 @@ class ScanNotableSymbols
: public StoreManager::BindingsHandler {
llvm::SmallSet<SymbolRef, 10> AlreadyProcessed;
- const ExplodedNode* N;
- const Stmt* S;
+ const ExplodedNode *N;
+ const Stmt *S;
GRBugReporter& BR;
PathDiagnostic& PD;
public:
- ScanNotableSymbols(const ExplodedNode* n, const Stmt* s,
+ ScanNotableSymbols(const ExplodedNode *n, const Stmt *s,
GRBugReporter& br, PathDiagnostic& pd)
: N(n), S(s), BR(br), PD(pd) {}
@@ -526,7 +516,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
const ExplodedNode *N) {
SourceManager& SMgr = PDB.getSourceManager();
- const ExplodedNode* NextNode = N->pred_empty()
+ const LocationContext *LC = PDB.getLocationContext();
+ const ExplodedNode *NextNode = N->pred_empty()
? NULL : *(N->pred_begin());
while (NextNode) {
N = NextNode;
@@ -534,15 +525,17 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
- if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
- const CFGBlock* Src = BE->getSrc();
- const CFGBlock* Dst = BE->getDst();
- const Stmt* T = Src->getTerminator();
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ const CFGBlock *Src = BE->getSrc();
+ const CFGBlock *Dst = BE->getDst();
+ const Stmt *T = Src->getTerminator();
if (!T)
continue;
- FullSourceLoc Start(T->getLocStart(), SMgr);
+ PathDiagnosticLocation Start =
+ PathDiagnosticLocation::createBegin(T, SMgr,
+ N->getLocationContext());
switch (T->getStmtClass()) {
default:
@@ -550,7 +543,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
case Stmt::GotoStmtClass:
case Stmt::IndirectGotoStmtClass: {
- const Stmt* S = GetNextStmt(N);
+ const Stmt *S = GetNextStmt(N);
if (!S)
continue;
@@ -560,7 +553,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
os << "Control jumps to line "
- << End.asLocation().getInstantiationLineNumber();
+ << End.asLocation().getExpansionLineNumber();
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
break;
@@ -571,45 +564,45 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- if (const Stmt* S = Dst->getLabel()) {
- PathDiagnosticLocation End(S, SMgr);
+ if (const Stmt *S = Dst->getLabel()) {
+ PathDiagnosticLocation End(S, SMgr, LC);
switch (S->getStmtClass()) {
default:
os << "No cases match in the switch statement. "
"Control jumps to line "
- << End.asLocation().getInstantiationLineNumber();
+ << End.asLocation().getExpansionLineNumber();
break;
case Stmt::DefaultStmtClass:
os << "Control jumps to the 'default' case at line "
- << End.asLocation().getInstantiationLineNumber();
+ << End.asLocation().getExpansionLineNumber();
break;
case Stmt::CaseStmtClass: {
os << "Control jumps to 'case ";
- const CaseStmt* Case = cast<CaseStmt>(S);
- const Expr* LHS = Case->getLHS()->IgnoreParenCasts();
+ const CaseStmt *Case = cast<CaseStmt>(S);
+ const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
// Determine if it is an enum.
bool GetRawInt = true;
- if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) {
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) {
// FIXME: Maybe this should be an assertion. Are there cases
// were it is not an EnumConstantDecl?
- const EnumConstantDecl* D =
+ const EnumConstantDecl *D =
dyn_cast<EnumConstantDecl>(DR->getDecl());
if (D) {
GetRawInt = false;
- os << D;
+ os << *D;
}
}
if (GetRawInt)
- os << LHS->EvaluateAsInt(PDB.getASTContext());
+ os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
os << ":' at line "
- << End.asLocation().getInstantiationLineNumber();
+ << End.asLocation().getExpansionLineNumber();
break;
}
}
@@ -673,14 +666,15 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (*(Src->succ_begin()+1) == Dst) {
os << "false";
- PathDiagnosticLocation End(B->getLHS(), SMgr);
- PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr);
+ PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
+ PathDiagnosticLocation Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SMgr);
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
os << "true";
- PathDiagnosticLocation Start(B->getLHS(), SMgr);
+ PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
@@ -692,15 +686,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (*(Src->succ_begin()+1) == Dst) {
os << "false";
- PathDiagnosticLocation Start(B->getLHS(), SMgr);
+ PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
else {
os << "true";
- PathDiagnosticLocation End(B->getLHS(), SMgr);
- PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr);
+ PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
+ PathDiagnosticLocation Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SMgr);
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
os.str()));
}
@@ -781,14 +776,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
}
if (NextNode) {
- for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(),
- E = PDB.visitor_end(); I!=E; ++I) {
- if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB))
+ // Add diagnostic pieces from custom visitors.
+ BugReport *R = PDB.getBugReport();
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end(); I!=E; ++I) {
+ if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R))
PD.push_front(p);
}
}
- if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
+ if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
// Scan the region bindings, and see if a "notable" symbol has a new
// lval binding.
ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD);
@@ -882,11 +879,11 @@ class EdgeBuilder {
}
if (S != Original)
- L = PathDiagnosticLocation(S, L.getManager());
+ L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext());
}
if (firstCharOnly)
- L = PathDiagnosticLocation(L.asLocation());
+ L = PathDiagnosticLocation::createSingleLocation(L);
return L;
}
@@ -915,17 +912,14 @@ public:
~EdgeBuilder() {
while (!CLocs.empty()) popLocation();
-
+
// Finally, add an initial edge from the start location of the first
// statement (if it doesn't already exist).
- // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
- if (const CompoundStmt *CS =
- dyn_cast_or_null<CompoundStmt>(PDB.getCodeDecl().getBody()))
- if (!CS->body_empty()) {
- SourceLocation Loc = (*CS->body_begin())->getLocStart();
- rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager()));
- }
-
+ PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
+ PDB.getLocationContext(),
+ PDB.getSourceManager());
+ if (L.isValid())
+ rawAddEdge(L);
}
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false);
@@ -974,15 +968,15 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
SourceRange ContaineeR = Containee.asRange();
SourceManager &SM = PDB.getSourceManager();
- SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin());
- SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd());
- SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin());
- SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd());
+ SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin());
+ SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd());
+ SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin());
+ SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd());
- unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg);
- unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd);
- unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg);
- unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd);
+ unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg);
+ unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd);
+ unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg);
+ unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd);
assert(ContainerBegLine <= ContainerEndLine);
assert(ContaineeBegLine <= ContaineeEndLine);
@@ -990,11 +984,11 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
return (ContainerBegLine <= ContaineeBegLine &&
ContainerEndLine >= ContaineeEndLine &&
(ContainerBegLine != ContaineeBegLine ||
- SM.getInstantiationColumnNumber(ContainerRBeg) <=
- SM.getInstantiationColumnNumber(ContaineeRBeg)) &&
+ SM.getExpansionColumnNumber(ContainerRBeg) <=
+ SM.getExpansionColumnNumber(ContaineeRBeg)) &&
(ContainerEndLine != ContaineeEndLine ||
- SM.getInstantiationColumnNumber(ContainerREnd) >=
- SM.getInstantiationColumnNumber(ContainerREnd)));
+ SM.getExpansionColumnNumber(ContainerREnd) >=
+ SM.getExpansionColumnNumber(ContainerREnd)));
}
void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
@@ -1010,8 +1004,8 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
return;
// FIXME: Ignore intra-macro edges for now.
- if (NewLocClean.asLocation().getInstantiationLoc() ==
- PrevLocClean.asLocation().getInstantiationLoc())
+ if (NewLocClean.asLocation().getExpansionLoc() ==
+ PrevLocClean.asLocation().getExpansionLoc())
return;
PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean));
@@ -1099,7 +1093,7 @@ void EdgeBuilder::addContext(const Stmt *S) {
if (!S)
return;
- PathDiagnosticLocation L(S, PDB.getSourceManager());
+ PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext());
while (!CLocs.empty()) {
const PathDiagnosticLocation &TopContextLoc = CLocs.back();
@@ -1124,8 +1118,9 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PathDiagnosticBuilder &PDB,
const ExplodedNode *N) {
EdgeBuilder EB(PD, PDB);
+ const SourceManager& SM = PDB.getSourceManager();
- const ExplodedNode* NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
+ const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
while (NextNode) {
N = NextNode;
NextNode = GetPredecessorNode(N);
@@ -1139,7 +1134,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
// Are we jumping to the head of a loop? Add a special diagnostic.
if (const Stmt *Loop = BE->getDst()->getLoopTarget()) {
- PathDiagnosticLocation L(Loop, PDB.getSourceManager());
+ PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext());
const CompoundStmt *CS = NULL;
if (!Term) {
@@ -1157,9 +1152,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
PD.push_front(p);
if (CS) {
- PathDiagnosticLocation BL(CS->getRBracLoc(),
- PDB.getSourceManager());
- BL = PathDiagnosticLocation(BL.asLocation());
+ PathDiagnosticLocation BL =
+ PathDiagnosticLocation::createEndBrace(CS, SM);
EB.addEdge(BL);
}
}
@@ -1188,9 +1182,11 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
if (!NextNode)
continue;
- for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(),
- E = PDB.visitor_end(); I!=E; ++I) {
- if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) {
+ // Add pieces from custom visitors.
+ BugReport *R = PDB.getBugReport();
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end(); I!=E; ++I) {
+ if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) {
const PathDiagnosticLocation &Loc = p->getLocation();
EB.addEdge(Loc, true);
PD.push_front(p);
@@ -1211,14 +1207,58 @@ void BugType::FlushReports(BugReporter &BR) {}
//===----------------------------------------------------------------------===//
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
-BugReport::~BugReport() {}
-RangedBugReport::~RangedBugReport() {}
-const Stmt* BugReport::getStmt() const {
+void BugReport::addVisitor(BugReporterVisitor* visitor) {
+ if (!visitor)
+ return;
+
+ llvm::FoldingSetNodeID ID;
+ visitor->Profile(ID);
+ void *InsertPos;
+
+ if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
+ delete visitor;
+ return;
+ }
+
+ CallbacksSet.InsertNode(visitor, InsertPos);
+ Callbacks = F.add(visitor, Callbacks);
+}
+
+BugReport::~BugReport() {
+ for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) {
+ delete *I;
+ }
+}
+
+void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
+ hash.AddPointer(&BT);
+ hash.AddString(Description);
+ if (Location.isValid()) {
+ Location.Profile(hash);
+ } else {
+ assert(ErrorNode);
+ hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
+ }
+
+ for (SmallVectorImpl<SourceRange>::const_iterator I =
+ Ranges.begin(), E = Ranges.end(); I != E; ++I) {
+ const SourceRange range = *I;
+ if (!range.isValid())
+ continue;
+ hash.AddInteger(range.getBegin().getRawEncoding());
+ hash.AddInteger(range.getEnd().getRawEncoding());
+ }
+}
+
+const Stmt *BugReport::getStmt() const {
+ if (!ErrorNode)
+ return 0;
+
ProgramPoint ProgP = ErrorNode->getLocation();
const Stmt *S = NULL;
- if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) {
+ if (BlockEntrance *BE = dyn_cast<BlockEntrance>(&ProgP)) {
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
if (BE->getBlock() == &Exit)
S = GetPreviousStmt(ErrorNode);
@@ -1229,61 +1269,47 @@ const Stmt* BugReport::getStmt() const {
return S;
}
-PathDiagnosticPiece*
-BugReport::getEndPath(BugReporterContext& BRC,
- const ExplodedNode* EndPathNode) {
-
- const Stmt* S = getStmt();
-
- if (!S)
- return NULL;
-
- BugReport::ranges_iterator Beg, End;
- llvm::tie(Beg, End) = getRanges();
- PathDiagnosticLocation L(S, BRC.getSourceManager());
-
- // Only add the statement itself as a range if we didn't specify any
- // special ranges for this report.
- PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(),
- Beg == End);
+std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator>
+BugReport::getRanges() {
+ // If no custom ranges, add the range of the statement corresponding to
+ // the error node.
+ if (Ranges.empty()) {
+ if (const Expr *E = dyn_cast_or_null<Expr>(getStmt()))
+ addRange(E->getSourceRange());
+ else
+ return std::make_pair(ranges_iterator(), ranges_iterator());
+ }
- for (; Beg != End; ++Beg)
- P->addRange(*Beg);
+ // User-specified absence of range info.
+ if (Ranges.size() == 1 && !Ranges.begin()->isValid())
+ return std::make_pair(ranges_iterator(), ranges_iterator());
- return P;
+ return std::make_pair(Ranges.begin(), Ranges.end());
}
-std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator>
-BugReport::getRanges() const {
- if (const Expr* E = dyn_cast_or_null<Expr>(getStmt())) {
- R = E->getSourceRange();
- assert(R.isValid());
- return std::make_pair(&R, &R+1);
- }
- else
- return std::make_pair(ranges_iterator(), ranges_iterator());
-}
+PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
+ if (ErrorNode) {
+ assert(!Location.isValid() &&
+ "Either Location or ErrorNode should be specified but not both.");
+
+ if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) {
+ const LocationContext *LC = ErrorNode->getLocationContext();
-SourceLocation BugReport::getLocation() const {
- if (ErrorNode)
- if (const Stmt* S = GetCurrentOrPreviousStmt(ErrorNode)) {
// For member expressions, return the location of the '.' or '->'.
if (const MemberExpr *ME = dyn_cast<MemberExpr>(S))
- return ME->getMemberLoc();
+ return PathDiagnosticLocation::createMemberLoc(ME, SM);
// For binary operators, return the location of the operator.
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S))
- return B->getOperatorLoc();
+ return PathDiagnosticLocation::createOperatorLoc(B, SM);
- return S->getLocStart();
+ return PathDiagnosticLocation::createBegin(S, SM, LC);
}
+ } else {
+ assert(Location.isValid());
+ return Location;
+ }
- return FullSourceLoc();
-}
-
-PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N,
- const ExplodedNode* PrevN,
- BugReporterContext &BRC) {
- return NULL;
+ return PathDiagnosticLocation();
}
//===----------------------------------------------------------------------===//
@@ -1299,10 +1325,19 @@ BugReporterData::~BugReporterData() {}
ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); }
-GRStateManager&
+ProgramStateManager&
GRBugReporter::getStateManager() { return Eng.getStateManager(); }
-BugReporter::~BugReporter() { FlushReports(); }
+BugReporter::~BugReporter() {
+ FlushReports();
+
+ // Free the bug reports we are tracking.
+ typedef std::vector<BugReportEquivClass *> ContTy;
+ for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end();
+ I != E; ++I) {
+ delete *I;
+ }
+}
void BugReporter::FlushReports() {
if (BugTypes.isEmpty())
@@ -1312,10 +1347,10 @@ void BugReporter::FlushReports() {
// warnings and new BugTypes.
// FIXME: Only NSErrorChecker needs BugType's FlushReports.
// Turn NSErrorChecker into a proper checker and remove this.
- llvm::SmallVector<const BugType*, 16> bugTypes;
+ SmallVector<const BugType*, 16> bugTypes;
for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I)
bugTypes.push_back(*I);
- for (llvm::SmallVector<const BugType*, 16>::iterator
+ for (SmallVector<const BugType*, 16>::iterator
I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
const_cast<BugType*>(*I)->FlushReports(*this);
@@ -1344,7 +1379,7 @@ void BugReporter::FlushReports() {
static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>,
std::pair<ExplodedNode*, unsigned> >
MakeReportGraph(const ExplodedGraph* G,
- llvm::SmallVectorImpl<const ExplodedNode*> &nodes) {
+ SmallVectorImpl<const ExplodedNode*> &nodes) {
// Create the trimmed graph. It will contain the shortest paths from the
// error nodes to the root. In the new graph we should only have one
@@ -1390,10 +1425,10 @@ MakeReportGraph(const ExplodedGraph* G,
llvm::DenseMap<const void*,unsigned> Visited;
unsigned cnt = 0;
- const ExplodedNode* Root = 0;
+ const ExplodedNode *Root = 0;
while (!WS.empty()) {
- const ExplodedNode* Node = WS.front();
+ const ExplodedNode *Node = WS.front();
WS.pop();
if (Visited.find(Node) != Visited.end())
@@ -1426,7 +1461,7 @@ MakeReportGraph(const ExplodedGraph* G,
// Create the equivalent node in the new graph with the same state
// and location.
- ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState());
+ ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState());
// Store the mapping to the original node.
llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N);
@@ -1495,7 +1530,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
// Determine the instantiation location, which is the location we group
// related PathDiagnosticPieces.
SourceLocation InstantiationLoc = Loc.isMacroID() ?
- SM.getInstantiationLoc(Loc) :
+ SM.getExpansionLoc(Loc) :
SourceLocation();
if (Loc.isFileID()) {
@@ -1517,7 +1552,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
PathDiagnosticMacroPiece *MacroGroup = 0;
SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
- SM.getInstantiationLoc(Loc) :
+ SM.getExpansionLoc(Loc) :
SourceLocation();
// Walk the entire macro stack.
@@ -1537,7 +1572,9 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
// Create a new macro group and add it to the stack.
- PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc);
+ PathDiagnosticMacroPiece *NewGroup =
+ new PathDiagnosticMacroPiece(
+ PathDiagnosticLocation::createSingleLocation(I->getLocation()));
if (MacroGroup)
MacroGroup->push_back(NewGroup);
@@ -1569,11 +1606,11 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
}
void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
- llvm::SmallVectorImpl<BugReport *> &bugReports) {
+ SmallVectorImpl<BugReport *> &bugReports) {
assert(!bugReports.empty());
- llvm::SmallVector<const ExplodedNode *, 10> errorNodes;
- for (llvm::SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(),
+ SmallVector<const ExplodedNode *, 10> errorNodes;
+ for (SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(),
E = bugReports.end(); I != E; ++I) {
errorNodes.push_back((*I)->getErrorNode());
}
@@ -1594,22 +1631,36 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
const ExplodedNode *N = GPair.second.first;
// Start building the path diagnostic...
- PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient());
-
- if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N))
- PD.push_back(Piece);
+ PathDiagnosticBuilder PDB(*this, R, BackMap.get(),
+ getPathDiagnosticConsumer());
+
+ // Register additional node visitors.
+ R->addVisitor(new NilReceiverBRVisitor());
+ R->addVisitor(new ConditionBRVisitor());
+
+ // Generate the very last diagnostic piece - the piece is visible before
+ // the trace is expanded.
+ PathDiagnosticPiece *LastPiece = 0;
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end(); I!=E; ++I) {
+ if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) {
+ assert (!LastPiece &&
+ "There can only be one final piece in a diagnostic.");
+ LastPiece = Piece;
+ }
+ }
+ if (!LastPiece)
+ LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R);
+ if (LastPiece)
+ PD.push_back(LastPiece);
else
return;
- // Register node visitors.
- R->registerInitialVisitors(PDB, N);
- bugreporter::registerNilReceiverVisitor(PDB);
-
switch (PDB.getGenerationScheme()) {
- case PathDiagnosticClient::Extensive:
+ case PathDiagnosticConsumer::Extensive:
GenerateExtensivePathDiagnostic(PD, PDB, N);
break;
- case PathDiagnosticClient::Minimal:
+ case PathDiagnosticConsumer::Minimal:
GenerateMinimalPathDiagnostic(PD, PDB, N);
break;
}
@@ -1633,6 +1684,7 @@ void BugReporter::EmitReport(BugReport* R) {
if (!EQ) {
EQ = new BugReportEquivClass(R);
EQClasses.InsertNode(EQ, InsertPos);
+ EQClassesVector.push_back(EQ);
}
else
EQ->AddReport(R);
@@ -1655,7 +1707,7 @@ struct FRIEC_WLItem {
static BugReport *
FindReportInEquivalenceClass(BugReportEquivClass& EQ,
- llvm::SmallVectorImpl<BugReport*> &bugReports) {
+ SmallVectorImpl<BugReport*> &bugReports) {
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
assert(I != E);
@@ -1667,7 +1719,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// to 'Nodes'. Any of the reports will serve as a "representative" report.
if (!BT.isSuppressOnSink()) {
for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) {
- const ExplodedNode* N = I->getErrorNode();
+ const ExplodedNode *N = I->getErrorNode();
if (N) {
R = *I;
bugReports.push_back(R);
@@ -1691,9 +1743,8 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
if (!errorNode)
continue;
if (errorNode->isSink()) {
- assert(false &&
+ llvm_unreachable(
"BugType::isSuppressSink() should not be 'true' for sink end nodes");
- return 0;
}
// No successors? By definition this nodes isn't post-dominated by a sink.
if (errorNode->succ_empty()) {
@@ -1706,7 +1757,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// At this point we know that 'N' is not a sink and it has at least one
// successor. Use a DFS worklist to find a non-sink end-of-path node.
typedef FRIEC_WLItem WLItem;
- typedef llvm::SmallVector<WLItem, 10> DFSWorkList;
+ typedef SmallVector<WLItem, 10> DFSWorkList;
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
DFSWorkList WL;
@@ -1763,11 +1814,8 @@ class DiagCacheItem : public llvm::FoldingSetNode {
llvm::FoldingSetNodeID ID;
public:
DiagCacheItem(BugReport *R, PathDiagnostic *PD) {
- ID.AddString(R->getBugType().getName());
- ID.AddString(R->getBugType().getCategory());
- ID.AddString(R->getDescription());
- ID.AddInteger(R->getLocation().getRawEncoding());
- PD->Profile(ID);
+ R->Profile(ID);
+ PD->Profile(ID);
}
void Profile(llvm::FoldingSetNodeID &id) {
@@ -1798,12 +1846,12 @@ static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) {
}
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
- llvm::SmallVector<BugReport*, 10> bugReports;
+ SmallVector<BugReport*, 10> bugReports;
BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports);
if (!exampleReport)
return;
- PathDiagnosticClient* PD = getPathDiagnosticClient();
+ PathDiagnosticConsumer* PD = getPathDiagnosticConsumer();
// FIXME: Make sure we use the 'R' for the path that was actually used.
// Probably doesn't make a difference in practice.
@@ -1823,47 +1871,50 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
return;
// Get the meta data.
- std::pair<const char**, const char**> Meta =
- exampleReport->getExtraDescriptiveText();
- for (const char** s = Meta.first; s != Meta.second; ++s)
- D->addMeta(*s);
+ const BugReport::ExtraTextList &Meta =
+ exampleReport->getExtraText();
+ for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
+ e = Meta.end(); i != e; ++i) {
+ D->addMeta(*i);
+ }
// Emit a summary diagnostic to the regular Diagnostics engine.
BugReport::ranges_iterator Beg, End;
llvm::tie(Beg, End) = exampleReport->getRanges();
- Diagnostic &Diag = getDiagnostic();
- FullSourceLoc L(exampleReport->getLocation(), getSourceManager());
+ DiagnosticsEngine &Diag = getDiagnostic();
// Search the description for '%', as that will be interpretted as a
// format character by FormatDiagnostics.
- llvm::StringRef desc = exampleReport->getShortDescription();
+ StringRef desc = exampleReport->getShortDescription();
unsigned ErrorDiag;
{
llvm::SmallString<512> TmpStr;
llvm::raw_svector_ostream Out(TmpStr);
- for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I)
+ for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I)
if (*I == '%')
Out << "%%";
else
Out << *I;
Out.flush();
- ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr);
+ ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr);
}
{
- DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag);
+ DiagnosticBuilder diagBuilder = Diag.Report(
+ exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag);
for (BugReport::ranges_iterator I = Beg; I != End; ++I)
diagBuilder << *I;
}
- // Emit a full diagnostic for the path if we have a PathDiagnosticClient.
+ // Emit a full diagnostic for the path if we have a PathDiagnosticConsumer.
if (!PD)
return;
if (D->empty()) {
- PathDiagnosticPiece* piece =
- new PathDiagnosticEventPiece(L, exampleReport->getDescription());
+ PathDiagnosticPiece *piece = new PathDiagnosticEventPiece(
+ exampleReport->getLocation(getSourceManager()),
+ exampleReport->getDescription());
for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
D->push_back(piece);
@@ -1872,27 +1923,26 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
PD->HandlePathDiagnostic(D.take());
}
-void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str,
- SourceLocation Loc,
+void BugReporter::EmitBasicReport(StringRef name, StringRef str,
+ PathDiagnosticLocation Loc,
SourceRange* RBeg, unsigned NumRanges) {
EmitBasicReport(name, "", str, Loc, RBeg, NumRanges);
}
-void BugReporter::EmitBasicReport(llvm::StringRef name,
- llvm::StringRef category,
- llvm::StringRef str, SourceLocation Loc,
+void BugReporter::EmitBasicReport(StringRef name,
+ StringRef category,
+ StringRef str, PathDiagnosticLocation Loc,
SourceRange* RBeg, unsigned NumRanges) {
// 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(name, category);
- FullSourceLoc L = getContext().getFullLoc(Loc);
- RangedBugReport *R = new DiagBugReport(*BT, str, L);
+ BugReport *R = new BugReport(*BT, str, Loc);
for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg);
EmitReport(R);
}
-BugType *BugReporter::getBugTypeForName(llvm::StringRef name,
- llvm::StringRef category) {
+BugType *BugReporter::getBugTypeForName(StringRef name,
+ StringRef category) {
llvm::SmallString<136> fullDesc;
llvm::raw_svector_ostream(fullDesc) << name << ":" << category;
llvm::StringMapEntry<BugType *> &
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 8e31ade..1abd8ba 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -11,13 +11,15 @@
// enhance the diagnostics reported for a bug.
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
using namespace clang;
using namespace ento;
@@ -39,8 +41,6 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
return ME->getBase()->IgnoreParenCasts();
}
else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
- // Retrieve the base for arrays since BasicStoreManager doesn't know how
- // to reason about them.
return AE->getBase();
}
@@ -73,248 +73,255 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
// Definitions for bug reporter visitors.
//===----------------------------------------------------------------------===//
-namespace {
-class FindLastStoreBRVisitor : public BugReporterVisitor {
- const MemRegion *R;
- SVal V;
- bool satisfied;
- const ExplodedNode *StoreSite;
-public:
- FindLastStoreBRVisitor(SVal v, const MemRegion *r)
- : R(r), V(v), satisfied(false), StoreSite(0) {}
-
- virtual void Profile(llvm::FoldingSetNodeID &ID) const {
- static int tag = 0;
- ID.AddPointer(&tag);
- ID.AddPointer(R);
- ID.Add(V);
+PathDiagnosticPiece*
+BugReporterVisitor::getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ BugReport &BR) {
+ return 0;
+}
+
+PathDiagnosticPiece*
+BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ BugReport &BR) {
+ const ProgramPoint &PP = EndPathNode->getLocation();
+ PathDiagnosticLocation L;
+
+ if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) {
+ const CFGBlock *block = BE->getBlock();
+ if (block->getBlockID() == 0) {
+ L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(),
+ BRC.getSourceManager());
+ }
}
- PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext& BRC) {
+ if (!L.isValid()) {
+ const Stmt *S = BR.getStmt();
- if (satisfied)
+ if (!S)
return NULL;
- if (!StoreSite) {
- const ExplodedNode *Node = N, *Last = NULL;
+ L = PathDiagnosticLocation(S, BRC.getSourceManager(),
+ PP.getLocationContext());
+ }
- for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
+ BugReport::ranges_iterator Beg, End;
+ llvm::tie(Beg, End) = BR.getRanges();
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
- if (const PostStmt *P = Node->getLocationAs<PostStmt>())
- if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
- if (DS->getSingleDecl() == VR->getDecl()) {
- Last = Node;
- break;
- }
- }
+ // Only add the statement itself as a range if we didn't specify any
+ // special ranges for this report.
+ PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L,
+ BR.getDescription(),
+ Beg == End);
+ for (; Beg != End; ++Beg)
+ P->addRange(*Beg);
- if (Node->getState()->getSVal(R) != V)
- break;
- }
+ return P;
+}
- if (!Node || !Last) {
- satisfied = true;
- return NULL;
+
+void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int tag = 0;
+ ID.AddPointer(&tag);
+ ID.AddPointer(R);
+ ID.Add(V);
+}
+
+PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+
+ if (satisfied)
+ return NULL;
+
+ if (!StoreSite) {
+ const ExplodedNode *Node = N, *Last = NULL;
+
+ for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ if (const PostStmt *P = Node->getLocationAs<PostStmt>())
+ if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
+ if (DS->getSingleDecl() == VR->getDecl()) {
+ Last = Node;
+ break;
+ }
}
- StoreSite = Last;
+ if (Node->getState()->getSVal(R) != V)
+ break;
}
- if (StoreSite != N)
+ if (!Node || !Last) {
+ satisfied = true;
return NULL;
+ }
- satisfied = true;
- llvm::SmallString<256> sbuf;
- llvm::raw_svector_ostream os(sbuf);
+ StoreSite = Last;
+ }
- if (const PostStmt *PS = N->getLocationAs<PostStmt>()) {
- if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
+ if (StoreSite != N)
+ return NULL;
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
- os << "Variable '" << VR->getDecl() << "' ";
- }
- else
- return NULL;
-
- if (isa<loc::ConcreteInt>(V)) {
- bool b = false;
- if (R->isBoundable()) {
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
- if (TR->getValueType()->isObjCObjectPointerType()) {
- os << "initialized to nil";
- b = true;
- }
- }
- }
+ satisfied = true;
+ llvm::SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
- if (!b)
- os << "initialized to a null pointer value";
- }
- else if (isa<nonloc::ConcreteInt>(V)) {
- os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
- }
- else if (V.isUndef()) {
- if (isa<VarRegion>(R)) {
- const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
- if (VD->getInit())
- os << "initialized to a garbage value";
- else
- os << "declared without an initial value";
- }
- }
+ if (const PostStmt *PS = N->getLocationAs<PostStmt>()) {
+ if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ os << "Variable '" << *VR->getDecl() << "' ";
}
- }
+ else
+ return NULL;
- if (os.str().empty()) {
if (isa<loc::ConcreteInt>(V)) {
bool b = false;
if (R->isBoundable()) {
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
if (TR->getValueType()->isObjCObjectPointerType()) {
- os << "nil object reference stored to ";
+ os << "initialized to nil";
b = true;
}
}
}
if (!b)
- os << "Null pointer value stored to ";
- }
- else if (V.isUndef()) {
- os << "Uninitialized value stored to ";
+ os << "initialized to a null pointer value";
}
else if (isa<nonloc::ConcreteInt>(V)) {
- os << "The value " << cast<nonloc::ConcreteInt>(V).getValue()
- << " is assigned to ";
+ os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
}
- else
- return NULL;
-
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
- os << '\'' << VR->getDecl() << '\'';
+ else if (V.isUndef()) {
+ if (isa<VarRegion>(R)) {
+ const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (VD->getInit())
+ os << "initialized to a garbage value";
+ else
+ os << "declared without an initial value";
+ }
}
- else
- return NULL;
}
+ }
- // FIXME: Refactor this into BugReporterContext.
- const Stmt *S = 0;
- ProgramPoint P = N->getLocation();
+ if (os.str().empty()) {
+ if (isa<loc::ConcreteInt>(V)) {
+ bool b = false;
+ if (R->isBoundable()) {
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
+ os << "nil object reference stored to ";
+ b = true;
+ }
+ }
+ }
- if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
- const CFGBlock *BSrc = BE->getSrc();
- S = BSrc->getTerminatorCondition();
+ if (!b)
+ os << "Null pointer value stored to ";
}
- else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
- S = PS->getStmt();
+ else if (V.isUndef()) {
+ os << "Uninitialized value stored to ";
}
-
- if (!S)
+ else if (isa<nonloc::ConcreteInt>(V)) {
+ os << "The value " << cast<nonloc::ConcreteInt>(V).getValue()
+ << " is assigned to ";
+ }
+ else
return NULL;
- // Construct a new PathDiagnosticPiece.
- PathDiagnosticLocation L(S, BRC.getSourceManager());
- return new PathDiagnosticEventPiece(L, os.str());
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ os << '\'' << *VR->getDecl() << '\'';
+ }
+ else
+ return NULL;
}
-};
-
-static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R,
- SVal V) {
- BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = N->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+ if (!L.isValid())
+ return NULL;
+ return new PathDiagnosticEventPiece(L, os.str());
}
-class TrackConstraintBRVisitor : public BugReporterVisitor {
- DefinedSVal Constraint;
- const bool Assumption;
- bool isSatisfied;
-public:
- TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption)
- : Constraint(constraint), Assumption(assumption), isSatisfied(false) {}
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- static int tag = 0;
- ID.AddPointer(&tag);
- ID.AddBoolean(Assumption);
- ID.Add(Constraint);
- }
-
- PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext& BRC) {
- if (isSatisfied)
- return NULL;
-
- // Check if in the previous state it was feasible for this constraint
- // to *not* be true.
- if (PrevN->getState()->assume(Constraint, !Assumption)) {
-
- isSatisfied = true;
-
- // As a sanity check, make sure that the negation of the constraint
- // was infeasible in the current state. If it is feasible, we somehow
- // missed the transition point.
- if (N->getState()->assume(Constraint, !Assumption))
- return NULL;
-
- // We found the transition point for the constraint. We now need to
- // pretty-print the constraint. (work-in-progress)
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
+void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int tag = 0;
+ ID.AddPointer(&tag);
+ ID.AddBoolean(Assumption);
+ ID.Add(Constraint);
+}
- if (isa<Loc>(Constraint)) {
- os << "Assuming pointer value is ";
- os << (Assumption ? "non-null" : "null");
- }
+PathDiagnosticPiece *
+TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ if (isSatisfied)
+ return NULL;
- if (os.str().empty())
- return NULL;
+ // Check if in the previous state it was feasible for this constraint
+ // to *not* be true.
+ if (PrevN->getState()->assume(Constraint, !Assumption)) {
- // FIXME: Refactor this into BugReporterContext.
- const Stmt *S = 0;
- ProgramPoint P = N->getLocation();
+ isSatisfied = true;
- if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
- const CFGBlock *BSrc = BE->getSrc();
- S = BSrc->getTerminatorCondition();
- }
- else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
- S = PS->getStmt();
- }
+ // As a sanity check, make sure that the negation of the constraint
+ // was infeasible in the current state. If it is feasible, we somehow
+ // missed the transition point.
+ if (N->getState()->assume(Constraint, !Assumption))
+ return NULL;
- if (!S)
- return NULL;
+ // We found the transition point for the constraint. We now need to
+ // pretty-print the constraint. (work-in-progress)
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
- // Construct a new PathDiagnosticPiece.
- PathDiagnosticLocation L(S, BRC.getSourceManager());
- return new PathDiagnosticEventPiece(L, os.str());
+ if (isa<Loc>(Constraint)) {
+ os << "Assuming pointer value is ";
+ os << (Assumption ? "non-null" : "null");
}
- return NULL;
+ if (os.str().empty())
+ return NULL;
+
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = N->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+ if (!L.isValid())
+ return NULL;
+ return new PathDiagnosticEventPiece(L, os.str());
}
-};
-} // end anonymous namespace
-static void registerTrackConstraint(BugReporterContext& BRC,
- DefinedSVal Constraint,
- bool Assumption) {
- BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption));
+ return NULL;
}
-void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
- const void *data,
- const ExplodedNode* N) {
-
- const Stmt *S = static_cast<const Stmt*>(data);
-
- if (!S)
- return;
+BugReporterVisitor *
+bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N,
+ const Stmt *S) {
+ if (!S || !N)
+ return 0;
+
+ ProgramStateManager &StateMgr = N->getState()->getStateManager();
+
+ // Walk through nodes until we get one that matches the statement
+ // exactly.
+ while (N) {
+ const ProgramPoint &pp = N->getLocation();
+ if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) {
+ if (ps->getStmt() == S)
+ break;
+ }
+ N = N->getFirstPred();
+ }
- GRStateManager &StateMgr = BRC.getStateManager();
- const GRState *state = N->getState();
+ if (!N)
+ return 0;
+
+ const ProgramState *state = N->getState();
// Walk through lvalue-to-rvalue conversions.
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
@@ -327,7 +334,7 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)
|| V.isUndef()) {
- ::registerFindLastStore(BRC, R, V);
+ return new FindLastStoreBRVisitor(V, R);
}
}
}
@@ -347,94 +354,73 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
if (R) {
assert(isa<SymbolicRegion>(R));
- registerTrackConstraint(BRC, loc::MemRegionVal(R), false);
+ return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false);
}
}
-}
-
-void bugreporter::registerFindLastStore(BugReporterContext& BRC,
- const void *data,
- const ExplodedNode* N) {
- const MemRegion *R = static_cast<const MemRegion*>(data);
+ return 0;
+}
- if (!R)
- return;
+BugReporterVisitor *
+FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N,
+ const MemRegion *R) {
+ assert(R && "The memory region is null.");
- const GRState *state = N->getState();
+ const ProgramState *state = N->getState();
SVal V = state->getSVal(R);
-
if (V.isUnknown())
- return;
+ return 0;
- BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
+ return new FindLastStoreBRVisitor(V, R);
}
-namespace {
-class NilReceiverVisitor : public BugReporterVisitor {
-public:
- NilReceiverVisitor() {}
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- static int x = 0;
- ID.AddPointer(&x);
- }
-
- PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext& BRC) {
-
- const PostStmt *P = N->getLocationAs<PostStmt>();
- if (!P)
- return 0;
- const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>();
- if (!ME)
- return 0;
- const Expr *Receiver = ME->getInstanceReceiver();
- if (!Receiver)
- return 0;
- const GRState *state = N->getState();
- const SVal &V = state->getSVal(Receiver);
- const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V);
- if (!DV)
- return 0;
- state = state->assume(*DV, true);
- if (state)
- return 0;
-
- // The receiver was nil, and hence the method was skipped.
- // Register a BugReporterVisitor to issue a message telling us how
- // the receiver was null.
- bugreporter::registerTrackNullOrUndefValue(BRC, Receiver, N);
- // Issue a message saying that the method was skipped.
- PathDiagnosticLocation L(Receiver, BRC.getSourceManager());
- return new PathDiagnosticEventPiece(L, "No method actually called "
- "because the receiver is nil");
- }
-};
-} // end anonymous namespace
-
-void bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) {
- BRC.addVisitor(new NilReceiverVisitor());
+PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ const PostStmt *P = N->getLocationAs<PostStmt>();
+ if (!P)
+ return 0;
+ const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>();
+ if (!ME)
+ return 0;
+ const Expr *Receiver = ME->getInstanceReceiver();
+ if (!Receiver)
+ return 0;
+ const ProgramState *state = N->getState();
+ const SVal &V = state->getSVal(Receiver);
+ const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V);
+ if (!DV)
+ return 0;
+ state = state->assume(*DV, true);
+ if (state)
+ return 0;
+
+ // 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));
+ // Issue a message saying that the method was skipped.
+ PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
+ N->getLocationContext());
+ return new PathDiagnosticEventPiece(L, "No method actually called "
+ "because the receiver is nil");
}
-// Registers every VarDecl inside a Stmt with a last store vistor.
-void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC,
- const void *stmt,
- const ExplodedNode *N) {
- const Stmt *S = static_cast<const Stmt *>(stmt);
-
+// Registers every VarDecl inside a Stmt with a last store visitor.
+void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
+ const Stmt *S) {
+ const ExplodedNode *N = BR.getErrorNode();
std::deque<const Stmt *> WorkList;
-
WorkList.push_back(S);
while (!WorkList.empty()) {
const Stmt *Head = WorkList.front();
WorkList.pop_front();
- GRStateManager &StateMgr = BRC.getStateManager();
- const GRState *state = N->getState();
+ const ProgramState *state = N->getState();
+ ProgramStateManager &StateMgr = state->getStateManager();
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) {
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
@@ -445,7 +431,8 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC,
SVal V = state->getSVal(S);
if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) {
- ::registerFindLastStore(BRC, R, V);
+ // Register a new visitor with the BugReport.
+ BR.addVisitor(new FindLastStoreBRVisitor(V, R));
}
}
}
@@ -455,3 +442,248 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC,
WorkList.push_back(*I);
}
}
+
+//===----------------------------------------------------------------------===//
+// Visitor that tries to report interesting diagnostics from conditions.
+//===----------------------------------------------------------------------===//
+PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *Prev,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+
+ const ProgramPoint &progPoint = N->getLocation();
+
+ const ProgramState *CurrentState = N->getState();
+ const ProgramState *PrevState = Prev->getState();
+
+ // Compare the GDMs of the state, because that is where constraints
+ // are managed. Note that ensure that we only look at nodes that
+ // were generated by the analyzer engine proper, not checkers.
+ if (CurrentState->getGDM().getRoot() ==
+ PrevState->getGDM().getRoot())
+ return 0;
+
+ // If an assumption was made on a branch, it should be caught
+ // here by looking at the state transition.
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) {
+ const CFGBlock *srcBlk = BE->getSrc();
+ if (const Stmt *term = srcBlk->getTerminator())
+ return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC);
+ return 0;
+ }
+
+ if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) {
+ // FIXME: Assuming that BugReporter is a GRBugReporter is a layering
+ // violation.
+ const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags =
+ cast<GRBugReporter>(BRC.getBugReporter()).
+ getEngine().getEagerlyAssumeTags();
+
+ const ProgramPointTag *tag = PS->getTag();
+ if (tag == tags.first)
+ return VisitTrueTest(cast<Expr>(PS->getStmt()), true,
+ BRC, N->getLocationContext());
+ if (tag == tags.second)
+ return VisitTrueTest(cast<Expr>(PS->getStmt()), false,
+ BRC, N->getLocationContext());
+
+ return 0;
+ }
+
+ return 0;
+}
+
+PathDiagnosticPiece *
+ConditionBRVisitor::VisitTerminator(const Stmt *Term,
+ const ExplodedNode *N,
+ const CFGBlock *srcBlk,
+ const CFGBlock *dstBlk,
+ BugReporterContext &BRC) {
+ const Expr *Cond = 0;
+
+ switch (Term->getStmtClass()) {
+ default:
+ return 0;
+ case Stmt::IfStmtClass:
+ Cond = cast<IfStmt>(Term)->getCond();
+ break;
+ case Stmt::ConditionalOperatorClass:
+ Cond = cast<ConditionalOperator>(Term)->getCond();
+ break;
+ }
+
+ assert(Cond);
+ assert(srcBlk->succ_size() == 2);
+ const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk;
+ return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()),
+ tookTrue, BRC, N->getLocationContext());
+}
+
+PathDiagnosticPiece *
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
+ bool tookTrue,
+ BugReporterContext &BRC,
+ const LocationContext *LC) {
+
+ const Expr *Ex = Cond;
+
+ while (true) {
+ Ex = Ex->IgnoreParens();
+ switch (Ex->getStmtClass()) {
+ default:
+ return 0;
+ case Stmt::BinaryOperatorClass:
+ return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC);
+ case Stmt::DeclRefExprClass:
+ return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC);
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UO = cast<UnaryOperator>(Ex);
+ if (UO->getOpcode() == UO_LNot) {
+ tookTrue = !tookTrue;
+ Ex = UO->getSubExpr()->IgnoreParenNoopCasts(BRC.getASTContext());
+ continue;
+ }
+ return 0;
+ }
+ }
+ }
+}
+
+bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out,
+ BugReporterContext &BRC) {
+ const Expr *OriginalExpr = Ex;
+ Ex = Ex->IgnoreParenCasts();
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) {
+ const bool quotes = isa<VarDecl>(DR->getDecl());
+ if (quotes)
+ Out << '\'';
+ Out << DR->getDecl()->getDeclName().getAsString();
+ if (quotes)
+ Out << '\'';
+ return quotes;
+ }
+
+ if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) {
+ QualType OriginalTy = OriginalExpr->getType();
+ if (OriginalTy->isPointerType()) {
+ if (IL->getValue() == 0) {
+ Out << "null";
+ return false;
+ }
+ }
+ else if (OriginalTy->isObjCObjectPointerType()) {
+ if (IL->getValue() == 0) {
+ Out << "nil";
+ return false;
+ }
+ }
+
+ Out << IL->getValue();
+ return false;
+ }
+
+ return false;
+}
+
+PathDiagnosticPiece *
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
+ const BinaryOperator *BExpr,
+ const bool tookTrue,
+ BugReporterContext &BRC,
+ const LocationContext *LC) {
+
+ bool shouldInvert = false;
+
+ llvm::SmallString<128> LhsString, RhsString;
+ {
+ llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
+ const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC);
+ const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC);
+
+ shouldInvert = !isVarLHS && isVarRHS;
+ }
+
+ if (LhsString.empty() || RhsString.empty())
+ return 0;
+
+ // Should we invert the strings if the LHS is not a variable name?
+
+ llvm::SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is ";
+
+ // Do we need to invert the opcode?
+ BinaryOperator::Opcode Op = BExpr->getOpcode();
+
+ if (shouldInvert)
+ switch (Op) {
+ default: break;
+ case BO_LT: Op = BO_GT; break;
+ case BO_GT: Op = BO_LT; break;
+ case BO_LE: Op = BO_GE; break;
+ case BO_GE: Op = BO_LE; break;
+ }
+
+ if (!tookTrue)
+ switch (Op) {
+ case BO_EQ: Op = BO_NE; break;
+ case BO_NE: Op = BO_EQ; break;
+ case BO_LT: Op = BO_GE; break;
+ case BO_GT: Op = BO_LE; break;
+ case BO_LE: Op = BO_GT; break;
+ case BO_GE: Op = BO_LT; break;
+ default:
+ return 0;
+ }
+
+ switch (BExpr->getOpcode()) {
+ case BO_EQ:
+ Out << "equal to ";
+ break;
+ case BO_NE:
+ Out << "not equal to ";
+ break;
+ default:
+ Out << BinaryOperator::getOpcodeStr(Op) << ' ';
+ break;
+ }
+
+ Out << (shouldInvert ? LhsString : RhsString);
+
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC);
+ return new PathDiagnosticEventPiece(Loc, Out.str());
+}
+
+PathDiagnosticPiece *
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
+ const DeclRefExpr *DR,
+ const bool tookTrue,
+ BugReporterContext &BRC,
+ const LocationContext *LC) {
+
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (!VD)
+ return 0;
+
+ llvm::SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ Out << "Assuming '";
+ VD->getDeclName().printName(Out);
+ Out << "' is ";
+
+ QualType VDTy = VD->getType();
+
+ if (VDTy->isPointerType())
+ Out << (tookTrue ? "non-null" : "null");
+ else if (VDTy->isObjCObjectPointerType())
+ Out << (tookTrue ? "non-nil" : "nil");
+ else if (VDTy->isScalarType())
+ Out << (tookTrue ? "not equal to 0" : "0");
+ else
+ return 0;
+
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC);
+ return new PathDiagnosticEventPiece(Loc, Out.str());
+}
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index 089a5cc..391a781 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -6,34 +6,36 @@ add_clang_library(clangStaticAnalyzerCore
AggExprVisitor.cpp
AnalysisManager.cpp
BasicConstraintManager.cpp
- BasicStore.cpp
BasicValueFactory.cpp
BlockCounter.cpp
BugReporter.cpp
BugReporterVisitors.cpp
- CFRefCount.cpp
- CXXExprEngine.cpp
+ Checker.cpp
CheckerContext.cpp
CheckerHelpers.cpp
CheckerManager.cpp
+ CheckerRegistry.cpp
CoreEngine.cpp
Environment.cpp
ExplodedGraph.cpp
ExprEngine.cpp
- FlatStore.cpp
- GRState.cpp
+ ExprEngineC.cpp
+ ExprEngineCXX.cpp
+ ExprEngineCallAndReturn.cpp
+ ExprEngineObjC.cpp
HTMLDiagnostics.cpp
MemRegion.cpp
ObjCMessage.cpp
PathDiagnostic.cpp
PlistDiagnostics.cpp
+ ProgramState.cpp
RangeConstraintManager.cpp
RegionStore.cpp
+ SValBuilder.cpp
+ SVals.cpp
SimpleConstraintManager.cpp
SimpleSValBuilder.cpp
Store.cpp
- SValBuilder.cpp
- SVals.cpp
SymbolManager.cpp
TextPathDiagnostics.cpp
)
diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp
new file mode 100644
index 0000000..a3bf2c2
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/Checker.cpp
@@ -0,0 +1,22 @@
+//== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines Checker, used to create and register checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+StringRef CheckerBase::getTagDescription() const {
+ // FIXME: We want to return the package + name of the checker here.
+ return "A Checker";
+}
diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp
index f6fb8f2..5356edc 100644
--- a/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -23,8 +23,8 @@ CheckerContext::~CheckerContext() {
// if we are building sinks or we generated a node and decided to not
// add it as a transition.
if (Dst.size() == size && !B.BuildSinks && !B.hasGeneratedNode) {
- if (ST && ST != B.GetState(Pred)) {
- static int autoTransitionTag = 0;
+ if (ST && ST != Pred->getState()) {
+ static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto");
addTransition(ST, &autoTransitionTag);
}
else
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index ba7c384..acacfb0 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -12,8 +12,9 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/CheckerProvider.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/AST/DeclBase.h"
@@ -33,7 +34,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const {
!DeadSymbolsCheckers.empty() ||
!RegionChangesCheckers.empty() ||
!EvalAssumeCheckers.empty() ||
- !EvalCallCheckers.empty();
+ !EvalCallCheckers.empty() ||
+ !InlineCallCheckers.empty();
}
void CheckerManager::finishedCheckerRegistration() {
@@ -122,7 +124,7 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx,
namespace {
struct CheckStmtContext {
- typedef llvm::SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy;
+ typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy;
bool IsPreVisit;
const CheckersTy &Checkers;
const Stmt *S;
@@ -138,9 +140,12 @@ namespace {
void runChecker(CheckerManager::CheckStmtFunc checkFn,
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
// FIXME: Remove respondsToCallback from CheckerContext;
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
- IsPreVisit ? ProgramPoint::PreStmtKind :
- ProgramPoint::PostStmtKind, 0, S);
+ ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
+ ProgramPoint::PostStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+
checkFn(S, C);
}
};
@@ -174,10 +179,12 @@ namespace {
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
- IsPreVisit ? ProgramPoint::PreStmtKind :
- ProgramPoint::PostStmtKind, 0,
- Msg.getOriginExpr());
+ ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
+ ProgramPoint::PostStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(Msg.getOriginExpr(),
+ K, Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+
checkFn(Msg, C);
}
};
@@ -214,10 +221,13 @@ namespace {
void runChecker(CheckerManager::CheckLocationFunc checkFn,
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
- IsLoad ? ProgramPoint::PreLoadKind :
- ProgramPoint::PreStoreKind, 0, S);
- checkFn(Loc, IsLoad, C);
+ ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind :
+ ProgramPoint::PreStoreKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+
+ checkFn(Loc, IsLoad, S, C);
}
};
}
@@ -249,9 +259,12 @@ namespace {
void runChecker(CheckerManager::CheckBindFunc checkFn,
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
- ProgramPoint::PreStmtKind, 0, S);
- checkFn(Loc, Val, C);
+ ProgramPoint::Kind K = ProgramPoint::PreStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+
+ checkFn(Loc, Val, S, C);
}
};
}
@@ -293,7 +306,7 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *condition,
}
/// \brief Run checkers for live symbols.
-void CheckerManager::runCheckersForLiveSymbols(const GRState *state,
+void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state,
SymbolReaper &SymReaper) {
for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i)
LiveSymbolsCheckers[i](state, SymReaper);
@@ -316,8 +329,11 @@ namespace {
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
- ProgramPoint::PostPurgeDeadSymbolsKind, 0, S);
+ ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0);
+
checkFn(SR, C);
}
};
@@ -334,7 +350,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
}
/// \brief True if at least one checker wants to check region changes.
-bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) {
+bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i)
if (RegionChangesCheckers[i].WantUpdateFn(state))
return true;
@@ -343,24 +359,25 @@ bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) {
}
/// \brief Run checkers for region changes.
-const GRState *
-CheckerManager::runCheckersForRegionChanges(const GRState *state,
+const ProgramState *
+CheckerManager::runCheckersForRegionChanges(const ProgramState *state,
const StoreManager::InvalidatedSymbols *invalidated,
- const MemRegion * const *Begin,
- const MemRegion * const *End) {
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return NULL;
- state = RegionChangesCheckers[i].CheckFn(state, invalidated, Begin, End);
+ state = RegionChangesCheckers[i].CheckFn(state, invalidated,
+ ExplicitRegions, Regions);
}
return state;
}
/// \brief Run checkers for handling assumptions on symbolic values.
-const GRState *
-CheckerManager::runCheckersForEvalAssume(const GRState *state,
+const ProgramState *
+CheckerManager::runCheckersForEvalAssume(const ProgramState *state,
SVal Cond, bool Assumption) {
for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
@@ -379,7 +396,9 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const CallExpr *CE,
ExprEngine &Eng,
GraphExpander *defaultEval) {
- if (EvalCallCheckers.empty() && defaultEval == 0) {
+ if (EvalCallCheckers.empty() &&
+ InlineCallCheckers.empty() &&
+ defaultEval == 0) {
Dst.insert(Src);
return;
}
@@ -389,13 +408,50 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
ExplodedNode *Pred = *NI;
bool anyEvaluated = false;
+
+ // First, check if any of the InlineCall callbacks can evaluate the call.
+ assert(InlineCallCheckers.size() <= 1 &&
+ "InlineCall is a special hacky callback to allow intrusive"
+ "evaluation of the call (which simulates inlining). It is "
+ "currently only used by OSAtomicChecker and should go away "
+ "at some point.");
+ for (std::vector<InlineCallFunc>::iterator
+ EI = InlineCallCheckers.begin(), EE = InlineCallCheckers.end();
+ EI != EE; ++EI) {
+ ExplodedNodeSet checkDst;
+ bool evaluated = (*EI)(CE, Eng, Pred, checkDst);
+ assert(!(evaluated && anyEvaluated)
+ && "There are more than one checkers evaluating the call");
+ if (evaluated) {
+ anyEvaluated = true;
+ Dst.insert(checkDst);
+#ifdef NDEBUG
+ break; // on release don't check that no other checker also evals.
+#endif
+ }
+ }
+
+#ifdef NDEBUG // on release don't check that no other checker also evals.
+ if (anyEvaluated) {
+ break;
+ }
+#endif
+
+ // Next, check if any of the EvalCall callbacks can evaluate the call.
for (std::vector<EvalCallFunc>::iterator
EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end();
EI != EE; ++EI) {
ExplodedNodeSet checkDst;
- CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, EI->Checker,
- ProgramPoint::PostStmtKind, 0, CE);
- bool evaluated = (*EI)(CE, C);
+ ProgramPoint::Kind K = ProgramPoint::PostStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K,
+ Pred->getLocationContext(), EI->Checker);
+ bool evaluated = false;
+ { // CheckerContext generates transitions(populates checkDest) on
+ // destruction, so introduce the scope to make sure it gets properly
+ // populated.
+ CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, L, 0);
+ evaluated = (*EI)(CE, C);
+ }
assert(!(evaluated && anyEvaluated)
&& "There are more than one checkers evaluating the call");
if (evaluated) {
@@ -407,6 +463,7 @@ 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);
@@ -425,6 +482,14 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
EndOfTranslationUnitCheckers[i](TU, mgr, BR);
}
+void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
+ const ProgramState *State,
+ const char *NL, const char *Sep) {
+ for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator
+ I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I)
+ I->second->printState(Out, State, NL, Sep);
+}
+
//===----------------------------------------------------------------------===//
// Internal registration functions for AST traversing.
//===----------------------------------------------------------------------===//
@@ -504,6 +569,10 @@ void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
EvalCallCheckers.push_back(checkfn);
}
+void CheckerManager::_registerForInlineCall(InlineCallFunc checkfn) {
+ InlineCallCheckers.push_back(checkfn);
+}
+
void CheckerManager::_registerForEndOfTranslationUnit(
CheckEndOfTranslationUnit checkfn) {
EndOfTranslationUnitCheckers.push_back(checkfn);
@@ -542,7 +611,4 @@ CheckerManager::~CheckerManager() {
}
// Anchor for the vtable.
-CheckerProvider::~CheckerProvider() { }
-
-// Anchor for the vtable.
GraphExpander::~GraphExpander() { }
diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
new file mode 100644
index 0000000..13401ac
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
@@ -0,0 +1,149 @@
+//===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
+#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
+
+using namespace clang;
+using namespace ento;
+
+static const char PackageSeparator = '.';
+typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
+
+
+static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
+ const CheckerRegistry::CheckerInfo &b) {
+ return a.FullName < b.FullName;
+}
+
+static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
+ StringRef packageName) {
+ // Does the checker's full name have the package as a prefix?
+ if (!checker.FullName.startswith(packageName))
+ return false;
+
+ // Is the package actually just the name of a specific checker?
+ if (checker.FullName.size() == packageName.size())
+ return true;
+
+ // Is the checker in the package (or a subpackage)?
+ if (checker.FullName[packageName.size()] == PackageSeparator)
+ return true;
+
+ return false;
+}
+
+static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers,
+ const llvm::StringMap<size_t> &packageSizes,
+ CheckerOptInfo &opt, CheckerInfoSet &collected) {
+ // Use a binary search to find the possible start of the package.
+ CheckerRegistry::CheckerInfo packageInfo(NULL, opt.getName(), "");
+ CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end();
+ CheckerRegistry::CheckerInfoList::const_iterator i =
+ std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT);
+
+ // If we didn't even find a possible package, give up.
+ if (i == e)
+ return;
+
+ // If what we found doesn't actually start the package, give up.
+ if (!isInPackage(*i, opt.getName()))
+ return;
+
+ // There is at least one checker in the package; claim the option.
+ opt.claim();
+
+ // See how large the package is.
+ // If the package doesn't exist, assume the option refers to a single checker.
+ size_t size = 1;
+ llvm::StringMap<size_t>::const_iterator packageSize =
+ packageSizes.find(opt.getName());
+ if (packageSize != packageSizes.end())
+ size = packageSize->getValue();
+
+ // Step through all the checkers in the package.
+ for (e = i+size; i != e; ++i) {
+ if (opt.isEnabled())
+ collected.insert(&*i);
+ else
+ collected.erase(&*i);
+ }
+}
+
+void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
+ StringRef desc) {
+ Checkers.push_back(CheckerInfo(fn, name, desc));
+
+ // Record the presence of the checker in its packages.
+ StringRef packageName, leafName;
+ llvm::tie(packageName, leafName) = name.rsplit(PackageSeparator);
+ while (!leafName.empty()) {
+ Packages[packageName] += 1;
+ llvm::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
+ }
+}
+
+void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
+ SmallVectorImpl<CheckerOptInfo> &opts) const {
+ // Sort checkers for efficient collection.
+ std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
+
+ // Collect checkers enabled by the options.
+ CheckerInfoSet enabledCheckers;
+ for (SmallVectorImpl<CheckerOptInfo>::iterator
+ i = opts.begin(), e = opts.end(); i != e; ++i) {
+ collectCheckers(Checkers, Packages, *i, enabledCheckers);
+ }
+
+ // Initialize the CheckerManager with all enabled checkers.
+ for (CheckerInfoSet::iterator
+ i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) {
+ (*i)->Initialize(checkerMgr);
+ }
+}
+
+void CheckerRegistry::printHelp(llvm::raw_ostream &out,
+ size_t maxNameChars) const {
+ // FIXME: Alphabetical sort puts 'experimental' in the middle.
+ // Would it be better to name it '~experimental' or something else
+ // that's ASCIIbetically last?
+ std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
+
+ // FIXME: Print available packages.
+
+ out << "CHECKERS:\n";
+
+ // Find the maximum option length.
+ size_t optionFieldWidth = 0;
+ for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
+ i != e; ++i) {
+ // Limit the amount of padding we are willing to give up for alignment.
+ // Package.Name Description [Hidden]
+ size_t nameLength = i->FullName.size();
+ if (nameLength <= maxNameChars)
+ optionFieldWidth = std::max(optionFieldWidth, nameLength);
+ }
+
+ const size_t initialPad = 2;
+ for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
+ i != e; ++i) {
+ out.indent(initialPad) << i->FullName;
+
+ int pad = optionFieldWidth - i->FullName.size();
+
+ // Break on long option names.
+ if (pad < 0) {
+ out << '\n';
+ pad = optionFieldWidth + initialPad;
+ }
+ out.indent(pad + 2) << i->Desc;
+
+ out << '\n';
+ }
+}
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 34cd6e8..5252198 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -17,22 +17,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/Index/TranslationUnit.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/StmtCXX.h"
#include "llvm/Support/Casting.h"
#include "llvm/ADT/DenseMap.h"
-
-using llvm::cast;
-using llvm::isa;
using namespace clang;
using namespace ento;
-// This should be removed in the future.
-namespace clang {
-namespace ento {
-TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled,
- const LangOptions& lopts);
-}
-}
-
//===----------------------------------------------------------------------===//
// Worklist classes for exploration of reachable states.
//===----------------------------------------------------------------------===//
@@ -41,7 +31,7 @@ WorkList::Visitor::~Visitor() {}
namespace {
class DFS : public WorkList {
- llvm::SmallVector<WorkListUnit,20> Stack;
+ SmallVector<WorkListUnit,20> Stack;
public:
virtual bool hasWork() const {
return !Stack.empty();
@@ -59,7 +49,7 @@ public:
}
virtual bool visitItemsInWorkList(Visitor &V) {
- for (llvm::SmallVectorImpl<WorkListUnit>::iterator
+ for (SmallVectorImpl<WorkListUnit>::iterator
I = Stack.begin(), E = Stack.end(); I != E; ++I) {
if (V.visit(*I))
return true;
@@ -107,7 +97,7 @@ WorkList *WorkList::makeBFS() { return new BFS(); }
namespace {
class BFSBlockDFSContents : public WorkList {
std::deque<WorkListUnit> Queue;
- llvm::SmallVector<WorkListUnit,20> Stack;
+ SmallVector<WorkListUnit,20> Stack;
public:
virtual bool hasWork() const {
return !Queue.empty() || !Stack.empty();
@@ -136,7 +126,7 @@ namespace {
return U;
}
virtual bool visitItemsInWorkList(Visitor &V) {
- for (llvm::SmallVectorImpl<WorkListUnit>::iterator
+ for (SmallVectorImpl<WorkListUnit>::iterator
I = Stack.begin(), E = Stack.end(); I != E; ++I) {
if (V.visit(*I))
return true;
@@ -162,12 +152,12 @@ WorkList* WorkList::makeBFSBlockDFSContents() {
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
- const GRState *InitState) {
+ const ProgramState *InitState) {
if (G->num_roots() == 0) { // Initialize the analysis by constructing
// the root if none exists.
- const CFGBlock* Entry = &(L->getCFG()->getEntry());
+ const CFGBlock *Entry = &(L->getCFG()->getEntry());
assert (Entry->empty() &&
"Entry block must be empty.");
@@ -176,7 +166,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
"Entry block must have 1 successor.");
// Get the solitary successor.
- const CFGBlock* Succ = *(Entry->succ_begin());
+ const CFGBlock *Succ = *(Entry->succ_begin());
// Construct an edge representing the
// starting location in the function.
@@ -208,7 +198,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
WList->setBlockCounter(WU.getBlockCounter());
// Retrieve the node.
- ExplodedNode* Node = WU.getNode();
+ ExplodedNode *Node = WU.getNode();
// Dispatch on the location type.
switch (Node->getLocation().getKind()) {
@@ -246,11 +236,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
}
void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
- unsigned Steps,
- const GRState *InitState,
- ExplodedNodeSet &Dst) {
+ unsigned Steps,
+ const ProgramState *InitState,
+ ExplodedNodeSet &Dst) {
ExecuteWorkList(L, Steps, InitState);
- for (llvm::SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(),
+ for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(),
E = G->EndNodes.end(); I != E; ++I) {
Dst.Add(*I);
}
@@ -268,9 +258,9 @@ void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) {
SubEng.processCallExit(Builder);
}
-void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) {
+void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
- const CFGBlock* Blk = L.getDst();
+ const CFGBlock *Blk = L.getDst();
// Check if we are entering the EXIT block.
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
@@ -305,15 +295,15 @@ void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) {
}
}
- for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator
+ for (SmallVectorImpl<ExplodedNode*>::const_iterator
I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end();
I != E; ++I) {
blocksExhausted.push_back(std::make_pair(L, *I));
}
}
-void CoreEngine::HandleBlockEntrance(const BlockEntrance& L,
- ExplodedNode* Pred) {
+void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
+ ExplodedNode *Pred) {
// Increment the block counter.
BlockCounter Counter = WList->getBlockCounter();
@@ -324,21 +314,19 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance& L,
// Process the entrance of the block.
if (CFGElement E = L.getFirstElement()) {
- StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this,
- SubEng.getStateManager());
+ StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this);
SubEng.processCFGElement(E, Builder);
}
else
HandleBlockExit(L.getBlock(), Pred);
}
-void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) {
+void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
- if (const Stmt* Term = B->getTerminator()) {
+ if (const Stmt *Term = B->getTerminator()) {
switch (Term->getStmtClass()) {
default:
- assert(false && "Analysis for this terminator not implemented.");
- break;
+ llvm_unreachable("Analysis for this terminator not implemented.");
case Stmt::BinaryOperatorClass: // '&&' and '||'
HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred);
@@ -361,6 +349,10 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) {
HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
return;
+ case Stmt::CXXForRangeStmtClass:
+ HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+
case Stmt::ForStmtClass:
HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred);
return;
@@ -422,34 +414,34 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) {
Pred->State, Pred);
}
-void CoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term,
- const CFGBlock * B, ExplodedNode* Pred) {
+void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
+ const CFGBlock * B, ExplodedNode *Pred) {
assert(B->succ_size() == 2);
BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1),
Pred, this);
SubEng.processBranch(Cond, Term, Builder);
}
-void CoreEngine::HandlePostStmt(const CFGBlock* B, unsigned StmtIdx,
- ExplodedNode* Pred) {
+void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
+ ExplodedNode *Pred) {
assert (!B->empty());
if (StmtIdx == B->size())
HandleBlockExit(B, Pred);
else {
- StmtNodeBuilder Builder(B, StmtIdx, Pred, this,
- SubEng.getStateManager());
+ StmtNodeBuilder Builder(B, StmtIdx, Pred, this);
SubEng.processCFGElement((*B)[StmtIdx], Builder);
}
}
/// generateNode - Utility method to generate nodes, hook up successors,
/// and add nodes to the worklist.
-void CoreEngine::generateNode(const ProgramPoint& Loc,
- const GRState* State, ExplodedNode* Pred) {
+void CoreEngine::generateNode(const ProgramPoint &Loc,
+ const ProgramState *State,
+ ExplodedNode *Pred) {
bool IsNew;
- ExplodedNode* Node = G->getNode(Loc, State, &IsNew);
+ ExplodedNode *Node = G->getNode(Loc, State, &IsNew);
if (Pred)
Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor.
@@ -463,7 +455,7 @@ void CoreEngine::generateNode(const ProgramPoint& Loc,
}
ExplodedNode *
-GenericNodeBuilderImpl::generateNodeImpl(const GRState *state,
+GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state,
ExplodedNode *pred,
ProgramPoint programPoint,
bool asSink) {
@@ -483,14 +475,14 @@ GenericNodeBuilderImpl::generateNodeImpl(const GRState *state,
return 0;
}
-StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx,
- ExplodedNode* N, CoreEngine* e,
- GRStateManager &mgr)
- : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr),
+StmtNodeBuilder::StmtNodeBuilder(const CFGBlock *b,
+ unsigned idx,
+ ExplodedNode *N,
+ CoreEngine* e)
+ : Eng(*e), B(*b), Idx(idx), Pred(N),
PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false),
PointKind(ProgramPoint::PostStmtKind), Tag(0) {
Deferred.insert(N);
- CleanedState = Pred->getState();
}
StmtNodeBuilder::~StmtNodeBuilder() {
@@ -499,7 +491,7 @@ StmtNodeBuilder::~StmtNodeBuilder() {
GenerateAutoTransition(*I);
}
-void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
+void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) {
assert (!N->isSink());
// Check if this node entered a callee.
@@ -526,18 +518,20 @@ void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
}
bool IsNew;
- ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew);
+ ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew);
Succ->addPredecessor(N, *Eng.G);
if (IsNew)
Eng.WList->enqueue(Succ, &B, Idx+1);
}
-ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
- ExplodedNode* Pred, const GRState* St,
- ProgramPoint::Kind K) {
+ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst,
+ const Stmt *S,
+ ExplodedNode *Pred,
+ const ProgramState *St,
+ ProgramPoint::Kind K) {
- ExplodedNode* N = generateNode(S, St, Pred, K);
+ ExplodedNode *N = generateNode(S, St, Pred, K);
if (N) {
if (BuildSinks)
@@ -549,46 +543,24 @@ ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
return N;
}
-static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K,
- const LocationContext *LC, const void *tag){
- switch (K) {
- default:
- assert(false && "Unhandled ProgramPoint kind");
- case ProgramPoint::PreStmtKind:
- return PreStmt(S, LC, tag);
- case ProgramPoint::PostStmtKind:
- return PostStmt(S, LC, tag);
- case ProgramPoint::PreLoadKind:
- return PreLoad(S, LC, tag);
- case ProgramPoint::PostLoadKind:
- return PostLoad(S, LC, tag);
- case ProgramPoint::PreStoreKind:
- return PreStore(S, LC, tag);
- case ProgramPoint::PostStoreKind:
- return PostStore(S, LC, tag);
- case ProgramPoint::PostLValueKind:
- return PostLValue(S, LC, tag);
- case ProgramPoint::PostPurgeDeadSymbolsKind:
- return PostPurgeDeadSymbols(S, LC, tag);
- }
-}
-
ExplodedNode*
-StmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state,
- ExplodedNode* Pred,
- ProgramPoint::Kind K,
- const void *tag) {
+StmtNodeBuilder::generateNodeInternal(const Stmt *S,
+ const ProgramState *state,
+ ExplodedNode *Pred,
+ ProgramPoint::Kind K,
+ const ProgramPointTag *tag) {
- const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag);
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), tag);
return generateNodeInternal(L, state, Pred);
}
ExplodedNode*
StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc,
- const GRState* State,
- ExplodedNode* Pred) {
+ const ProgramState *State,
+ ExplodedNode *Pred) {
bool IsNew;
- ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew);
+ ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew);
N->addPredecessor(Pred, *Eng.G);
Deferred.erase(Pred);
@@ -601,11 +573,11 @@ StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc,
}
// This function generate a new ExplodedNode but not a new branch(block edge).
-ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition,
- const GRState* State) {
+ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition,
+ const ProgramState *State) {
bool IsNew;
- ExplodedNode* Succ
+ ExplodedNode *Succ
= Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State,
&IsNew);
@@ -619,8 +591,8 @@ ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition,
return NULL;
}
-ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State,
- bool branch) {
+ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State,
+ bool branch) {
// If the branch has been marked infeasible we should not generate a node.
if (!isFeasible(branch))
@@ -628,7 +600,7 @@ ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State,
bool IsNew;
- ExplodedNode* Succ =
+ ExplodedNode *Succ =
Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()),
State, &IsNew);
@@ -657,11 +629,12 @@ BranchNodeBuilder::~BranchNodeBuilder() {
ExplodedNode*
-IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St,
- bool isSink) {
+IndirectGotoNodeBuilder::generateNode(const iterator &I,
+ const ProgramState *St,
+ bool isSink) {
bool IsNew;
- ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
+ ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
Pred->getLocationContext()), St, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
@@ -681,34 +654,38 @@ IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St,
ExplodedNode*
-SwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){
+SwitchNodeBuilder::generateCaseStmtNode(const iterator &I,
+ const ProgramState *St) {
bool IsNew;
-
- ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
- Pred->getLocationContext()), St, &IsNew);
+ ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(),
+ Pred->getLocationContext()),
+ St, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
-
if (IsNew) {
Eng.WList->enqueue(Succ);
return Succ;
}
-
return NULL;
}
ExplodedNode*
-SwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) {
-
+SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St,
+ bool isSink) {
// Get the block for the default case.
- assert (Src->succ_rbegin() != Src->succ_rend());
- CFGBlock* DefaultBlock = *Src->succ_rbegin();
+ assert(Src->succ_rbegin() != Src->succ_rend());
+ CFGBlock *DefaultBlock = *Src->succ_rbegin();
+ // Sanity check for default blocks that are unreachable and not caught
+ // by earlier stages.
+ if (!DefaultBlock)
+ return NULL;
+
bool IsNew;
- ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock,
- Pred->getLocationContext()), St, &IsNew);
+ ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock,
+ Pred->getLocationContext()), St, &IsNew);
Succ->addPredecessor(Pred, *Eng.G);
if (IsNew) {
@@ -735,12 +712,13 @@ EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() {
}
ExplodedNode*
-EndOfFunctionNodeBuilder::generateNode(const GRState* State,
- ExplodedNode* P, const void *tag) {
+EndOfFunctionNodeBuilder::generateNode(const ProgramState *State,
+ ExplodedNode *P,
+ const ProgramPointTag *tag) {
hasGeneratedNode = true;
bool IsNew;
- ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B,
+ ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B,
Pred->getLocationContext(), tag ? tag : Tag),
State, &IsNew);
@@ -754,7 +732,7 @@ EndOfFunctionNodeBuilder::generateNode(const GRState* State,
return NULL;
}
-void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) {
+void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) {
hasGeneratedNode = true;
// Create a CallExit node and enqueue it.
const StackFrameContext *LocCtx
@@ -773,7 +751,7 @@ void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) {
}
-void CallEnterNodeBuilder::generateNode(const GRState *state) {
+void CallEnterNodeBuilder::generateNode(const ProgramState *state) {
// Check if the callee is in the same translation unit.
if (CalleeCtx->getTranslationUnit() !=
Pred->getLocationContext()->getTranslationUnit()) {
@@ -787,30 +765,13 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) {
// Create a new AnalysisManager with components of the callee's
// TranslationUnit.
- // The Diagnostic is actually shared when we create ASTUnits from AST files.
- AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(),
- OldMgr.getLangOptions(),
- OldMgr.getPathDiagnosticClient(),
- OldMgr.getStoreManagerCreator(),
- OldMgr.getConstraintManagerCreator(),
- OldMgr.getCheckerManager(),
- OldMgr.getIndexer(),
- OldMgr.getMaxNodes(), OldMgr.getMaxVisit(),
- OldMgr.shouldVisualizeGraphviz(),
- OldMgr.shouldVisualizeUbigraph(),
- OldMgr.shouldPurgeDead(),
- OldMgr.shouldEagerlyAssume(),
- OldMgr.shouldTrimGraph(),
- OldMgr.shouldInlineCall(),
- OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(),
- OldMgr.getAnalysisContextManager().getAddImplicitDtors(),
- OldMgr.getAnalysisContextManager().getAddInitializers(),
- OldMgr.shouldEagerlyTrimExplodedGraph());
- llvm::OwningPtr<TransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(),
- /* GCEnabled */ false,
- AMgr.getLangOptions()));
+ // The Diagnostic is actually shared when we create ASTUnits from AST files.
+ AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), OldMgr);
+
// Create the new engine.
- ExprEngine NewEng(AMgr, TF.take());
+ // FIXME: This cast isn't really safe.
+ bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled();
+ ExprEngine NewEng(AMgr, GCEnabled);
// Create the new LocationContext.
AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(),
@@ -823,8 +784,8 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) {
OldLocCtx->getIndex());
// Now create an initial state for the new engine.
- const GRState *NewState = NewEng.getStateManager().MarshalState(state,
- NewLocCtx);
+ const ProgramState *NewState =
+ NewEng.getStateManager().MarshalState(state, NewLocCtx);
ExplodedNodeSet ReturnNodes;
NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(),
NewState, ReturnNodes);
@@ -850,7 +811,7 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) {
Eng.WList->enqueue(Node);
}
-void CallExitNodeBuilder::generateNode(const GRState *state) {
+void CallExitNodeBuilder::generateNode(const ProgramState *state) {
// Get the callee's location context.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(Pred->getLocationContext());
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index 3961c7b..e1b982c 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -11,14 +11,15 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ExprObjC.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
using namespace clang;
using namespace ento;
-SVal Environment::lookupExpr(const Stmt* E) const {
+SVal Environment::lookupExpr(const Stmt *E) const {
const SVal* X = ExprBindings.lookup(E);
if (X) {
SVal V = *X;
@@ -83,9 +84,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
case Stmt::CXXBindTemporaryExprClass:
E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
continue;
- case Stmt::MaterializeTemporaryExprClass:
- E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr();
- continue;
+ case Stmt::ObjCPropertyRefExprClass:
+ return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E));
+
// Handle all other Stmt* using a lookup.
default:
break;
@@ -129,18 +130,6 @@ public:
};
} // end anonymous namespace
-static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) {
- const LocationContext *ParentLC = LC->getParent();
- while (ParentLC) {
- CFG &C = *ParentLC->getCFG();
- if (C.isBlkExpr(E))
- return true;
- ParentLC = ParentLC->getParent();
- }
-
- return false;
-}
-
// In addition to mapping from Stmt * - > SVals in the Environment, we also
// maintain a mapping from Stmt * -> SVals (locations) that were used during
// a load and store.
@@ -158,24 +147,27 @@ static inline bool IsLocation(const Stmt *S) {
Environment
EnvironmentManager::removeDeadBindings(Environment Env,
SymbolReaper &SymReaper,
- const GRState *ST,
- llvm::SmallVectorImpl<const MemRegion*> &DRoots) {
-
- CFG &C = *SymReaper.getLocationContext()->getCFG();
+ const ProgramState *ST) {
// We construct a new Environment object entirely, as this is cheaper than
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
Environment NewEnv = getInitialEnvironment();
- llvm::SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations;
+ SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations;
+
+ MarkLiveCallback CB(SymReaper);
+ ScanReachableSymbols RSScaner(ST, CB);
+
+ llvm::ImmutableMapRef<const Stmt*,SVal>
+ EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
+ F.getTreeFactory());
// Iterate over the block-expr bindings.
for (Environment::iterator I = Env.begin(), E = Env.end();
I != E; ++I) {
const Stmt *BlkExpr = I.getKey();
-
// For recorded locations (used when evaluating loads and stores), we
// consider them live only when their associated normal expression is
// also live.
@@ -185,41 +177,20 @@ EnvironmentManager::removeDeadBindings(Environment Env,
deferredLocations.push_back(std::make_pair(BlkExpr, I.getData()));
continue;
}
-
const SVal &X = I.getData();
- // Block-level expressions in callers are assumed always live.
- if (isBlockExprInCallers(BlkExpr, SymReaper.getLocationContext())) {
- NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X);
-
- if (isa<loc::MemRegionVal>(X)) {
- const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion();
- DRoots.push_back(R);
- }
-
- // Mark all symbols in the block expr's value live.
- MarkLiveCallback cb(SymReaper);
- ST->scanReachableSymbols(X, cb);
- continue;
- }
-
- // Not a block-level expression?
- if (!C.isBlkExpr(BlkExpr))
- continue;
-
if (SymReaper.isLive(BlkExpr)) {
// Copy the binding to the new map.
- NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X);
+ EBMapRef = EBMapRef.add(BlkExpr, X);
// If the block expr's value is a memory region, then mark that region.
if (isa<loc::MemRegionVal>(X)) {
- const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion();
- DRoots.push_back(R);
+ const MemRegion *R = cast<loc::MemRegionVal>(X).getRegion();
+ SymReaper.markLive(R);
}
// Mark all symbols in the block expr's value live.
- MarkLiveCallback cb(SymReaper);
- ST->scanReachableSymbols(X, cb);
+ RSScaner.scan(X);
continue;
}
@@ -228,17 +199,18 @@ EnvironmentManager::removeDeadBindings(Environment Env,
// beginning of itself, but we need its UndefinedVal to determine its
// SVal.
if (X.isUndef() && cast<UndefinedVal>(X).getData())
- NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X);
+ EBMapRef = EBMapRef.add(BlkExpr, X);
}
// Go through he deferred locations and add them to the new environment if
// the correspond Stmt* is in the map as well.
- for (llvm::SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator
+ for (SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator
I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) {
const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1);
- if (NewEnv.ExprBindings.lookup(S))
- NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, I->first, I->second);
+ if (EBMapRef.lookup(S))
+ EBMapRef = EBMapRef.add(I->first, I->second);
}
+ NewEnv.ExprBindings = EBMapRef.asImmutableMap();
return NewEnv;
}
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index fa16fea..5762a21 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -13,7 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/Stmt.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
@@ -93,18 +93,17 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
ProgramPoint progPoint = node->getLocation();
if (!isa<PostStmt>(progPoint))
continue;
-
// Condition 4.
PostStmt ps = cast<PostStmt>(progPoint);
- if (ps.getTag() || isa<PostStmtCustom>(ps))
+ if (ps.getTag())
continue;
if (isa<BinaryOperator>(ps.getStmt()))
continue;
// Conditions 5, 6, and 7.
- const GRState *state = node->getState();
- const GRState *pred_state = pred->getState();
+ const ProgramState *state = node->getState();
+ const ProgramState *pred_state = pred->getState();
if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
progPoint.getLocationContext() != pred->getLocationContext())
continue;
@@ -134,11 +133,11 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
// ExplodedNode.
//===----------------------------------------------------------------------===//
-static inline BumpVector<ExplodedNode*>& getVector(void* P) {
+static inline BumpVector<ExplodedNode*>& getVector(void *P) {
return *reinterpret_cast<BumpVector<ExplodedNode*>*>(P);
}
-void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) {
+void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
assert (!V->isSink());
Preds.addNode(V, G);
V->Succs.addNode(this, G);
@@ -153,12 +152,12 @@ void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
assert(getKind() == Size1);
}
-void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) {
+void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
assert((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0);
assert(!getFlag());
if (getKind() == Size1) {
- if (ExplodedNode* NOld = getNode()) {
+ if (ExplodedNode *NOld = getNode()) {
BumpVectorContext &Ctx = G.getNodeAllocator();
BumpVector<ExplodedNode*> *V =
G.getAllocator().Allocate<BumpVector<ExplodedNode*> >();
@@ -215,11 +214,11 @@ ExplodedNode** ExplodedNode::NodeGroup::end() const {
}
}
-ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L,
- const GRState* State, bool* IsNew) {
+ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
+ const ProgramState *State, bool* IsNew) {
// Profile 'State' to determine if we already have an existing node.
llvm::FoldingSetNodeID profile;
- void* InsertPos = 0;
+ void *InsertPos = 0;
NodeTy::Profile(profile, L, State);
NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
@@ -285,7 +284,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty;
Pass2Ty& Pass2 = M->M;
- llvm::SmallVector<const ExplodedNode*, 10> WL1, WL2;
+ SmallVector<const ExplodedNode*, 10> WL1, WL2;
// ===- Pass 1 (reverse DFS) -===
for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) {
@@ -326,7 +325,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
// ===- Pass 2 (forward DFS to construct the new graph) -===
while (!WL2.empty()) {
- const ExplodedNode* N = WL2.back();
+ const ExplodedNode *N = WL2.back();
WL2.pop_back();
// Skip this node if we have already processed it.
@@ -335,7 +334,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
// Create the corresponding node in the new graph and record the mapping
// from the old node to the new node.
- ExplodedNode* NewN = G->getNode(N->getLocation(), N->State, NULL);
+ ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, NULL);
Pass2[N] = NewN;
// Also record the reverse mapping from the new node to the old node.
@@ -383,7 +382,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources,
}
ExplodedNode*
-InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const {
+InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const {
llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I =
M.find(N);
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index ffe5f0b..ac9cf0b 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -18,6 +18,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtObjC.h"
@@ -35,25 +36,13 @@
using namespace clang;
using namespace ento;
-using llvm::dyn_cast;
-using llvm::dyn_cast_or_null;
-using llvm::cast;
using llvm::APSInt;
-namespace {
- // Trait class for recording returned expression in the state.
- struct ReturnExpr {
- static int TagInt;
- typedef const Stmt *data_type;
- };
- int ReturnExpr::TagInt;
-}
-
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
-static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
+static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(0, &II);
}
@@ -62,7 +51,7 @@ static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
-ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf)
+ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled)
: AMgr(mgr),
Engine(*this),
G(Engine.getGraph()),
@@ -75,11 +64,7 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf)
EntryNode(NULL), currentStmt(NULL),
NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL),
RaiseSel(GetNullarySelector("raise", getContext())),
- BR(mgr, *this), TF(tf) {
-
- // FIXME: Eventually remove the TF object entirely.
- TF->RegisterChecks(*this);
- TF->RegisterPrinters(getStateManager().Printers);
+ ObjCGCEnabled(gcEnabled), BR(mgr, *this) {
if (mgr.shouldEagerlyTrimExplodedGraph()) {
// Enable eager node reclaimation when constructing the ExplodedGraph.
@@ -96,8 +81,8 @@ ExprEngine::~ExprEngine() {
// Utility methods.
//===----------------------------------------------------------------------===//
-const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) {
- const GRState *state = StateMgr.getInitialState(InitLoc);
+const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) {
+ const ProgramState *state = StateMgr.getInitialState(InitLoc);
// Preconditions.
@@ -132,7 +117,7 @@ const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) {
if (!Constraint)
break;
- if (const GRState *newState = state->assume(*Constraint, true))
+ if (const ProgramState *newState = state->assume(*Constraint, true))
state = newState;
break;
@@ -162,11 +147,11 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const
if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) {
SVal calleeV = callOrMessage.getFunctionCallee();
if (const FunctionTextRegion *codeR =
- llvm::dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) {
+ dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) {
const FunctionDecl *fd = codeR->getDecl();
if (const IdentifierInfo *ii = fd->getIdentifier()) {
- llvm::StringRef fname = ii->getName();
+ StringRef fname = ii->getName();
if (fname == "strlen")
return false;
}
@@ -183,28 +168,27 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const
/// evalAssume - Called by ConstraintManager. Used to call checker-specific
/// logic for handling assumptions on symbolic values.
-const GRState *ExprEngine::processAssume(const GRState *state, SVal cond,
- bool assumption) {
- state = getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
-
- // If the state is infeasible at this point, bail out.
- if (!state)
- return NULL;
-
- return TF->evalAssume(state, cond, assumption);
+const ProgramState *ExprEngine::processAssume(const ProgramState *state,
+ SVal cond, bool assumption) {
+ return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
}
-bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) {
+bool ExprEngine::wantsRegionChangeUpdate(const ProgramState *state) {
return getCheckerManager().wantsRegionChangeUpdate(state);
}
-const GRState *
-ExprEngine::processRegionChanges(const GRState *state,
+const ProgramState *
+ExprEngine::processRegionChanges(const ProgramState *state,
const StoreManager::InvalidatedSymbols *invalidated,
- const MemRegion * const *Begin,
- const MemRegion * const *End) {
+ ArrayRef<const MemRegion *> Explicits,
+ ArrayRef<const MemRegion *> Regions) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
- Begin, End);
+ Explicits, Regions);
+}
+
+void ExprEngine::printState(raw_ostream &Out, const ProgramState *State,
+ const char *NL, const char *Sep) {
+ getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
}
void ExprEngine::processEndWorklist(bool hasWorkRemaining) {
@@ -217,7 +201,7 @@ void ExprEngine::processCFGElement(const CFGElement E,
case CFGElement::Invalid:
llvm_unreachable("Unexpected CFGElement kind.");
case CFGElement::Statement:
- ProcessStmt(E.getAs<CFGStmt>()->getStmt(), builder);
+ ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), builder);
return;
case CFGElement::Initializer:
ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), builder);
@@ -232,9 +216,12 @@ void ExprEngine::processCFGElement(const CFGElement E,
}
void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
+ // TODO: Use RAII to remove the unnecessary, tagged nodes.
+ //RegisterCreatedNodes registerCreatedNodes(getGraph());
+
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
- // Recycle any unused states in the GRStateManager.
+ // Recycle any unused states in the ProgramStateManager.
StateMgr.recycleUnusedStates();
currentStmt = S.getStmt();
@@ -242,74 +229,93 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
currentStmt->getLocStart(),
"Error evaluating statement");
+ // A tag to track convenience transitions, which can be removed at cleanup.
+ static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
Builder = &builder;
EntryNode = builder.getPredecessor();
+ const ProgramState *EntryState = EntryNode->getState();
+ CleanedState = EntryState;
+ ExplodedNode *CleanedNode = 0;
+
// Create the cleaned state.
const LocationContext *LC = EntryNode->getLocationContext();
- SymbolReaper SymReaper(LC, currentStmt, SymMgr);
+ SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
- if (AMgr.shouldPurgeDead()) {
- const GRState *St = EntryNode->getState();
- getCheckerManager().runCheckersForLiveSymbols(St, SymReaper);
+ if (AMgr.getPurgeMode() != PurgeNone) {
+ getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
const StackFrameContext *SFC = LC->getCurrentStackFrame();
- CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper);
- } else {
- CleanedState = EntryNode->getState();
+
+ // 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);
}
// Process any special transfer function for dead symbols.
ExplodedNodeSet Tmp;
+ if (!SymReaper.hasDeadSymbols()) {
+ // Generate a CleanedNode that has the environment and store cleaned
+ // up. Since no symbols are dead, we can optimize and not clean out
+ // the constraint manager.
+ CleanedNode =
+ Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag);
+ Tmp.Add(CleanedNode);
- if (!SymReaper.hasDeadSymbols())
- Tmp.Add(EntryNode);
- else {
+ } else {
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
SaveOr OldHasGen(Builder->hasGeneratedNode);
SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
Builder->PurgingDeadSymbols = true;
- // FIXME: This should soon be removed.
- ExplodedNodeSet Tmp2;
- getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode,
- CleanedState, SymReaper);
-
- getCheckerManager().runCheckersForDeadSymbols(Tmp, Tmp2,
+ // 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);
- if (!Builder->BuildSinks && !Builder->hasGeneratedNode)
- Tmp.Add(EntryNode);
- }
+ // 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.
+ for (ExplodedNodeSet::const_iterator
+ I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
+ const ProgramState *CheckerState = (*I)->getState();
- bool HasAutoGenerated = false;
+ // The constraint manager has not been cleaned up yet, so clean up now.
+ CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
+ SymReaper);
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- ExplodedNodeSet Dst;
+ assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
+ "Checkers are not allowed to modify the Environment as a part of "
+ "checkDeadSymbols processing.");
+ assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
+ "Checkers are not allowed to modify the Store as a part of "
+ "checkDeadSymbols processing.");
- // Set the cleaned state.
- Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));
+ // Create a state based on CleanedState with CheckerState GDM and
+ // generate a transition to that state.
+ const ProgramState *CleanedCheckerSt =
+ StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
+ ExplodedNode *CleanedNode = Builder->generateNode(currentStmt,
+ CleanedCheckerSt, *I,
+ &cleanupTag);
+ Tmp.Add(CleanedNode);
+ }
+ }
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ // TODO: Remove Dest set, it's no longer needed.
+ ExplodedNodeSet Dst;
// Visit the statement.
Visit(currentStmt, *I, Dst);
-
- // Do we need to auto-generate a node? We only need to do this to generate
- // a node with a "cleaned" state; CoreEngine will actually handle
- // auto-transitions for other cases.
- if (Dst.size() == 1 && *Dst.begin() == EntryNode
- && !Builder->hasGeneratedNode && !HasAutoGenerated) {
- HasAutoGenerated = true;
- builder.generateNode(currentStmt, GetState(EntryNode), *I);
- }
}
// NULL out these variables to cleanup.
CleanedState = NULL;
EntryNode = NULL;
-
currentStmt = 0;
-
Builder = NULL;
}
@@ -334,7 +340,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){
ExplodedNode *Pred = *I;
- const GRState *state = Pred->getState();
+ const ProgramState *state = Pred->getState();
const FieldDecl *FD = BMI->getAnyMember();
@@ -391,7 +397,7 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor,
StmtNodeBuilder &builder) {
ExplodedNode *pred = builder.getPredecessor();
- const GRState *state = pred->getState();
+ const ProgramState *state = pred->getState();
const VarDecl *varDecl = dtor.getVarDecl();
QualType varType = varDecl->getType();
@@ -422,8 +428,8 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
StmtNodeBuilder &builder) {
}
-void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
+void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
S->getLocStart(),
"Error evaluating statement");
@@ -447,9 +453,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXCatchStmtClass:
case Stmt::CXXDependentScopeMemberExprClass:
- case Stmt::CXXForRangeStmtClass:
case Stmt::CXXPseudoDestructorExprClass:
- case Stmt::CXXTemporaryObjectExprClass:
case Stmt::CXXThrowExprClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXTypeidExprClass:
@@ -472,7 +476,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
{
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Builder->BuildSinks = true;
- const ExplodedNode *node = MakeNode(Dst, S, Pred, GetState(Pred));
+ const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState());
Engine.addAbortedBlock(node, Builder->getBlock());
break;
}
@@ -495,6 +499,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CaseStmtClass:
case Stmt::CompoundStmtClass:
case Stmt::ContinueStmtClass:
+ case Stmt::CXXForRangeStmtClass:
case Stmt::DefaultStmtClass:
case Stmt::DoStmtClass:
case Stmt::ForStmtClass:
@@ -511,7 +516,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::GNUNullExprClass: {
// GNU __null is a pointer-width integer, not an actual pointer.
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false));
MakeNode(Dst, S, Pred, state);
break;
@@ -522,11 +527,12 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
case Stmt::ObjCPropertyRefExprClass:
- VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst);
+ // Implicitly handled by Environment::getSVal().
+ Dst.Add(Pred);
break;
case Stmt::ImplicitValueInitExprClass: {
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
QualType ty = cast<ImplicitValueInitExpr>(S)->getType();
SVal val = svalBuilder.makeZeroVal(ty);
MakeNode(Dst, S, Pred, state->BindExpr(S, val));
@@ -558,6 +564,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CUDAKernelCallExprClass:
case Stmt::OpaqueValueExprClass:
case Stmt::AsTypeExprClass:
+ case Stmt::AtomicExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
@@ -597,7 +604,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
}
else if (B->getOpcode() == BO_Comma) {
- const GRState* state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS())));
break;
}
@@ -621,6 +628,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
}
+ case Stmt::CXXTemporaryObjectExprClass:
case Stmt::CXXConstructExprClass: {
const CXXConstructExpr *C = cast<CXXConstructExpr>(S);
// For block-level CXXConstructExpr, we don't have a destination region.
@@ -644,7 +652,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
// the CFG do not model them as explicit control-flow.
case Stmt::ChooseExprClass: { // __builtin_choose_expr
- const ChooseExpr* C = cast<ChooseExpr>(S);
+ const ChooseExpr *C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
break;
}
@@ -687,7 +695,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
- const CastExpr* C = cast<CastExpr>(S);
+ const CastExpr *C = cast<CastExpr>(S);
// Handle the previsit checks.
ExplodedNodeSet dstPrevisit;
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this);
@@ -708,7 +716,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
const MaterializeTemporaryExpr *Materialize
= cast<MaterializeTemporaryExpr>(S);
if (!Materialize->getType()->isRecordType())
- CreateCXXTemporaryObject(Materialize->GetTemporaryExpr(), Pred, Dst);
+ CreateCXXTemporaryObject(Materialize, Pred, Dst);
else
Visit(Materialize->GetTemporaryExpr(), Pred, Dst);
break;
@@ -730,7 +738,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
case Stmt::ObjCMessageExprClass:
- VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst);
+ VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
break;
case Stmt::ObjCAtThrowStmtClass: {
@@ -738,7 +746,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
// an abort.
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Builder->BuildSinks = true;
- MakeNode(Dst, S, Pred, GetState(Pred));
+ MakeNode(Dst, S, Pred, Pred->getState());
break;
}
@@ -756,7 +764,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
case Stmt::StmtExprClass: {
- const StmtExpr* SE = cast<StmtExpr>(S);
+ const StmtExpr *SE = cast<StmtExpr>(S);
if (SE->getSubStmt()->body_empty()) {
// Empty statement expression.
@@ -766,8 +774,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
}
- if (Expr* LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
- const GRState* state = GetState(Pred);
+ if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
+ const ProgramState *state = Pred->getState();
MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr)));
}
else
@@ -777,7 +785,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
}
case Stmt::StringLiteralClass: {
- const GRState* state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
SVal V = state->getLValue(cast<StringLiteral>(S));
MakeNode(Dst, S, Pred, state->BindExpr(S, V));
return;
@@ -811,8 +819,7 @@ void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
if (nodeBuilder.getBlockCounter().getNumVisited(
pred->getLocationContext()->getCurrentStackFrame(),
block->getBlockID()) >= AMgr.getMaxVisit()) {
-
- static int tag = 0;
+ static SimpleProgramPointTag tag("ExprEngine : Block count exceeded");
nodeBuilder.generateNode(pred->getState(), pred, &tag, true);
}
}
@@ -821,11 +828,12 @@ void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
// Generic node creation.
//===----------------------------------------------------------------------===//
-ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
- ExplodedNode* Pred, const GRState* St,
- ProgramPoint::Kind K, const void *tag) {
+ExplodedNode *ExprEngine::MakeNode(ExplodedNodeSet &Dst, const Stmt *S,
+ ExplodedNode *Pred, const ProgramState *St,
+ ProgramPoint::Kind K,
+ const ProgramPointTag *tag) {
assert (Builder && "StmtNodeBuilder not present.");
- SaveAndRestore<const void*> OldTag(Builder->Tag);
+ SaveAndRestore<const ProgramPointTag*> OldTag(Builder->Tag);
Builder->Tag = tag;
return Builder->MakeNode(Dst, S, Pred, St, K);
}
@@ -834,8 +842,8 @@ ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
// Branch processing.
//===----------------------------------------------------------------------===//
-const GRState* ExprEngine::MarkBranch(const GRState* state,
- const Stmt* Terminator,
+const ProgramState *ExprEngine::MarkBranch(const ProgramState *state,
+ const Stmt *Terminator,
bool branchTaken) {
switch (Terminator->getStmtClass()) {
@@ -855,7 +863,7 @@ const GRState* ExprEngine::MarkBranch(const GRState* state,
// 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) ||
+ const Expr *Ex = (Op == BO_LAnd && branchTaken) ||
(Op == BO_LOr && !branchTaken)
? B->getRHS() : B->getLHS();
@@ -870,7 +878,7 @@ const GRState* ExprEngine::MarkBranch(const GRState* state,
// For ?, if branchTaken == true then the value is either the LHS or
// the condition itself. (GNU extension).
- const Expr* Ex;
+ const Expr *Ex;
if (branchTaken)
Ex = C->getTrueExpr();
@@ -882,9 +890,9 @@ const GRState* ExprEngine::MarkBranch(const GRState* state,
case Stmt::ChooseExprClass: { // ?:
- const ChooseExpr* C = cast<ChooseExpr>(Terminator);
+ const ChooseExpr *C = cast<ChooseExpr>(Terminator);
- const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS();
+ const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS();
return state->BindExpr(C, UndefinedVal(Ex));
}
}
@@ -895,8 +903,10 @@ const GRState* ExprEngine::MarkBranch(const GRState* state,
/// integers that promote their values (which are currently not tracked well).
/// This function returns the SVal bound to Condition->IgnoreCasts if all the
// cast(s) did was sign-extend the original value.
-static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state,
- const Stmt* Condition, ASTContext& Ctx) {
+static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
+ const ProgramState *state,
+ const Stmt *Condition,
+ ASTContext &Ctx) {
const Expr *Ex = dyn_cast<Expr>(Condition);
if (!Ex)
@@ -929,7 +939,7 @@ static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state,
return state->getSVal(Ex);
}
-void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
+void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
BranchNodeBuilder& builder) {
// Check for NULL conditions; e.g. "for(;;)"
@@ -948,7 +958,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
if (!builder.isFeasible(true) && !builder.isFeasible(false))
return;
- const GRState* PrevState = builder.getState();
+ const ProgramState *PrevState = builder.getState();
SVal X = PrevState->getSVal(Condition);
if (X.isUnknownOrUndef()) {
@@ -980,7 +990,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
// Process the true branch.
if (builder.isFeasible(true)) {
- if (const GRState *state = PrevState->assume(V, true))
+ if (const ProgramState *state = PrevState->assume(V, true))
builder.generateNode(MarkBranch(state, Term, true), true);
else
builder.markInfeasible(true);
@@ -988,7 +998,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
// Process the false branch.
if (builder.isFeasible(false)) {
- if (const GRState *state = PrevState->assume(V, false))
+ if (const ProgramState *state = PrevState->assume(V, false))
builder.generateNode(MarkBranch(state, Term, false), false);
else
builder.markInfeasible(false);
@@ -999,7 +1009,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term,
/// nodes by processing the 'effects' of a computed goto jump.
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
- const GRState *state = builder.getState();
+ const ProgramState *state = builder.getState();
SVal V = state->getSVal(builder.getTarget());
// Three possibilities:
@@ -1021,8 +1031,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
}
}
- assert(false && "No block with label.");
- return;
+ llvm_unreachable("No block with label.");
}
if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) {
@@ -1040,31 +1049,9 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
builder.generateNode(I, state);
}
-
-void ExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L,
- const Expr* R,
- ExplodedNode* Pred, ExplodedNodeSet& Dst) {
-
- assert(Ex == currentStmt &&
- Pred->getLocationContext()->getCFG()->isBlkExpr(Ex));
-
- const GRState* state = GetState(Pred);
- SVal X = state->getSVal(Ex);
-
- assert (X.isUndef());
-
- const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
- assert(SE);
- X = state->getSVal(SE);
-
- // Make sure that we invalidate the previous binding.
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true));
-}
-
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) {
- getTF().evalEndPath(*this, builder);
StateMgr.EndPath(builder.getState());
getCheckerManager().runCheckersForEndPath(builder, *this);
}
@@ -1073,8 +1060,8 @@ void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) {
/// nodes by processing the 'effects' of a switch statement.
void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
typedef SwitchNodeBuilder::iterator iterator;
- const GRState* state = builder.getState();
- const Expr* CondE = builder.getCondition();
+ const ProgramState *state = builder.getState();
+ const Expr *CondE = builder.getCondition();
SVal CondV_untested = state->getSVal(CondE);
if (CondV_untested.isUndef()) {
@@ -1086,7 +1073,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
}
DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested);
- const GRState *DefaultSt = state;
+ const ProgramState *DefaultSt = state;
iterator I = builder.begin(), EI = builder.end();
bool defaultIsFeasible = I == EI;
@@ -1096,28 +1083,16 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
if (!I.getBlock())
continue;
- const CaseStmt* Case = I.getCase();
+ const CaseStmt *Case = I.getCase();
// Evaluate the LHS of the case value.
- Expr::EvalResult V1;
- bool b = Case->getLHS()->Evaluate(V1, getContext());
-
- // Sanity checks. These go away in Release builds.
- assert(b && V1.Val.isInt() && !V1.HasSideEffects
- && "Case condition must evaluate to an integer constant.");
- (void)b; // silence unused variable warning
- assert(V1.Val.getInt().getBitWidth() ==
- getContext().getTypeSize(CondE->getType()));
+ llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
+ assert(V1.getBitWidth() == getContext().getTypeSize(CondE->getType()));
// Get the RHS of the case, if it exists.
- Expr::EvalResult V2;
-
- if (const Expr* E = Case->getRHS()) {
- b = E->Evaluate(V2, getContext());
- assert(b && V2.Val.isInt() && !V2.HasSideEffects
- && "Case condition must evaluate to an integer constant.");
- (void)b; // silence unused variable warning
- }
+ llvm::APSInt V2;
+ if (const Expr *E = Case->getRHS())
+ V2 = E->EvaluateKnownConstInt(getContext());
else
V2 = V1;
@@ -1126,12 +1101,12 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
// This should be easy once we have "ranges" for NonLVals.
do {
- nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt()));
+ nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1));
DefinedOrUnknownSVal Res = svalBuilder.evalEQ(DefaultSt ? DefaultSt : state,
CondV, CaseVal);
// Now "assume" that the case matches.
- if (const GRState* stateNew = state->assume(Res, true)) {
+ if (const ProgramState *stateNew = state->assume(Res, true)) {
builder.generateCaseStmtNode(I, stateNew);
// If CondV evaluates to a constant, then we know that this
@@ -1144,7 +1119,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
// Now "assume" that the case doesn't match. Add this state
// to the default state (if it is feasible).
if (DefaultSt) {
- if (const GRState *stateNew = DefaultSt->assume(Res, false)) {
+ if (const ProgramState *stateNew = DefaultSt->assume(Res, false)) {
defaultIsFeasible = true;
DefaultSt = stateNew;
}
@@ -1155,11 +1130,11 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
}
// Concretize the next value in the range.
- if (V1.Val.getInt() == V2.Val.getInt())
+ if (V1 == V2)
break;
- ++V1.Val.getInt();
- assert (V1.Val.getInt() <= V2.Val.getInt());
+ ++V1;
+ assert (V1 <= V2);
} while (true);
}
@@ -1184,120 +1159,16 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
builder.generateDefaultCaseNode(DefaultSt);
}
-void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) {
- const GRState *state = B.getState()->enterStackFrame(B.getCalleeContext());
- B.generateNode(state);
-}
-
-void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
- const GRState *state = B.getState();
- const ExplodedNode *Pred = B.getPredecessor();
- const StackFrameContext *calleeCtx =
- cast<StackFrameContext>(Pred->getLocationContext());
- const Stmt *CE = calleeCtx->getCallSite();
-
- // If the callee returns an expression, bind its value to CallExpr.
- const Stmt *ReturnedExpr = state->get<ReturnExpr>();
- if (ReturnedExpr) {
- SVal RetVal = state->getSVal(ReturnedExpr);
- state = state->BindExpr(CE, RetVal);
- // Clear the return expr GDM.
- state = state->remove<ReturnExpr>();
- }
-
- // Bind the constructed object value to CXXConstructExpr.
- if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
- const CXXThisRegion *ThisR =
- getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
-
- SVal ThisV = state->getSVal(ThisR);
- // Always bind the region to the CXXConstructExpr.
- state = state->BindExpr(CCE, ThisV);
- }
-
- B.generateNode(state);
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer functions: logical operations ('&&', '||').
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
-
- assert(B->getOpcode() == BO_LAnd ||
- B->getOpcode() == BO_LOr);
-
- assert(B==currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B));
-
- const GRState* state = GetState(Pred);
- SVal X = state->getSVal(B);
- assert(X.isUndef());
-
- const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
- assert(Ex);
-
- if (Ex == B->getRHS()) {
- X = state->getSVal(Ex);
-
- // Handle undefined values.
- if (X.isUndef()) {
- MakeNode(Dst, B, Pred, state->BindExpr(B, X));
- return;
- }
-
- DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
-
- // We took the RHS. Because the value of the '&&' or '||' expression must
- // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
- // or 1. Alternatively, we could take a lazy approach, and calculate this
- // value later when necessary. We don't have the machinery in place for
- // this right now, and since most logical expressions are used for branches,
- // the payoff is not likely to be large. Instead, we do eager evaluation.
- if (const GRState *newState = state->assume(XD, true))
- MakeNode(Dst, B, Pred,
- newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType())));
-
- if (const GRState *newState = state->assume(XD, false))
- MakeNode(Dst, B, Pred,
- newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType())));
- }
- else {
- // We took the LHS expression. Depending on whether we are '&&' or
- // '||' we know what the value of the expression is via properties of
- // the short-circuiting.
- X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
- B->getType());
- MakeNode(Dst, B, Pred, state->BindExpr(B, X));
- }
-}
-
//===----------------------------------------------------------------------===//
// Transfer functions: Loads and stores.
//===----------------------------------------------------------------------===//
-void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
-
- ExplodedNodeSet Tmp;
-
- CanQualType T = getContext().getCanonicalType(BE->getType());
- SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
- Pred->getLocationContext());
-
- MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V),
- ProgramPoint::PostLValueKind);
-
- // Post-visit the BlockExpr.
- getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
-}
-
void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
- if (const VarDecl* VD = dyn_cast<VarDecl>(D)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
assert(Ex->isLValue());
SVal V = state->getLValue(VD, Pred->getLocationContext());
@@ -1314,13 +1185,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramPoint::PostLValueKind);
return;
}
- if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) {
+ if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!Ex->isLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V));
return;
}
- if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
SVal V = svalBuilder.getFunctionPointer(FD);
MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V),
ProgramPoint::PostLValueKind);
@@ -1331,124 +1202,93 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
}
/// VisitArraySubscriptExpr - Transfer function for array accesses
-void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* A,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst){
+void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst){
- const Expr* Base = A->getBase()->IgnoreParens();
- const Expr* Idx = A->getIdx()->IgnoreParens();
+ const Expr *Base = A->getBase()->IgnoreParens();
+ const Expr *Idx = A->getIdx()->IgnoreParens();
- // Evaluate the base.
- ExplodedNodeSet Tmp;
- Visit(Base, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) {
- ExplodedNodeSet Tmp2;
- Visit(Idx, *I1, Tmp2); // Evaluate the index.
- ExplodedNodeSet Tmp3;
- getCheckerManager().runCheckersForPreStmt(Tmp3, Tmp2, A, *this);
-
- for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) {
- const GRState* state = GetState(*I2);
- SVal V = state->getLValue(A->getType(), state->getSVal(Idx),
- state->getSVal(Base));
- assert(A->isLValue());
- MakeNode(Dst, A, *I2, state->BindExpr(A, V), ProgramPoint::PostLValueKind);
- }
+
+ ExplodedNodeSet checkerPreStmt;
+ getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this);
+
+ for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(),
+ ei = checkerPreStmt.end(); it != ei; ++it) {
+ const ProgramState *state = (*it)->getState();
+ SVal V = state->getLValue(A->getType(), state->getSVal(Idx),
+ state->getSVal(Base));
+ assert(A->isLValue());
+ MakeNode(Dst, A, *it, state->BindExpr(A, V), ProgramPoint::PostLValueKind);
}
}
/// VisitMemberExpr - Transfer function for member expressions.
-void ExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
-
- Expr *baseExpr = M->getBase()->IgnoreParens();
- ExplodedNodeSet dstBase;
- Visit(baseExpr, Pred, dstBase);
+void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
- FieldDecl *field = dyn_cast<FieldDecl>(M->getMemberDecl());
+ Decl *member = M->getMemberDecl();
+ if (VarDecl *VD = dyn_cast<VarDecl>(member)) {
+ assert(M->isLValue());
+ VisitCommonDeclRefExpr(M, VD, Pred, Dst);
+ return;
+ }
+
+ FieldDecl *field = dyn_cast<FieldDecl>(member);
if (!field) // FIXME: skipping member expressions for non-fields
return;
- for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end();
- I != E; ++I) {
- const GRState* state = GetState(*I);
- SVal baseExprVal = state->getSVal(baseExpr);
- if (isa<nonloc::LazyCompoundVal>(baseExprVal) ||
- isa<nonloc::CompoundVal>(baseExprVal) ||
- // FIXME: This can originate by conjuring a symbol for an unknown
- // temporary struct object, see test/Analysis/fields.c:
- // (p = getit()).x
- isa<nonloc::SymbolVal>(baseExprVal)) {
- MakeNode(Dst, M, *I, state->BindExpr(M, UnknownVal()));
- continue;
- }
+ Expr *baseExpr = M->getBase()->IgnoreParens();
+ const ProgramState *state = Pred->getState();
+ SVal baseExprVal = state->getSVal(baseExpr);
+ if (isa<nonloc::LazyCompoundVal>(baseExprVal) ||
+ isa<nonloc::CompoundVal>(baseExprVal) ||
+ // FIXME: This can originate by conjuring a symbol for an unknown
+ // temporary struct object, see test/Analysis/fields.c:
+ // (p = getit()).x
+ isa<nonloc::SymbolVal>(baseExprVal)) {
+ MakeNode(Dst, M, Pred, state->BindExpr(M, UnknownVal()));
+ return;
+ }
- // FIXME: Should we insert some assumption logic in here to determine
- // if "Base" is a valid piece of memory? Before we put this assumption
- // later when using FieldOffset lvals (which we no longer have).
+ // FIXME: Should we insert some assumption logic in here to determine
+ // if "Base" is a valid piece of memory? Before we put this assumption
+ // later when using FieldOffset lvals (which we no longer have).
- // For all other cases, compute an lvalue.
- SVal L = state->getLValue(field, baseExprVal);
- if (M->isLValue())
- MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind);
- else
- evalLoad(Dst, M, *I, state, L);
- }
+ // For all other cases, compute an lvalue.
+ SVal L = state->getLValue(field, baseExprVal);
+ if (M->isLValue())
+ MakeNode(Dst, M, Pred, state->BindExpr(M, L), ProgramPoint::PostLValueKind);
+ else
+ evalLoad(Dst, M, Pred, state, L);
}
/// evalBind - Handle the semantics of binding a value to a specific location.
/// This method is used by evalStore and (soon) VisitDeclStmt, and others.
-void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE,
- ExplodedNode* Pred, const GRState* state,
- SVal location, SVal Val, bool atDeclInit) {
-
+void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
+ ExplodedNode *Pred,
+ SVal location, SVal Val, bool atDeclInit) {
// Do a previsit of the bind.
- ExplodedNodeSet CheckedSet, Src;
- Src.Add(Pred);
- getCheckerManager().runCheckersForBind(CheckedSet, Src, location, Val, StoreE,
- *this);
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val,
+ StoreE, *this);
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I!=E; ++I) {
- if (Pred != *I)
- state = GetState(*I);
-
- const GRState* newState = 0;
+ const ProgramState *state = (*I)->getState();
if (atDeclInit) {
const VarRegion *VR =
cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion());
- newState = state->bindDecl(VR, Val);
- }
- else {
- if (location.isUnknown()) {
- // We know that the new state will be the same as the old state since
- // the location of the binding is "unknown". Consequently, there
- // is no reason to just create a new node.
- newState = state;
- }
- else {
- // We are binding to a value other than 'unknown'. Perform the binding
- // using the StoreManager.
- newState = state->bindLoc(cast<Loc>(location), Val);
- }
+ state = state->bindDecl(VR, Val);
+ } else {
+ state = state->bindLoc(location, Val);
}
- // The next thing to do is check if the TransferFuncs object wants to
- // update the state based on the new binding. If the GRTransferFunc object
- // doesn't do anything, just auto-propagate the current state.
-
- // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE'
- // is non-NULL. Checkers typically care about
-
- StmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE,
- true);
-
- getTF().evalBind(BuilderRef, location, Val);
+ MakeNode(Dst, StoreE, *I, state);
}
}
@@ -1460,11 +1300,11 @@ void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE,
/// @param state The current simulation state
/// @param location The location to store the value
/// @param Val The value to be stored
-void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE,
- const Expr* LocationE,
- ExplodedNode* Pred,
- const GRState* state, SVal location, SVal Val,
- const void *tag) {
+void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
+ const Expr *LocationE,
+ ExplodedNode *Pred,
+ const ProgramState *state, SVal location, SVal Val,
+ const ProgramPointTag *tag) {
assert(Builder && "StmtNodeBuilder must be defined.");
@@ -1474,9 +1314,8 @@ void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE,
if (isa<loc::ObjCPropRef>(location)) {
loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
- ExplodedNodeSet src = Pred;
return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(),
- StoreE, Val), src, Dst);
+ StoreE, Val), Pred, Dst);
}
// Evaluate the location (checks for bad dereferences).
@@ -1493,38 +1332,38 @@ void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE,
ProgramPoint::PostStoreKind);
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
- evalBind(Dst, StoreE, *NI, GetState(*NI), location, Val);
+ evalBind(Dst, StoreE, *NI, location, Val);
}
-void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex,
- ExplodedNode* Pred,
- const GRState* state, SVal location,
- const void *tag, QualType LoadTy) {
+void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex,
+ ExplodedNode *Pred,
+ const ProgramState *state, SVal location,
+ const ProgramPointTag *tag, QualType LoadTy) {
assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
if (isa<loc::ObjCPropRef>(location)) {
loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location);
- ExplodedNodeSet src = Pred;
return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex),
- src, Dst);
+ Pred, Dst);
}
// 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
// referenced value.
- if (const TypedRegion *TR =
- dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
+ if (const TypedValueRegion *TR =
+ dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
QualType ValTy = TR->getValueType();
if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) {
- static int loadReferenceTag = 0;
+ static SimpleProgramPointTag
+ loadReferenceTag("ExprEngine : Load Reference");
ExplodedNodeSet Tmp;
evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag,
getContext().getPointerType(RT->getPointeeType()));
// Perform the load from the referenced value.
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) {
- state = GetState(*I);
+ state = (*I)->getState();
location = state->getSVal(Ex);
evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy);
}
@@ -1535,10 +1374,10 @@ void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex,
evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy);
}
-void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex,
- ExplodedNode* Pred,
- const GRState* state, SVal location,
- const void *tag, QualType LoadTy) {
+void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *Ex,
+ ExplodedNode *Pred,
+ const ProgramState *state, SVal location,
+ const ProgramPointTag *tag, QualType LoadTy) {
// Evaluate the location (checks for bad dereferences).
ExplodedNodeSet Tmp;
@@ -1554,7 +1393,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex,
// Proceed with the load.
for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) {
- state = GetState(*NI);
+ state = (*NI)->getState();
if (location.isUnknown()) {
// This is important. We must nuke the old binding.
@@ -1572,9 +1411,9 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex,
}
void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
- ExplodedNode* Pred,
- const GRState* state, SVal location,
- const void *tag, bool isLoad) {
+ ExplodedNode *Pred,
+ const ProgramState *state, SVal location,
+ const ProgramPointTag *tag, bool isLoad) {
// Early checks for performance reason.
if (location.isUnknown()) {
Dst.Add(Pred);
@@ -1582,7 +1421,7 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
}
ExplodedNodeSet Src;
- if (Builder->GetState(Pred) == state) {
+ if (Pred->getState() == state) {
Src.Add(Pred);
} else {
// Associate this new state with an ExplodedNode.
@@ -1593,7 +1432,11 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S,
// "p = 0" is not noted as "Null pointer value stored to 'p'" but
// instead "int *p" is noted as
// "Variable 'p' initialized to a null pointer value"
- ExplodedNode *N = Builder->generateNode(S, state, Pred, this);
+
+ // FIXME: why is 'tag' not used instead of etag?
+ static SimpleProgramPointTag etag("ExprEngine: Location");
+
+ ExplodedNode *N = Builder->generateNode(S, state, Pred, &etag);
Src.Add(N ? N : Pred);
}
getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S,
@@ -1611,7 +1454,7 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
// cases as well.
#if 0
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
@@ -1627,7 +1470,7 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
case Stmt::CXXOperatorCallExprClass: {
const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE);
methodDecl =
- llvm::dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl());
+ dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl());
break;
}
case Stmt::CXXMemberCallExprClass: {
@@ -1676,126 +1519,18 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
#endif
}
-void ExprEngine::VisitCallExpr(const CallExpr* CE, ExplodedNode* Pred,
- ExplodedNodeSet& dst) {
-
- // Determine the type of function we're calling (if available).
- const FunctionProtoType *Proto = NULL;
- QualType FnType = CE->getCallee()->IgnoreParens()->getType();
- if (const PointerType *FnTypePtr = FnType->getAs<PointerType>())
- Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>();
-
- // Should the first argument be evaluated as an lvalue?
- bool firstArgumentAsLvalue = false;
- switch (CE->getStmtClass()) {
- case Stmt::CXXOperatorCallExprClass:
- firstArgumentAsLvalue = true;
- break;
- default:
- break;
- }
-
- // Evaluate the arguments.
- ExplodedNodeSet dstArgsEvaluated;
- evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, dstArgsEvaluated,
- firstArgumentAsLvalue);
-
- // Evaluate the callee.
- ExplodedNodeSet dstCalleeEvaluated;
- evalCallee(CE, dstArgsEvaluated, dstCalleeEvaluated);
-
- // Perform the previsit of the CallExpr.
- ExplodedNodeSet dstPreVisit;
- getCheckerManager().runCheckersForPreStmt(dstPreVisit, dstCalleeEvaluated,
- CE, *this);
-
- // Now evaluate the call itself.
- class DefaultEval : public GraphExpander {
- ExprEngine &Eng;
- const CallExpr *CE;
- public:
-
- DefaultEval(ExprEngine &eng, const CallExpr *ce)
- : Eng(eng), CE(ce) {}
- virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
- // Should we inline the call?
- if (Eng.getAnalysisManager().shouldInlineCall() &&
- Eng.InlineCall(Dst, CE, Pred)) {
- return;
- }
-
- StmtNodeBuilder &Builder = Eng.getBuilder();
- assert(&Builder && "StmtNodeBuilder must be defined.");
-
- // Dispatch to the plug-in transfer function.
- unsigned oldSize = Dst.size();
- SaveOr OldHasGen(Builder.hasGeneratedNode);
-
- // Dispatch to transfer function logic to handle the call itself.
- const Expr* Callee = CE->getCallee()->IgnoreParens();
- const GRState* state = Eng.GetState(Pred);
- SVal L = state->getSVal(Callee);
- Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
-
- // Handle the case where no nodes where generated. Auto-generate that
- // contains the updated state if we aren't generating sinks.
- if (!Builder.BuildSinks && Dst.size() == oldSize &&
- !Builder.hasGeneratedNode)
- Eng.MakeNode(Dst, CE, Pred, state);
- }
- };
-
- // Finally, evaluate the function call. We try each of the checkers
- // to see if the can evaluate the function call.
- ExplodedNodeSet dstCallEvaluated;
- DefaultEval defEval(*this, CE);
- getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
- dstPreVisit,
- CE, *this, &defEval);
-
- // Finally, perform the post-condition check of the CallExpr and store
- // the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
- *this);
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C dot-syntax to access a property.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Ex,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- ExplodedNodeSet dstBase;
-
- // Visit the receiver (if any).
- if (Ex->isObjectReceiver())
- Visit(Ex->getBase(), Pred, dstBase);
- else
- dstBase = Pred;
-
- ExplodedNodeSet dstPropRef;
-
- // Using the base, compute the lvalue of the instance variable.
- for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end();
- I!=E; ++I) {
- ExplodedNode *nodeBase = *I;
- const GRState *state = GetState(nodeBase);
- MakeNode(dstPropRef, Ex, *I, state->BindExpr(Ex, loc::ObjCPropRef(Ex)));
- }
-
- Dst.insert(dstPropRef);
+std::pair<const ProgramPointTag *, const ProgramPointTag*>
+ExprEngine::getEagerlyAssumeTags() {
+ static SimpleProgramPointTag
+ EagerlyAssumeTrue("ExprEngine : Eagerly Assume True"),
+ EagerlyAssumeFalse("ExprEngine : Eagerly Assume False");
+ return std::make_pair(&EagerlyAssumeTrue, &EagerlyAssumeFalse);
}
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C ivar references.
-//===----------------------------------------------------------------------===//
-
-static std::pair<const void*,const void*> EagerlyAssumeTag
- = std::pair<const void*,const void*>(&EagerlyAssumeTag,static_cast<void*>(0));
-
void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
const Expr *Ex) {
+
+
for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
ExplodedNode *Pred = *I;
@@ -1808,25 +1543,24 @@ void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
continue;
}
- const GRState* state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
SVal V = state->getSVal(Ex);
if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) {
+ const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags =
+ getEagerlyAssumeTags();
+
// First assume that the condition is true.
- if (const GRState *stateTrue = state->assume(*SEV, true)) {
- stateTrue = stateTrue->BindExpr(Ex,
- svalBuilder.makeIntVal(1U, Ex->getType()));
- Dst.Add(Builder->generateNode(PostStmtCustom(Ex,
- &EagerlyAssumeTag, Pred->getLocationContext()),
- stateTrue, Pred));
+ if (const ProgramState *StateTrue = state->assume(*SEV, true)) {
+ SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
+ StateTrue = StateTrue->BindExpr(Ex, Val);
+ Dst.Add(Builder->generateNode(Ex, StateTrue, Pred, tags.first));
}
// Next, assume that the condition is false.
- if (const GRState *stateFalse = state->assume(*SEV, false)) {
- stateFalse = stateFalse->BindExpr(Ex,
- svalBuilder.makeIntVal(0U, Ex->getType()));
- Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag,
- Pred->getLocationContext()),
- stateFalse, Pred));
+ if (const ProgramState *StateFalse = state->assume(*SEV, false)) {
+ SVal Val = svalBuilder.makeIntVal(0U, Ex->getType());
+ StateFalse = StateFalse->BindExpr(Ex, Val);
+ Dst.Add(Builder->generateNode(Ex, StateFalse, Pred, tags.second));
}
}
else
@@ -1834,954 +1568,15 @@ void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
}
}
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C @synchronized.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
-
- // The mutex expression is a CFGElement, so we don't need to explicitly
- // visit it since it will already be processed.
-
- // Pre-visit the ObjCAtSynchronizedStmt.
- ExplodedNodeSet Tmp;
- Tmp.Add(Pred);
- getCheckerManager().runCheckersForPreStmt(Dst, Tmp, S, *this);
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C ivar references.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* Ex,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
-
- // Visit the base expression, which is needed for computing the lvalue
- // of the ivar.
- ExplodedNodeSet dstBase;
- const Expr *baseExpr = Ex->getBase();
- Visit(baseExpr, Pred, dstBase);
-
- ExplodedNodeSet dstIvar;
-
- // Using the base, compute the lvalue of the instance variable.
- for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end();
- I!=E; ++I) {
- ExplodedNode *nodeBase = *I;
- const GRState *state = GetState(nodeBase);
- SVal baseVal = state->getSVal(baseExpr);
- SVal location = state->getLValue(Ex->getDecl(), baseVal);
- MakeNode(dstIvar, Ex, *I, state->BindExpr(Ex, location));
- }
-
- // Perform the post-condition check of the ObjCIvarRefExpr and store
- // the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C fast enumeration 'for' statements.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S,
- ExplodedNode* Pred, ExplodedNodeSet& Dst) {
-
- // ObjCForCollectionStmts are processed in two places. This method
- // handles the case where an ObjCForCollectionStmt* occurs as one of the
- // statements within a basic block. This transfer function does two things:
- //
- // (1) binds the next container value to 'element'. This creates a new
- // node in the ExplodedGraph.
- //
- // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
- // whether or not the container has any more elements. This value
- // will be tested in ProcessBranch. We need to explicitly bind
- // this value because a container can contain nil elements.
- //
- // FIXME: Eventually this logic should actually do dispatches to
- // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
- // This will require simulating a temporary NSFastEnumerationState, either
- // through an SVal or through the use of MemRegions. This value can
- // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
- // terminates we reclaim the temporary (it goes out of scope) and we
- // we can test if the SVal is 0 or if the MemRegion is null (depending
- // on what approach we take).
- //
- // For now: simulate (1) by assigning either a symbol or nil if the
- // container is empty. Thus this transfer function will by default
- // result in state splitting.
-
- const Stmt* elem = S->getElement();
- SVal ElementV;
-
- if (const DeclStmt* DS = dyn_cast<DeclStmt>(elem)) {
- const VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl());
- assert (ElemD->getInit() == 0);
- ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext());
- VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV);
- return;
- }
-
- ExplodedNodeSet Tmp;
- Visit(cast<Expr>(elem), Pred, Tmp);
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
- const GRState* state = GetState(*I);
- VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem));
- }
-}
-
-void ExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S,
- ExplodedNode* Pred, ExplodedNodeSet& Dst,
- SVal ElementV) {
-
- // Check if the location we are writing back to is a null pointer.
- const Stmt* elem = S->getElement();
- ExplodedNodeSet Tmp;
- evalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false);
-
- if (Tmp.empty())
- return;
-
- for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) {
- Pred = *NI;
- const GRState *state = GetState(Pred);
-
- // Handle the case where the container still has elements.
- SVal TrueV = svalBuilder.makeTruthVal(1);
- const GRState *hasElems = state->BindExpr(S, TrueV);
-
- // Handle the case where the container has no elements.
- SVal FalseV = svalBuilder.makeTruthVal(0);
- const GRState *noElems = state->BindExpr(S, FalseV);
-
- if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV))
- if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) {
- // FIXME: The proper thing to do is to really iterate over the
- // container. We will do this with dispatch logic to the store.
- // For now, just 'conjure' up a symbolic value.
- QualType T = R->getValueType();
- assert(Loc::isLocType(T));
- unsigned Count = Builder->getCurrentBlockCount();
- SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count);
- SVal V = svalBuilder.makeLoc(Sym);
- hasElems = hasElems->bindLoc(ElementV, V);
-
- // Bind the location to 'nil' on the false branch.
- SVal nilV = svalBuilder.makeIntVal(0, T);
- noElems = noElems->bindLoc(ElementV, nilV);
- }
-
- // Create the new nodes.
- MakeNode(Dst, S, Pred, hasElems);
- MakeNode(Dst, S, Pred, noElems);
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer function: Objective-C message expressions.
-//===----------------------------------------------------------------------===//
-
-namespace {
-class ObjCMsgWLItem {
-public:
- ObjCMessageExpr::const_arg_iterator I;
- ExplodedNode *N;
-
- ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n)
- : I(i), N(n) {}
-};
-} // end anonymous namespace
-
-void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst){
-
- // Create a worklist to process both the arguments.
- llvm::SmallVector<ObjCMsgWLItem, 20> WL;
-
- // But first evaluate the receiver (if any).
- ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end();
- if (const Expr *Receiver = ME->getInstanceReceiver()) {
- ExplodedNodeSet Tmp;
- Visit(Receiver, Pred, Tmp);
-
- if (Tmp.empty())
- return;
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I)
- WL.push_back(ObjCMsgWLItem(AI, *I));
- }
- else
- WL.push_back(ObjCMsgWLItem(AI, Pred));
-
- // Evaluate the arguments.
- ExplodedNodeSet ArgsEvaluated;
- while (!WL.empty()) {
- ObjCMsgWLItem Item = WL.back();
- WL.pop_back();
-
- if (Item.I == AE) {
- ArgsEvaluated.insert(Item.N);
- continue;
- }
-
- // Evaluate the subexpression.
- ExplodedNodeSet Tmp;
-
- // FIXME: [Objective-C++] handle arguments that are references
- Visit(*Item.I, Item.N, Tmp);
-
- // Enqueue evaluating the next argument on the worklist.
- ++(Item.I);
- for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
- WL.push_back(ObjCMsgWLItem(Item.I, *NI));
- }
-
- // Now that the arguments are processed, handle the ObjC message.
- VisitObjCMessage(ME, ArgsEvaluated, Dst);
-}
-
-void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
- ExplodedNodeSet &Src, ExplodedNodeSet& Dst) {
-
- // Handle the previsits checks.
- ExplodedNodeSet DstPrevisit;
- getCheckerManager().runCheckersForPreObjCMessage(DstPrevisit, Src, msg,*this);
-
- // Proceed with evaluate the message expression.
- ExplodedNodeSet dstEval;
-
- for (ExplodedNodeSet::iterator DI = DstPrevisit.begin(),
- DE = DstPrevisit.end(); DI != DE; ++DI) {
-
- ExplodedNode *Pred = *DI;
- bool RaisesException = false;
- unsigned oldSize = dstEval.size();
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- SaveOr OldHasGen(Builder->hasGeneratedNode);
-
- if (const Expr *Receiver = msg.getInstanceReceiver()) {
- const GRState *state = GetState(Pred);
- SVal recVal = state->getSVal(Receiver);
- if (!recVal.isUndef()) {
- // Bifurcate the state into nil and non-nil ones.
- DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
-
- const GRState *notNilState, *nilState;
- llvm::tie(notNilState, nilState) = state->assume(receiverVal);
-
- // There are three cases: can be nil or non-nil, must be nil, must be
- // non-nil. We ignore must be nil, and merge the rest two into non-nil.
- if (nilState && !notNilState) {
- dstEval.insert(Pred);
- continue;
- }
-
- // Check if the "raise" message was sent.
- assert(notNilState);
- if (msg.getSelector() == RaiseSel)
- RaisesException = true;
-
- // Check if we raise an exception. For now treat these as sinks.
- // Eventually we will want to handle exceptions properly.
- if (RaisesException)
- Builder->BuildSinks = true;
-
- // Dispatch to plug-in transfer function.
- evalObjCMessage(dstEval, msg, Pred, notNilState);
- }
- }
- else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
- IdentifierInfo* ClsName = Iface->getIdentifier();
- Selector S = msg.getSelector();
-
- // Check for special instance methods.
- if (!NSExceptionII) {
- ASTContext& Ctx = getContext();
- NSExceptionII = &Ctx.Idents.get("NSException");
- }
-
- if (ClsName == NSExceptionII) {
- enum { NUM_RAISE_SELECTORS = 2 };
-
- // Lazily create a cache of the selectors.
- if (!NSExceptionInstanceRaiseSelectors) {
- ASTContext& Ctx = getContext();
- NSExceptionInstanceRaiseSelectors =
- new Selector[NUM_RAISE_SELECTORS];
- llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
- unsigned idx = 0;
-
- // raise:format:
- II.push_back(&Ctx.Idents.get("raise"));
- II.push_back(&Ctx.Idents.get("format"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
-
- // raise:format::arguments:
- II.push_back(&Ctx.Idents.get("arguments"));
- NSExceptionInstanceRaiseSelectors[idx++] =
- Ctx.Selectors.getSelector(II.size(), &II[0]);
- }
-
- for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
- if (S == NSExceptionInstanceRaiseSelectors[i]) {
- RaisesException = true;
- break;
- }
- }
-
- // Check if we raise an exception. For now treat these as sinks.
- // Eventually we will want to handle exceptions properly.
- if (RaisesException)
- Builder->BuildSinks = true;
-
- // Dispatch to plug-in transfer function.
- evalObjCMessage(dstEval, msg, Pred, Builder->GetState(Pred));
- }
-
- // Handle the case where no nodes where generated. Auto-generate that
- // contains the updated state if we aren't generating sinks.
- if (!Builder->BuildSinks && dstEval.size() == oldSize &&
- !Builder->hasGeneratedNode)
- MakeNode(dstEval, msg.getOriginExpr(), Pred, GetState(Pred));
- }
-
- // Finally, perform the post-condition check of the ObjCMessageExpr and store
- // the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer functions: Miscellaneous statements.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
- ExplodedNode *Pred, ExplodedNodeSet &Dst) {
-
- ExplodedNodeSet S1;
- Visit(Ex, Pred, S1);
- ExplodedNodeSet S2;
- getCheckerManager().runCheckersForPreStmt(S2, S1, CastE, *this);
-
- if (CastE->getCastKind() == CK_LValueToRValue ||
- CastE->getCastKind() == CK_GetObjCProperty) {
- for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I!=E; ++I) {
- ExplodedNode *subExprNode = *I;
- const GRState *state = GetState(subExprNode);
- evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex));
- }
- return;
- }
-
- // All other casts.
- QualType T = CastE->getType();
- QualType ExTy = Ex->getType();
-
- if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
- T = ExCast->getTypeAsWritten();
-
- for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) {
- Pred = *I;
-
- switch (CastE->getCastKind()) {
- case CK_LValueToRValue:
- assert(false && "LValueToRValue casts handled earlier.");
- case CK_GetObjCProperty:
- assert(false && "GetObjCProperty casts handled earlier.");
- case CK_ToVoid:
- Dst.Add(Pred);
- continue;
- // The analyzer doesn't do anything special with these casts,
- // since it understands retain/release semantics already.
- case CK_ObjCProduceObject:
- case CK_ObjCConsumeObject:
- case CK_ObjCReclaimReturnedObject: // Fall-through.
- // True no-ops.
- case CK_NoOp:
- case CK_FunctionToPointerDecay: {
- // Copy the SVal of Ex to CastE.
- const GRState *state = GetState(Pred);
- SVal V = state->getSVal(Ex);
- state = state->BindExpr(CastE, V);
- MakeNode(Dst, CastE, Pred, state);
- continue;
- }
- case CK_Dependent:
- case CK_ArrayToPointerDecay:
- case CK_BitCast:
- case CK_LValueBitCast:
- case CK_IntegralCast:
- case CK_NullToPointer:
- case CK_IntegralToPointer:
- case CK_PointerToIntegral:
- case CK_PointerToBoolean:
- case CK_IntegralToBoolean:
- case CK_IntegralToFloating:
- case CK_FloatingToIntegral:
- case CK_FloatingToBoolean:
- case CK_FloatingCast:
- case CK_FloatingRealToComplex:
- case CK_FloatingComplexToReal:
- case CK_FloatingComplexToBoolean:
- case CK_FloatingComplexCast:
- case CK_FloatingComplexToIntegralComplex:
- case CK_IntegralRealToComplex:
- case CK_IntegralComplexToReal:
- case CK_IntegralComplexToBoolean:
- case CK_IntegralComplexCast:
- case CK_IntegralComplexToFloatingComplex:
- case CK_AnyPointerToObjCPointerCast:
- case CK_AnyPointerToBlockPointerCast:
- case CK_ObjCObjectLValueCast: {
- // Delegate to SValBuilder to process.
- const GRState* state = GetState(Pred);
- SVal V = state->getSVal(Ex);
- V = svalBuilder.evalCast(V, T, ExTy);
- state = state->BindExpr(CastE, V);
- MakeNode(Dst, CastE, Pred, state);
- continue;
- }
- case CK_DerivedToBase:
- case CK_UncheckedDerivedToBase: {
- // For DerivedToBase cast, delegate to the store manager.
- const GRState *state = GetState(Pred);
- SVal val = state->getSVal(Ex);
- val = getStoreManager().evalDerivedToBase(val, T);
- state = state->BindExpr(CastE, val);
- MakeNode(Dst, CastE, Pred, state);
- continue;
- }
- // Various C++ casts that are not handled yet.
- case CK_Dynamic:
- case CK_ToUnion:
- case CK_BaseToDerived:
- case CK_NullToMemberPointer:
- case CK_BaseToDerivedMemberPointer:
- case CK_DerivedToBaseMemberPointer:
- case CK_UserDefinedConversion:
- case CK_ConstructorConversion:
- case CK_VectorSplat:
- case CK_MemberPointerToBoolean: {
- // Recover some path-sensitivty by conjuring a new value.
- QualType resultType = CastE->getType();
- if (CastE->isLValue())
- resultType = getContext().getPointerType(resultType);
-
- SVal result =
- svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType,
- Builder->getCurrentBlockCount());
-
- const GRState *state = GetState(Pred)->BindExpr(CastE, result);
- MakeNode(Dst, CastE, Pred, state);
- continue;
- }
- }
- }
-}
-
-void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
- const InitListExpr* ILE
- = cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
- ExplodedNodeSet Tmp;
- Visit(ILE, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) {
- const GRState* state = GetState(*I);
- SVal ILV = state->getSVal(ILE);
- const LocationContext *LC = (*I)->getLocationContext();
- state = state->bindCompoundLiteral(CL, LC, ILV);
-
- if (CL->isLValue()) {
- MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC)));
- }
- else
- MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV));
- }
-}
-
-void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
- ExplodedNodeSet& Dst) {
-
- // The CFG has one DeclStmt per Decl.
- const Decl* D = *DS->decl_begin();
-
- if (!D || !isa<VarDecl>(D))
- return;
-
- const VarDecl* VD = dyn_cast<VarDecl>(D);
- const Expr* InitEx = VD->getInit();
-
- // FIXME: static variables may have an initializer, but the second
- // time a function is called those values may not be current.
- ExplodedNodeSet Tmp;
-
- if (InitEx)
- Visit(InitEx, Pred, Tmp);
- else
- Tmp.Add(Pred);
-
- ExplodedNodeSet Tmp2;
- getCheckerManager().runCheckersForPreStmt(Tmp2, Tmp, DS, *this);
-
- for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) {
- ExplodedNode *N = *I;
- const GRState *state = GetState(N);
-
- // Decls without InitExpr are not initialized explicitly.
- const LocationContext *LC = N->getLocationContext();
-
- if (InitEx) {
- SVal InitVal = state->getSVal(InitEx);
-
- // We bound the temp obj region to the CXXConstructExpr. Now recover
- // the lazy compound value when the variable is not a reference.
- if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() &&
- !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
- InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
- assert(isa<nonloc::LazyCompoundVal>(InitVal));
- }
-
- // Recover some path-sensitivity if a scalar value evaluated to
- // UnknownVal.
- if ((InitVal.isUnknown() ||
- !getConstraintManager().canReasonAbout(InitVal)) &&
- !VD->getType()->isReferenceType()) {
- InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx,
- Builder->getCurrentBlockCount());
- }
-
- evalBind(Dst, DS, *I, state,
- loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true);
- }
- else {
- state = state->bindDeclWithNoInit(state->getRegion(VD, LC));
- MakeNode(Dst, DS, *I, state);
- }
- }
-}
-
-namespace {
- // This class is used by VisitInitListExpr as an item in a worklist
- // for processing the values contained in an InitListExpr.
-class InitListWLItem {
-public:
- llvm::ImmutableList<SVal> Vals;
- ExplodedNode* N;
- InitListExpr::const_reverse_iterator Itr;
-
- InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals,
- InitListExpr::const_reverse_iterator itr)
- : Vals(vals), N(n), Itr(itr) {}
-};
-}
-
-
-void ExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
-
- const GRState* state = GetState(Pred);
- QualType T = getContext().getCanonicalType(E->getType());
- unsigned NumInitElements = E->getNumInits();
-
- if (T->isArrayType() || T->isRecordType() || T->isVectorType()) {
- llvm::ImmutableList<SVal> StartVals = getBasicVals().getEmptySValList();
-
- // Handle base case where the initializer has no elements.
- // e.g: static int* myArray[] = {};
- if (NumInitElements == 0) {
- SVal V = svalBuilder.makeCompoundVal(T, StartVals);
- MakeNode(Dst, E, Pred, state->BindExpr(E, V));
- return;
- }
-
- // Create a worklist to process the initializers.
- llvm::SmallVector<InitListWLItem, 10> WorkList;
- WorkList.reserve(NumInitElements);
- WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin()));
- InitListExpr::const_reverse_iterator ItrEnd = E->rend();
- assert(!(E->rbegin() == E->rend()));
-
- // Process the worklist until it is empty.
- while (!WorkList.empty()) {
- InitListWLItem X = WorkList.back();
- WorkList.pop_back();
-
- ExplodedNodeSet Tmp;
- Visit(*X.Itr, X.N, Tmp);
-
- InitListExpr::const_reverse_iterator NewItr = X.Itr + 1;
-
- for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) {
- // Get the last initializer value.
- state = GetState(*NI);
- SVal InitV = state->getSVal(cast<Expr>(*X.Itr));
-
- // Construct the new list of values by prepending the new value to
- // the already constructed list.
- llvm::ImmutableList<SVal> NewVals =
- getBasicVals().consVals(InitV, X.Vals);
-
- if (NewItr == ItrEnd) {
- // Now we have a list holding all init values. Make CompoundValData.
- SVal V = svalBuilder.makeCompoundVal(T, NewVals);
-
- // Make final state and node.
- MakeNode(Dst, E, *NI, state->BindExpr(E, V));
- }
- else {
- // Still some initializer values to go. Push them onto the worklist.
- WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr));
- }
- }
- }
-
- return;
- }
-
- if (Loc::isLocType(T) || T->isIntegerType()) {
- assert (E->getNumInits() == 1);
- ExplodedNodeSet Tmp;
- const Expr* Init = E->getInit(0);
- Visit(Init, Pred, Tmp);
- for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) {
- state = GetState(*I);
- MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init)));
- }
- return;
- }
-
- assert(0 && "unprocessed InitListExpr type");
-}
-
-/// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof(type).
-void ExprEngine::VisitUnaryExprOrTypeTraitExpr(
- const UnaryExprOrTypeTraitExpr* Ex,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
- QualType T = Ex->getTypeOfArgument();
-
- if (Ex->getKind() == UETT_SizeOf) {
- if (!T->isIncompleteType() && !T->isConstantSizeType()) {
- assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
-
- // FIXME: Add support for VLA type arguments, not just VLA expressions.
- // When that happens, we should probably refactor VLASizeChecker's code.
- if (Ex->isArgumentType()) {
- Dst.Add(Pred);
- return;
- }
-
- // Get the size by getting the extent of the sub-expression.
- // First, visit the sub-expression to find its region.
- const Expr *Arg = Ex->getArgumentExpr();
- ExplodedNodeSet Tmp;
- Visit(Arg, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- const GRState* state = GetState(*I);
- const MemRegion *MR = state->getSVal(Arg).getAsRegion();
-
- // If the subexpression can't be resolved to a region, we don't know
- // anything about its size. Just leave the state as is and continue.
- if (!MR) {
- Dst.Add(*I);
- continue;
- }
-
- // The result is the extent of the VLA.
- SVal Extent = cast<SubRegion>(MR)->getExtent(svalBuilder);
- MakeNode(Dst, Ex, *I, state->BindExpr(Ex, Extent));
- }
-
- return;
- }
- else if (T->getAs<ObjCObjectType>()) {
- // Some code tries to take the sizeof an ObjCObjectType, relying that
- // the compiler has laid out its representation. Just report Unknown
- // for these.
- Dst.Add(Pred);
- return;
- }
- }
-
- Expr::EvalResult Result;
- Ex->Evaluate(Result, getContext());
- CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue());
-
- MakeNode(Dst, Ex, Pred,
- GetState(Pred)->BindExpr(Ex,
- svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType())));
-}
-
-void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE,
- ExplodedNode* Pred, ExplodedNodeSet& Dst) {
- Expr::EvalResult Res;
- if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) {
- const APSInt &IV = Res.Val.getInt();
- assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
- assert(OOE->getType()->isIntegerType());
- assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType());
- SVal X = svalBuilder.makeIntVal(IV);
- MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X));
- return;
- }
- // FIXME: Handle the case where __builtin_offsetof is not a constant.
- Dst.Add(Pred);
-}
-
-void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
-
- switch (U->getOpcode()) {
-
- default:
- break;
-
- case UO_Real: {
- const Expr* Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
-
- // FIXME: We don't have complex SValues yet.
- if (Ex->getType()->isAnyComplexType()) {
- // Just report "Unknown."
- Dst.Add(*I);
- continue;
- }
-
- // For all other types, UO_Real is an identity operation.
- assert (U->getType() == Ex->getType());
- const GRState* state = GetState(*I);
- MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
- }
-
- return;
- }
-
- case UO_Imag: {
-
- const Expr* Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- // FIXME: We don't have complex SValues yet.
- if (Ex->getType()->isAnyComplexType()) {
- // Just report "Unknown."
- Dst.Add(*I);
- continue;
- }
-
- // For all other types, UO_Imag returns 0.
- const GRState* state = GetState(*I);
- SVal X = svalBuilder.makeZeroVal(Ex->getType());
- MakeNode(Dst, U, *I, state->BindExpr(U, X));
- }
-
- return;
- }
-
- case UO_Plus:
- assert(!U->isLValue());
- // FALL-THROUGH.
- case UO_Deref:
- case UO_AddrOf:
- case UO_Extension: {
-
- // Unary "+" is a no-op, similar to a parentheses. We still have places
- // where it may be a block-level expression, so we need to
- // generate an extra node that just propagates the value of the
- // subexpression.
-
- const Expr* Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- const GRState* state = GetState(*I);
- MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
- }
-
- return;
- }
-
- case UO_LNot:
- case UO_Minus:
- case UO_Not: {
- assert (!U->isLValue());
- const Expr* Ex = U->getSubExpr()->IgnoreParens();
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
- const GRState* state = GetState(*I);
-
- // Get the value of the subexpression.
- SVal V = state->getSVal(Ex);
-
- if (V.isUnknownOrUndef()) {
- MakeNode(Dst, U, *I, state->BindExpr(U, V));
- continue;
- }
-
-// QualType DstT = getContext().getCanonicalType(U->getType());
-// QualType SrcT = getContext().getCanonicalType(Ex->getType());
-//
-// if (DstT != SrcT) // Perform promotions.
-// V = evalCast(V, DstT);
-//
-// if (V.isUnknownOrUndef()) {
-// MakeNode(Dst, U, *I, BindExpr(St, U, V));
-// continue;
-// }
-
- switch (U->getOpcode()) {
- default:
- assert(false && "Invalid Opcode.");
- break;
-
- case UO_Not:
- // FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, evalComplement(cast<NonLoc>(V)));
- break;
-
- case UO_Minus:
- // FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, evalMinus(cast<NonLoc>(V)));
- break;
-
- case UO_LNot:
-
- // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
- //
- // Note: technically we do "E == 0", but this is the same in the
- // transfer functions as "0 == E".
- SVal Result;
-
- if (isa<Loc>(V)) {
- Loc X = svalBuilder.makeNull();
- Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X,
- U->getType());
- }
- else {
- nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
- Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
- U->getType());
- }
-
- state = state->BindExpr(U, Result);
-
- break;
- }
-
- MakeNode(Dst, U, *I, state);
- }
-
- return;
- }
- }
-
- // Handle ++ and -- (both pre- and post-increment).
- assert (U->isIncrementDecrementOp());
- ExplodedNodeSet Tmp;
- const Expr* Ex = U->getSubExpr()->IgnoreParens();
- Visit(Ex, Pred, Tmp);
-
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
-
- const GRState* state = GetState(*I);
- SVal loc = state->getSVal(Ex);
-
- // Perform a load.
- ExplodedNodeSet Tmp2;
- evalLoad(Tmp2, Ex, *I, state, loc);
-
- for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) {
-
- state = GetState(*I2);
- SVal V2_untested = state->getSVal(Ex);
-
- // Propagate unknown and undefined values.
- if (V2_untested.isUnknownOrUndef()) {
- MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested));
- continue;
- }
- DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
-
- // Handle all other values.
- BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add
- : BO_Sub;
-
- // If the UnaryOperator has non-location type, use its type to create the
- // constant value. If the UnaryOperator has location type, create the
- // constant with int type and pointer width.
- SVal RHS;
-
- if (U->getType()->isAnyPointerType())
- RHS = svalBuilder.makeArrayIndex(1);
- else
- RHS = svalBuilder.makeIntVal(1, U->getType());
-
- SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
-
- // Conjure a new symbol if necessary to recover precision.
- if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){
- DefinedOrUnknownSVal SymVal =
- svalBuilder.getConjuredSymbolVal(NULL, Ex,
- Builder->getCurrentBlockCount());
- Result = SymVal;
-
- // If the value is a location, ++/-- should always preserve
- // non-nullness. Check if the original value was non-null, and if so
- // propagate that constraint.
- if (Loc::isLocType(U->getType())) {
- DefinedOrUnknownSVal Constraint =
- svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
-
- if (!state->assume(Constraint, true)) {
- // It isn't feasible for the original value to be null.
- // Propagate this constraint.
- Constraint = svalBuilder.evalEQ(state, SymVal,
- svalBuilder.makeZeroVal(U->getType()));
-
-
- state = state->assume(Constraint, false);
- assert(state);
- }
- }
- }
-
- // Since the lvalue-to-rvalue conversion is explicit in the AST,
- // we bind an l-value if the operator is prefix and an lvalue (in C++).
- if (U->isLValue())
- state = state->BindExpr(U, loc);
- else
- state = state->BindExpr(U, U->isPostfix() ? V2 : Result);
-
- // Perform the store.
- evalStore(Dst, NULL, U, *I2, state, loc, Result);
- }
- }
-}
-
-void ExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
+void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst);
}
-void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A,
+void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt *A,
AsmStmt::const_outputs_iterator I,
AsmStmt::const_outputs_iterator E,
- ExplodedNode* Pred, ExplodedNodeSet& Dst) {
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
if (I == E) {
VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst);
return;
@@ -2795,11 +1590,11 @@ void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A,
VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst);
}
-void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A,
+void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt *A,
AsmStmt::const_inputs_iterator I,
AsmStmt::const_inputs_iterator E,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
if (I == E) {
// We have processed both the inputs and the outputs. All of the outputs
@@ -2809,7 +1604,7 @@ void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A,
// which interprets the inline asm and stores proper results in the
// outputs.
- const GRState* state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(),
OE = A->end_outputs(); OI != OE; ++OI) {
@@ -2834,198 +1629,6 @@ void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A,
VisitAsmStmtHelperInputs(A, I, E, *NI, Dst);
}
-void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- ExplodedNodeSet Src;
- if (const Expr *RetE = RS->getRetValue()) {
- // Record the returned expression in the state. It will be used in
- // processCallExit to bind the return value to the call expr.
- {
- static int tag = 0;
- const GRState *state = GetState(Pred);
- state = state->set<ReturnExpr>(RetE);
- Pred = Builder->generateNode(RetE, state, Pred, &tag);
- }
- // We may get a NULL Pred because we generated a cached node.
- if (Pred)
- Visit(RetE, Pred, Src);
- }
- else {
- Src.Add(Pred);
- }
-
- ExplodedNodeSet CheckedSet;
- getCheckerManager().runCheckersForPreStmt(CheckedSet, Src, RS, *this);
-
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I != E; ++I) {
-
- assert(Builder && "StmtNodeBuilder must be defined.");
-
- Pred = *I;
- unsigned size = Dst.size();
-
- SaveAndRestore<bool> OldSink(Builder->BuildSinks);
- SaveOr OldHasGen(Builder->hasGeneratedNode);
-
- getTF().evalReturn(Dst, *this, *Builder, RS, Pred);
-
- // Handle the case where no nodes where generated.
- if (!Builder->BuildSinks && Dst.size() == size &&
- !Builder->hasGeneratedNode)
- MakeNode(Dst, RS, Pred, GetState(Pred));
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer functions: Binary operators.
-//===----------------------------------------------------------------------===//
-
-void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
- ExplodedNode* Pred,
- ExplodedNodeSet& Dst) {
- ExplodedNodeSet Tmp1;
- Expr* LHS = B->getLHS()->IgnoreParens();
- Expr* RHS = B->getRHS()->IgnoreParens();
-
- Visit(LHS, Pred, Tmp1);
- ExplodedNodeSet Tmp3;
-
- for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) {
- SVal LeftV = GetState(*I1)->getSVal(LHS);
- ExplodedNodeSet Tmp2;
- Visit(RHS, *I1, Tmp2);
-
- ExplodedNodeSet CheckedSet;
- getCheckerManager().runCheckersForPreStmt(CheckedSet, Tmp2, B, *this);
-
- // With both the LHS and RHS evaluated, process the operation itself.
-
- for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end();
- I2 != E2; ++I2) {
-
- const GRState *state = GetState(*I2);
- SVal RightV = state->getSVal(RHS);
-
- BinaryOperator::Opcode Op = B->getOpcode();
-
- if (Op == BO_Assign) {
- // EXPERIMENTAL: "Conjured" symbols.
- // FIXME: Handle structs.
- if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV))
- {
- unsigned Count = Builder->getCurrentBlockCount();
- RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count);
- }
-
- SVal ExprVal = B->isLValue() ? LeftV : RightV;
-
- // Simulate the effects of a "store": bind the value of the RHS
- // to the L-Value represented by the LHS.
- evalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV);
- continue;
- }
-
- if (!B->isAssignmentOp()) {
- // Process non-assignments except commas or short-circuited
- // logical expressions (LAnd and LOr).
- SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
-
- if (Result.isUnknown()) {
- MakeNode(Tmp3, B, *I2, state);
- continue;
- }
-
- state = state->BindExpr(B, Result);
-
- MakeNode(Tmp3, B, *I2, state);
- continue;
- }
-
- assert (B->isCompoundAssignmentOp());
-
- switch (Op) {
- default:
- assert(0 && "Invalid opcode for compound assignment.");
- case BO_MulAssign: Op = BO_Mul; break;
- case BO_DivAssign: Op = BO_Div; break;
- case BO_RemAssign: Op = BO_Rem; break;
- case BO_AddAssign: Op = BO_Add; break;
- case BO_SubAssign: Op = BO_Sub; break;
- case BO_ShlAssign: Op = BO_Shl; break;
- case BO_ShrAssign: Op = BO_Shr; break;
- case BO_AndAssign: Op = BO_And; break;
- case BO_XorAssign: Op = BO_Xor; break;
- case BO_OrAssign: Op = BO_Or; break;
- }
-
- // Perform a load (the LHS). This performs the checks for
- // null dereferences, and so on.
- ExplodedNodeSet Tmp4;
- SVal location = state->getSVal(LHS);
- evalLoad(Tmp4, LHS, *I2, state, location);
-
- for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4;
- ++I4) {
- state = GetState(*I4);
- SVal V = state->getSVal(LHS);
-
- // Get the computation type.
- QualType CTy =
- cast<CompoundAssignOperator>(B)->getComputationResultType();
- CTy = getContext().getCanonicalType(CTy);
-
- QualType CLHSTy =
- cast<CompoundAssignOperator>(B)->getComputationLHSType();
- CLHSTy = getContext().getCanonicalType(CLHSTy);
-
- QualType LTy = getContext().getCanonicalType(LHS->getType());
-
- // Promote LHS.
- V = svalBuilder.evalCast(V, CLHSTy, LTy);
-
- // Compute the result of the operation.
- SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
- B->getType(), CTy);
-
- // EXPERIMENTAL: "Conjured" symbols.
- // FIXME: Handle structs.
-
- SVal LHSVal;
-
- if (Result.isUnknown() ||
- !getConstraintManager().canReasonAbout(Result)) {
-
- unsigned Count = Builder->getCurrentBlockCount();
-
- // The symbolic value is actually for the type of the left-hand side
- // expression, not the computation type, as this is the value the
- // LValue on the LHS will bind to.
- LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count);
-
- // However, we need to convert the symbol to the computation type.
- Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
- }
- else {
- // The left-hand side may bind to a different value then the
- // computation type.
- LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
- }
-
- // In C++, assignment and compound assignment operators return an
- // lvalue.
- if (B->isLValue())
- state = state->BindExpr(B, location);
- else
- state = state->BindExpr(B, Result);
-
- evalStore(Tmp3, B, LHS, *I4, state, location, LHSVal);
- }
- }
- }
-
- getCheckerManager().runCheckersForPostStmt(Dst, Tmp3, B, *this);
-}
//===----------------------------------------------------------------------===//
// Visualization.
@@ -3044,7 +1647,7 @@ struct DOTGraphTraits<ExplodedNode*> :
// FIXME: Since we do not cache error nodes in ExprEngine now, this does not
// work.
- static std::string getNodeAttributes(const ExplodedNode* N, void*) {
+ static std::string getNodeAttributes(const ExplodedNode *N, void*) {
#if 0
// FIXME: Replace with a general scheme to tell if the node is
@@ -3065,7 +1668,7 @@ struct DOTGraphTraits<ExplodedNode*> :
return "";
}
- static std::string getNodeLabel(const ExplodedNode* N, void*){
+ static std::string getNodeLabel(const ExplodedNode *N, void*){
std::string sbuf;
llvm::raw_string_ostream Out(sbuf);
@@ -3093,7 +1696,7 @@ struct DOTGraphTraits<ExplodedNode*> :
default: {
if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) {
- const Stmt* S = L->getStmt();
+ const Stmt *S = L->getStmt();
SourceLocation SLoc = S->getLocStart();
Out << S->getStmtClassName() << ' ' << (void*) S << ' ';
@@ -3102,9 +1705,9 @@ struct DOTGraphTraits<ExplodedNode*> :
if (SLoc.isFileID()) {
Out << "\\lline="
- << GraphPrintSourceManager->getInstantiationLineNumber(SLoc)
+ << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
<< " col="
- << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc)
+ << GraphPrintSourceManager->getExpansionColumnNumber(SLoc)
<< "\\l";
}
@@ -3141,11 +1744,11 @@ struct DOTGraphTraits<ExplodedNode*> :
break;
}
- const BlockEdge& E = cast<BlockEdge>(Loc);
+ const BlockEdge &E = cast<BlockEdge>(Loc);
Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B"
<< E.getDst()->getBlockID() << ')';
- if (const Stmt* T = E.getSrc()->getTerminator()) {
+ if (const Stmt *T = E.getSrc()->getTerminator()) {
SourceLocation SLoc = T->getLocStart();
@@ -3155,21 +1758,21 @@ struct DOTGraphTraits<ExplodedNode*> :
if (SLoc.isFileID()) {
Out << "\\lline="
- << GraphPrintSourceManager->getInstantiationLineNumber(SLoc)
+ << GraphPrintSourceManager->getExpansionLineNumber(SLoc)
<< " col="
- << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc);
+ << GraphPrintSourceManager->getExpansionColumnNumber(SLoc);
}
if (isa<SwitchStmt>(T)) {
- const Stmt* Label = E.getDst()->getLabel();
+ const Stmt *Label = E.getDst()->getLabel();
if (Label) {
- if (const CaseStmt* C = dyn_cast<CaseStmt>(Label)) {
+ if (const CaseStmt *C = dyn_cast<CaseStmt>(Label)) {
Out << "\\lcase ";
LangOptions LO; // FIXME.
C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO));
- if (const Stmt* RHS = C->getRHS()) {
+ if (const Stmt *RHS = C->getRHS()) {
Out << " .. ";
RHS->printPretty(Out, 0, PrintingPolicy(LO));
}
@@ -3208,11 +1811,17 @@ struct DOTGraphTraits<ExplodedNode*> :
}
}
- const GRState *state = N->getState();
+ const ProgramState *state = N->getState();
Out << "\\|StateID: " << (void*) state
<< " NodeID: " << (void*) N << "\\|";
state->printDOT(Out, *N->getLocationContext()->getCFG());
- Out << "\\l";
+
+ Out << "\\l";
+
+ if (const ProgramPointTag *tag = Loc.getTag()) {
+ Out << "\\|Tag: " << tag->getTagDescription();
+ Out << "\\l";
+ }
return Out.str();
}
};
@@ -3221,7 +1830,7 @@ struct DOTGraphTraits<ExplodedNode*> :
#ifndef NDEBUG
template <typename ITERATOR>
-ExplodedNode* GetGraphNode(ITERATOR I) { return *I; }
+ExplodedNode *GetGraphNode(ITERATOR I) { return *I; }
template <> ExplodedNode*
GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator>
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
new file mode 100644
index 0000000..68ccc59
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -0,0 +1,752 @@
+//=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for C expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/Support/SaveAndRestore.h"
+
+using namespace clang;
+using namespace ento;
+using llvm::APSInt;
+
+void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ Expr *LHS = B->getLHS()->IgnoreParens();
+ Expr *RHS = B->getRHS()->IgnoreParens();
+
+ // FIXME: Prechecks eventually go in ::Visit().
+ ExplodedNodeSet CheckedSet;
+ ExplodedNodeSet Tmp2;
+ getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this);
+
+ // With both the LHS and RHS evaluated, process the operation itself.
+ for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
+ it != ei; ++it) {
+
+ const ProgramState *state = (*it)->getState();
+ SVal LeftV = state->getSVal(LHS);
+ SVal RightV = state->getSVal(RHS);
+
+ BinaryOperator::Opcode Op = B->getOpcode();
+
+ if (Op == BO_Assign) {
+ // EXPERIMENTAL: "Conjured" symbols.
+ // FIXME: Handle structs.
+ if (RightV.isUnknown() ||
+ !getConstraintManager().canReasonAbout(RightV)) {
+ unsigned Count = Builder->getCurrentBlockCount();
+ RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count);
+ }
+ // Simulate the effects of a "store": bind the value of the RHS
+ // to the L-Value represented by the LHS.
+ SVal ExprVal = B->isLValue() ? LeftV : RightV;
+ evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, ExprVal), LeftV, RightV);
+ continue;
+ }
+
+ if (!B->isAssignmentOp()) {
+ // Process non-assignments except commas or short-circuited
+ // logical expressions (LAnd and LOr).
+ SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
+ if (Result.isUnknown()) {
+ MakeNode(Tmp2, B, *it, state);
+ continue;
+ }
+
+ state = state->BindExpr(B, Result);
+ MakeNode(Tmp2, B, *it, state);
+ continue;
+ }
+
+ assert (B->isCompoundAssignmentOp());
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Invalid opcode for compound assignment.");
+ case BO_MulAssign: Op = BO_Mul; break;
+ case BO_DivAssign: Op = BO_Div; break;
+ case BO_RemAssign: Op = BO_Rem; break;
+ case BO_AddAssign: Op = BO_Add; break;
+ case BO_SubAssign: Op = BO_Sub; break;
+ case BO_ShlAssign: Op = BO_Shl; break;
+ case BO_ShrAssign: Op = BO_Shr; break;
+ case BO_AndAssign: Op = BO_And; break;
+ case BO_XorAssign: Op = BO_Xor; break;
+ case BO_OrAssign: Op = BO_Or; break;
+ }
+
+ // Perform a load (the LHS). This performs the checks for
+ // null dereferences, and so on.
+ ExplodedNodeSet Tmp;
+ SVal location = LeftV;
+ evalLoad(Tmp, LHS, *it, state, location);
+
+ for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
+ ++I) {
+
+ state = (*I)->getState();
+ SVal V = state->getSVal(LHS);
+
+ // Get the computation type.
+ QualType CTy =
+ cast<CompoundAssignOperator>(B)->getComputationResultType();
+ CTy = getContext().getCanonicalType(CTy);
+
+ QualType CLHSTy =
+ cast<CompoundAssignOperator>(B)->getComputationLHSType();
+ CLHSTy = getContext().getCanonicalType(CLHSTy);
+
+ QualType LTy = getContext().getCanonicalType(LHS->getType());
+
+ // Promote LHS.
+ V = svalBuilder.evalCast(V, CLHSTy, LTy);
+
+ // Compute the result of the operation.
+ SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
+ B->getType(), CTy);
+
+ // EXPERIMENTAL: "Conjured" symbols.
+ // FIXME: Handle structs.
+
+ SVal LHSVal;
+
+ if (Result.isUnknown() ||
+ !getConstraintManager().canReasonAbout(Result)) {
+
+ unsigned Count = Builder->getCurrentBlockCount();
+
+ // The symbolic value is actually for the type of the left-hand side
+ // expression, not the computation type, as this is the value the
+ // LValue on the LHS will bind to.
+ LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy,
+ Count);
+
+ // However, we need to convert the symbol to the computation type.
+ Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
+ }
+ else {
+ // The left-hand side may bind to a different value then the
+ // computation type.
+ LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
+ }
+
+ // In C++, assignment and compound assignment operators return an
+ // lvalue.
+ if (B->isLValue())
+ state = state->BindExpr(B, location);
+ else
+ state = state->BindExpr(B, Result);
+
+ evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
+ }
+ }
+
+ // FIXME: postvisits eventually go in ::Visit()
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this);
+}
+
+void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ CanQualType T = getContext().getCanonicalType(BE->getType());
+ SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
+ Pred->getLocationContext());
+
+ ExplodedNodeSet Tmp;
+ MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V),
+ ProgramPoint::PostLValueKind);
+
+ // FIXME: Move all post/pre visits to ::Visit().
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
+}
+
+void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+
+ ExplodedNodeSet dstPreStmt;
+ getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
+
+ if (CastE->getCastKind() == CK_LValueToRValue ||
+ CastE->getCastKind() == CK_GetObjCProperty) {
+ for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
+ I!=E; ++I) {
+ ExplodedNode *subExprNode = *I;
+ const ProgramState *state = subExprNode->getState();
+ evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex));
+ }
+ return;
+ }
+
+ // All other casts.
+ QualType T = CastE->getType();
+ QualType ExTy = Ex->getType();
+
+ if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
+ T = ExCast->getTypeAsWritten();
+
+ for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
+ I != E; ++I) {
+
+ Pred = *I;
+
+ switch (CastE->getCastKind()) {
+ case CK_LValueToRValue:
+ llvm_unreachable("LValueToRValue casts handled earlier.");
+ case CK_GetObjCProperty:
+ llvm_unreachable("GetObjCProperty casts handled earlier.");
+ case CK_ToVoid:
+ Dst.Add(Pred);
+ continue;
+ // The analyzer doesn't do anything special with these casts,
+ // since it understands retain/release semantics already.
+ case CK_ARCProduceObject:
+ case CK_ARCConsumeObject:
+ case CK_ARCReclaimReturnedObject:
+ case CK_ARCExtendBlockObject: // Fall-through.
+ // True no-ops.
+ case CK_NoOp:
+ case CK_FunctionToPointerDecay: {
+ // Copy the SVal of Ex to CastE.
+ const ProgramState *state = Pred->getState();
+ SVal V = state->getSVal(Ex);
+ state = state->BindExpr(CastE, V);
+ MakeNode(Dst, CastE, Pred, state);
+ continue;
+ }
+ case CK_Dependent:
+ case CK_ArrayToPointerDecay:
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_IntegralCast:
+ case CK_NullToPointer:
+ case CK_IntegralToPointer:
+ case CK_PointerToIntegral:
+ case CK_PointerToBoolean:
+ case CK_IntegralToBoolean:
+ case CK_IntegralToFloating:
+ case CK_FloatingToIntegral:
+ case CK_FloatingToBoolean:
+ case CK_FloatingCast:
+ case CK_FloatingRealToComplex:
+ case CK_FloatingComplexToReal:
+ case CK_FloatingComplexToBoolean:
+ case CK_FloatingComplexCast:
+ case CK_FloatingComplexToIntegralComplex:
+ case CK_IntegralRealToComplex:
+ case CK_IntegralComplexToReal:
+ case CK_IntegralComplexToBoolean:
+ case CK_IntegralComplexCast:
+ case CK_IntegralComplexToFloatingComplex:
+ case CK_CPointerToObjCPointerCast:
+ case CK_BlockPointerToObjCPointerCast:
+ case CK_AnyPointerToBlockPointerCast:
+ case CK_ObjCObjectLValueCast: {
+ // Delegate to SValBuilder to process.
+ const ProgramState *state = Pred->getState();
+ SVal V = state->getSVal(Ex);
+ V = svalBuilder.evalCast(V, T, ExTy);
+ state = state->BindExpr(CastE, V);
+ MakeNode(Dst, CastE, Pred, state);
+ continue;
+ }
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase: {
+ // For DerivedToBase cast, delegate to the store manager.
+ const ProgramState *state = Pred->getState();
+ SVal val = state->getSVal(Ex);
+ val = getStoreManager().evalDerivedToBase(val, T);
+ state = state->BindExpr(CastE, val);
+ MakeNode(Dst, CastE, Pred, state);
+ continue;
+ }
+ // Various C++ casts that are not handled yet.
+ case CK_Dynamic:
+ case CK_ToUnion:
+ case CK_BaseToDerived:
+ case CK_NullToMemberPointer:
+ case CK_BaseToDerivedMemberPointer:
+ case CK_DerivedToBaseMemberPointer:
+ case CK_UserDefinedConversion:
+ case CK_ConstructorConversion:
+ case CK_VectorSplat:
+ case CK_MemberPointerToBoolean: {
+ // Recover some path-sensitivty by conjuring a new value.
+ QualType resultType = CastE->getType();
+ if (CastE->isLValue())
+ resultType = getContext().getPointerType(resultType);
+
+ SVal result =
+ svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType,
+ Builder->getCurrentBlockCount());
+
+ const ProgramState *state = Pred->getState()->BindExpr(CastE, result);
+ MakeNode(Dst, CastE, Pred, state);
+ continue;
+ }
+ }
+ }
+}
+
+void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const InitListExpr *ILE
+ = cast<InitListExpr>(CL->getInitializer()->IgnoreParens());
+
+ const ProgramState *state = Pred->getState();
+ SVal ILV = state->getSVal(ILE);
+ const LocationContext *LC = Pred->getLocationContext();
+ state = state->bindCompoundLiteral(CL, LC, ILV);
+
+ if (CL->isLValue())
+ MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC)));
+ else
+ MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV));
+}
+
+void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ // FIXME: static variables may have an initializer, but the second
+ // time a function is called those values may not be current.
+ // This may need to be reflected in the CFG.
+
+ // Assumption: The CFG has one DeclStmt per Decl.
+ const Decl *D = *DS->decl_begin();
+
+ if (!D || !isa<VarDecl>(D))
+ return;
+
+ // FIXME: all pre/post visits should eventually be handled by ::Visit().
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
+
+ const VarDecl *VD = dyn_cast<VarDecl>(D);
+
+ for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
+ I!=E; ++I) {
+ ExplodedNode *N = *I;
+ const ProgramState *state = N->getState();
+
+ // Decls without InitExpr are not initialized explicitly.
+ const LocationContext *LC = N->getLocationContext();
+
+ if (const Expr *InitEx = VD->getInit()) {
+ SVal InitVal = state->getSVal(InitEx);
+
+ // We bound the temp obj region to the CXXConstructExpr. Now recover
+ // the lazy compound value when the variable is not a reference.
+ if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() &&
+ !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){
+ InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion());
+ assert(isa<nonloc::LazyCompoundVal>(InitVal));
+ }
+
+ // Recover some path-sensitivity if a scalar value evaluated to
+ // UnknownVal.
+ if ((InitVal.isUnknown() ||
+ !getConstraintManager().canReasonAbout(InitVal)) &&
+ !VD->getType()->isReferenceType()) {
+ InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx,
+ Builder->getCurrentBlockCount());
+ }
+
+ evalBind(Dst, DS, N, state->getLValue(VD, LC), InitVal, true);
+ }
+ else {
+ MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC)));
+ }
+ }
+}
+
+void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ assert(B->getOpcode() == BO_LAnd ||
+ B->getOpcode() == BO_LOr);
+
+ const ProgramState *state = Pred->getState();
+ SVal X = state->getSVal(B);
+ assert(X.isUndef());
+
+ const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData();
+ assert(Ex);
+
+ if (Ex == B->getRHS()) {
+ X = state->getSVal(Ex);
+
+ // Handle undefined values.
+ if (X.isUndef()) {
+ MakeNode(Dst, B, Pred, state->BindExpr(B, X));
+ return;
+ }
+
+ DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X);
+
+ // We took the RHS. Because the value of the '&&' or '||' expression must
+ // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0
+ // or 1. Alternatively, we could take a lazy approach, and calculate this
+ // value later when necessary. We don't have the machinery in place for
+ // this right now, and since most logical expressions are used for branches,
+ // the payoff is not likely to be large. Instead, we do eager evaluation.
+ if (const ProgramState *newState = state->assume(XD, true))
+ MakeNode(Dst, B, Pred,
+ newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType())));
+
+ if (const ProgramState *newState = state->assume(XD, false))
+ MakeNode(Dst, B, Pred,
+ newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType())));
+ }
+ else {
+ // We took the LHS expression. Depending on whether we are '&&' or
+ // '||' we know what the value of the expression is via properties of
+ // the short-circuiting.
+ X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U,
+ B->getType());
+ MakeNode(Dst, B, Pred, state->BindExpr(B, X));
+ }
+}
+
+void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ const ProgramState *state = Pred->getState();
+ QualType T = getContext().getCanonicalType(IE->getType());
+ unsigned NumInitElements = IE->getNumInits();
+
+ if (T->isArrayType() || T->isRecordType() || T->isVectorType()) {
+ llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
+
+ // Handle base case where the initializer has no elements.
+ // e.g: static int* myArray[] = {};
+ if (NumInitElements == 0) {
+ SVal V = svalBuilder.makeCompoundVal(T, vals);
+ MakeNode(Dst, IE, Pred, state->BindExpr(IE, V));
+ return;
+ }
+
+ for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
+ ei = IE->rend(); it != ei; ++it) {
+ vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals);
+ }
+
+ MakeNode(Dst, IE, Pred,
+ state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals)));
+ return;
+ }
+
+ if (Loc::isLocType(T) || T->isIntegerType()) {
+ assert(IE->getNumInits() == 1);
+ const Expr *initEx = IE->getInit(0);
+ MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx)));
+ return;
+ }
+
+ llvm_unreachable("unprocessed InitListExpr type");
+}
+
+void ExprEngine::VisitGuardedExpr(const Expr *Ex,
+ const Expr *L,
+ const Expr *R,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ const ProgramState *state = Pred->getState();
+ SVal X = state->getSVal(Ex);
+ assert (X.isUndef());
+ const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData();
+ assert(SE);
+ X = state->getSVal(SE);
+
+ // Make sure that we invalidate the previous binding.
+ MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true));
+}
+
+void ExprEngine::
+VisitOffsetOfExpr(const OffsetOfExpr *OOE,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ Expr::EvalResult Res;
+ if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) {
+ const APSInt &IV = Res.Val.getInt();
+ assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
+ assert(OOE->getType()->isIntegerType());
+ assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType());
+ SVal X = svalBuilder.makeIntVal(IV);
+ MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X));
+ return;
+ }
+ // FIXME: Handle the case where __builtin_offsetof is not a constant.
+ Dst.Add(Pred);
+}
+
+
+void ExprEngine::
+VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ QualType T = Ex->getTypeOfArgument();
+
+ if (Ex->getKind() == UETT_SizeOf) {
+ if (!T->isIncompleteType() && !T->isConstantSizeType()) {
+ assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
+
+ // FIXME: Add support for VLA type arguments and VLA expressions.
+ // When that happens, we should probably refactor VLASizeChecker's code.
+ Dst.Add(Pred);
+ return;
+ }
+ else if (T->getAs<ObjCObjectType>()) {
+ // Some code tries to take the sizeof an ObjCObjectType, relying that
+ // the compiler has laid out its representation. Just report Unknown
+ // for these.
+ Dst.Add(Pred);
+ return;
+ }
+ }
+
+ Expr::EvalResult Result;
+ Ex->Evaluate(Result, getContext());
+ CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue());
+
+ const ProgramState *state = Pred->getState();
+ state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(),
+ Ex->getType()));
+ MakeNode(Dst, Ex, Pred, state);
+}
+
+void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ switch (U->getOpcode()) {
+ default:
+ break;
+ case UO_Real: {
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ExplodedNodeSet Tmp;
+ Visit(Ex, Pred, Tmp);
+
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ Dst.Add(*I);
+ continue;
+ }
+
+ // For all other types, UO_Real is an identity operation.
+ assert (U->getType() == Ex->getType());
+ const ProgramState *state = (*I)->getState();
+ MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
+ }
+
+ return;
+ }
+
+ case UO_Imag: {
+
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ExplodedNodeSet Tmp;
+ Visit(Ex, Pred, Tmp);
+
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ Dst.Add(*I);
+ continue;
+ }
+
+ // For all other types, UO_Imag returns 0.
+ const ProgramState *state = (*I)->getState();
+ SVal X = svalBuilder.makeZeroVal(Ex->getType());
+ MakeNode(Dst, U, *I, state->BindExpr(U, X));
+ }
+
+ return;
+ }
+
+ case UO_Plus:
+ assert(!U->isLValue());
+ // FALL-THROUGH.
+ case UO_Deref:
+ case UO_AddrOf:
+ case UO_Extension: {
+
+ // Unary "+" is a no-op, similar to a parentheses. We still have places
+ // where it may be a block-level expression, so we need to
+ // generate an extra node that just propagates the value of the
+ // subexpression.
+
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ExplodedNodeSet Tmp;
+ Visit(Ex, Pred, Tmp);
+
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ const ProgramState *state = (*I)->getState();
+ MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex)));
+ }
+
+ return;
+ }
+
+ case UO_LNot:
+ case UO_Minus:
+ case UO_Not: {
+ assert (!U->isLValue());
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ExplodedNodeSet Tmp;
+ Visit(Ex, Pred, Tmp);
+
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
+ const ProgramState *state = (*I)->getState();
+
+ // Get the value of the subexpression.
+ SVal V = state->getSVal(Ex);
+
+ if (V.isUnknownOrUndef()) {
+ MakeNode(Dst, U, *I, state->BindExpr(U, V));
+ continue;
+ }
+
+ switch (U->getOpcode()) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+
+ case UO_Not:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, evalComplement(cast<NonLoc>(V)));
+ break;
+
+ case UO_Minus:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, evalMinus(cast<NonLoc>(V)));
+ break;
+
+ case UO_LNot:
+
+ // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
+ //
+ // Note: technically we do "E == 0", but this is the same in the
+ // transfer functions as "0 == E".
+ SVal Result;
+
+ if (isa<Loc>(V)) {
+ Loc X = svalBuilder.makeNull();
+ Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X,
+ U->getType());
+ }
+ else {
+ nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
+ Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X,
+ U->getType());
+ }
+
+ state = state->BindExpr(U, Result);
+
+ break;
+ }
+
+ MakeNode(Dst, U, *I, state);
+ }
+
+ return;
+ }
+ }
+
+ // Handle ++ and -- (both pre- and post-increment).
+ assert (U->isIncrementDecrementOp());
+ ExplodedNodeSet Tmp;
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ Visit(Ex, Pred, Tmp);
+
+ for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) {
+
+ const ProgramState *state = (*I)->getState();
+ SVal loc = state->getSVal(Ex);
+
+ // Perform a load.
+ ExplodedNodeSet Tmp2;
+ evalLoad(Tmp2, Ex, *I, state, loc);
+
+ for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) {
+
+ state = (*I2)->getState();
+ SVal V2_untested = state->getSVal(Ex);
+
+ // Propagate unknown and undefined values.
+ if (V2_untested.isUnknownOrUndef()) {
+ MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested));
+ continue;
+ }
+ DefinedSVal V2 = cast<DefinedSVal>(V2_untested);
+
+ // Handle all other values.
+ BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add
+ : BO_Sub;
+
+ // If the UnaryOperator has non-location type, use its type to create the
+ // constant value. If the UnaryOperator has location type, create the
+ // constant with int type and pointer width.
+ SVal RHS;
+
+ if (U->getType()->isAnyPointerType())
+ RHS = svalBuilder.makeArrayIndex(1);
+ else
+ RHS = svalBuilder.makeIntVal(1, U->getType());
+
+ SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
+
+ // Conjure a new symbol if necessary to recover precision.
+ if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){
+ DefinedOrUnknownSVal SymVal =
+ svalBuilder.getConjuredSymbolVal(NULL, Ex,
+ Builder->getCurrentBlockCount());
+ Result = SymVal;
+
+ // If the value is a location, ++/-- should always preserve
+ // non-nullness. Check if the original value was non-null, and if so
+ // propagate that constraint.
+ if (Loc::isLocType(U->getType())) {
+ DefinedOrUnknownSVal Constraint =
+ svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
+
+ if (!state->assume(Constraint, true)) {
+ // It isn't feasible for the original value to be null.
+ // Propagate this constraint.
+ Constraint = svalBuilder.evalEQ(state, SymVal,
+ svalBuilder.makeZeroVal(U->getType()));
+
+
+ state = state->assume(Constraint, false);
+ assert(state);
+ }
+ }
+ }
+
+ // Since the lvalue-to-rvalue conversion is explicit in the AST,
+ // we bind an l-value if the operator is prefix and an lvalue (in C++).
+ if (U->isLValue())
+ state = state->BindExpr(U, loc);
+ else
+ state = state->BindExpr(U, U->isPostfix() ? V2 : Result);
+
+ // Perform the store.
+ evalStore(Dst, NULL, U, *I2, state, loc, Result);
+ }
+ }
+}
diff --git a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index ef7bc20..acb0074 100644
--- a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1,4 +1,4 @@
-//===- GRCXXExprEngine.cpp - C++ expr evaluation engine ---------*- C++ -*-===//
+//===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,6 +14,7 @@
#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/AST/DeclCXX.h"
using namespace clang;
@@ -36,7 +37,7 @@ void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE,
bool FstArgAsLValue) {
- llvm::SmallVector<CallExprWLItem, 20> WorkList;
+ SmallVector<CallExprWLItem, 20> WorkList;
WorkList.reserve(AE - AI);
WorkList.push_back(CallExprWLItem(AI, Pred));
@@ -103,24 +104,22 @@ const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl,
getCXXThisRegion(decl->getThisType(getContext()), frameCtx);
}
-void ExprEngine::CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- ExplodedNodeSet Tmp;
- Visit(Ex, Pred, Tmp);
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) {
- const GRState *state = GetState(*I);
+void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens();
+ const ProgramState *state = Pred->getState();
- // Bind the temporary object to the value of the expression. Then bind
- // the expression to the location of the object.
- SVal V = state->getSVal(Ex);
+ // Bind the temporary object to the value of the expression. Then bind
+ // the expression to the location of the object.
+ SVal V = state->getSVal(tempExpr);
- const MemRegion *R =
- svalBuilder.getRegionManager().getCXXTempObjectRegion(Ex,
- Pred->getLocationContext());
+ const MemRegion *R =
+ svalBuilder.getRegionManager().getCXXTempObjectRegion(ME,
+ Pred->getLocationContext());
- state = state->bindLoc(loc::MemRegionVal(R), V);
- MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R)));
- }
+ state = state->bindLoc(loc::MemRegionVal(R), V);
+ MakeNode(Dst, ME, Pred, state->BindExpr(ME, loc::MemRegionVal(R)));
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
@@ -186,7 +185,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(),
NE = argsEvaluated.end(); NI != NE; ++NI) {
- const GRState *state = GetState(*NI);
+ const ProgramState *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));
@@ -197,17 +196,6 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
#endif
// Default semantics: invalidate all regions passed as arguments.
- llvm::SmallVector<const MemRegion*, 10> regionsToInvalidate;
-
- // 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 blockCount = Builder->getCurrentBlockCount();
-
- // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate
- // global variables.
ExplodedNodeSet destCall;
for (ExplodedNodeSet::iterator
@@ -215,25 +203,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E,
i != e; ++i)
{
ExplodedNode *Pred = *i;
- const GRState *state = GetState(Pred);
+ const LocationContext *LC = Pred->getLocationContext();
+ const ProgramState *state = Pred->getState();
- // Accumulate list of regions that are invalidated.
- for (CXXConstructExpr::const_arg_iterator
- ai = E->arg_begin(), ae = E->arg_end();
- ai != ae; ++ai)
- {
- SVal val = state->getSVal(*ai);
- if (const MemRegion *region = val.getAsRegion())
- regionsToInvalidate.push_back(region);
- }
-
- // Invalidate the regions.
- state = state->invalidateRegions(regionsToInvalidate.data(),
- regionsToInvalidate.data() +
- regionsToInvalidate.size(),
- E, blockCount, 0,
- /* invalidateGlobals = */ true);
-
+ state = invalidateArguments(state, CallOrObjCMessage(E, state), LC);
Builder->MakeNode(destCall, E, Pred, state);
}
@@ -258,7 +231,7 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
CallEnter PP(S, SFC, Pred->getLocationContext());
- const GRState *state = Pred->getState();
+ const ProgramState *state = Pred->getState();
state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest));
ExplodedNode *N = Builder->generateNode(PP, state, Pred);
if (N)
@@ -279,7 +252,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
state = state->BindExpr(CNE, loc::MemRegionVal(EleReg));
MakeNode(Dst, CNE, Pred, state);
return;
@@ -298,12 +271,12 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
for (ExplodedNodeSet::iterator I = argsEvaluated.begin(),
E = argsEvaluated.end(); I != E; ++I) {
- const GRState *state = GetState(*I);
+ const ProgramState *state = (*I)->getState();
// Accumulate list of regions that are invalidated.
// FIXME: Eventually we should unify the logic for constructor
// processing in one place.
- llvm::SmallVector<const MemRegion*, 10> regionsToInvalidate;
+ SmallVector<const MemRegion*, 10> regionsToInvalidate;
for (CXXNewExpr::const_arg_iterator
ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end();
ai != ae; ++ai)
@@ -316,17 +289,13 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (ObjTy->isRecordType()) {
regionsToInvalidate.push_back(EleReg);
// Invalidate the regions.
- state = state->invalidateRegions(regionsToInvalidate.data(),
- regionsToInvalidate.data() +
- regionsToInvalidate.size(),
+ state = state->invalidateRegions(regionsToInvalidate,
CNE, blockCount, 0,
/* invalidateGlobals = */ true);
} else {
// Invalidate the regions.
- state = state->invalidateRegions(regionsToInvalidate.data(),
- regionsToInvalidate.data() +
- regionsToInvalidate.size(),
+ state = state->invalidateRegions(regionsToInvalidate,
CNE, blockCount, 0,
/* invalidateGlobals = */ true);
@@ -351,7 +320,7 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
Visit(CDE->getArgument(), Pred, Argevaluated);
for (ExplodedNodeSet::iterator I = Argevaluated.begin(),
E = Argevaluated.end(); I != E; ++I) {
- const GRState *state = GetState(*I);
+ const ProgramState *state = (*I)->getState();
MakeNode(Dst, CDE, *I, state);
}
}
@@ -364,7 +333,7 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
getContext().getCanonicalType(TE->getType()),
Pred->getLocationContext());
- const GRState *state = GetState(Pred);
+ const ProgramState *state = Pred->getState();
SVal V = state->getSVal(loc::MemRegionVal(R));
MakeNode(Dst, TE, Pred, state->BindExpr(TE, V));
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
new file mode 100644
index 0000000..6d377b9
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -0,0 +1,253 @@
+//=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for calls and returns.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Analysis/Support/SaveAndRestore.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+ // Trait class for recording returned expression in the state.
+ struct ReturnExpr {
+ static int TagInt;
+ typedef const Stmt *data_type;
+ };
+ int ReturnExpr::TagInt;
+}
+
+void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) {
+ const ProgramState *state =
+ B.getState()->enterStackFrame(B.getCalleeContext());
+ B.generateNode(state);
+}
+
+void ExprEngine::processCallExit(CallExitNodeBuilder &B) {
+ const ProgramState *state = B.getState();
+ const ExplodedNode *Pred = B.getPredecessor();
+ const StackFrameContext *calleeCtx =
+ cast<StackFrameContext>(Pred->getLocationContext());
+ const Stmt *CE = calleeCtx->getCallSite();
+
+ // If the callee returns an expression, bind its value to CallExpr.
+ const Stmt *ReturnedExpr = state->get<ReturnExpr>();
+ if (ReturnedExpr) {
+ SVal RetVal = state->getSVal(ReturnedExpr);
+ state = state->BindExpr(CE, RetVal);
+ // Clear the return expr GDM.
+ state = state->remove<ReturnExpr>();
+ }
+
+ // Bind the constructed object value to CXXConstructExpr.
+ if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
+ const CXXThisRegion *ThisR =
+ getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx);
+
+ SVal ThisV = state->getSVal(ThisR);
+ // Always bind the region to the CXXConstructExpr.
+ state = state->BindExpr(CCE, ThisV);
+ }
+
+ B.generateNode(state);
+}
+
+const ProgramState *
+ExprEngine::invalidateArguments(const ProgramState *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.
+ if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) {
+ if (isa<BlockDataRegion>(Callee))
+ RegionsToInvalidate.push_back(Callee);
+ }
+ }
+
+ for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) {
+ 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 underying 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
+ // approriately 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));
+ }
+ }
+
+ // Invalidate designated regions using the batch invalidation API.
+
+ // 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.
+ assert(Builder && "Invalidating arguments outside of a statement context");
+ unsigned Count = Builder->getCurrentBlockCount();
+ StoreManager::InvalidatedSymbols IS;
+
+ // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
+ // global variables.
+ return State->invalidateRegions(RegionsToInvalidate,
+ Call.getOriginExpr(), Count,
+ &IS, doesInvalidateGlobals(Call));
+
+}
+
+void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
+ ExplodedNodeSet &dst) {
+ // Perform the previsit of the CallExpr.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
+
+ // Now evaluate the call itself.
+ class DefaultEval : public GraphExpander {
+ ExprEngine &Eng;
+ const CallExpr *CE;
+ public:
+
+ DefaultEval(ExprEngine &eng, const CallExpr *ce)
+ : Eng(eng), CE(ce) {}
+ virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ // Should we inline the call?
+ if (Eng.getAnalysisManager().shouldInlineCall() &&
+ Eng.InlineCall(Dst, CE, Pred)) {
+ return;
+ }
+
+ // First handle the return value.
+ StmtNodeBuilder &Builder = Eng.getBuilder();
+ assert(&Builder && "StmtNodeBuilder must be defined.");
+
+ // Get the callee.
+ const Expr *Callee = CE->getCallee()->IgnoreParens();
+ const ProgramState *state = Pred->getState();
+ SVal L = state->getSVal(Callee);
+
+ // 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();
+
+ if (CE->isLValue())
+ ResultTy = Eng.getContext().getPointerType(ResultTy);
+
+ // Conjure a symbol value to use as the result.
+ SValBuilder &SVB = Eng.getSValBuilder();
+ unsigned Count = Builder.getCurrentBlockCount();
+ SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count);
+
+ // Generate a new state with the return value set.
+ state = state->BindExpr(CE, RetVal);
+
+ // Invalidate the arguments.
+ const LocationContext *LC = Pred->getLocationContext();
+ state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC);
+
+ // And make the result node.
+ Eng.MakeNode(Dst, CE, Pred, state);
+ }
+ };
+
+ // Finally, evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call.
+ ExplodedNodeSet dstCallEvaluated;
+ DefaultEval defEval(*this, CE);
+ getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
+ dstPreVisit,
+ CE, *this, &defEval);
+
+ // Finally, perform the post-condition check of the CallExpr and store
+ // the created nodes in 'Dst'.
+ getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
+ *this);
+}
+
+void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ExplodedNodeSet Src;
+ if (const Expr *RetE = RS->getRetValue()) {
+ // Record the returned expression in the state. It will be used in
+ // processCallExit to bind the return value to the call expr.
+ {
+ static SimpleProgramPointTag tag("ExprEngine: ReturnStmt");
+ const ProgramState *state = Pred->getState();
+ state = state->set<ReturnExpr>(RetE);
+ Pred = Builder->generateNode(RetE, state, Pred, &tag);
+ }
+ // We may get a NULL Pred because we generated a cached node.
+ if (Pred)
+ Visit(RetE, Pred, Src);
+ }
+ else {
+ Src.Add(Pred);
+ }
+
+ getCheckerManager().runCheckersForPreStmt(Dst, Src, RS, *this);
+}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
new file mode 100644
index 0000000..e0560fd
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -0,0 +1,279 @@
+//=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for Objective-C expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/Analysis/Support/SaveAndRestore.h"
+
+using namespace clang;
+using namespace ento;
+
+void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ const ProgramState *state = Pred->getState();
+ SVal baseVal = state->getSVal(Ex->getBase());
+ SVal location = state->getLValue(Ex->getDecl(), baseVal);
+
+ ExplodedNodeSet dstIvar;
+ MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location));
+
+ // Perform the post-condition check of the ObjCIvarRefExpr and store
+ // the created nodes in 'Dst'.
+ getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
+}
+
+void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
+}
+
+void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ // ObjCForCollectionStmts are processed in two places. This method
+ // handles the case where an ObjCForCollectionStmt* occurs as one of the
+ // statements within a basic block. This transfer function does two things:
+ //
+ // (1) binds the next container value to 'element'. This creates a new
+ // node in the ExplodedGraph.
+ //
+ // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
+ // whether or not the container has any more elements. This value
+ // will be tested in ProcessBranch. We need to explicitly bind
+ // this value because a container can contain nil elements.
+ //
+ // FIXME: Eventually this logic should actually do dispatches to
+ // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
+ // This will require simulating a temporary NSFastEnumerationState, either
+ // through an SVal or through the use of MemRegions. This value can
+ // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
+ // terminates we reclaim the temporary (it goes out of scope) and we
+ // we can test if the SVal is 0 or if the MemRegion is null (depending
+ // on what approach we take).
+ //
+ // For now: simulate (1) by assigning either a symbol or nil if the
+ // container is empty. Thus this transfer function will by default
+ // result in state splitting.
+
+ const Stmt *elem = S->getElement();
+ const ProgramState *state = Pred->getState();
+ SVal elementV;
+
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
+ const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
+ assert(elemD->getInit() == 0);
+ elementV = state->getLValue(elemD, Pred->getLocationContext());
+ }
+ else {
+ elementV = state->getSVal(elem);
+ }
+
+ ExplodedNodeSet dstLocation;
+ evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false);
+
+ if (dstLocation.empty())
+ return;
+
+ for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
+ NE = dstLocation.end(); NI!=NE; ++NI) {
+ Pred = *NI;
+ const ProgramState *state = Pred->getState();
+
+ // Handle the case where the container still has elements.
+ SVal TrueV = svalBuilder.makeTruthVal(1);
+ const ProgramState *hasElems = state->BindExpr(S, TrueV);
+
+ // Handle the case where the container has no elements.
+ SVal FalseV = svalBuilder.makeTruthVal(0);
+ const ProgramState *noElems = state->BindExpr(S, FalseV);
+
+ if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV))
+ if (const TypedValueRegion *R =
+ dyn_cast<TypedValueRegion>(MV->getRegion())) {
+ // FIXME: The proper thing to do is to really iterate over the
+ // container. We will do this with dispatch logic to the store.
+ // For now, just 'conjure' up a symbolic value.
+ QualType T = R->getValueType();
+ assert(Loc::isLocType(T));
+ unsigned Count = Builder->getCurrentBlockCount();
+ SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count);
+ SVal V = svalBuilder.makeLoc(Sym);
+ hasElems = hasElems->bindLoc(elementV, V);
+
+ // Bind the location to 'nil' on the false branch.
+ SVal nilV = svalBuilder.makeIntVal(0, T);
+ noElems = noElems->bindLoc(elementV, nilV);
+ }
+
+ // Create the new nodes.
+ MakeNode(Dst, S, Pred, hasElems);
+ MakeNode(Dst, S, Pred, noElems);
+ }
+}
+
+void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ // Handle the previsits checks.
+ ExplodedNodeSet dstPrevisit;
+ getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
+ msg, *this);
+
+ // Proceed with evaluate the message expression.
+ ExplodedNodeSet dstEval;
+
+ for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(),
+ DE = dstPrevisit.end(); DI != DE; ++DI) {
+
+ ExplodedNode *Pred = *DI;
+ bool RaisesException = false;
+ SaveAndRestore<bool> OldSink(Builder->BuildSinks);
+ SaveOr OldHasGen(Builder->hasGeneratedNode);
+
+ if (const Expr *Receiver = msg.getInstanceReceiver()) {
+ const ProgramState *state = Pred->getState();
+ SVal recVal = state->getSVal(Receiver);
+ if (!recVal.isUndef()) {
+ // Bifurcate the state into nil and non-nil ones.
+ DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
+
+ const ProgramState *notNilState, *nilState;
+ llvm::tie(notNilState, nilState) = state->assume(receiverVal);
+
+ // There are three cases: can be nil or non-nil, must be nil, must be
+ // non-nil. We ignore must be nil, and merge the rest two into non-nil.
+ if (nilState && !notNilState) {
+ dstEval.insert(Pred);
+ continue;
+ }
+
+ // Check if the "raise" message was sent.
+ assert(notNilState);
+ if (msg.getSelector() == RaiseSel)
+ RaisesException = true;
+
+ // Check if we raise an exception. For now treat these as sinks.
+ // Eventually we will want to handle exceptions properly.
+ if (RaisesException)
+ Builder->BuildSinks = true;
+
+ // Dispatch to plug-in transfer function.
+ evalObjCMessage(dstEval, msg, Pred, notNilState);
+ }
+ }
+ else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
+ IdentifierInfo* ClsName = Iface->getIdentifier();
+ Selector S = msg.getSelector();
+
+ // Check for special instance methods.
+ if (!NSExceptionII) {
+ ASTContext &Ctx = getContext();
+ NSExceptionII = &Ctx.Idents.get("NSException");
+ }
+
+ if (ClsName == NSExceptionII) {
+ enum { NUM_RAISE_SELECTORS = 2 };
+
+ // Lazily create a cache of the selectors.
+ if (!NSExceptionInstanceRaiseSelectors) {
+ ASTContext &Ctx = getContext();
+ NSExceptionInstanceRaiseSelectors =
+ new Selector[NUM_RAISE_SELECTORS];
+ SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
+ unsigned idx = 0;
+
+ // raise:format:
+ II.push_back(&Ctx.Idents.get("raise"));
+ II.push_back(&Ctx.Idents.get("format"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
+
+ // raise:format::arguments:
+ II.push_back(&Ctx.Idents.get("arguments"));
+ NSExceptionInstanceRaiseSelectors[idx++] =
+ Ctx.Selectors.getSelector(II.size(), &II[0]);
+ }
+
+ for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
+ if (S == NSExceptionInstanceRaiseSelectors[i]) {
+ RaisesException = true;
+ break;
+ }
+ }
+
+ // Check if we raise an exception. For now treat these as sinks.
+ // Eventually we will want to handle exceptions properly.
+ if (RaisesException)
+ Builder->BuildSinks = true;
+
+ // Dispatch to plug-in transfer function.
+ evalObjCMessage(dstEval, msg, Pred, Pred->getState());
+ }
+
+ assert(Builder->BuildSinks || Builder->hasGeneratedNode);
+ }
+
+ // Finally, perform the post-condition check of the ObjCMessageExpr and store
+ // the created nodes in 'Dst'.
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this);
+}
+
+void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg,
+ ExplodedNode *Pred,
+ const ProgramState *state) {
+ assert (Builder && "StmtNodeBuilder must be defined.");
+
+ // 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);
+ 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 = Builder->getCurrentBlockCount();
+ const Expr *CurrentE = cast<Expr>(currentStmt);
+ ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count);
+ }
+
+ // Bind the return value.
+ state = state->BindExpr(currentStmt, ReturnValue);
+
+ // Invalidate the arguments (and the receiver)
+ const LocationContext *LC = Pred->getLocationContext();
+ state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC);
+
+ // And create the new node.
+ MakeNode(Dst, msg.getOriginExpr(), Pred, state);
+}
+
diff --git a/lib/StaticAnalyzer/Core/FlatStore.cpp b/lib/StaticAnalyzer/Core/FlatStore.cpp
deleted file mode 100644
index ca867ae..0000000
--- a/lib/StaticAnalyzer/Core/FlatStore.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-//=== FlatStore.cpp - Flat region-based store model -------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "llvm/ADT/ImmutableIntervalMap.h"
-#include "llvm/Support/ErrorHandling.h"
-
-using namespace clang;
-using namespace ento;
-using llvm::Interval;
-
-// The actual store type.
-typedef llvm::ImmutableIntervalMap<SVal> BindingVal;
-typedef llvm::ImmutableMap<const MemRegion *, BindingVal> RegionBindings;
-
-namespace {
-class FlatStoreManager : public StoreManager {
- RegionBindings::Factory RBFactory;
- BindingVal::Factory BVFactory;
-
-public:
- FlatStoreManager(GRStateManager &mgr)
- : StoreManager(mgr),
- RBFactory(mgr.getAllocator()),
- BVFactory(mgr.getAllocator()) {}
-
- SVal Retrieve(Store store, Loc L, QualType T);
- StoreRef Bind(Store store, Loc L, SVal val);
- StoreRef Remove(Store St, Loc L);
- StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr* cl,
- const LocationContext *LC, SVal v);
-
- StoreRef getInitialStore(const LocationContext *InitLoc) {
- return StoreRef(RBFactory.getEmptyMap().getRoot(), *this);
- }
-
- SubRegionMap *getSubRegionMap(Store store) {
- return 0;
- }
-
- SVal ArrayToPointer(Loc Array);
- StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
- llvm::SmallVectorImpl<const MemRegion*>& RegionRoots){
- return StoreRef(store, *this);
- }
-
- StoreRef BindDecl(Store store, const VarRegion *VR, SVal initVal);
-
- StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR);
-
- typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols;
-
- StoreRef invalidateRegions(Store store, const MemRegion * const *I,
- const MemRegion * const *E, const Expr *Ex,
- unsigned Count, InvalidatedSymbols &IS,
- bool invalidateGlobals,
- InvalidatedRegions *Regions);
-
- void print(Store store, llvm::raw_ostream& Out, const char* nl,
- const char *sep);
- void iterBindings(Store store, BindingsHandler& f);
-
-private:
- static RegionBindings getRegionBindings(Store store) {
- return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store));
- }
-
- class RegionInterval {
- public:
- const MemRegion *R;
- Interval I;
- RegionInterval(const MemRegion *r, int64_t s, int64_t e) : R(r), I(s, e){}
- };
-
- RegionInterval RegionToInterval(const MemRegion *R);
-
- SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T);
-};
-} // end anonymous namespace
-
-StoreManager *ento::CreateFlatStoreManager(GRStateManager &StMgr) {
- return new FlatStoreManager(StMgr);
-}
-
-SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) {
- // For access to concrete addresses, return UnknownVal. Checks
- // for null dereferences (and similar errors) are done by checkers, not
- // the Store.
- // FIXME: We can consider lazily symbolicating such memory, but we really
- // should defer this when we can reason easily about symbolicating arrays
- // of bytes.
- if (isa<loc::ConcreteInt>(L)) {
- return UnknownVal();
- }
- if (!isa<loc::MemRegionVal>(L)) {
- return UnknownVal();
- }
-
- const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
- RegionInterval RI = RegionToInterval(R);
- // FIXME: FlatStore should handle regions with unknown intervals.
- if (!RI.R)
- return UnknownVal();
-
- RegionBindings B = getRegionBindings(store);
- const BindingVal *BV = B.lookup(RI.R);
- if (BV) {
- const SVal *V = BVFactory.lookup(*BV, RI.I);
- if (V)
- return *V;
- else
- return RetrieveRegionWithNoBinding(R, T);
- }
- return RetrieveRegionWithNoBinding(R, T);
-}
-
-SVal FlatStoreManager::RetrieveRegionWithNoBinding(const MemRegion *R,
- QualType T) {
- if (R->hasStackNonParametersStorage())
- return UndefinedVal();
- else
- return svalBuilder.getRegionValueSymbolVal(cast<TypedRegion>(R));
-}
-
-StoreRef FlatStoreManager::Bind(Store store, Loc L, SVal val) {
- const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion();
- RegionBindings B = getRegionBindings(store);
- const BindingVal *V = B.lookup(R);
-
- BindingVal BV = BVFactory.getEmptyMap();
- if (V)
- BV = *V;
-
- RegionInterval RI = RegionToInterval(R);
- // FIXME: FlatStore should handle regions with unknown intervals.
- if (!RI.R)
- return StoreRef(B.getRoot(), *this);
- BV = BVFactory.add(BV, RI.I, val);
- B = RBFactory.add(B, RI.R, BV);
- return StoreRef(B.getRoot(), *this);
-}
-
-StoreRef FlatStoreManager::Remove(Store store, Loc L) {
- return StoreRef(store, *this);
-}
-
-StoreRef FlatStoreManager::BindCompoundLiteral(Store store,
- const CompoundLiteralExpr* cl,
- const LocationContext *LC,
- SVal v) {
- return StoreRef(store, *this);
-}
-
-SVal FlatStoreManager::ArrayToPointer(Loc Array) {
- return Array;
-}
-
-StoreRef FlatStoreManager::BindDecl(Store store, const VarRegion *VR,
- SVal initVal) {
- return Bind(store, svalBuilder.makeLoc(VR), initVal);
-}
-
-StoreRef FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR){
- return StoreRef(store, *this);
-}
-
-StoreRef FlatStoreManager::invalidateRegions(Store store,
- const MemRegion * const *I,
- const MemRegion * const *E,
- const Expr *Ex, unsigned Count,
- InvalidatedSymbols &IS,
- bool invalidateGlobals,
- InvalidatedRegions *Regions) {
- assert(false && "Not implemented");
- return StoreRef(store, *this);
-}
-
-void FlatStoreManager::print(Store store, llvm::raw_ostream& Out,
- const char* nl, const char *sep) {
-}
-
-void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) {
-}
-
-FlatStoreManager::RegionInterval
-FlatStoreManager::RegionToInterval(const MemRegion *R) {
- switch (R->getKind()) {
- case MemRegion::VarRegionKind: {
- QualType T = cast<VarRegion>(R)->getValueType();
- int64_t Size = Ctx.getTypeSize(T);
- return RegionInterval(R, 0, Size-1);
- }
-
- case MemRegion::ElementRegionKind:
- case MemRegion::FieldRegionKind: {
- RegionOffset Offset = R->getAsOffset();
- // We cannot compute offset for all regions, for example, elements
- // with symbolic offsets.
- if (!Offset.getRegion())
- return RegionInterval(0, 0, 0);
- int64_t Start = Offset.getOffset();
- int64_t Size = Ctx.getTypeSize(cast<TypedRegion>(R)->getValueType());
- return RegionInterval(Offset.getRegion(), Start, Start+Size);
- }
-
- default:
- llvm_unreachable("Region kind unhandled.");
- return RegionInterval(0, 0, 0);
- }
-}
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 1ebc28c..0c4e427 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -11,7 +11,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -35,7 +35,7 @@ using namespace ento;
namespace {
-class HTMLDiagnostics : public PathDiagnosticClient {
+class HTMLDiagnostics : public PathDiagnosticConsumer {
llvm::sys::Path Directory, FilePrefix;
bool createdDir, noDir;
const Preprocessor &PP;
@@ -45,15 +45,15 @@ public:
virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); }
- virtual void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade);
+ virtual void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade);
- virtual void HandlePathDiagnostic(const PathDiagnostic* D);
+ virtual void HandlePathDiagnosticImpl(const PathDiagnostic* D);
- virtual llvm::StringRef getName() const {
+ virtual StringRef getName() const {
return "HTMLDiagnostics";
}
- unsigned ProcessMacroPiece(llvm::raw_ostream& os,
+ unsigned ProcessMacroPiece(raw_ostream &os,
const PathDiagnosticMacroPiece& P,
unsigned num);
@@ -65,7 +65,7 @@ public:
const char *HighlightEnd = "</span>");
void ReportDiag(const PathDiagnostic& D,
- llvm::SmallVectorImpl<std::string> *FilesMade);
+ SmallVectorImpl<std::string> *FilesMade);
};
} // end anonymous namespace
@@ -78,8 +78,8 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix,
FilePrefix.appendComponent("report");
}
-PathDiagnosticClient*
-ento::createHTMLDiagnosticClient(const std::string& prefix,
+PathDiagnosticConsumer*
+ento::createHTMLDiagnosticConsumer(const std::string& prefix,
const Preprocessor &PP) {
return new HTMLDiagnostics(prefix, PP);
}
@@ -88,7 +88,7 @@ ento::createHTMLDiagnosticClient(const std::string& prefix,
// Report processing.
//===----------------------------------------------------------------------===//
-void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
+void HTMLDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
if (!D)
return;
@@ -102,7 +102,7 @@ void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
}
void
-HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade)
+HTMLDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade)
{
while (!BatchedDiags.empty()) {
const PathDiagnostic* D = BatchedDiags.back();
@@ -115,7 +115,7 @@ HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade)
}
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
- llvm::SmallVectorImpl<std::string> *FilesMade){
+ SmallVectorImpl<std::string> *FilesMade){
// Create the HTML directory if it is missing.
if (!createdDir) {
createdDir = true;
@@ -143,7 +143,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
// Verify that the entire path is from the same FileID.
for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) {
- FullSourceLoc L = I->getLocation().asLocation().getInstantiationLoc();
+ FullSourceLoc L = I->getLocation().asLocation().getExpansionLoc();
if (FID.isInvalid()) {
FID = SMgr.getFileID(L);
@@ -154,12 +154,12 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
RE=I->ranges_end(); RI!=RE; ++RI) {
- SourceLocation L = SMgr.getInstantiationLoc(RI->getBegin());
+ SourceLocation L = SMgr.getExpansionLoc(RI->getBegin());
if (!L.isFileID() || SMgr.getFileID(L) != FID)
return; // FIXME: Emit a warning?
- L = SMgr.getInstantiationLoc(RI->getEnd());
+ L = SMgr.getExpansionLoc(RI->getEnd());
if (!L.isFileID() || SMgr.getFileID(L) != FID)
return; // FIXME: Emit a warning?
@@ -221,9 +221,9 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
<< html::EscapeText(Entry->getName())
<< "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>"
"<a href=\"#EndPath\">line "
- << (*D.rbegin()).getLocation().asLocation().getInstantiationLineNumber()
+ << (*D.rbegin()).getLocation().asLocation().getExpansionLineNumber()
<< ", column "
- << (*D.rbegin()).getLocation().asLocation().getInstantiationColumnNumber()
+ << (*D.rbegin()).getLocation().asLocation().getExpansionColumnNumber()
<< "</a></td></tr>\n"
"<tr><td class=\"rowname\">Description:</td><td>"
<< D.getDescription() << "</td></tr>\n";
@@ -261,7 +261,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
os << "\n<!-- BUGLINE "
- << D.back()->getLocation().asLocation().getInstantiationLineNumber()
+ << D.back()->getLocation().asLocation().getExpansionLineNumber()
<< " -->\n";
os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n";
@@ -324,7 +324,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
SourceManager &SM = R.getSourceMgr();
assert(&Pos.getManager() == &SM && "SourceManagers are different!");
- std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedInstantiationLoc(Pos);
+ std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);
if (LPosInfo.first != BugFileID)
return;
@@ -335,7 +335,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Compute the column number. Rewind from the current position to the start
// of the line.
unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
- const char *TokInstantiationPtr =Pos.getInstantiationLoc().getCharacterData();
+ const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData();
const char *LineStart = TokInstantiationPtr-ColNo;
// Compute LineEnd.
@@ -441,9 +441,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Get the name of the macro by relexing it.
{
- FullSourceLoc L = MP->getLocation().asLocation().getInstantiationLoc();
+ FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
assert(L.isFileID());
- llvm::StringRef BufferInfo = L.getBufferData();
+ StringRef BufferInfo = L.getBufferData();
const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data();
Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(),
MacroName, BufferInfo.end());
@@ -474,7 +474,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Insert the new html.
unsigned DisplayPos = LineEnd - FileStart;
SourceLocation Loc =
- SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos);
+ SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
R.InsertTextBefore(Loc, os.str());
@@ -504,7 +504,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
#endif
}
-static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) {
+static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
unsigned x = n % ('z' - 'a');
n /= 'z' - 'a';
@@ -514,7 +514,7 @@ static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) {
os << char('a' + x);
}
-unsigned HTMLDiagnostics::ProcessMacroPiece(llvm::raw_ostream& os,
+unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
const PathDiagnosticMacroPiece& P,
unsigned num) {
@@ -549,11 +549,11 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
SourceManager &SM = R.getSourceMgr();
const LangOptions &LangOpts = R.getLangOpts();
- SourceLocation InstantiationStart = SM.getInstantiationLoc(Range.getBegin());
- unsigned StartLineNo = SM.getInstantiationLineNumber(InstantiationStart);
+ SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin());
+ unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);
- SourceLocation InstantiationEnd = SM.getInstantiationLoc(Range.getEnd());
- unsigned EndLineNo = SM.getInstantiationLineNumber(InstantiationEnd);
+ SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd());
+ unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);
if (EndLineNo < StartLineNo)
return;
@@ -563,7 +563,7 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
return;
// Compute the column number of the end.
- unsigned EndColNo = SM.getInstantiationColumnNumber(InstantiationEnd);
+ unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);
unsigned OldEndColNo = EndColNo;
if (EndColNo) {
@@ -575,7 +575,7 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
// selected range.
SourceLocation E =
- InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo);
+ InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo);
html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
}
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index d9e884a..6f92da8 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -38,7 +38,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1) {
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, a1, superRegion);
- void* InsertPos;
+ void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
@@ -56,7 +56,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1,
const MemRegion *superRegion) {
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, a1, superRegion);
- void* InsertPos;
+ void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
@@ -77,7 +77,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) {
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, a1, a2, superRegion);
- void* InsertPos;
+ void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
@@ -96,7 +96,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2,
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, a1, a2, superRegion);
- void* InsertPos;
+ void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
@@ -115,7 +115,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3,
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion);
- void* InsertPos;
+ void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
@@ -178,7 +178,7 @@ const StackFrameContext *VarRegion::getStackFrame() const {
//===----------------------------------------------------------------------===//
DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const {
- ASTContext& Ctx = svalBuilder.getContext();
+ ASTContext &Ctx = svalBuilder.getContext();
QualType T = getDesugaredValueType(Ctx);
if (isa<VariableArrayType>(T))
@@ -250,7 +250,7 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
}
void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
- const Expr* Ex, unsigned cnt,
+ const Expr *Ex, unsigned cnt,
const MemRegion *) {
ID.AddInteger((unsigned) AllocaRegionKind);
ID.AddPointer(Ex);
@@ -266,7 +266,7 @@ void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const {
}
void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
- const CompoundLiteralExpr* CL,
+ const CompoundLiteralExpr *CL,
const MemRegion* superRegion) {
ID.AddInteger((unsigned) CompoundLiteralRegionKind);
ID.AddPointer(CL);
@@ -285,7 +285,7 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
}
-void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D,
+void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
const MemRegion* superRegion, Kind k) {
ID.AddInteger((unsigned) k);
ID.AddPointer(D);
@@ -398,81 +398,82 @@ std::string MemRegion::getString() const {
return os.str();
}
-void MemRegion::dumpToStream(llvm::raw_ostream& os) const {
+void MemRegion::dumpToStream(raw_ostream &os) const {
os << "<Unknown Region>";
}
-void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const {
+void AllocaRegion::dumpToStream(raw_ostream &os) const {
os << "alloca{" << (void*) Ex << ',' << Cnt << '}';
}
-void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const {
+void FunctionTextRegion::dumpToStream(raw_ostream &os) const {
os << "code{" << getDecl()->getDeclName().getAsString() << '}';
}
-void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const {
+void BlockTextRegion::dumpToStream(raw_ostream &os) const {
os << "block_code{" << (void*) this << '}';
}
-void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const {
+void BlockDataRegion::dumpToStream(raw_ostream &os) const {
os << "block_data{" << BC << '}';
}
-void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const {
+void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const {
// FIXME: More elaborate pretty-printing.
os << "{ " << (void*) CL << " }";
}
-void CXXTempObjectRegion::dumpToStream(llvm::raw_ostream &os) const {
- os << "temp_object";
+void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
+ os << "temp_object{" << getValueType().getAsString() << ','
+ << (void*) Ex << '}';
}
-void CXXBaseObjectRegion::dumpToStream(llvm::raw_ostream &os) const {
+void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
os << "base " << decl->getName();
}
-void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const {
+void CXXThisRegion::dumpToStream(raw_ostream &os) const {
os << "this";
}
-void ElementRegion::dumpToStream(llvm::raw_ostream& os) const {
+void ElementRegion::dumpToStream(raw_ostream &os) const {
os << "element{" << superRegion << ','
<< Index << ',' << getElementType().getAsString() << '}';
}
-void FieldRegion::dumpToStream(llvm::raw_ostream& os) const {
- os << superRegion << "->" << getDecl();
+void FieldRegion::dumpToStream(raw_ostream &os) const {
+ os << superRegion << "->" << *getDecl();
}
-void NonStaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const {
+void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "NonStaticGlobalSpaceRegion";
}
-void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const {
- os << "ivar{" << superRegion << ',' << getDecl() << '}';
+void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
+ os << "ivar{" << superRegion << ',' << *getDecl() << '}';
}
-void StringRegion::dumpToStream(llvm::raw_ostream& os) const {
+void StringRegion::dumpToStream(raw_ostream &os) const {
Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions()));
}
-void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolicRegion::dumpToStream(raw_ostream &os) const {
os << "SymRegion{" << sym << '}';
}
-void VarRegion::dumpToStream(llvm::raw_ostream& os) const {
- os << cast<VarDecl>(D);
+void VarRegion::dumpToStream(raw_ostream &os) const {
+ os << *cast<VarDecl>(D);
}
void RegionRawOffset::dump() const {
dumpToStream(llvm::errs());
}
-void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const {
+void RegionRawOffset::dumpToStream(raw_ostream &os) const {
os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}';
}
-void StaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const {
+void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "StaticGlobalsMemSpace{" << CR << '}';
}
@@ -631,7 +632,7 @@ MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC,
}
const CompoundLiteralRegion*
-MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL,
+MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
const LocationContext *LC) {
const MemRegion *sReg = 0;
@@ -650,14 +651,14 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL,
const ElementRegion*
MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
const MemRegion* superRegion,
- ASTContext& Ctx){
+ ASTContext &Ctx){
QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType();
llvm::FoldingSetNodeID ID;
ElementRegion::ProfileRegion(ID, T, Idx, superRegion);
- void* InsertPos;
+ void *InsertPos;
MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos);
ElementRegion* R = cast_or_null<ElementRegion>(data);
@@ -688,13 +689,13 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
}
const FieldRegion*
-MemRegionManager::getFieldRegion(const FieldDecl* d,
+MemRegionManager::getFieldRegion(const FieldDecl *d,
const MemRegion* superRegion){
return getSubRegion<FieldRegion>(d, superRegion);
}
const ObjCIvarRegion*
-MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d,
+MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d,
const MemRegion* superRegion) {
return getSubRegion<ObjCIvarRegion>(d, superRegion);
}
@@ -724,7 +725,7 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
}
const AllocaRegion*
-MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt,
+MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt,
const LocationContext *LC) {
const StackFrameContext *STC = LC->getCurrentStackFrame();
assert(STC);
@@ -896,7 +897,7 @@ RegionOffset MemRegion::getAsOffset() const {
case FieldRegionKind: {
const FieldRegion *FR = cast<FieldRegion>(R);
const RecordDecl *RD = FR->getDecl()->getParent();
- if (!RD->isDefinition())
+ if (!RD->isCompleteDefinition())
// We cannot compute offset for incomplete type.
return RegionOffset(0);
// Get the field number.
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
index c000600..0974fe8 100644
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
@@ -112,18 +112,22 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
QualType resultTy;
bool isLVal = false;
- if (CallE) {
- isLVal = CallE->isLValue();
- const Expr *Callee = CallE->getCallee();
- if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl())
- resultTy = FD->getResultType();
- else
- resultTy = CallE->getType();
- }
- else {
+ if (isObjCMessage()) {
isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) &&
Msg.getOriginExpr()->isLValue();
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).getAsFunctionDecl())
+ resultTy = FD->getResultType();
+ else
+ resultTy = FunctionCall->getType();
}
if (isLVal)
@@ -132,25 +136,29 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
return resultTy;
}
-SVal CallOrObjCMessage::getArgSValAsScalarOrLoc(unsigned i) const {
- assert(i < getNumArgs());
- if (CallE) return State->getSValAsScalarOrLoc(CallE->getArg(i));
- QualType argT = Msg.getArgType(i);
- if (Loc::isLocType(argT) || argT->isIntegerType())
- return Msg.getArgSVal(i, State);
- return UnknownVal();
-}
-
SVal CallOrObjCMessage::getFunctionCallee() const {
assert(isFunctionCall());
assert(!isCXXCall());
- const Expr *callee = CallE->getCallee()->IgnoreParenCasts();
- return State->getSVal(callee);
+ const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
+ return State->getSVal(Fun);
}
SVal CallOrObjCMessage::getCXXCallee() const {
assert(isCXXCall());
+ const CallExpr *ActualCall = CallE.get<const CallExpr *>();
const Expr *callee =
- cast<CXXMemberCallExpr>(CallE)->getImplicitObjectArgument();
- return State->getSVal(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);
+}
+
+SVal
+CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
+ assert(isObjCMessage());
+ return Msg.getInstanceReceiverSVal(State, LC);
}
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 872bbfe..3a87903 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -12,17 +12,16 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtCXX.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/Casting.h"
using namespace clang;
using namespace ento;
-using llvm::dyn_cast;
-using llvm::isa;
bool PathDiagnosticMacroPiece::containsEvent() const {
for (const_iterator I = begin(), E = end(); I!=E; ++I) {
@@ -37,14 +36,14 @@ bool PathDiagnosticMacroPiece::containsEvent() const {
return false;
}
-static llvm::StringRef StripTrailingDots(llvm::StringRef s) {
- for (llvm::StringRef::size_type i = s.size(); i != 0; --i)
+static StringRef StripTrailingDots(StringRef s) {
+ for (StringRef::size_type i = s.size(); i != 0; --i)
if (s[i - 1] != '.')
return s.substr(0, i);
return "";
}
-PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s,
+PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
Kind k, DisplayHint hint)
: str(StripTrailingDots(s)), kind(k), Hint(hint) {}
@@ -76,55 +75,161 @@ void PathDiagnostic::resetPath(bool deletePieces) {
}
-PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc,
- llvm::StringRef category)
+PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc,
+ StringRef category)
: Size(0),
BugType(StripTrailingDots(bugtype)),
Desc(StripTrailingDots(desc)),
Category(StripTrailingDots(category)) {}
-void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel,
- const DiagnosticInfo &Info) {
- // Default implementation (Warnings/errors count).
- DiagnosticClient::HandleDiagnostic(DiagLevel, Info);
+void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) {
+ // For now this simply forwards to HandlePathDiagnosticImpl. In the future
+ // we can use this indirection to control for multi-threaded access to
+ // the PathDiagnosticConsumer from multiple bug reporters.
+ HandlePathDiagnosticImpl(D);
+}
+
+//===----------------------------------------------------------------------===//
+// PathDiagnosticLocation methods.
+//===----------------------------------------------------------------------===//
+
+static SourceLocation getValidSourceLocation(const Stmt* S,
+ LocationOrAnalysisContext LAC) {
+ SourceLocation L = S->getLocStart();
+ assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext 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.
+ if (!L.isValid()) {
+
+ ParentMap *PM = 0;
+ if (LAC.is<const LocationContext*>())
+ PM = &LAC.get<const LocationContext*>()->getParentMap();
+ else
+ PM = &LAC.get<AnalysisContext*>()->getParentMap();
+
+ while (!L.isValid()) {
+ S = PM->getParent(S);
+ L = S->getLocStart();
+ }
+ }
+
+ return L;
+}
- // Create a PathDiagnostic with a single piece.
+PathDiagnosticLocation
+ PathDiagnosticLocation::createBegin(const Decl *D,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
+}
- PathDiagnostic* D = new PathDiagnostic();
+PathDiagnosticLocation
+ PathDiagnosticLocation::createBegin(const Stmt *S,
+ const SourceManager &SM,
+ LocationOrAnalysisContext LAC) {
+ return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
+ SM, SingleLocK);
+}
- const char *LevelStr;
- switch (DiagLevel) {
- default:
- case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
- case Diagnostic::Note: LevelStr = "note: "; break;
- case Diagnostic::Warning: LevelStr = "warning: "; break;
- case Diagnostic::Error: LevelStr = "error: "; break;
- case Diagnostic::Fatal: LevelStr = "fatal error: "; break;
+PathDiagnosticLocation
+ PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
+ const SourceManager &SM) {
+ SourceLocation L = CS->getLBracLoc();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
+ const SourceManager &SM) {
+ SourceLocation L = CS->getRBracLoc();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
+ const SourceManager &SM) {
+ // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
+ if (const CompoundStmt *CS =
+ dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
+ if (!CS->body_empty()) {
+ SourceLocation Loc = (*CS->body_begin())->getLocStart();
+ return PathDiagnosticLocation(Loc, SM, SingleLocK);
+ }
+
+ return PathDiagnosticLocation();
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
+ const SourceManager &SM) {
+ SourceLocation L = LC->getDecl()->getBodyRBrace();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::create(const ProgramPoint& P,
+ const SourceManager &SMng) {
+
+ const Stmt* S = 0;
+ if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ const CFGBlock *BSrc = BE->getSrc();
+ S = BSrc->getTerminatorCondition();
+ }
+ else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) {
+ S = PS->getStmt();
}
- llvm::SmallString<100> StrC;
- StrC += LevelStr;
- Info.FormatDiagnostic(StrC);
+ return PathDiagnosticLocation(S, SMng, P.getLocationContext());
- PathDiagnosticPiece *P =
- new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(),
- Info.getSourceManager()),
- StrC.str());
+ if (!S)
+ return PathDiagnosticLocation();
+}
- for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i)
- P->addRange(Info.getRange(i).getAsRange());
- for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i)
- P->addFixItHint(Info.getFixItHint(i));
- D->push_front(P);
+PathDiagnosticLocation
+ PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N,
+ const SourceManager &SM) {
+ assert(N && "Cannot create a location with a null node.");
+
+ const ExplodedNode *NI = N;
+
+ while (NI) {
+ ProgramPoint P = NI->getLocation();
+ const LocationContext *LC = P.getLocationContext();
+ if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P))
+ return PathDiagnosticLocation(PS->getStmt(), SM, LC);
+ else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
+ const Stmt *Term = BE->getSrc()->getTerminator();
+ assert(Term);
+ return PathDiagnosticLocation(Term, SM, LC);
+ }
+ NI = NI->succ_empty() ? 0 : *(NI->succ_begin());
+ }
- HandlePathDiagnostic(D);
+ return createDeclEnd(N->getLocationContext(), SM);
}
-//===----------------------------------------------------------------------===//
-// PathDiagnosticLocation methods.
-//===----------------------------------------------------------------------===//
+PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
+ const PathDiagnosticLocation &PDL) {
+ FullSourceLoc L = PDL.asLocation();
+ return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
+}
-FullSourceLoc PathDiagnosticLocation::asLocation() const {
+FullSourceLoc
+ PathDiagnosticLocation::genLocation(SourceLocation L,
+ LocationOrAnalysisContext LAC) const {
assert(isValid());
// Note that we want a 'switch' here so that the compiler can warn us in
// case we add more cases.
@@ -133,21 +238,23 @@ FullSourceLoc PathDiagnosticLocation::asLocation() const {
case RangeK:
break;
case StmtK:
- return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM));
+ return FullSourceLoc(getValidSourceLocation(S, LAC),
+ const_cast<SourceManager&>(*SM));
case DeclK:
return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
}
- return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM));
+ return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
}
-PathDiagnosticRange PathDiagnosticLocation::asRange() const {
+PathDiagnosticRange
+ PathDiagnosticLocation::genRange(LocationOrAnalysisContext LAC) const {
assert(isValid());
// Note that we want a 'switch' here so that the compiler can warn us in
// case we add more cases.
switch (K) {
case SingleLocK:
- return PathDiagnosticRange(R, true);
+ return PathDiagnosticRange(SourceRange(Loc,Loc), true);
case RangeK:
break;
case StmtK: {
@@ -176,12 +283,14 @@ PathDiagnosticRange PathDiagnosticLocation::asRange() const {
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass:
case Stmt::ObjCForCollectionStmtClass: {
- SourceLocation L = S->getLocStart();
+ SourceLocation L = getValidSourceLocation(S, LAC);
return SourceRange(L, L);
}
}
-
- return S->getSourceRange();
+ SourceRange R = S->getSourceRange();
+ if (R.isValid())
+ return R;
+ break;
}
case DeclK:
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
@@ -196,19 +305,16 @@ PathDiagnosticRange PathDiagnosticLocation::asRange() const {
}
}
- return R;
+ return SourceRange(Loc,Loc);
}
void PathDiagnosticLocation::flatten() {
if (K == StmtK) {
- R = asRange();
K = RangeK;
S = 0;
D = 0;
}
else if (K == DeclK) {
- SourceLocation L = D->getLocation();
- R = SourceRange(L, L);
K = SingleLocK;
S = 0;
D = 0;
@@ -220,22 +326,9 @@ void PathDiagnosticLocation::flatten() {
//===----------------------------------------------------------------------===//
void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger((unsigned) K);
- switch (K) {
- case RangeK:
- ID.AddInteger(R.getBegin().getRawEncoding());
- ID.AddInteger(R.getEnd().getRawEncoding());
- break;
- case SingleLocK:
- ID.AddInteger(R.getBegin().getRawEncoding());
- break;
- case StmtK:
- ID.Add(S);
- break;
- case DeclK:
- ID.Add(D);
- break;
- }
+ ID.AddInteger(Range.getBegin().getRawEncoding());
+ ID.AddInteger(Range.getEnd().getRawEncoding());
+ ID.AddInteger(Loc.getRawEncoding());
return;
}
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index fbbbd46..5ae95c6 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -11,7 +11,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
@@ -22,14 +22,9 @@
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace ento;
-using llvm::cast;
typedef llvm::DenseMap<FileID, unsigned> FIDMap;
-namespace clang {
- class Preprocessor;
-}
-
namespace {
struct CompareDiagnostics {
// Compare if 'X' is "<" than 'Y'.
@@ -43,16 +38,16 @@ struct CompareDiagnostics {
return false;
// Next, compare by bug type.
- llvm::StringRef XBugType = X->getBugType();
- llvm::StringRef YBugType = Y->getBugType();
+ StringRef XBugType = X->getBugType();
+ StringRef YBugType = Y->getBugType();
if (XBugType < YBugType)
return true;
if (XBugType != YBugType)
return false;
// Next, compare by bug description.
- llvm::StringRef XDesc = X->getDescription();
- llvm::StringRef YDesc = Y->getDescription();
+ StringRef XDesc = X->getDescription();
+ StringRef YDesc = Y->getDescription();
if (XDesc < YDesc)
return true;
if (XDesc != YDesc)
@@ -65,23 +60,23 @@ struct CompareDiagnostics {
}
namespace {
- class PlistDiagnostics : public PathDiagnosticClient {
+ class PlistDiagnostics : public PathDiagnosticConsumer {
std::vector<const PathDiagnostic*> BatchedDiags;
const std::string OutputFile;
const LangOptions &LangOpts;
- llvm::OwningPtr<PathDiagnosticClient> SubPD;
+ llvm::OwningPtr<PathDiagnosticConsumer> SubPD;
bool flushed;
public:
PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
- PathDiagnosticClient *subPD);
+ PathDiagnosticConsumer *subPD);
~PlistDiagnostics() { FlushDiagnostics(NULL); }
- void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade);
+ void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade);
- void HandlePathDiagnostic(const PathDiagnostic* D);
+ void HandlePathDiagnosticImpl(const PathDiagnostic* D);
- virtual llvm::StringRef getName() const {
+ virtual StringRef getName() const {
return "PlistDiagnostics";
}
@@ -94,27 +89,27 @@ namespace {
PlistDiagnostics::PlistDiagnostics(const std::string& output,
const LangOptions &LO,
- PathDiagnosticClient *subPD)
+ PathDiagnosticConsumer *subPD)
: OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {}
-PathDiagnosticClient*
-ento::createPlistDiagnosticClient(const std::string& s, const Preprocessor &PP,
- PathDiagnosticClient *subPD) {
+PathDiagnosticConsumer*
+ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP,
+ PathDiagnosticConsumer *subPD) {
return new PlistDiagnostics(s, PP.getLangOptions(), subPD);
}
-PathDiagnosticClient::PathGenerationScheme
+PathDiagnosticConsumer::PathGenerationScheme
PlistDiagnostics::getGenerationScheme() const {
- if (const PathDiagnosticClient *PD = SubPD.get())
+ if (const PathDiagnosticConsumer *PD = SubPD.get())
return PD->getGenerationScheme();
return Extensive;
}
-static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V,
+static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V,
const SourceManager* SM, SourceLocation L) {
- FileID FID = SM->getFileID(SM->getInstantiationLoc(L));
+ FileID FID = SM->getFileID(SM->getExpansionLoc(L));
FIDMap::iterator I = FIDs.find(FID);
if (I != FIDs.end()) return;
FIDs[FID] = V.size();
@@ -123,23 +118,23 @@ static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V,
static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM,
SourceLocation L) {
- FileID FID = SM.getFileID(SM.getInstantiationLoc(L));
+ FileID FID = SM.getFileID(SM.getExpansionLoc(L));
FIDMap::const_iterator I = FIDs.find(FID);
assert(I != FIDs.end());
return I->second;
}
-static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) {
+static raw_ostream &Indent(raw_ostream &o, const unsigned indent) {
for (unsigned i = 0; i < indent; ++i) o << ' ';
return o;
}
-static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
+static void EmitLocation(raw_ostream &o, const SourceManager &SM,
const LangOptions &LangOpts,
SourceLocation L, const FIDMap &FM,
unsigned indent, bool extend = false) {
- FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM));
+ FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM));
// Add in the length of the token, so that we cover multi-char tokens.
unsigned offset =
@@ -147,22 +142,22 @@ static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
Indent(o, indent) << "<dict>\n";
Indent(o, indent) << " <key>line</key><integer>"
- << Loc.getInstantiationLineNumber() << "</integer>\n";
+ << Loc.getExpansionLineNumber() << "</integer>\n";
Indent(o, indent) << " <key>col</key><integer>"
- << Loc.getInstantiationColumnNumber() + offset << "</integer>\n";
+ << Loc.getExpansionColumnNumber() + offset << "</integer>\n";
Indent(o, indent) << " <key>file</key><integer>"
<< GetFID(FM, SM, Loc) << "</integer>\n";
Indent(o, indent) << "</dict>\n";
}
-static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
+static void EmitLocation(raw_ostream &o, const SourceManager &SM,
const LangOptions &LangOpts,
const PathDiagnosticLocation &L, const FIDMap& FM,
unsigned indent, bool extend = false) {
EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend);
}
-static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM,
+static void EmitRange(raw_ostream &o, const SourceManager &SM,
const LangOptions &LangOpts,
PathDiagnosticRange R, const FIDMap &FM,
unsigned indent) {
@@ -172,7 +167,7 @@ static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM,
Indent(o, indent) << "</array>\n";
}
-static llvm::raw_ostream& EmitString(llvm::raw_ostream& o,
+static raw_ostream &EmitString(raw_ostream &o,
const std::string& s) {
o << "<string>";
for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) {
@@ -190,7 +185,7 @@ static llvm::raw_ostream& EmitString(llvm::raw_ostream& o,
return o;
}
-static void ReportControlFlow(llvm::raw_ostream& o,
+static void ReportControlFlow(raw_ostream &o,
const PathDiagnosticControlFlowPiece& P,
const FIDMap& FM,
const SourceManager &SM,
@@ -233,7 +228,7 @@ static void ReportControlFlow(llvm::raw_ostream& o,
Indent(o, indent) << "</dict>\n";
}
-static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
+static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM,
const SourceManager &SM,
const LangOptions &LangOpts,
@@ -280,7 +275,7 @@ static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
Indent(o, indent); o << "</dict>\n";
}
-static void ReportMacro(llvm::raw_ostream& o,
+static void ReportMacro(raw_ostream &o,
const PathDiagnosticMacroPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts,
@@ -304,7 +299,7 @@ static void ReportMacro(llvm::raw_ostream& o,
}
}
-static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
+static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts) {
@@ -326,7 +321,7 @@ static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
}
}
-void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
+void PlistDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
if (!D)
return;
@@ -342,7 +337,7 @@ void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
BatchedDiags.push_back(D);
}
-void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
+void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string>
*FilesMade) {
if (flushed)
@@ -358,7 +353,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
// Build up a set of FIDs that we use by scanning the locations and
// ranges of the diagnostics.
FIDMap FM;
- llvm::SmallVector<FileID, 10> Fids;
+ SmallVector<FileID, 10> Fids;
const SourceManager* SM = 0;
if (!BatchedDiags.empty())
@@ -401,7 +396,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
" <key>files</key>\n"
" <array>\n";
- for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
+ for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
I!=E; ++I) {
o << " ";
EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n';
@@ -444,7 +439,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
// Output the diagnostic to the sub-diagnostic client, if any.
if (SubPD) {
SubPD->HandlePathDiagnostic(OwnedD.take());
- llvm::SmallVector<std::string, 1> SubFilesMade;
+ SmallVector<std::string, 1> SubFilesMade;
SubPD->FlushDiagnostics(SubFilesMade);
if (!SubFilesMade.empty()) {
diff --git a/lib/StaticAnalyzer/Core/GRState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 0f6ff1e..73788cc 100644
--- a/lib/StaticAnalyzer/Core/GRState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -1,4 +1,4 @@
-//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=//
+//= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--=
//
// The LLVM Compiler Infrastructure
//
@@ -7,15 +7,14 @@
//
//===----------------------------------------------------------------------===//
//
-// This file implements GRState and GRStateManager.
+// This file implements ProgramState and ProgramStateManager.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/CFG.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -25,7 +24,7 @@ using namespace ento;
// FIXME: Move this elsewhere.
ConstraintManager::~ConstraintManager() {}
-GRState::GRState(GRStateManager *mgr, const Environment& env,
+ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env,
StoreRef st, GenericDataMap gdm)
: stateMgr(mgr),
Env(env),
@@ -35,7 +34,7 @@ GRState::GRState(GRStateManager *mgr, const Environment& env,
stateMgr->getStoreManager().incrementReferenceCount(store);
}
-GRState::GRState(const GRState& RHS)
+ProgramState::ProgramState(const ProgramState &RHS)
: llvm::FoldingSetNode(),
stateMgr(RHS.stateMgr),
Env(RHS.Env),
@@ -45,23 +44,19 @@ GRState::GRState(const GRState& RHS)
stateMgr->getStoreManager().incrementReferenceCount(store);
}
-GRState::~GRState() {
+ProgramState::~ProgramState() {
if (store)
stateMgr->getStoreManager().decrementReferenceCount(store);
}
-GRStateManager::~GRStateManager() {
- for (std::vector<GRState::Printer*>::iterator I=Printers.begin(),
- E=Printers.end(); I!=E; ++I)
- delete *I;
-
+ProgramStateManager::~ProgramStateManager() {
for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end();
I!=E; ++I)
I->second.second(I->second.first);
}
-const GRState*
-GRStateManager::removeDeadBindings(const GRState* state,
+const ProgramState*
+ProgramStateManager::removeDeadBindings(const ProgramState *state,
const StackFrameContext *LCtx,
SymbolReaper& SymReaper) {
@@ -71,23 +66,23 @@ GRStateManager::removeDeadBindings(const GRState* state,
// those around. This code more than likely can be made faster, and the
// frequency of which this method is called should be experimented with
// for optimum performance.
- llvm::SmallVector<const MemRegion*, 10> RegionRoots;
- GRState NewState = *state;
+ ProgramState NewState = *state;
- NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper,
- state, RegionRoots);
+ NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, state);
// Clean up the store.
- NewState.setStore(StoreMgr->removeDeadBindings(NewState.getStore(), LCtx,
- SymReaper, RegionRoots));
- state = getPersistentState(NewState);
- return ConstraintMgr->removeDeadBindings(state, SymReaper);
+ StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx,
+ SymReaper);
+ NewState.setStore(newStore);
+ SymReaper.setReapedStore(newStore);
+
+ return getPersistentState(NewState);
}
-const GRState *GRStateManager::MarshalState(const GRState *state,
+const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state,
const StackFrameContext *InitLoc) {
// make up an empty state for now.
- GRState State(this,
+ ProgramState State(this,
EnvMgr.getInitialEnvironment(),
StoreMgr->getInitialStore(InitLoc),
GDMFactory.getEmptyMap());
@@ -95,7 +90,7 @@ const GRState *GRStateManager::MarshalState(const GRState *state,
return getPersistentState(State);
}
-const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL,
+const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL,
const LocationContext *LC,
SVal V) const {
const StoreRef &newStore =
@@ -103,21 +98,21 @@ const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL,
return makeWithStore(newStore);
}
-const GRState *GRState::bindDecl(const VarRegion* VR, SVal IVal) const {
+const ProgramState *ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const {
const StoreRef &newStore =
getStateManager().StoreMgr->BindDecl(getStore(), VR, IVal);
return makeWithStore(newStore);
}
-const GRState *GRState::bindDeclWithNoInit(const VarRegion* VR) const {
+const ProgramState *ProgramState::bindDeclWithNoInit(const VarRegion* VR) const {
const StoreRef &newStore =
getStateManager().StoreMgr->BindDeclWithNoInit(getStore(), VR);
return makeWithStore(newStore);
}
-const GRState *GRState::bindLoc(Loc LV, SVal V) const {
- GRStateManager &Mgr = getStateManager();
- const GRState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
+const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const {
+ ProgramStateManager &Mgr = getStateManager();
+ const ProgramState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
LV, V));
const MemRegion *MR = LV.getAsRegion();
if (MR && Mgr.getOwningEngine())
@@ -126,56 +121,53 @@ const GRState *GRState::bindLoc(Loc LV, SVal V) const {
return newState;
}
-const GRState *GRState::bindDefault(SVal loc, SVal V) const {
- GRStateManager &Mgr = getStateManager();
+const ProgramState *ProgramState::bindDefault(SVal loc, SVal V) const {
+ ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
- const GRState *new_state = makeWithStore(newStore);
+ const ProgramState *new_state = makeWithStore(newStore);
return Mgr.getOwningEngine() ?
Mgr.getOwningEngine()->processRegionChange(new_state, R) :
new_state;
}
-const GRState *GRState::invalidateRegions(const MemRegion * const *Begin,
- const MemRegion * const *End,
- const Expr *E, unsigned Count,
- StoreManager::InvalidatedSymbols *IS,
- bool invalidateGlobals) const {
+const ProgramState *
+ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions,
+ const Expr *E, unsigned Count,
+ StoreManager::InvalidatedSymbols *IS,
+ bool invalidateGlobals) const {
if (!IS) {
StoreManager::InvalidatedSymbols invalidated;
- return invalidateRegionsImpl(Begin, End, E, Count,
- invalidated, invalidateGlobals);
+ return invalidateRegionsImpl(Regions, E, Count,
+ invalidated, invalidateGlobals);
}
- return invalidateRegionsImpl(Begin, End, E, Count, *IS, invalidateGlobals);
+ return invalidateRegionsImpl(Regions, E, Count, *IS, invalidateGlobals);
}
-const GRState *
-GRState::invalidateRegionsImpl(const MemRegion * const *Begin,
- const MemRegion * const *End,
- const Expr *E, unsigned Count,
- StoreManager::InvalidatedSymbols &IS,
- bool invalidateGlobals) const {
- GRStateManager &Mgr = getStateManager();
+const ProgramState *
+ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions,
+ const Expr *E, unsigned Count,
+ StoreManager::InvalidatedSymbols &IS,
+ bool invalidateGlobals) const {
+ ProgramStateManager &Mgr = getStateManager();
SubEngine* Eng = Mgr.getOwningEngine();
if (Eng && Eng->wantsRegionChangeUpdate(this)) {
- StoreManager::InvalidatedRegions Regions;
+ StoreManager::InvalidatedRegions Invalidated;
const StoreRef &newStore
- = Mgr.StoreMgr->invalidateRegions(getStore(), Begin, End, E, Count, IS,
- invalidateGlobals, &Regions);
- const GRState *newState = makeWithStore(newStore);
- return Eng->processRegionChanges(newState, &IS,
- &Regions.front(),
- &Regions.back()+1);
+ = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS,
+ invalidateGlobals, &Invalidated);
+ const ProgramState *newState = makeWithStore(newStore);
+ return Eng->processRegionChanges(newState, &IS, Regions, Invalidated);
}
const StoreRef &newStore =
- Mgr.StoreMgr->invalidateRegions(getStore(), Begin, End, E, Count, IS,
+ Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS,
invalidateGlobals, NULL);
return makeWithStore(newStore);
}
-const GRState *GRState::unbindLoc(Loc LV) const {
+const ProgramState *ProgramState::unbindLoc(Loc LV) const {
assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead.");
Store OldStore = getStore();
@@ -187,20 +179,20 @@ const GRState *GRState::unbindLoc(Loc LV) const {
return makeWithStore(newStore);
}
-const GRState *GRState::enterStackFrame(const StackFrameContext *frame) const {
+const ProgramState *ProgramState::enterStackFrame(const StackFrameContext *frame) const {
const StoreRef &new_store =
getStateManager().StoreMgr->enterStackFrame(this, frame);
return makeWithStore(new_store);
}
-SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const {
+SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
// We only want to do fetches from regions that we can actually bind
// values. For example, SymbolicRegions of type 'id<...>' cannot
// have direct bindings (but their can be bindings on their subregions).
if (!R->isBoundable())
return UnknownVal();
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
QualType T = TR->getValueType();
if (Loc::isLocType(T) || T->isIntegerType())
return getSVal(R);
@@ -209,7 +201,7 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const {
return UnknownVal();
}
-SVal GRState::getSVal(Loc location, QualType T) const {
+SVal ProgramState::getSVal(Loc location, QualType T) const {
SVal V = getRawSVal(cast<Loc>(location), T);
// If 'V' is a symbolic value that is *perfectly* constrained to
@@ -246,18 +238,18 @@ SVal GRState::getSVal(Loc location, QualType T) const {
return V;
}
-const GRState *GRState::BindExpr(const Stmt* S, SVal V, bool Invalidate) const{
+const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidate) const{
Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V,
Invalidate);
if (NewEnv == Env)
return this;
- GRState NewSt = *this;
+ ProgramState NewSt = *this;
NewSt.Env = NewEnv;
return getStateManager().getPersistentState(NewSt);
}
-const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location,
+const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal location,
SVal V) const {
Environment NewEnv =
getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V);
@@ -265,12 +257,12 @@ const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location,
if (NewEnv == Env)
return this;
- GRState NewSt = *this;
+ ProgramState NewSt = *this;
NewSt.Env = NewEnv;
return getStateManager().getPersistentState(NewSt);
}
-const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx,
+const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
DefinedOrUnknownSVal UpperBound,
bool Assumption) const {
if (Idx.isUnknown() || UpperBound.isUnknown())
@@ -279,7 +271,7 @@ const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx,
// Build an expression for 0 <= Idx < UpperBound.
// This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed.
// FIXME: This should probably be part of SValBuilder.
- GRStateManager &SM = getStateManager();
+ ProgramStateManager &SM = getStateManager();
SValBuilder &svalBuilder = SM.getSValBuilder();
ASTContext &Ctx = svalBuilder.getContext();
@@ -315,8 +307,8 @@ const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx,
return CM.assume(this, cast<DefinedSVal>(inBound), Assumption);
}
-const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) {
- GRState State(this,
+const ProgramState *ProgramStateManager::getInitialState(const LocationContext *InitLoc) {
+ ProgramState State(this,
EnvMgr.getInitialEnvironment(),
StoreMgr->getInitialStore(InitLoc),
GDMFactory.getEmptyMap());
@@ -324,49 +316,57 @@ const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) {
return getPersistentState(State);
}
-void GRStateManager::recycleUnusedStates() {
- for (std::vector<GRState*>::iterator i = recentlyAllocatedStates.begin(),
+void ProgramStateManager::recycleUnusedStates() {
+ for (std::vector<ProgramState*>::iterator i = recentlyAllocatedStates.begin(),
e = recentlyAllocatedStates.end(); i != e; ++i) {
- GRState *state = *i;
+ ProgramState *state = *i;
if (state->referencedByExplodedNode())
continue;
StateSet.RemoveNode(state);
freeStates.push_back(state);
- state->~GRState();
+ state->~ProgramState();
}
recentlyAllocatedStates.clear();
}
-const GRState* GRStateManager::getPersistentState(GRState& State) {
+const ProgramState *ProgramStateManager::getPersistentStateWithGDM(
+ const ProgramState *FromState,
+ const ProgramState *GDMState) {
+ ProgramState NewState = *FromState;
+ NewState.GDM = GDMState->GDM;
+ return getPersistentState(NewState);
+}
+
+const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) {
llvm::FoldingSetNodeID ID;
State.Profile(ID);
- void* InsertPos;
+ void *InsertPos;
- if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos))
+ if (ProgramState *I = StateSet.FindNodeOrInsertPos(ID, InsertPos))
return I;
- GRState *newState = 0;
+ ProgramState *newState = 0;
if (!freeStates.empty()) {
newState = freeStates.back();
freeStates.pop_back();
}
else {
- newState = (GRState*) Alloc.Allocate<GRState>();
+ newState = (ProgramState*) Alloc.Allocate<ProgramState>();
}
- new (newState) GRState(State);
+ new (newState) ProgramState(State);
StateSet.InsertNode(newState, InsertPos);
recentlyAllocatedStates.push_back(newState);
return newState;
}
-const GRState* GRState::makeWithStore(const StoreRef &store) const {
- GRState NewSt = *this;
+const ProgramState *ProgramState::makeWithStore(const StoreRef &store) const {
+ ProgramState NewSt = *this;
NewSt.setStore(store);
return getStateManager().getPersistentState(NewSt);
}
-void GRState::setStore(const StoreRef &newStore) {
+void ProgramState::setStore(const StoreRef &newStore) {
Store newStoreStore = newStore.getStore();
if (newStoreStore)
stateMgr->getStoreManager().incrementReferenceCount(newStoreStore);
@@ -384,11 +384,11 @@ static bool IsEnvLoc(const Stmt *S) {
return (bool) (((uintptr_t) S) & 0x1);
}
-void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
- const char* sep) const {
+void ProgramState::print(raw_ostream &Out, CFG &C,
+ const char *NL, const char *Sep) const {
// Print the store.
- GRStateManager &Mgr = getStateManager();
- Mgr.getStoreManager().print(getStore(), Out, nl, sep);
+ ProgramStateManager &Mgr = getStateManager();
+ Mgr.getStoreManager().print(getStore(), Out, NL, Sep);
// Print Subexpression bindings.
bool isFirst = true;
@@ -399,10 +399,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
continue;
if (isFirst) {
- Out << nl << nl << "Sub-Expressions:" << nl;
+ Out << NL << NL << "Sub-Expressions:" << NL;
isFirst = false;
+ } else {
+ Out << NL;
}
- else { Out << nl; }
Out << " (" << (void*) I.getKey() << ") ";
LangOptions LO; // FIXME.
@@ -418,10 +419,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
continue;
if (isFirst) {
- Out << nl << nl << "Block-level Expressions:" << nl;
+ Out << NL << NL << "Block-level Expressions:" << NL;
isFirst = false;
+ } else {
+ Out << NL;
}
- else { Out << nl; }
Out << " (" << (void*) I.getKey() << ") ";
LangOptions LO; // FIXME.
@@ -437,10 +439,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
continue;
if (isFirst) {
- Out << nl << nl << "Load/store locations:" << nl;
+ Out << NL << NL << "Load/store locations:" << NL;
isFirst = false;
+ } else {
+ Out << NL;
}
- else { Out << nl; }
const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1));
@@ -450,20 +453,17 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl,
Out << " : " << I.getData();
}
- Mgr.getConstraintManager().print(this, Out, nl, sep);
+ Mgr.getConstraintManager().print(this, Out, NL, Sep);
// Print checker-specific data.
- for (std::vector<Printer*>::iterator I = Mgr.Printers.begin(),
- E = Mgr.Printers.end(); I != E; ++I) {
- (*I)->Print(Out, this, nl, sep);
- }
+ Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
}
-void GRState::printDOT(llvm::raw_ostream& Out, CFG &C) const {
+void ProgramState::printDOT(raw_ostream &Out, CFG &C) const {
print(Out, C, "\\l", "\\|");
}
-void GRState::printStdErr(CFG &C) const {
+void ProgramState::printStdErr(CFG &C) const {
print(llvm::errs(), C);
}
@@ -471,13 +471,13 @@ void GRState::printStdErr(CFG &C) const {
// Generic Data Map.
//===----------------------------------------------------------------------===//
-void* const* GRState::FindGDM(void* K) const {
+void *const* ProgramState::FindGDM(void *K) const {
return GDM.lookup(K);
}
void*
-GRStateManager::FindGDMContext(void* K,
- void* (*CreateContext)(llvm::BumpPtrAllocator&),
+ProgramStateManager::FindGDMContext(void *K,
+ void *(*CreateContext)(llvm::BumpPtrAllocator&),
void (*DeleteContext)(void*)) {
std::pair<void*, void (*)(void*)>& p = GDMContexts[K];
@@ -489,58 +489,30 @@ GRStateManager::FindGDMContext(void* K,
return p.first;
}
-const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){
- GRState::GenericDataMap M1 = St->getGDM();
- GRState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data);
+const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Key, void *Data){
+ ProgramState::GenericDataMap M1 = St->getGDM();
+ ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data);
if (M1 == M2)
return St;
- GRState NewSt = *St;
+ ProgramState NewSt = *St;
NewSt.GDM = M2;
return getPersistentState(NewSt);
}
-const GRState *GRStateManager::removeGDM(const GRState *state, void *Key) {
- GRState::GenericDataMap OldM = state->getGDM();
- GRState::GenericDataMap NewM = GDMFactory.remove(OldM, Key);
+const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, void *Key) {
+ ProgramState::GenericDataMap OldM = state->getGDM();
+ ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key);
if (NewM == OldM)
return state;
- GRState NewState = *state;
+ ProgramState NewState = *state;
NewState.GDM = NewM;
return getPersistentState(NewState);
}
-//===----------------------------------------------------------------------===//
-// Utility.
-//===----------------------------------------------------------------------===//
-
-namespace {
-class ScanReachableSymbols : public SubRegionMap::Visitor {
- typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy;
-
- VisitedRegionsTy visited;
- const GRState *state;
- SymbolVisitor &visitor;
- llvm::OwningPtr<SubRegionMap> SRM;
-public:
-
- ScanReachableSymbols(const GRState *st, SymbolVisitor& v)
- : state(st), visitor(v) {}
-
- bool scan(nonloc::CompoundVal val);
- bool scan(SVal val);
- bool scan(const MemRegion *R);
-
- // From SubRegionMap::Visitor.
- bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) {
- return scan(SubRegion);
- }
-};
-}
-
bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
if (!scan(*I))
@@ -549,6 +521,33 @@ bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
return true;
}
+bool ScanReachableSymbols::scan(const SymExpr *sym) {
+ unsigned &isVisited = visited[sym];
+ if (isVisited)
+ return true;
+ isVisited = 1;
+
+ if (const SymbolData *sData = dyn_cast<SymbolData>(sym))
+ if (!visitor.VisitSymbol(sData))
+ return false;
+
+ switch (sym->getKind()) {
+ case SymExpr::RegionValueKind:
+ case SymExpr::ConjuredKind:
+ case SymExpr::DerivedKind:
+ case SymExpr::ExtentKind:
+ case SymExpr::MetadataKind:
+ break;
+ case SymExpr::SymIntKind:
+ return scan(cast<SymIntExpr>(sym)->getLHS());
+ case SymExpr::SymSymKind: {
+ const SymSymExpr *x = cast<SymSymExpr>(sym);
+ return scan(x->getLHS()) && scan(x->getRHS());
+ }
+ }
+ return true;
+}
+
bool ScanReachableSymbols::scan(SVal val) {
if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val))
return scan(X->getRegion());
@@ -557,7 +556,10 @@ bool ScanReachableSymbols::scan(SVal val) {
return scan(X->getLoc());
if (SymbolRef Sym = val.getAsSymbol())
- return visitor.VisitSymbol(Sym);
+ return scan(Sym);
+
+ if (const SymExpr *Sym = val.getAsSymbolicExpression())
+ return scan(Sym);
if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val))
return scan(*X);
@@ -566,10 +568,13 @@ bool ScanReachableSymbols::scan(SVal val) {
}
bool ScanReachableSymbols::scan(const MemRegion *R) {
- if (isa<MemSpaceRegion>(R) || visited.count(R))
+ if (isa<MemSpaceRegion>(R))
return true;
-
- visited.insert(R);
+
+ unsigned &isVisited = visited[R];
+ if (isVisited)
+ return true;
+ isVisited = 1;
// If this is a symbolic region, visit the symbol for the region.
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
@@ -593,12 +598,12 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
return SRM->iterSubRegions(R, *this);
}
-bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {
+bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {
ScanReachableSymbols S(this, visitor);
return S.scan(val);
}
-bool GRState::scanReachableSymbols(const SVal *I, const SVal *E,
+bool ProgramState::scanReachableSymbols(const SVal *I, const SVal *E,
SymbolVisitor &visitor) const {
ScanReachableSymbols S(this, visitor);
for ( ; I != E; ++I) {
@@ -608,7 +613,7 @@ bool GRState::scanReachableSymbols(const SVal *I, const SVal *E,
return true;
}
-bool GRState::scanReachableSymbols(const MemRegion * const *I,
+bool ProgramState::scanReachableSymbols(const MemRegion * const *I,
const MemRegion * const *E,
SymbolVisitor &visitor) const {
ScanReachableSymbols S(this, visitor);
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 389fff5..9337788 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -8,14 +8,13 @@
//===----------------------------------------------------------------------===//
//
// This file defines RangeConstraintManager, a class that tracks simple
-// equality and inequality constraints on symbolic values of GRState.
+// equality and inequality constraints on symbolic values of ProgramState.
//
//===----------------------------------------------------------------------===//
#include "SimpleConstraintManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
@@ -170,7 +169,7 @@ public:
return newRanges;
}
- void print(llvm::raw_ostream &os) const {
+ void print(raw_ostream &os) const {
bool isFirst = true;
os << "{ ";
for (iterator i = begin(), e = end(); i != e; ++i) {
@@ -196,55 +195,55 @@ typedef llvm::ImmutableMap<SymbolRef,RangeSet> ConstraintRangeTy;
namespace clang {
namespace ento {
template<>
-struct GRStateTrait<ConstraintRange>
- : public GRStatePartialTrait<ConstraintRangeTy> {
- static inline void* GDMIndex() { return &ConstraintRangeIndex; }
+struct ProgramStateTrait<ConstraintRange>
+ : public ProgramStatePartialTrait<ConstraintRangeTy> {
+ static inline void *GDMIndex() { return &ConstraintRangeIndex; }
};
}
}
namespace {
class RangeConstraintManager : public SimpleConstraintManager{
- RangeSet GetRange(const GRState *state, SymbolRef sym);
+ RangeSet GetRange(const ProgramState *state, SymbolRef sym);
public:
RangeConstraintManager(SubEngine &subengine)
: SimpleConstraintManager(subengine) {}
- const GRState *assumeSymNE(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const GRState *assumeSymEQ(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const GRState *assumeSymLT(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const GRState *assumeSymGT(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const GRState *assumeSymGE(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const GRState *assumeSymLE(const GRState* state, SymbolRef sym,
+ const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment);
- const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const;
+ const llvm::APSInt* getSymVal(const ProgramState *St, SymbolRef sym) const;
// FIXME: Refactor into SimpleConstraintManager?
- bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const {
+ bool isEqual(const ProgramState *St, SymbolRef sym, const llvm::APSInt& V) const {
const llvm::APSInt *i = getSymVal(St, sym);
return i ? *i == V : false;
}
- const GRState* removeDeadBindings(const GRState* St, SymbolReaper& SymReaper);
+ const ProgramState *removeDeadBindings(const ProgramState *St, SymbolReaper& SymReaper);
- void print(const GRState* St, llvm::raw_ostream& Out,
+ void print(const ProgramState *St, raw_ostream &Out,
const char* nl, const char *sep);
private:
@@ -253,12 +252,12 @@ private:
} // end anonymous namespace
-ConstraintManager* ento::CreateRangeConstraintManager(GRStateManager&,
+ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&,
SubEngine &subeng) {
return new RangeConstraintManager(subeng);
}
-const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St,
+const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St,
SymbolRef sym) const {
const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym);
return T ? T->getConcreteValue() : NULL;
@@ -266,8 +265,8 @@ const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St,
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
-const GRState*
-RangeConstraintManager::removeDeadBindings(const GRState* state,
+const ProgramState*
+RangeConstraintManager::removeDeadBindings(const ProgramState *state,
SymbolReaper& SymReaper) {
ConstraintRangeTy CR = state->get<ConstraintRange>();
@@ -283,7 +282,7 @@ RangeConstraintManager::removeDeadBindings(const GRState* state,
}
RangeSet
-RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) {
+RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) {
if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym))
return *V;
@@ -306,8 +305,8 @@ RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) {
// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1,
// UINT_MAX, 0, 1, and 2.
-const GRState*
-RangeConstraintManager::assumeSymNE(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -323,8 +322,8 @@ RangeConstraintManager::assumeSymNE(const GRState* state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const GRState*
-RangeConstraintManager::assumeSymEQ(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
// [Int-Adjustment, Int-Adjustment]
@@ -334,8 +333,8 @@ RangeConstraintManager::assumeSymEQ(const GRState* state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const GRState*
-RangeConstraintManager::assumeSymLT(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -355,8 +354,8 @@ RangeConstraintManager::assumeSymLT(const GRState* state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const GRState*
-RangeConstraintManager::assumeSymGT(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -376,8 +375,8 @@ RangeConstraintManager::assumeSymGT(const GRState* state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const GRState*
-RangeConstraintManager::assumeSymGE(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -398,8 +397,8 @@ RangeConstraintManager::assumeSymGE(const GRState* state, SymbolRef sym,
return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New);
}
-const GRState*
-RangeConstraintManager::assumeSymLE(const GRState* state, SymbolRef sym,
+const ProgramState*
+RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& Int,
const llvm::APSInt& Adjustment) {
BasicValueFactory &BV = state->getBasicVals();
@@ -424,7 +423,7 @@ RangeConstraintManager::assumeSymLE(const GRState* state, SymbolRef sym,
// Pretty-printing.
//===------------------------------------------------------------------------===/
-void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out,
+void RangeConstraintManager::print(const ProgramState *St, raw_ostream &Out,
const char* nl, const char *sep) {
ConstraintRangeTy Ranges = St->get<ConstraintRange>();
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index 23dd641..4b76cf1 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -20,8 +20,8 @@
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Basic/TargetInfo.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -94,7 +94,7 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
namespace llvm {
static inline
- llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) {
+ raw_ostream &operator<<(raw_ostream &os, BindingKey K) {
os << '(' << K.getRegion() << ',' << K.getOffset()
<< ',' << (K.isDirect() ? "direct" : "default")
<< ')';
@@ -157,7 +157,7 @@ public:
return false;
}
- void process(llvm::SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R);
+ void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R);
~RegionStoreSubRegionMap() {}
@@ -183,7 +183,7 @@ public:
};
void
-RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL,
+RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL,
const SubRegion *R) {
const MemRegion *superR = R->getSuperRegion();
if (add(superR, R))
@@ -196,7 +196,7 @@ class RegionStoreManager : public StoreManager {
RegionBindings::Factory RBFactory;
public:
- RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f)
+ RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f)
: StoreManager(mgr),
Features(f),
RBFactory(mgr.getAllocator()) {}
@@ -236,13 +236,11 @@ public:
// Binding values to regions.
//===-------------------------------------------------------------------===//
- StoreRef invalidateRegions(Store store,
- const MemRegion * const *Begin,
- const MemRegion * const *End,
+ StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions,
const Expr *E, unsigned Count,
InvalidatedSymbols &IS,
bool invalidateGlobals,
- InvalidatedRegions *Regions);
+ InvalidatedRegions *Invalidated);
public: // Made public for helper classes.
@@ -278,7 +276,7 @@ public: // Part of public interface to class.
return StoreRef(addBinding(B, R, BindingKey::Default, V).getRootWithoutRetain(), *this);
}
- StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr* CL,
+ StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr *CL,
const LocationContext *LC, SVal V);
StoreRef BindDecl(Store store, const VarRegion *VR, SVal InitVal);
@@ -288,9 +286,9 @@ public: // Part of public interface to class.
}
/// BindStruct - Bind a compound value to a structure.
- StoreRef BindStruct(Store store, const TypedRegion* R, SVal V);
+ StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V);
- StoreRef BindArray(Store store, const TypedRegion* 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);
@@ -307,6 +305,8 @@ public: // Part of public interface to class.
void decrementReferenceCount(Store store) {
GetRegionBindings(store).manualRelease();
}
+
+ bool includedInBindings(Store store, const MemRegion *region) const;
//===------------------------------------------------------------------===//
// Loading values from regions.
@@ -333,9 +333,9 @@ public: // Part of public interface to class.
SVal RetrieveVar(Store store, const VarRegion *R);
- SVal RetrieveLazySymbol(const TypedRegion *R);
+ SVal RetrieveLazySymbol(const TypedValueRegion *R);
- SVal RetrieveFieldOrElementCommon(Store store, const TypedRegion *R,
+ SVal RetrieveFieldOrElementCommon(Store store, const TypedValueRegion *R,
QualType Ty, const MemRegion *superR);
SVal RetrieveLazyBinding(const MemRegion *lazyBindingRegion,
@@ -346,15 +346,16 @@ public: // Part of public interface to class.
/// struct s x, y;
/// x = y;
/// y's value is retrieved by this method.
- SVal RetrieveStruct(Store store, const TypedRegion* R);
+ SVal RetrieveStruct(Store store, const TypedValueRegion* R);
- SVal RetrieveArray(Store store, const TypedRegion* R);
+ SVal RetrieveArray(Store store, const TypedValueRegion* R);
/// Used to lazily generate derived symbols for bindings that are defined
/// implicitly by default bindings in a super region.
Optional<SVal> RetrieveDerivedDefaultValue(RegionBindings B,
const MemRegion *superR,
- const TypedRegion *R, QualType Ty);
+ const TypedValueRegion *R,
+ QualType Ty);
/// Get the state and region whose binding this region R corresponds to.
std::pair<Store, const MemRegion*>
@@ -371,17 +372,17 @@ public: // Part of public interface to class.
/// removeDeadBindings - Scans the RegionStore of 'state' for dead values.
/// It returns a new Store with these values removed.
StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
- llvm::SmallVectorImpl<const MemRegion*>& RegionRoots);
+ SymbolReaper& SymReaper);
- StoreRef enterStackFrame(const GRState *state, const StackFrameContext *frame);
+ StoreRef enterStackFrame(const ProgramState *state,
+ const StackFrameContext *frame);
//===------------------------------------------------------------------===//
// Region "extents".
//===------------------------------------------------------------------===//
// FIXME: This method will soon be eliminated; see the note in Store.h.
- DefinedOrUnknownSVal getSizeInElements(const GRState *state,
+ DefinedOrUnknownSVal getSizeInElements(const ProgramState *state,
const MemRegion* R, QualType EleTy);
//===------------------------------------------------------------------===//
@@ -392,7 +393,7 @@ public: // Part of public interface to class.
return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store));
}
- void print(Store store, llvm::raw_ostream& Out, const char* nl,
+ void print(Store store, raw_ostream &Out, const char* nl,
const char *sep);
void iterBindings(Store store, BindingsHandler& f) {
@@ -416,12 +417,12 @@ public: // Part of public interface to class.
// RegionStore creation.
//===----------------------------------------------------------------------===//
-StoreManager *ento::CreateRegionStoreManager(GRStateManager& StMgr) {
+StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) {
RegionStoreFeatures F = maximal_features_tag();
return new RegionStoreManager(StMgr, F);
}
-StoreManager *ento::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) {
+StoreManager *ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
RegionStoreFeatures F = minimal_features_tag();
F.enableFields(true);
return new RegionStoreManager(StMgr, F);
@@ -433,7 +434,7 @@ RegionStoreManager::getRegionStoreSubRegionMap(Store store) {
RegionBindings B = GetRegionBindings(store);
RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap();
- llvm::SmallVector<const SubRegion*, 10> WL;
+ 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()))
@@ -461,7 +462,7 @@ protected:
typedef BumpVector<BindingKey> RegionCluster;
typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap;
llvm::DenseMap<const RegionCluster*, unsigned> Visited;
- typedef llvm::SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10>
+ typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10>
WorkList;
BumpVectorContext BVC;
@@ -477,7 +478,7 @@ protected:
const bool includeGlobals;
public:
- ClusterAnalysis(RegionStoreManager &rm, GRStateManager &StateMgr,
+ ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr,
RegionBindings b, const bool includeGlobals)
: RM(rm), Ctx(StateMgr.getContext()),
svalBuilder(StateMgr.getSValBuilder()),
@@ -590,7 +591,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker>
StoreManager::InvalidatedRegions *Regions;
public:
invalidateRegionsWorker(RegionStoreManager &rm,
- GRStateManager &stateMgr,
+ ProgramStateManager &stateMgr,
RegionBindings b,
const Expr *ex, unsigned count,
StoreManager::InvalidatedSymbols &is,
@@ -681,7 +682,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
if (!baseR->isBoundable())
return;
- const TypedRegion *TR = cast<TypedRegion>(baseR);
+ const TypedValueRegion *TR = cast<TypedValueRegion>(baseR);
QualType T = TR->getValueType();
// Invalidate the binding.
@@ -718,21 +719,21 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) {
}
StoreRef RegionStoreManager::invalidateRegions(Store store,
- const MemRegion * const *I,
- const MemRegion * const *E,
+ ArrayRef<const MemRegion *> Regions,
const Expr *Ex, unsigned Count,
InvalidatedSymbols &IS,
bool invalidateGlobals,
- InvalidatedRegions *Regions) {
+ InvalidatedRegions *Invalidated) {
invalidateRegionsWorker W(*this, StateMgr,
RegionStoreManager::GetRegionBindings(store),
- Ex, Count, IS, Regions, invalidateGlobals);
+ Ex, Count, IS, Invalidated, invalidateGlobals);
// Scan the bindings and generate the clusters.
W.GenerateClusters();
- // Add I .. E to the worklist.
- for ( ; I != E; ++I)
+ // Add the regions to the worklist.
+ for (ArrayRef<const MemRegion *>::iterator
+ I = Regions.begin(), E = Regions.end(); I != E; ++I)
W.AddToWorkList(*I);
W.RunWorkList();
@@ -752,8 +753,8 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
// Even if there are no bindings in the global scope, we still need to
// record that we touched it.
- if (Regions)
- Regions->push_back(GS);
+ if (Invalidated)
+ Invalidated->push_back(GS);
}
return StoreRef(B.getRootWithoutRetain(), *this);
@@ -763,7 +764,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store,
// Extents for regions.
//===----------------------------------------------------------------------===//
-DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state,
+DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const ProgramState *state,
const MemRegion *R,
QualType EleTy) {
SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder);
@@ -803,7 +804,7 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) {
return UnknownVal();
const MemRegion* R = cast<loc::MemRegionVal>(&Array)->getRegion();
- const TypedRegion* ArrayR = dyn_cast<TypedRegion>(R);
+ const TypedValueRegion* ArrayR = dyn_cast<TypedValueRegion>(R);
if (!ArrayR)
return UnknownVal();
@@ -852,7 +853,7 @@ Optional<SVal> RegionStoreManager::getDirectBinding(RegionBindings B,
Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B,
const MemRegion *R) {
if (R->isBoundable())
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(R))
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R))
if (TR->getValueType()->isUnionType())
return UnknownVal();
@@ -890,13 +891,12 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) {
}
if (isa<CodeTextRegion>(MR)) {
- assert(0 && "Why load from a code text region?");
- return UnknownVal();
+ llvm_unreachable("Why load from a code text region?");
}
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
// instead of 'Loc', and have the other Loc cases handled at a higher level.
- const TypedRegion *R = cast<TypedRegion>(MR);
+ const TypedValueRegion *R = cast<TypedValueRegion>(MR);
QualType RTy = R->getValueType();
// FIXME: We should eventually handle funny addressing. e.g.:
@@ -1042,6 +1042,10 @@ SVal RegionStoreManager::RetrieveElement(Store store,
SVal Idx = R->getIndex();
if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) {
int64_t i = CI->getValue().getSExtValue();
+ // Abort on string underrun. This can be possible by arbitrary
+ // clients of RetrieveElement().
+ if (i < 0)
+ return UndefinedVal();
int64_t byteLength = Str->getByteLength();
// Technically, only i == byteLength is guaranteed to be null.
// However, such overflows should be caught before reaching this point;
@@ -1068,7 +1072,8 @@ SVal RegionStoreManager::RetrieveElement(Store store,
if (!O.getRegion())
return UnknownVal();
- if (const TypedRegion *baseR = dyn_cast_or_null<TypedRegion>(O.getRegion())) {
+ if (const TypedValueRegion *baseR =
+ dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
QualType baseT = baseR->getValueType();
if (baseT->isScalarType()) {
QualType elemT = R->getElementType();
@@ -1106,7 +1111,7 @@ SVal RegionStoreManager::RetrieveField(Store store,
Optional<SVal>
RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B,
const MemRegion *superR,
- const TypedRegion *R,
+ const TypedValueRegion *R,
QualType Ty) {
if (const Optional<SVal> &D = getDefaultBinding(B, superR)) {
@@ -1124,7 +1129,7 @@ RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B,
if (isa<nonloc::LazyCompoundVal>(val))
return Optional<SVal>();
- assert(0 && "Unknown default value");
+ llvm_unreachable("Unknown default value");
}
return Optional<SVal>();
@@ -1140,7 +1145,7 @@ SVal RegionStoreManager::RetrieveLazyBinding(const MemRegion *lazyBindingRegion,
}
SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
- const TypedRegion *R,
+ const TypedValueRegion *R,
QualType Ty,
const MemRegion *superR) {
@@ -1175,7 +1180,8 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store,
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
// Currently we don't reason specially about Clang-style vectors. Check
// if superR is a vector and if so return Unknown.
- if (const TypedRegion *typedSuperR = dyn_cast<TypedRegion>(superR)) {
+ if (const TypedValueRegion *typedSuperR =
+ dyn_cast<TypedValueRegion>(superR)) {
if (typedSuperR->getValueType()->isVectorType())
return UnknownVal();
}
@@ -1264,22 +1270,41 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) {
return UndefinedVal();
}
-SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) {
+SVal RegionStoreManager::RetrieveLazySymbol(const TypedValueRegion *R) {
// All other values are symbolic.
return svalBuilder.getRegionValueSymbolVal(R);
}
-SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) {
+SVal RegionStoreManager::RetrieveStruct(Store store,
+ const TypedValueRegion* R) {
QualType T = R->getValueType();
assert(T->isStructureOrClassType());
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
-SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) {
+SVal RegionStoreManager::RetrieveArray(Store store,
+ const TypedValueRegion * R) {
assert(Ctx.getAsConstantArrayType(R->getValueType()));
return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R);
}
+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;
+ }
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Binding values to regions.
//===----------------------------------------------------------------------===//
@@ -1302,14 +1327,14 @@ 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 TypedRegion* TR = dyn_cast<TypedRegion>(R))
+ if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R))
if (TR->getValueType()->isStructureOrClassType())
return BindStruct(store, TR, V);
if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
if (ER->getIndex().isZeroConstant()) {
- if (const TypedRegion *superR =
- dyn_cast<TypedRegion>(ER->getSuperRegion())) {
+ 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.
@@ -1389,7 +1414,7 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store,
V).getRootWithoutRetain(), *this);
}
-StoreRef RegionStoreManager::BindArray(Store store, const TypedRegion* R,
+StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R,
SVal Init) {
const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType()));
@@ -1448,7 +1473,7 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedRegion* R,
return newStore;
}
-StoreRef RegionStoreManager::BindStruct(Store store, const TypedRegion* R,
+StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R,
SVal V) {
if (!Features.supportsFields())
@@ -1458,9 +1483,9 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedRegion* R,
assert(T->isStructureOrClassType());
const RecordType* RT = T->getAs<RecordType>();
- RecordDecl* RD = RT->getDecl();
+ RecordDecl *RD = RT->getDecl();
- if (!RD->isDefinition())
+ if (!RD->isCompleteDefinition())
return StoreRef(store, *this);
// Handle lazy compound values.
@@ -1611,12 +1636,12 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B,
namespace {
class removeDeadBindingsWorker :
public ClusterAnalysis<removeDeadBindingsWorker> {
- llvm::SmallVector<const SymbolicRegion*, 12> Postponed;
+ SmallVector<const SymbolicRegion*, 12> Postponed;
SymbolReaper &SymReaper;
const StackFrameContext *CurrentLCtx;
public:
- removeDeadBindingsWorker(RegionStoreManager &rm, GRStateManager &stateMgr,
+ removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr,
RegionBindings b, SymbolReaper &symReaper,
const StackFrameContext *LCtx)
: ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b,
@@ -1736,7 +1761,7 @@ bool removeDeadBindingsWorker::UpdatePostponed() {
// having done a scan.
bool changed = false;
- for (llvm::SmallVectorImpl<const SymbolicRegion*>::iterator
+ for (SmallVectorImpl<const SymbolicRegion*>::iterator
I = Postponed.begin(), E = Postponed.end() ; I != E ; ++I) {
if (const SymbolicRegion *SR = cast_or_null<SymbolicRegion>(*I)) {
if (SymReaper.isLive(SR->getSymbol())) {
@@ -1751,17 +1776,16 @@ bool removeDeadBindingsWorker::UpdatePostponed() {
StoreRef RegionStoreManager::removeDeadBindings(Store store,
const StackFrameContext *LCtx,
- SymbolReaper& SymReaper,
- llvm::SmallVectorImpl<const MemRegion*>& RegionRoots)
-{
+ SymbolReaper& SymReaper) {
RegionBindings B = GetRegionBindings(store);
removeDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
W.GenerateClusters();
// Enqueue the region roots onto the worklist.
- for (llvm::SmallVectorImpl<const MemRegion*>::iterator I=RegionRoots.begin(),
- E=RegionRoots.end(); I!=E; ++I)
+ for (SymbolReaper::region_iterator I = SymReaper.region_begin(),
+ E = SymReaper.region_end(); I != E; ++I) {
W.AddToWorkList(*I);
+ }
do W.RunWorkList(); while (W.UpdatePostponed());
@@ -1792,7 +1816,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
}
-StoreRef RegionStoreManager::enterStackFrame(const GRState *state,
+StoreRef RegionStoreManager::enterStackFrame(const ProgramState *state,
const StackFrameContext *frame) {
FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl());
FunctionDecl::param_const_iterator PI = FD->param_begin(),
@@ -1831,7 +1855,7 @@ StoreRef RegionStoreManager::enterStackFrame(const GRState *state,
// Utility methods.
//===----------------------------------------------------------------------===//
-void RegionStoreManager::print(Store store, llvm::raw_ostream& OS,
+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;
diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 71f2b4a..ebf7ae2 100644
--- a/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -15,7 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
using namespace clang;
@@ -70,7 +70,7 @@ SVal SValBuilder::convertToArrayIndex(SVal val) {
}
DefinedOrUnknownSVal
-SValBuilder::getRegionValueSymbolVal(const TypedRegion* region) {
+SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
QualType T = region->getValueType();
if (!SymbolManager::canSymbolicate(T))
@@ -133,7 +133,7 @@ DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
DefinedOrUnknownSVal
SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
- const TypedRegion *region) {
+ const TypedValueRegion *region) {
QualType T = region->getValueType();
if (!SymbolManager::canSymbolicate(T))
@@ -147,7 +147,7 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
return nonloc::SymbolVal(sym);
}
-DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl* func) {
+DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) {
return loc::MemRegionVal(MemMgr.getFunctionTextRegion(func));
}
@@ -162,7 +162,7 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
//===----------------------------------------------------------------------===//
-SVal SValBuilder::evalBinOp(const GRState *state, BinaryOperator::Opcode op,
+SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type) {
if (lhs.isUndef() || rhs.isUndef())
@@ -190,7 +190,7 @@ SVal SValBuilder::evalBinOp(const GRState *state, BinaryOperator::Opcode op,
return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type);
}
-DefinedOrUnknownSVal SValBuilder::evalEQ(const GRState *state,
+DefinedOrUnknownSVal SValBuilder::evalEQ(const ProgramState *state,
DefinedOrUnknownSVal lhs,
DefinedOrUnknownSVal rhs) {
return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs,
@@ -213,8 +213,13 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
return UnknownVal();
// Check for casts from integers to integers.
- if (castTy->isIntegerType() && originalTy->isIntegerType())
- return evalCastFromNonLoc(cast<NonLoc>(val), castTy);
+ if (castTy->isIntegerType() && originalTy->isIntegerType()) {
+ if (isa<Loc>(val))
+ // This can be a cast to ObjC property of type int.
+ return evalCastFromLoc(cast<Loc>(val), castTy);
+ else
+ return evalCastFromNonLoc(cast<NonLoc>(val), castTy);
+ }
// Check for casts from pointers to integers.
if (castTy->isIntegerType() && Loc::isLocType(originalTy))
diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp
index 4614e34..b5980b9 100644
--- a/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/lib/StaticAnalyzer/Core/SVals.cpp
@@ -12,14 +12,11 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Basic/IdentifierTable.h"
-
using namespace clang;
using namespace ento;
-using llvm::dyn_cast;
-using llvm::cast;
using llvm::APSInt;
//===----------------------------------------------------------------------===//
@@ -146,7 +143,7 @@ SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) {
while (!isa<SymbolData>(itr.back())) expand();
}
-SVal::symbol_iterator& SVal::symbol_iterator::operator++() {
+SVal::symbol_iterator &SVal::symbol_iterator::operator++() {
assert(!itr.empty() && "attempting to iterate on an 'end' iterator");
assert(isa<SymbolData>(itr.back()));
itr.pop_back();
@@ -174,7 +171,7 @@ void SVal::symbol_iterator::expand() {
return;
}
- assert(false && "unhandled expansion case");
+ llvm_unreachable("unhandled expansion case");
}
const void *nonloc::LazyCompoundVal::getStore() const {
@@ -270,7 +267,7 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals,
void SVal::dump() const { dumpToStream(llvm::errs()); }
-void SVal::dumpToStream(llvm::raw_ostream& os) const {
+void SVal::dumpToStream(raw_ostream &os) const {
switch (getBaseKind()) {
case UnknownKind:
os << "Unknown";
@@ -289,7 +286,7 @@ void SVal::dumpToStream(llvm::raw_ostream& os) const {
}
}
-void NonLoc::dumpToStream(llvm::raw_ostream& os) const {
+void NonLoc::dumpToStream(raw_ostream &os) const {
switch (getSubKind()) {
case nonloc::ConcreteIntKind: {
const nonloc::ConcreteInt& C = *cast<nonloc::ConcreteInt>(this);
@@ -344,7 +341,7 @@ void NonLoc::dumpToStream(llvm::raw_ostream& os) const {
}
}
-void Loc::dumpToStream(llvm::raw_ostream& os) const {
+void Loc::dumpToStream(raw_ostream &os) const {
switch (getSubKind()) {
case loc::ConcreteIntKind:
os << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() << " (Loc)";
@@ -372,7 +369,6 @@ void Loc::dumpToStream(llvm::raw_ostream& os) const {
break;
}
default:
- assert(false && "Pretty-printing not implemented for this Loc.");
- break;
+ llvm_unreachable("Pretty-printing not implemented for this Loc.");
}
}
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 20762e0..79d8b8b 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -14,7 +14,7 @@
#include "SimpleConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
namespace clang {
@@ -56,7 +56,7 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const {
return true;
}
-const GRState *SimpleConstraintManager::assume(const GRState *state,
+const ProgramState *SimpleConstraintManager::assume(const ProgramState *state,
DefinedSVal Cond,
bool Assumption) {
if (isa<NonLoc>(Cond))
@@ -65,13 +65,13 @@ const GRState *SimpleConstraintManager::assume(const GRState *state,
return assume(state, cast<Loc>(Cond), Assumption);
}
-const GRState *SimpleConstraintManager::assume(const GRState *state, Loc cond,
+const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, Loc cond,
bool assumption) {
state = assumeAux(state, cond, assumption);
return SU.processAssume(state, cond, assumption);
}
-const GRState *SimpleConstraintManager::assumeAux(const GRState *state,
+const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state,
Loc Cond, bool Assumption) {
BasicValueFactory &BasicVals = state->getBasicVals();
@@ -113,7 +113,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state,
} // end switch
}
-const GRState *SimpleConstraintManager::assume(const GRState *state,
+const ProgramState *SimpleConstraintManager::assume(const ProgramState *state,
NonLoc cond,
bool assumption) {
state = assumeAux(state, cond, assumption);
@@ -125,7 +125,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
// the only place it's used. (This code was copied from SimpleSValBuilder.cpp.)
switch (op) {
default:
- assert(false && "Invalid opcode.");
+ llvm_unreachable("Invalid opcode.");
case BO_LT: return BO_GE;
case BO_GT: return BO_LE;
case BO_LE: return BO_GT;
@@ -135,7 +135,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
}
}
-const GRState *SimpleConstraintManager::assumeAux(const GRState *state,
+const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state,
NonLoc Cond,
bool Assumption) {
@@ -152,7 +152,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state,
switch (Cond.getSubKind()) {
default:
- assert(false && "'Assume' not implemented for this NonLoc");
+ llvm_unreachable("'Assume' not implemented for this NonLoc");
case nonloc::SymbolValKind: {
nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond);
@@ -202,7 +202,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state,
} // end switch
}
-const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state,
+const ProgramState *SimpleConstraintManager::assumeSymRel(const ProgramState *state,
const SymExpr *LHS,
BinaryOperator::Opcode op,
const llvm::APSInt& Int) {
@@ -256,7 +256,7 @@ const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state,
// 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.
- GRStateManager &StateMgr = state->getStateManager();
+ ProgramStateManager &StateMgr = state->getStateManager();
ASTContext &Ctx = StateMgr.getContext();
QualType T = Sym->getType(Ctx);
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
index a2952af..d4295d4 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
@@ -15,7 +15,7 @@
#define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H
#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
namespace clang {
@@ -33,14 +33,14 @@ public:
bool canReasonAbout(SVal X) const;
- const GRState *assume(const GRState *state, DefinedSVal Cond,
+ const ProgramState *assume(const ProgramState *state, DefinedSVal Cond,
bool Assumption);
- const GRState *assume(const GRState *state, Loc Cond, bool Assumption);
+ const ProgramState *assume(const ProgramState *state, Loc Cond, bool Assumption);
- const GRState *assume(const GRState *state, NonLoc Cond, bool Assumption);
+ const ProgramState *assume(const ProgramState *state, NonLoc Cond, bool Assumption);
- const GRState *assumeSymRel(const GRState *state,
+ const ProgramState *assumeSymRel(const ProgramState *state,
const SymExpr *LHS,
BinaryOperator::Opcode op,
const llvm::APSInt& Int);
@@ -53,27 +53,27 @@ protected:
// Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison
// operation for the method being invoked.
- virtual const GRState *assumeSymNE(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const GRState *assumeSymEQ(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const GRState *assumeSymLT(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const GRState *assumeSymGT(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const GRState *assumeSymLE(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
- virtual const GRState *assumeSymGE(const GRState *state, SymbolRef sym,
+ virtual const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym,
const llvm::APSInt& V,
const llvm::APSInt& Adjustment) = 0;
@@ -81,9 +81,9 @@ protected:
// Internal implementation.
//===------------------------------------------------------------------===//
- const GRState *assumeAux(const GRState *state, Loc Cond,bool Assumption);
+ const ProgramState *assumeAux(const ProgramState *state, Loc Cond,bool Assumption);
- const GRState *assumeAux(const GRState *state, NonLoc Cond, bool Assumption);
+ const ProgramState *assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption);
};
} // end GR namespace
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 80c18a3..bd63ecf 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -12,7 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
using namespace clang;
using namespace ento;
@@ -25,22 +25,22 @@ protected:
public:
SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
- GRStateManager &stateMgr)
+ ProgramStateManager &stateMgr)
: SValBuilder(alloc, context, stateMgr) {}
virtual ~SimpleSValBuilder() {}
virtual SVal evalMinus(NonLoc val);
virtual SVal evalComplement(NonLoc val);
- virtual SVal evalBinOpNN(const GRState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs, QualType resultTy);
- virtual SVal evalBinOpLL(const GRState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op,
Loc lhs, Loc rhs, QualType resultTy);
- virtual SVal evalBinOpLN(const GRState *state, BinaryOperator::Opcode op,
+ virtual SVal evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op,
Loc lhs, NonLoc rhs, QualType resultTy);
/// getKnownValue - evaluates a given SVal. If the SVal has only one possible
/// (integer) value, that value is returned. Otherwise, returns NULL.
- virtual const llvm::APSInt *getKnownValue(const GRState *state, SVal V);
+ virtual const llvm::APSInt *getKnownValue(const ProgramState *state, SVal V);
SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
const llvm::APSInt &RHS, QualType resultTy);
@@ -49,7 +49,7 @@ public:
SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
ASTContext &context,
- GRStateManager &stateMgr) {
+ ProgramStateManager &stateMgr) {
return new SimpleSValBuilder(alloc, context, stateMgr);
}
@@ -171,7 +171,7 @@ SVal SimpleSValBuilder::evalComplement(NonLoc X) {
static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
switch (op) {
default:
- assert(false && "Invalid opcode.");
+ llvm_unreachable("Invalid opcode.");
case BO_LT: return BO_GE;
case BO_GT: return BO_LE;
case BO_LE: return BO_GT;
@@ -184,7 +184,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) {
static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) {
switch (op) {
default:
- assert(false && "Invalid opcode.");
+ llvm_unreachable("Invalid opcode.");
case BO_LT: return BO_GT;
case BO_GT: return BO_LT;
case BO_LE: return BO_GE;
@@ -270,7 +270,7 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
return makeNonLoc(LHS, op, RHS, resultTy);
}
-SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
+SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state,
BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs,
QualType resultTy) {
@@ -347,8 +347,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
break;
case BO_LAnd:
case BO_LOr:
- assert(false && "Logical operators handled by branching logic.");
- return UnknownVal();
+ llvm_unreachable("Logical operators handled by branching logic.");
case BO_Assign:
case BO_MulAssign:
case BO_DivAssign:
@@ -361,12 +360,10 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
case BO_XorAssign:
case BO_OrAssign:
case BO_Comma:
- assert(false && "'=' and ',' operators handled by ExprEngine.");
- return UnknownVal();
+ llvm_unreachable("'=' and ',' operators handled by ExprEngine.");
case BO_PtrMemD:
case BO_PtrMemI:
- assert(false && "Pointer arithmetic not handled here.");
- return UnknownVal();
+ llvm_unreachable("Pointer arithmetic not handled here.");
case BO_LT:
case BO_GT:
case BO_LE:
@@ -539,7 +536,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
}
// FIXME: all this logic will change if/when we have MemRegion::getLocation().
-SVal SimpleSValBuilder::evalBinOpLL(const GRState *state,
+SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state,
BinaryOperator::Opcode op,
Loc lhs, Loc rhs,
QualType resultTy) {
@@ -556,8 +553,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state,
if (lhs == rhs) {
switch (op) {
default:
- assert(false && "Unimplemented operation for two identical values");
- return UnknownVal();
+ llvm_unreachable("Unimplemented operation for two identical values");
case BO_Sub:
return makeZeroVal(resultTy);
case BO_EQ:
@@ -573,8 +569,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state,
switch (lhs.getSubKind()) {
default:
- assert(false && "Ordering not implemented for this Loc.");
- return UnknownVal();
+ llvm_unreachable("Ordering not implemented for this Loc.");
case loc::GotoLabelKind:
// The only thing we know about labels is that they're non-null.
@@ -827,7 +822,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state,
return makeTruthVal(!leftFirst, resultTy);
}
- assert(false && "Fields not found in parent record's definition");
+ llvm_unreachable("Fields not found in parent record's definition");
}
// If we get here, we have no way of comparing the regions.
@@ -836,7 +831,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state,
}
}
-SVal SimpleSValBuilder::evalBinOpLN(const GRState *state,
+SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state,
BinaryOperator::Opcode op,
Loc lhs, NonLoc rhs, QualType resultTy) {
@@ -930,7 +925,7 @@ SVal SimpleSValBuilder::evalBinOpLN(const GRState *state,
return UnknownVal();
}
-const llvm::APSInt *SimpleSValBuilder::getKnownValue(const GRState *state,
+const llvm::APSInt *SimpleSValBuilder::getKnownValue(const ProgramState *state,
SVal V) {
if (V.isUnknownOrUndef())
return NULL;
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index b936738..48a6f4f 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -12,17 +12,17 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/AST/CharUnits.h"
using namespace clang;
using namespace ento;
-StoreManager::StoreManager(GRStateManager &stateMgr)
+StoreManager::StoreManager(ProgramStateManager &stateMgr)
: svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
-StoreRef StoreManager::enterStackFrame(const GRState *state,
+StoreRef StoreManager::enterStackFrame(const ProgramState *state,
const StackFrameContext *frame) {
return StoreRef(state->getStore(), *this);
}
@@ -57,7 +57,7 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R,
const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) {
- ASTContext& Ctx = StateMgr.getContext();
+ ASTContext &Ctx = StateMgr.getContext();
// Handle casts to Objective-C objects.
if (CastToTy->isObjCObjectPointerType())
@@ -87,7 +87,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
// Handle casts from compatible types.
if (R->isBoundable())
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
if (CanonPointeeTy == ObjTy)
return R;
@@ -103,8 +103,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::UnknownSpaceRegionKind:
case MemRegion::NonStaticGlobalSpaceRegionKind:
case MemRegion::StaticGlobalSpaceRegionKind: {
- assert(0 && "Invalid region cast");
- break;
+ llvm_unreachable("Invalid region cast");
}
case MemRegion::FunctionTextRegionKind:
@@ -157,7 +156,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
// Edge case: we are at 0 bytes off the beginning of baseR. We
// check to see if type we are casting to is the same as the base
// region. If so, just return the base region.
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) {
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(baseR)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
if (CanonPointeeTy == ObjTy)
@@ -203,15 +202,14 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
}
}
- assert(0 && "unreachable");
- return 0;
+ llvm_unreachable("unreachable");
}
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
/// as another region.
-SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R,
+SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
QualType castTy, bool performTestOnly) {
if (castTy.isNull())
@@ -237,7 +235,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R,
return V;
}
-SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) {
+SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
if (Base.isUnknownOrUndef())
return Base;
@@ -261,8 +259,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) {
return Base;
default:
- assert(0 && "Unhandled Base.");
- return Base;
+ llvm_unreachable("Unhandled Base.");
}
// NOTE: We must have this check first because ObjCIvarDecl is a subclass
@@ -336,3 +333,6 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR,
Ctx));
}
+
+StoreManager::BindingsHandler::~BindingsHandler() {}
+
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index c1ca1cf..b843ab1 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -24,11 +25,10 @@ void SymExpr::dump() const {
dumpToStream(llvm::errs());
}
-static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) {
+static void print(raw_ostream &os, BinaryOperator::Opcode Op) {
switch (Op) {
default:
- assert(false && "operator printing not implemented");
- break;
+ llvm_unreachable("operator printing not implemented");
case BO_Mul: os << '*' ; break;
case BO_Div: os << '/' ; break;
case BO_Rem: os << '%' ; break;
@@ -48,7 +48,7 @@ static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) {
}
}
-void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const {
+void SymIntExpr::dumpToStream(raw_ostream &os) const {
os << '(';
getLHS()->dumpToStream(os);
os << ") ";
@@ -57,7 +57,7 @@ void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const {
if (getRHS().isUnsigned()) os << 'U';
}
-void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const {
+void SymSymExpr::dumpToStream(raw_ostream &os) const {
os << '(';
getLHS()->dumpToStream(os);
os << ") ";
@@ -66,33 +66,33 @@ void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const {
os << ')';
}
-void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolConjured::dumpToStream(raw_ostream &os) const {
os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}';
}
-void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolDerived::dumpToStream(raw_ostream &os) const {
os << "derived_$" << getSymbolID() << '{'
<< getParentSymbol() << ',' << getRegion() << '}';
}
-void SymbolExtent::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolExtent::dumpToStream(raw_ostream &os) const {
os << "extent_$" << getSymbolID() << '{' << getRegion() << '}';
}
-void SymbolMetadata::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolMetadata::dumpToStream(raw_ostream &os) const {
os << "meta_$" << getSymbolID() << '{'
<< getRegion() << ',' << T.getAsString() << '}';
}
-void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const {
+void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
os << "reg_$" << getSymbolID() << "<" << R << ">";
}
const SymbolRegionValue*
-SymbolManager::getRegionValueSymbol(const TypedRegion* R) {
+SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
llvm::FoldingSetNodeID profile;
SymbolRegionValue::Profile(profile, R);
- void* InsertPos;
+ void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>();
@@ -105,12 +105,12 @@ SymbolManager::getRegionValueSymbol(const TypedRegion* R) {
}
const SymbolConjured*
-SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count,
- const void* SymbolTag) {
+SymbolManager::getConjuredSymbol(const Stmt *E, QualType T, unsigned Count,
+ const void *SymbolTag) {
llvm::FoldingSetNodeID profile;
SymbolConjured::Profile(profile, E, T, Count, SymbolTag);
- void* InsertPos;
+ void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>();
@@ -124,11 +124,11 @@ SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count,
const SymbolDerived*
SymbolManager::getDerivedSymbol(SymbolRef parentSymbol,
- const TypedRegion *R) {
+ const TypedValueRegion *R) {
llvm::FoldingSetNodeID profile;
SymbolDerived::Profile(profile, parentSymbol, R);
- void* InsertPos;
+ void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>();
@@ -144,7 +144,7 @@ const SymbolExtent*
SymbolManager::getExtentSymbol(const SubRegion *R) {
llvm::FoldingSetNodeID profile;
SymbolExtent::Profile(profile, R);
- void* InsertPos;
+ void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolExtent>();
@@ -157,12 +157,12 @@ SymbolManager::getExtentSymbol(const SubRegion *R) {
}
const SymbolMetadata*
-SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt* S, QualType T,
- unsigned Count, const void* SymbolTag) {
+SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T,
+ unsigned Count, const void *SymbolTag) {
llvm::FoldingSetNodeID profile;
SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag);
- void* InsertPos;
+ void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>();
@@ -214,11 +214,11 @@ QualType SymbolConjured::getType(ASTContext&) const {
return T;
}
-QualType SymbolDerived::getType(ASTContext& Ctx) const {
+QualType SymbolDerived::getType(ASTContext &Ctx) const {
return R->getValueType();
}
-QualType SymbolExtent::getType(ASTContext& Ctx) const {
+QualType SymbolExtent::getType(ASTContext &Ctx) const {
return Ctx.getSizeType();
}
@@ -226,11 +226,17 @@ QualType SymbolMetadata::getType(ASTContext&) const {
return T;
}
-QualType SymbolRegionValue::getType(ASTContext& C) const {
+QualType SymbolRegionValue::getType(ASTContext &C) const {
return R->getValueType();
}
-SymbolManager::~SymbolManager() {}
+SymbolManager::~SymbolManager() {
+ for (SymbolDependTy::const_iterator I = SymbolDependencies.begin(),
+ E = SymbolDependencies.end(); I != E; ++I) {
+ delete I->second;
+ }
+
+}
bool SymbolManager::canSymbolicate(QualType T) {
T = T.getCanonicalType();
@@ -247,9 +253,53 @@ bool SymbolManager::canSymbolicate(QualType T) {
return false;
}
+void SymbolManager::addSymbolDependency(const SymbolRef Primary,
+ const SymbolRef Dependent) {
+ SymbolDependTy::iterator I = SymbolDependencies.find(Primary);
+ SymbolRefSmallVectorTy *dependencies = 0;
+ if (I == SymbolDependencies.end()) {
+ dependencies = new SymbolRefSmallVectorTy();
+ SymbolDependencies[Primary] = dependencies;
+ } else {
+ dependencies = I->second;
+ }
+ dependencies->push_back(Dependent);
+}
+
+const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols(
+ const SymbolRef Primary) {
+ SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary);
+ if (I == SymbolDependencies.end())
+ return 0;
+ return I->second;
+}
+
+void SymbolReaper::markDependentsLive(SymbolRef sym) {
+ // Do not mark dependents more then once.
+ SymbolMapTy::iterator LI = TheLiving.find(sym);
+ assert(LI != TheLiving.end() && "The primary symbol is not live.");
+ if (LI->second == HaveMarkedDependents)
+ return;
+ LI->second = HaveMarkedDependents;
+
+ if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) {
+ for (SymbolRefSmallVectorTy::const_iterator I = Deps->begin(),
+ E = Deps->end(); I != E; ++I) {
+ if (TheLiving.find(*I) != TheLiving.end())
+ continue;
+ markLive(*I);
+ }
+ }
+}
+
void SymbolReaper::markLive(SymbolRef sym) {
- TheLiving.insert(sym);
+ TheLiving[sym] = NotProcessed;
TheDead.erase(sym);
+ markDependentsLive(sym);
+}
+
+void SymbolReaper::markLive(const MemRegion *region) {
+ RegionRoots.insert(region);
}
void SymbolReaper::markInUse(SymbolRef sym) {
@@ -265,14 +315,17 @@ bool SymbolReaper::maybeDead(SymbolRef sym) {
return true;
}
-static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) {
+bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
+ if (RegionRoots.count(MR))
+ return true;
+
MR = MR->getBaseRegion();
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
- return Reaper.isLive(SR->getSymbol());
+ return isLive(SR->getSymbol());
if (const VarRegion *VR = dyn_cast<VarRegion>(MR))
- return Reaper.isLive(VR);
+ return isLive(VR, true);
// FIXME: This is a gross over-approximation. What we really need is a way to
// tell if anything still refers to this region. Unlike SymbolicRegions,
@@ -291,8 +344,10 @@ static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) {
}
bool SymbolReaper::isLive(SymbolRef sym) {
- if (TheLiving.count(sym))
+ if (TheLiving.count(sym)) {
+ markDependentsLive(sym);
return true;
+ }
if (const SymbolDerived *derived = dyn_cast<SymbolDerived>(sym)) {
if (isLive(derived->getParentSymbol())) {
@@ -303,7 +358,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
}
if (const SymbolExtent *extent = dyn_cast<SymbolExtent>(sym)) {
- if (IsLiveRegion(*this, extent->getRegion())) {
+ if (isLiveRegion(extent->getRegion())) {
markLive(sym);
return true;
}
@@ -312,7 +367,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
if (const SymbolMetadata *metadata = dyn_cast<SymbolMetadata>(sym)) {
if (MetadataInUse.count(sym)) {
- if (IsLiveRegion(*this, metadata->getRegion())) {
+ if (isLiveRegion(metadata->getRegion())) {
markLive(sym);
MetadataInUse.erase(sym);
return true;
@@ -326,18 +381,38 @@ bool SymbolReaper::isLive(SymbolRef sym) {
return isa<SymbolRegionValue>(sym);
}
-bool SymbolReaper::isLive(const Stmt* ExprVal) const {
- return LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
- isLive(Loc, ExprVal);
+bool SymbolReaper::isLive(const Stmt *ExprVal) const {
+ return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
-bool SymbolReaper::isLive(const VarRegion *VR) const {
+bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *VarContext = VR->getStackFrame();
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
- if (VarContext == CurrentContext)
- return LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
- isLive(Loc, VR->getDecl());
+ if (VarContext == CurrentContext) {
+ if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
+ return true;
+
+ if (!includeStoreBindings)
+ return false;
+
+ unsigned &cachedQuery =
+ const_cast<SymbolReaper*>(this)->includedRegionCache[VR];
+
+ if (cachedQuery) {
+ return cachedQuery == 1;
+ }
+
+ // Query the store to see if the region occurs in any live bindings.
+ if (Store store = reapedStore.getStore()) {
+ bool hasRegion =
+ reapedStore.getStoreManager().includedInBindings(store, VR);
+ cachedQuery = hasRegion ? 1 : 2;
+ return hasRegion;
+ }
+
+ return false;
+ }
return VarContext->isParentOf(CurrentContext);
}
diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
index 230b6a10..3543f7f 100644
--- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp
@@ -11,7 +11,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/raw_ostream.h"
@@ -23,19 +23,19 @@ namespace {
/// \brief Simple path diagnostic client used for outputting as diagnostic notes
/// the sequence of events.
-class TextPathDiagnostics : public PathDiagnosticClient {
+class TextPathDiagnostics : public PathDiagnosticConsumer {
const std::string OutputFile;
- Diagnostic &Diag;
+ DiagnosticsEngine &Diag;
public:
- TextPathDiagnostics(const std::string& output, Diagnostic &diag)
+ TextPathDiagnostics(const std::string& output, DiagnosticsEngine &diag)
: OutputFile(output), Diag(diag) {}
- void HandlePathDiagnostic(const PathDiagnostic* D);
+ void HandlePathDiagnosticImpl(const PathDiagnostic* D);
- void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade) { }
+ void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { }
- virtual llvm::StringRef getName() const {
+ virtual StringRef getName() const {
return "TextPathDiagnostics";
}
@@ -47,13 +47,13 @@ public:
} // end anonymous namespace
-PathDiagnosticClient*
-ento::createTextPathDiagnosticClient(const std::string& out,
+PathDiagnosticConsumer*
+ento::createTextPathDiagnosticConsumer(const std::string& out,
const Preprocessor &PP) {
return new TextPathDiagnostics(out, PP.getDiagnostics());
}
-void TextPathDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
+void TextPathDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) {
if (!D)
return;
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index b8dbb54..34a358f 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -25,8 +25,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
-#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
@@ -43,15 +42,15 @@ using namespace ento;
static ExplodedNode::Auditor* CreateUbiViz();
//===----------------------------------------------------------------------===//
-// Special PathDiagnosticClients.
+// Special PathDiagnosticConsumers.
//===----------------------------------------------------------------------===//
-static PathDiagnosticClient*
-createPlistHTMLDiagnosticClient(const std::string& prefix,
+static PathDiagnosticConsumer*
+createPlistHTMLDiagnosticConsumer(const std::string& prefix,
const Preprocessor &PP) {
- PathDiagnosticClient *PD =
- createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP);
- return createPlistDiagnosticClient(prefix, PP, PD);
+ PathDiagnosticConsumer *PD =
+ createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP);
+ return createPlistDiagnosticConsumer(prefix, PP, PD);
}
//===----------------------------------------------------------------------===//
@@ -62,13 +61,14 @@ namespace {
class AnalysisConsumer : public ASTConsumer {
public:
- ASTContext* Ctx;
+ ASTContext *Ctx;
const Preprocessor &PP;
const std::string OutDir;
AnalyzerOptions Opts;
+ ArrayRef<std::string> Plugins;
// PD is owned by AnalysisManager.
- PathDiagnosticClient *PD;
+ PathDiagnosticConsumer *PD;
StoreManagerCreator CreateStoreMgr;
ConstraintManagerCreator CreateConstraintMgr;
@@ -78,14 +78,14 @@ public:
AnalysisConsumer(const Preprocessor& pp,
const std::string& outdir,
- const AnalyzerOptions& opts)
- : Ctx(0), PP(pp), OutDir(outdir),
- Opts(opts), PD(0) {
+ const AnalyzerOptions& opts,
+ ArrayRef<std::string> plugins)
+ : Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) {
DigestAnalyzerOptions();
}
void DigestAnalyzerOptions() {
- // Create the PathDiagnosticClient.
+ // Create the PathDiagnosticConsumer.
if (!OutDir.empty()) {
switch (Opts.AnalysisDiagOpt) {
default:
@@ -96,13 +96,13 @@ public:
} else if (Opts.AnalysisDiagOpt == PD_TEXT) {
// Create the text client even without a specified output file since
// it just uses diagnostic notes.
- PD = createTextPathDiagnosticClient("", PP);
+ PD = createTextPathDiagnosticConsumer("", PP);
}
// Create the analyzer component creators.
switch (Opts.AnalysisStoreOpt) {
default:
- assert(0 && "Unknown store manager.");
+ llvm_unreachable("Unknown store manager.");
#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \
case NAME##Model: CreateStoreMgr = CREATEFN; break;
#include "clang/Frontend/Analyses.def"
@@ -110,7 +110,7 @@ public:
switch (Opts.AnalysisConstraintsOpt) {
default:
- assert(0 && "Unknown store manager.");
+ llvm_unreachable("Unknown store manager.");
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
case NAME##Model: CreateConstraintMgr = CREATEFN; break;
#include "clang/Frontend/Analyses.def"
@@ -128,7 +128,7 @@ public:
if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
- llvm::errs() << ' ' << ND << '\n';
+ llvm::errs() << ' ' << *ND << '\n';
}
else if (isa<BlockDecl>(D)) {
llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
@@ -143,8 +143,8 @@ public:
virtual void Initialize(ASTContext &Context) {
Ctx = &Context;
- checkerMgr.reset(registerCheckers(Opts, PP.getLangOptions(),
- PP.getDiagnostics()));
+ checkerMgr.reset(createCheckerManager(Opts, PP.getLangOptions(), Plugins,
+ PP.getDiagnostics()));
Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
PP.getLangOptions(), PD,
CreateStoreMgr, CreateConstraintMgr,
@@ -152,7 +152,7 @@ public:
/* Indexer */ 0,
Opts.MaxNodes, Opts.MaxLoop,
Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
- Opts.PurgeDead, Opts.EagerlyAssume,
+ Opts.AnalysisPurgeOpt, Opts.EagerlyAssume,
Opts.TrimGraph, Opts.InlineCall,
Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
Opts.CFGAddInitializers,
@@ -161,6 +161,7 @@ public:
virtual void HandleTranslationUnit(ASTContext &C);
void HandleDeclContext(ASTContext &C, DeclContext *dc);
+ void HandleDeclContextDecl(ASTContext &C, Decl *D);
void HandleCode(Decl *D);
};
@@ -171,61 +172,67 @@ public:
//===----------------------------------------------------------------------===//
void AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) {
- BugReporter BR(*Mgr);
for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end();
I != E; ++I) {
- Decl *D = *I;
+ HandleDeclContextDecl(C, *I);
+ }
+}
+
+void AnalysisConsumer::HandleDeclContextDecl(ASTContext &C, Decl *D) {
+ { // Handle callbacks for arbitrary decls.
+ BugReporter BR(*Mgr);
checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR);
+ }
- switch (D->getKind()) {
- case Decl::Namespace: {
- HandleDeclContext(C, cast<NamespaceDecl>(D));
- break;
+ switch (D->getKind()) {
+ case Decl::Namespace: {
+ HandleDeclContext(C, cast<NamespaceDecl>(D));
+ break;
+ }
+ case Decl::CXXConstructor:
+ case Decl::CXXDestructor:
+ case Decl::CXXConversion:
+ case Decl::CXXMethod:
+ case Decl::Function: {
+ FunctionDecl *FD = cast<FunctionDecl>(D);
+ // We skip function template definitions, as their semantics is
+ // only determined when they are instantiated.
+ if (FD->isThisDeclarationADefinition() &&
+ !FD->isDependentContext()) {
+ if (!Opts.AnalyzeSpecificFunction.empty() &&
+ FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
+ break;
+ DisplayFunction(FD);
+ HandleCode(FD);
}
- case Decl::CXXConstructor:
- case Decl::CXXDestructor:
- case Decl::CXXConversion:
- case Decl::CXXMethod:
- case Decl::Function: {
- FunctionDecl* FD = cast<FunctionDecl>(D);
- // We skip function template definitions, as their semantics is
- // only determined when they are instantiated.
- if (FD->isThisDeclarationADefinition() &&
- !FD->isDependentContext()) {
+ break;
+ }
+
+ case Decl::ObjCCategoryImpl:
+ case Decl::ObjCImplementation: {
+ ObjCImplDecl *ID = cast<ObjCImplDecl>(D);
+ HandleCode(ID);
+
+ for (ObjCContainerDecl::method_iterator MI = ID->meth_begin(),
+ ME = ID->meth_end(); MI != ME; ++MI) {
+ BugReporter BR(*Mgr);
+ checkerMgr->runCheckersOnASTDecl(*MI, *Mgr, BR);
+
+ if ((*MI)->isThisDeclarationADefinition()) {
if (!Opts.AnalyzeSpecificFunction.empty() &&
- FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
- break;
- DisplayFunction(FD);
- HandleCode(FD);
+ Opts.AnalyzeSpecificFunction !=
+ (*MI)->getSelector().getAsString())
+ continue;
+ DisplayFunction(*MI);
+ HandleCode(*MI);
}
- break;
}
-
- case Decl::ObjCCategoryImpl:
- case Decl::ObjCImplementation: {
- ObjCImplDecl* ID = cast<ObjCImplDecl>(*I);
- HandleCode(ID);
-
- for (ObjCContainerDecl::method_iterator MI = ID->meth_begin(),
- ME = ID->meth_end(); MI != ME; ++MI) {
- checkerMgr->runCheckersOnASTDecl(*MI, *Mgr, BR);
-
- if ((*MI)->isThisDeclarationADefinition()) {
- if (!Opts.AnalyzeSpecificFunction.empty() &&
- Opts.AnalyzeSpecificFunction !=
- (*MI)->getSelector().getAsString())
- break;
- DisplayFunction(*MI);
- HandleCode(*MI);
- }
- }
- break;
- }
-
- default:
- break;
+ break;
}
- }
+
+ default:
+ break;
+ }
}
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
@@ -237,14 +244,14 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
// After all decls handled, run checkers on the entire TranslationUnit.
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
- // Explicitly destroy the PathDiagnosticClient. This will flush its output.
+ // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
// FIXME: This should be replaced with something that doesn't rely on
- // side-effects in PathDiagnosticClient's destructor. This is required when
+ // side-effects in PathDiagnosticConsumer's destructor. This is required when
// used with option -disable-free.
Mgr.reset(NULL);
}
-static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
+static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) {
if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
WL.push_back(BD);
@@ -254,20 +261,20 @@ static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
FindBlocks(DC, WL);
}
-static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
- Decl *D);
+static void RunPathSensitiveChecks(AnalysisConsumer &C, AnalysisManager &mgr,
+ Decl *D);
void AnalysisConsumer::HandleCode(Decl *D) {
// Don't run the actions if an error has occurred with parsing the file.
- Diagnostic &Diags = PP.getDiagnostics();
+ DiagnosticsEngine &Diags = PP.getDiagnostics();
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
return;
// Don't run the actions on declarations in header files unless
// otherwise specified.
SourceManager &SM = Ctx->getSourceManager();
- SourceLocation SL = SM.getInstantiationLoc(D->getLocation());
+ SourceLocation SL = SM.getExpansionLoc(D->getLocation());
if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
return;
@@ -275,19 +282,19 @@ void AnalysisConsumer::HandleCode(Decl *D) {
Mgr->ClearContexts();
// Dispatch on the actions.
- llvm::SmallVector<Decl*, 10> WL;
+ SmallVector<Decl*, 10> WL;
WL.push_back(D);
if (D->hasBody() && Opts.AnalyzeNestedBlocks)
FindBlocks(cast<DeclContext>(D), WL);
BugReporter BR(*Mgr);
- for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
+ for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
WI != WE; ++WI)
if ((*WI)->hasBody()) {
checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
if (checkerMgr->hasPathSensitiveCheckers())
- ActionObjCMemChecker(*this, *Mgr, *WI);
+ RunPathSensitiveChecks(*this, *Mgr, *WI);
}
}
@@ -295,18 +302,13 @@ void AnalysisConsumer::HandleCode(Decl *D) {
// Path-sensitive checking.
//===----------------------------------------------------------------------===//
-static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
- Decl *D,
- TransferFuncs* tf) {
-
- llvm::OwningPtr<TransferFuncs> TF(tf);
-
- // Construct the analysis engine. We first query for the LiveVariables
- // information to see if the CFG is valid.
+static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager &mgr,
+ Decl *D, bool ObjCGCEnabled) {
+ // Construct the analysis engine. First check if the CFG is valid.
// FIXME: Inter-procedural analysis will need to handle invalid CFGs.
- if (!mgr.getLiveVariables(D))
+ if (!mgr.getCFG(D))
return;
- ExprEngine Eng(mgr, TF.take());
+ ExprEngine Eng(mgr, ObjCGCEnabled);
// Set the graph auditor.
llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
@@ -330,35 +332,25 @@ static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
Eng.getBugReporter().FlushReports();
}
-static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr,
- Decl *D, bool GCEnabled) {
-
- TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(),
- GCEnabled,
- mgr.getLangOptions());
-
- ActionExprEngine(C, mgr, D, TF);
-}
-
-static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
- Decl *D) {
-
- switch (mgr.getLangOptions().getGCMode()) {
- default:
- assert (false && "Invalid GC mode.");
- case LangOptions::NonGC:
- ActionObjCMemCheckerAux(C, mgr, D, false);
- break;
-
- case LangOptions::GCOnly:
- ActionObjCMemCheckerAux(C, mgr, D, true);
- break;
-
- case LangOptions::HybridGC:
- ActionObjCMemCheckerAux(C, mgr, D, false);
- ActionObjCMemCheckerAux(C, mgr, D, true);
- break;
- }
+static void RunPathSensitiveChecks(AnalysisConsumer &C, AnalysisManager &mgr,
+ Decl *D) {
+
+ switch (mgr.getLangOptions().getGC()) {
+ default:
+ llvm_unreachable("Invalid GC mode.");
+ case LangOptions::NonGC:
+ ActionExprEngine(C, mgr, D, false);
+ break;
+
+ case LangOptions::GCOnly:
+ ActionExprEngine(C, mgr, D, true);
+ break;
+
+ case LangOptions::HybridGC:
+ ActionExprEngine(C, mgr, D, false);
+ ActionExprEngine(C, mgr, D, true);
+ break;
+ }
}
//===----------------------------------------------------------------------===//
@@ -366,14 +358,13 @@ static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
//===----------------------------------------------------------------------===//
ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
- const std::string& OutDir,
- const AnalyzerOptions& Opts) {
- llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts));
-
- // Last, disable the effects of '-Werror' when using the AnalysisConsumer.
+ const std::string& outDir,
+ const AnalyzerOptions& opts,
+ ArrayRef<std::string> plugins) {
+ // Disable the effects of '-Werror' when using the AnalysisConsumer.
pp.getDiagnostics().setWarningsAsErrors(false);
- return C.take();
+ return new AnalysisConsumer(pp, outDir, opts, plugins);
}
//===----------------------------------------------------------------------===//
@@ -383,7 +374,7 @@ ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
namespace {
class UbigraphViz : public ExplodedNode::Auditor {
- llvm::OwningPtr<llvm::raw_ostream> Out;
+ llvm::OwningPtr<raw_ostream> Out;
llvm::sys::Path Dir, Filename;
unsigned Cntr;
@@ -391,12 +382,12 @@ class UbigraphViz : public ExplodedNode::Auditor {
VMap M;
public:
- UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
+ UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
llvm::sys::Path& filename);
~UbigraphViz();
- virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst);
+ virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst);
};
} // end anonymous namespace
@@ -426,7 +417,7 @@ static ExplodedNode::Auditor* CreateUbiViz() {
return new UbigraphViz(Stream.take(), Dir, Filename);
}
-void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
+void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) {
assert (Src != Dst && "Self-edges are not allowed.");
@@ -460,7 +451,7 @@ void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
<< ", ('arrow','true'), ('oriented', 'true'))\n";
}
-UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
+UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
llvm::sys::Path& filename)
: Out(out), Dir(dir), Filename(filename), Cntr(0) {
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h
index 646fe97..5a16bff 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h
@@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_GR_ANALYSISCONSUMER_H
#define LLVM_CLANG_GR_ANALYSISCONSUMER_H
+#include "clang/Basic/LLVM.h"
#include <string>
namespace clang {
@@ -22,7 +23,7 @@ namespace clang {
class AnalyzerOptions;
class ASTConsumer;
class Preprocessor;
-class Diagnostic;
+class DiagnosticsEngine;
namespace ento {
class CheckerManager;
@@ -32,7 +33,8 @@ class CheckerManager;
/// options.)
ASTConsumer* CreateAnalysisConsumer(const Preprocessor &pp,
const std::string &output,
- const AnalyzerOptions& Opts);
+ const AnalyzerOptions& opts,
+ ArrayRef<std::string> plugins);
} // end GR namespace
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index d7edc7e..a59fcad 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -13,53 +13,121 @@
#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
-#include "../Checkers/ClangSACheckerProvider.h"
+#include "clang/StaticAnalyzer/Checkers/ClangCheckers.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/CheckerProvider.h"
+#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
+#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/Frontend/AnalyzerOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Basic/Diagnostic.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace ento;
+using llvm::sys::DynamicLibrary;
-CheckerManager *ento::registerCheckers(const AnalyzerOptions &opts,
- const LangOptions &langOpts,
- Diagnostic &diags) {
+namespace {
+class ClangCheckerRegistry : public CheckerRegistry {
+ typedef void (*RegisterCheckersFn)(CheckerRegistry &);
+
+ static bool isCompatibleAPIVersion(const char *versionString);
+ static void warnIncompatible(DiagnosticsEngine *diags, StringRef pluginPath,
+ const char *pluginAPIVersion);
+
+public:
+ ClangCheckerRegistry(ArrayRef<std::string> plugins,
+ DiagnosticsEngine *diags = 0);
+};
+
+} // end anonymous namespace
+
+ClangCheckerRegistry::ClangCheckerRegistry(ArrayRef<std::string> plugins,
+ DiagnosticsEngine *diags) {
+ registerBuiltinCheckers(*this);
+
+ for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
+ i != e; ++i) {
+ // Get access to the plugin.
+ DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str());
+
+ // See if it's compatible with this build of clang.
+ const char *pluginAPIVersion =
+ (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
+ if (!isCompatibleAPIVersion(pluginAPIVersion)) {
+ warnIncompatible(diags, *i, pluginAPIVersion);
+ continue;
+ }
+
+ // Register its checkers.
+ RegisterCheckersFn registerPluginCheckers =
+ (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
+ "clang_registerCheckers");
+ if (registerPluginCheckers)
+ registerPluginCheckers(*this);
+ }
+}
+
+bool ClangCheckerRegistry::isCompatibleAPIVersion(const char *versionString) {
+ // If the version string is null, it's not an analyzer plugin.
+ if (versionString == 0)
+ return false;
+
+ // For now, none of the static analyzer API is considered stable.
+ // Versions must match exactly.
+ if (strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0)
+ return true;
+
+ return false;
+}
+
+void ClangCheckerRegistry::warnIncompatible(DiagnosticsEngine *diags,
+ StringRef pluginPath,
+ const char *pluginAPIVersion) {
+ if (!diags)
+ return;
+ if (!pluginAPIVersion)
+ return;
+
+ diags->Report(diag::warn_incompatible_analyzer_plugin_api)
+ << llvm::sys::path::filename(pluginPath);
+ diags->Report(diag::note_incompatible_analyzer_plugin_api)
+ << CLANG_ANALYZER_API_VERSION_STRING
+ << pluginAPIVersion;
+}
+
+
+CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts,
+ const LangOptions &langOpts,
+ ArrayRef<std::string> plugins,
+ DiagnosticsEngine &diags) {
llvm::OwningPtr<CheckerManager> checkerMgr(new CheckerManager(langOpts));
- llvm::SmallVector<CheckerOptInfo, 8> checkerOpts;
+ SmallVector<CheckerOptInfo, 8> checkerOpts;
for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) {
const std::pair<std::string, bool> &opt = opts.CheckersControlList[i];
checkerOpts.push_back(CheckerOptInfo(opt.first.c_str(), opt.second));
}
- llvm::OwningPtr<CheckerProvider> provider(createClangSACheckerProvider());
- provider->registerCheckers(*checkerMgr,
- checkerOpts.data(), checkerOpts.size());
-
- // FIXME: Load CheckerProviders from plugins.
-
+ ClangCheckerRegistry allCheckers(plugins, &diags);
+ allCheckers.initializeManager(*checkerMgr, checkerOpts);
checkerMgr->finishedCheckerRegistration();
for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) {
if (checkerOpts[i].isUnclaimed())
- diags.Report(diag::warn_unkwown_analyzer_checker)
+ diags.Report(diag::warn_unknown_analyzer_checker)
<< checkerOpts[i].getName();
}
return checkerMgr.take();
}
-void ento::printCheckerHelp(llvm::raw_ostream &OS) {
- OS << "OVERVIEW: Clang Static Analyzer Checkers List\n";
- OS << '\n';
-
- llvm::OwningPtr<CheckerProvider> provider(createClangSACheckerProvider());
- provider->printHelp(OS);
+void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins) {
+ out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n";
+ out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n";
- // FIXME: Load CheckerProviders from plugins.
+ ClangCheckerRegistry(plugins).printHelp(out);
}
diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
index a59cc68..85a18ec 100644
--- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
+++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
@@ -14,9 +14,10 @@ using namespace clang;
using namespace ento;
ASTConsumer *AnalysisAction::CreateASTConsumer(CompilerInstance &CI,
- llvm::StringRef InFile) {
+ StringRef InFile) {
return CreateAnalysisConsumer(CI.getPreprocessor(),
CI.getFrontendOpts().OutputFile,
- CI.getAnalyzerOpts());
+ CI.getAnalyzerOpts(),
+ CI.getFrontendOpts().Plugins);
}
diff --git a/lib/StaticAnalyzer/README.txt b/lib/StaticAnalyzer/README.txt
index 1406eca..d4310c5 100644
--- a/lib/StaticAnalyzer/README.txt
+++ b/lib/StaticAnalyzer/README.txt
@@ -20,7 +20,7 @@ The analyzer is inspired by several foundational research papers ([1],
In a nutshell, the analyzer is basically a source code simulator that
traces out possible paths of execution. The state of the program
(values of variables and expressions) is encapsulated by the state
-(GRState). A location in the program is called a program point
+(ProgramState). A location in the program is called a program point
(ProgramPoint), and the combination of state and program point is a
node in an exploded graph (ExplodedGraph). The term "exploded" comes
from exploding the control-flow edges in the control-flow graph (CFG).
@@ -39,7 +39,7 @@ then bifurcating the state: on the true branch the conditions of the
branch are assumed to be true and on the false branch the conditions
of the branch are assumed to be false. Such "assumptions" create
constraints on the values of the program, and those constraints are
-recorded in the GRState object (and are manipulated by the
+recorded in the ProgramState object (and are manipulated by the
ConstraintManager). If assuming the conditions of a branch would
cause the constraints to be unsatisfiable, the branch is considered
infeasible and that path is not taken. This is how we get
@@ -49,9 +49,9 @@ would get generated, the path "caches out" and we simply reuse the
existing node. Thus the ExplodedGraph is not a DAG; it can contain
cycles as paths loop back onto each other and cache out.
-GRState and ExplodedNodes are basically immutable once created. Once
-one creates a GRState, you need to create a new one to get a new
-GRState. This immutability is key since the ExplodedGraph represents
+ProgramState and ExplodedNodes are basically immutable once created. Once
+one creates a ProgramState, you need to create a new one to get a new
+ProgramState. This immutability is key since the ExplodedGraph represents
the behavior of the analyzed program from the entry point. To
represent these efficiently, we use functional data structures (e.g.,
ImmutableMaps) which share data between instances.
@@ -62,7 +62,7 @@ For example, the PreVisitCallExpr() method is called by GRExprEngine
to tell the Checker that we are about to analyze a CallExpr, and the
checker is asked to check for any preconditions that might not be
satisfied. The checker can do nothing, or it can generate a new
-GRState and ExplodedNode which contains updated checker state. If it
+ProgramState and ExplodedNode which contains updated checker state. If it
finds a bug, it can tell the BugReporter object about the bug,
providing it an ExplodedNode which is the last node in the path that
triggered the problem.
OpenPOWER on IntegriCloud