diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
106 files changed, 7392 insertions, 7109 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/CMakeLists.txt b/contrib/llvm/tools/clang/lib/StaticAnalyzer/CMakeLists.txt deleted file mode 100644 index 3d15092..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(Core) -add_subdirectory(Checkers) -add_subdirectory(Frontend) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp index 8fc6d2a..dc524ba 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 983427a..cd977bf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index eb9665a..6935c5f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 65a6e63..6175028 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index d88a111..8296eb9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 9fc8163..08cff0f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 12ac652..a57d031 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt deleted file mode 100644 index e172a52..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ /dev/null @@ -1,70 +0,0 @@ -set(LLVM_TARGET_DEFINITIONS Checkers.td) -tablegen(Checkers.inc - -gen-clang-sa-checkers - -I ${CMAKE_CURRENT_SOURCE_DIR}/../../../include) -add_custom_target(ClangSACheckers - DEPENDS Checkers.inc) - -set(LLVM_USED_LIBS clangBasic clangAST) - -add_clang_library(clangStaticAnalyzerCheckers - AdjustedReturnValueChecker.cpp - AnalyzerStatsChecker.cpp - ArrayBoundChecker.cpp - ArrayBoundCheckerV2.cpp - AttrNonNullChecker.cpp - BasicObjCFoundationChecks.cpp - BuiltinFunctionChecker.cpp - CStringChecker.cpp - CallAndMessageChecker.cpp - CastSizeChecker.cpp - CastToStructChecker.cpp - CheckObjCDealloc.cpp - CheckObjCInstMethSignature.cpp - CheckSecuritySyntaxOnly.cpp - CheckSizeofPointer.cpp - ChrootChecker.cpp - ClangSACheckerProvider.cpp - DeadStoresChecker.cpp - DebugCheckers.cpp - DereferenceChecker.cpp - DivZeroChecker.cpp - ExprEngine.cpp - ExperimentalChecks.cpp - FixedAddressChecker.cpp - IdempotentOperationChecker.cpp - LLVMConventionsChecker.cpp - MacOSXAPIChecker.cpp - MallocChecker.cpp - NSAutoreleasePoolChecker.cpp - NSErrorChecker.cpp - NoReturnFunctionChecker.cpp - OSAtomicChecker.cpp - ObjCAtSyncChecker.cpp - ObjCSelfInitChecker.cpp - ObjCUnusedIVarsChecker.cpp - PointerArithChecker.cpp - PointerSubChecker.cpp - PthreadLockChecker.cpp - ReturnPointerRangeChecker.cpp - ReturnUndefChecker.cpp - StackAddrLeakChecker.cpp - StreamChecker.cpp - UndefBranchChecker.cpp - UndefCapturedBlockVarChecker.cpp - UndefResultChecker.cpp - UndefinedArraySubscriptChecker.cpp - UndefinedAssignmentChecker.cpp - UnixAPIChecker.cpp - UnreachableCodeChecker.cpp - VLASizeChecker.cpp - ) - -add_dependencies(clangStaticAnalyzerCheckers - clangStaticAnalyzerCore - ClangAttrClasses - ClangAttrList - ClangDeclNodes - ClangStmtNodes - ClangSACheckers - ) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index c5dac5d..1625219 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 6c3dfac..4db6ac0 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 585a87d..84a9e6b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 3210b0a..c855210 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 0c693a0..c325bb1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index fec06a9..c076c1e3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 53810ee..bf7ba18 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index abf53fd..469be05 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td index 2c196b5..d53e0b8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 50b57d1..3c92381 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp new file mode 100644 index 0000000..77a5a72 --- /dev/null +++ b/contrib/llvm/tools/clang/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 ®istry) { +#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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp deleted file mode 100644 index 291f8e0..0000000 --- a/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h deleted file mode 100644 index f6c8011..0000000 --- a/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h index 5524b0f..289ce8d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index ec2a88a..901af43 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 486b7f7..d9d5694 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index baaf8b3..eeda734 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 07fb5aa..75b7cc4 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index d699dee..531d87e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index b0c07fc..5c257e5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp index de6da4f..fbc57d3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index 3d1b5e2..e398fcb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp new file mode 100644 index 0000000..2607db8 --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index f8d076b..88d492e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Makefile b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Makefile deleted file mode 100644 index 97f4642..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# This implements analyses built on top of source-level CFGs. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../../.. -LIBRARYNAME := clangStaticAnalyzerCheckers - -BUILT_SOURCES = Checkers.inc -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 - $(Echo) "Building Clang SA Checkers tables with tblgen" - $(Verb) $(TableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $< diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 9100215..5631802 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp new file mode 100644 index 0000000..cf5501a --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index f11db64..7f74a7d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index a51d8e0..5678998 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index 2d0af9c..81f1924 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp index 7262bc3..f426265 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index a118049..3e4e07b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 4c05867..2fb9944 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index d78e5ce..bbc262f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 7c21acc..202522b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 16ede20..924c7f2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 74199bb..c02b5b1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index bf53029..93e0fe5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 1729b25..e761bff 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7c215b7..e8c8d90 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 73ce359..91c4b96 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 711c672..1d14e9e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 1fb1815..b860b34 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 69958d1..2aebed9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 7fa3804..7ae9668 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index e51ab20..bb6831b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 28806e3..5ca4a9f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 0ecc391..cec286d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index b540bce..459ee65 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 875dce2..b34b97c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp index 901190d..0936d61 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5f4f83c..17ec70d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 3050ca3..6c748b6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicStore.cpp deleted file mode 100644 index 7c9f45a..0000000 --- a/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 0ed4ff1..fe96700 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp index ed52b6b..74d761e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index 8b5d383..fbbdb04 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 8e31ade..1abd8ba 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CMakeLists.txt deleted file mode 100644 index 14c636c..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(LLVM_LINK_COMPONENTS support) - -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) - -add_clang_library(clangStaticAnalyzerCore - AggExprVisitor.cpp - AnalysisManager.cpp - BasicConstraintManager.cpp - BasicStore.cpp - BasicValueFactory.cpp - BugReporter.cpp - BugReporterVisitors.cpp - CFRefCount.cpp - Checker.cpp - CheckerHelpers.cpp - CheckerManager.cpp - Environment.cpp - ExplodedGraph.cpp - FlatStore.cpp - BlockCounter.cpp - CXXExprEngine.cpp - CoreEngine.cpp - GRState.cpp - HTMLDiagnostics.cpp - MemRegion.cpp - ObjCMessage.cpp - PathDiagnostic.cpp - PlistDiagnostics.cpp - RangeConstraintManager.cpp - RegionStore.cpp - SimpleConstraintManager.cpp - SimpleSValBuilder.cpp - Store.cpp - SValBuilder.cpp - SVals.cpp - SymbolManager.cpp - TextPathDiagnostics.cpp - ) - -add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp new file mode 100644 index 0000000..a3bf2c2 --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index f6fb8f2..5356edc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index ba7c384..acacfb0 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp new file mode 100644 index 0000000..13401ac --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 34cd6e8..5252198 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp index 3961c7b..e1b982c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index fa16fea..5762a21 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index ffe5f0b..ac9cf0b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp new file mode 100644 index 0000000..68ccc59 --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CXXExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index ef7bc20..acb0074 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CXXExprEngine.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp new file mode 100644 index 0000000..6d377b9 --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp new file mode 100644 index 0000000..e0560fd --- /dev/null +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FlatStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FlatStore.cpp deleted file mode 100644 index ca867ae..0000000 --- a/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 1ebc28c..0c4e427 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Makefile b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Makefile deleted file mode 100644 index 4aebc16..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -##===- clang/lib/StaticAnalyzer/Core/Makefile --------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# This implements analyses built on top of source-level CFGs. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../../.. -LIBRARYNAME := clangStaticAnalyzerCore - -include $(CLANG_LEVEL)/Makefile diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index d9e884a..6f92da8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp index c000600..0974fe8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 872bbfe..3a87903 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index fbbbd46..5ae95c6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/GRState.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index 0f6ff1e..73788cc 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/GRState.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 389fff5..9337788 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 23dd641..4b76cf1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index 71f2b4a..ebf7ae2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp index 4614e34..b5980b9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 20762e0..79d8b8b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index a2952af..d4295d4 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 80c18a3..bd63ecf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp index b936738..48a6f4f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index c1ca1cf..b843ab1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index 230b6a10..3543f7f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index b8dbb54..34a358f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h index 646fe97..5a16bff 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.h +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt deleted file mode 100644 index cd9ac1b..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(LLVM_NO_RTTI 1) - -set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) - -include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../Checkers ) - -add_clang_library(clangStaticAnalyzerFrontend - AnalysisConsumer.cpp - CheckerRegistration.cpp - FrontendActions.cpp - ) - -add_dependencies(clangStaticAnalyzerFrontend - clangStaticAnalyzerCheckers - clangStaticAnalyzerCore - ClangAttrClasses - ClangAttrList - ClangDeclNodes - ClangStmtNode - ) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index d7edc7e..a59fcad 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index a59cc68..85a18ec 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/contrib/llvm/tools/clang/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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/Makefile b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/Makefile deleted file mode 100644 index 2698120..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- clang/lib/StaticAnalyzer/Frontend/Makefile ----------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# Starting point into the static analyzer land for the driver. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../../.. -LIBRARYNAME := clangStaticAnalyzerFrontend - -CPP.Flags += -I${PROJ_OBJ_DIR}/../Checkers - -include $(CLANG_LEVEL)/Makefile diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Makefile b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Makefile deleted file mode 100644 index c166f06..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -##===- clang/lib/StaticAnalyzer/Makefile -------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# This implements analyses built on top of source-level CFGs. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. -DIRS := Checkers Frontend -PARALLEL_DIRS := Core - -include $(CLANG_LEVEL)/Makefile diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/README.txt b/contrib/llvm/tools/clang/lib/StaticAnalyzer/README.txt deleted file mode 100644 index 1406eca..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/README.txt +++ /dev/null @@ -1,139 +0,0 @@ -//===----------------------------------------------------------------------===// -// Clang Static Analyzer -//===----------------------------------------------------------------------===// - -= Library Structure = - -The analyzer library has two layers: a (low-level) static analysis -engine (GRExprEngine.cpp and friends), and some static checkers -(*Checker.cpp). The latter are built on top of the former via the -Checker and CheckerVisitor interfaces (Checker.h and -CheckerVisitor.h). The Checker interface is designed to be minimal -and simple for checker writers, and attempts to isolate them from much -of the gore of the internal analysis engine. - -= How It Works = - -The analyzer is inspired by several foundational research papers ([1], -[2]). (FIXME: kremenek to add more links) - -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 -(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). - -Conceptually the analyzer does a reachability analysis through the -ExplodedGraph. We start at a root node, which has the entry program -point and initial state, and then simulate transitions by analyzing -individual expressions. The analysis of an expression can cause the -state to change, resulting in a new node in the ExplodedGraph with an -updated program point and an updated state. A bug is found by hitting -a node that satisfies some "bug condition" (basically a violation of a -checking invariant). - -The analyzer traces out multiple paths by reasoning about branches and -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 -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 -path-sensitivity. We reduce exponential blow-up by caching nodes. If -a new node with the same state and program point as an existing node -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 -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. - -Finally, individual Checkers work by also manipulating the analysis -state. The analyzer engine talks to them via a visitor interface. -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 -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. - -= Notes about C++ = - -Since now constructors are seen before the variable that is constructed -in the CFG, we create a temporary object as the destination region that -is constructed into. See ExprEngine::VisitCXXConstructExpr(). - -In ExprEngine::processCallExit(), we always bind the object region to the -evaluated CXXConstructExpr. Then in VisitDeclStmt(), we compute the -corresponding lazy compound value if the variable is not a reference, and -bind the variable region to the lazy compound value. If the variable -is a reference, just use the object region as the initilizer value. - -Before entering a C++ method (or ctor/dtor), the 'this' region is bound -to the object region. In ctors, we synthesize 'this' region with -CXXRecordDecl*, which means we do not use type qualifiers. In methods, we -synthesize 'this' region with CXXMethodDecl*, which has getThisType() -taking type qualifiers into account. It does not matter we use qualified -'this' region in one method and unqualified 'this' region in another -method, because we only need to ensure the 'this' region is consistent -when we synthesize it and create it directly from CXXThisExpr in a single -method call. - -= Working on the Analyzer = - -If you are interested in bringing up support for C++ expressions, the -best place to look is the visitation logic in GRExprEngine, which -handles the simulation of individual expressions. There are plenty of -examples there of how other expressions are handled. - -If you are interested in writing checkers, look at the Checker and -CheckerVisitor interfaces (Checker.h and CheckerVisitor.h). Also look -at the files named *Checker.cpp for examples on how you can implement -these interfaces. - -= Debugging the Analyzer = - -There are some useful command-line options for debugging. For example: - -$ clang -cc1 -help | grep analyze - -analyze-function <value> - -analyzer-display-progress - -analyzer-viz-egraph-graphviz - ... - -The first allows you to specify only analyzing a specific function. -The second prints to the console what function is being analyzed. The -third generates a graphviz dot file of the ExplodedGraph. This is -extremely useful when debugging the analyzer and viewing the -simulation results. - -Of course, viewing the CFG (Control-Flow Graph) is also useful: - -$ clang -cc1 -help | grep cfg - -cfg-add-implicit-dtors Add C++ implicit destructors to CFGs for all analyses - -cfg-add-initializers Add C++ initializers to CFGs for all analyses - -cfg-dump Display Control-Flow Graphs - -cfg-view View Control-Flow Graphs using GraphViz - -unoptimized-cfg Generate unoptimized CFGs for all analyses - --cfg-dump dumps a textual representation of the CFG to the console, -and -cfg-view creates a GraphViz representation. - -= References = - -[1] Precise interprocedural dataflow analysis via graph reachability, - T Reps, S Horwitz, and M Sagiv, POPL '95, - http://portal.acm.org/citation.cfm?id=199462 - -[2] A memory model for static analysis of C programs, Z Xu, T - Kremenek, and J Zhang, http://lcs.ios.ac.cn/~xzx/memmodel.pdf |