diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
39 files changed, 3765 insertions, 7684 deletions
diff --git a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp index 901190d901dd..0936d61784ac 100644 --- a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp +++ b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp @@ -46,7 +46,7 @@ public: void AggExprVisitor::VisitCastExpr(CastExpr *E) { switch (E->getCastKind()) { default: - assert(0 && "Unhandled cast kind"); + llvm_unreachable("Unhandled cast kind"); case CK_NoOp: case CK_ConstructorConversion: case CK_UserDefinedConversion: diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5f4f83c0a8dc..17ec70d39b90 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -14,6 +14,58 @@ using namespace clang; using namespace ento; +AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + const LangOptions &lang, + PathDiagnosticConsumer *pd, + StoreManagerCreator storemgr, + ConstraintManagerCreator constraintmgr, + CheckerManager *checkerMgr, + idx::Indexer *idxer, + unsigned maxnodes, unsigned maxvisit, + bool vizdot, bool vizubi, + AnalysisPurgeMode purge, + bool eager, bool trim, + bool inlinecall, bool useUnoptimizedCFG, + bool addImplicitDtors, bool addInitializers, + bool eagerlyTrimEGraph) + : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), + Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd), + CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), + CheckerMgr(checkerMgr), Idxer(idxer), + AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), + VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), + EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall), + EagerlyTrimEGraph(eagerlyTrimEGraph) +{ + AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +} + +AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + AnalysisManager &ParentAM) + : AnaCtxMgr(ParentAM.AnaCtxMgr.getUseUnoptimizedCFG(), + ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, + ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), + Ctx(ctx), Diags(diags), + LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()), + CreateStoreMgr(ParentAM.CreateStoreMgr), + CreateConstraintMgr(ParentAM.CreateConstraintMgr), + CheckerMgr(ParentAM.CheckerMgr), + Idxer(ParentAM.Idxer), + AScope(ScopeDecl), + MaxNodes(ParentAM.MaxNodes), + MaxVisit(ParentAM.MaxVisit), + VisualizeEGDot(ParentAM.VisualizeEGDot), + VisualizeEGUbi(ParentAM.VisualizeEGUbi), + PurgeDead(ParentAM.PurgeDead), + EagerlyAssume(ParentAM.EagerlyAssume), + TrimGraph(ParentAM.TrimGraph), + InlineCall(ParentAM.InlineCall), + EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph) +{ + AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +} + + AnalysisContext * AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D), diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 3050ca3ce3cc..6c748b6ad01a 100644 --- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -8,14 +8,13 @@ //===----------------------------------------------------------------------===// // // This file defines BasicConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. +// equality and inequality constraints on symbolic values of ProgramState. // //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -25,7 +24,7 @@ using namespace ento; namespace { class ConstNotEq {}; } namespace { class ConstEq {}; } -typedef llvm::ImmutableMap<SymbolRef,GRState::IntSetTy> ConstNotEqTy; +typedef llvm::ImmutableMap<SymbolRef,ProgramState::IntSetTy> ConstNotEqTy; typedef llvm::ImmutableMap<SymbolRef,const llvm::APSInt*> ConstEqTy; static int ConstEqIndex = 0; @@ -34,13 +33,14 @@ static int ConstNotEqIndex = 0; namespace clang { namespace ento { template<> -struct GRStateTrait<ConstNotEq> : public GRStatePartialTrait<ConstNotEqTy> { - static inline void* GDMIndex() { return &ConstNotEqIndex; } +struct ProgramStateTrait<ConstNotEq> : + public ProgramStatePartialTrait<ConstNotEqTy> { + static inline void *GDMIndex() { return &ConstNotEqIndex; } }; template<> -struct GRStateTrait<ConstEq> : public GRStatePartialTrait<ConstEqTy> { - static inline void* GDMIndex() { return &ConstEqIndex; } +struct ProgramStateTrait<ConstEq> : public ProgramStatePartialTrait<ConstEqTy> { + static inline void *GDMIndex() { return &ConstEqIndex; } }; } } @@ -50,62 +50,81 @@ namespace { // constants and integer variables. class BasicConstraintManager : public SimpleConstraintManager { - GRState::IntSetTy::Factory ISetFactory; + ProgramState::IntSetTy::Factory ISetFactory; public: - BasicConstraintManager(GRStateManager &statemgr, SubEngine &subengine) + BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine) : SimpleConstraintManager(subengine), ISetFactory(statemgr.getAllocator()) {} - const GRState *assumeSymNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymLT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymGT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymGE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; - bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - - const GRState* removeDeadBindings(const GRState* state, SymbolReaper& SymReaper); - - void print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep); + const ProgramState *assumeSymNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymEQ(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymLT(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymGT(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymGE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymLE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *AddEQ(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V); + + const ProgramState *AddNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const ProgramState *state, + SymbolRef sym) const; + + bool isNotEqual(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) const; + + bool isEqual(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) const; + + const ProgramState *removeDeadBindings(const ProgramState *state, + SymbolReaper& SymReaper); + + void print(const ProgramState *state, + raw_ostream &Out, + const char* nl, + const char *sep); }; } // end anonymous namespace -ConstraintManager* ento::CreateBasicConstraintManager(GRStateManager& statemgr, - SubEngine &subengine) { +ConstraintManager* +ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, + SubEngine &subengine) { return new BasicConstraintManager(statemgr, subengine); } - -const GRState* -BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymNE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // First, determine if sym == X, where X+Adjustment != V. @@ -124,8 +143,9 @@ BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym, return AddNE(state, sym, Adjusted); } -const GRState* -BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymEQ(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // First, determine if sym == X, where X+Adjustment != V. @@ -145,8 +165,9 @@ BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym, } // The logic for these will be handled in another ConstraintManager. -const GRState* -BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymLT(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Is 'V' the smallest possible value? @@ -159,8 +180,9 @@ BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym, return assumeSymNE(state, sym, V, Adjustment); } -const GRState* -BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymGT(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Is 'V' the largest possible value? @@ -173,8 +195,9 @@ BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym, return assumeSymNE(state, sym, V, Adjustment); } -const GRState* -BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymGE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Reject a path if the value of sym is a constant X and !(X+Adj >= V). @@ -201,8 +224,9 @@ BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym, return state; } -const GRState* -BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymLE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Reject a path if the value of sym is a constant X and !(X+Adj <= V). @@ -229,18 +253,20 @@ BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym, return state; } -const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, +const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) { // Create a new state with the old binding replaced. return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V)); } -const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { +const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) { // First, retrieve the NE-set associated with the given symbol. ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); - GRState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); + ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); // Now add V to the NE set. S = ISetFactory.add(S, &state->getBasicVals().getValue(V)); @@ -249,13 +275,14 @@ const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym return state->set<ConstNotEq>(sym, S); } -const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, +const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state, SymbolRef sym) const { const ConstEqTy::data_type* T = state->get<ConstEq>(sym); return T ? *T : NULL; } -bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, +bool BasicConstraintManager::isNotEqual(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the NE-set associated with the given symbol. @@ -265,7 +292,8 @@ bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, return T ? T->contains(&state->getBasicVals().getValue(V)) : false; } -bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, +bool BasicConstraintManager::isEqual(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the EQ-set associated with the given symbol. const ConstEqTy::data_type* T = state->get<ConstEq>(sym); @@ -275,8 +303,8 @@ bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -BasicConstraintManager::removeDeadBindings(const GRState* state, +const ProgramState* +BasicConstraintManager::removeDeadBindings(const ProgramState *state, SymbolReaper& SymReaper) { ConstEqTy CE = state->get<ConstEq>(); @@ -301,7 +329,8 @@ BasicConstraintManager::removeDeadBindings(const GRState* state, return state->set<ConstNotEq>(CNE); } -void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, +void BasicConstraintManager::print(const ProgramState *state, + raw_ostream &Out, const char* nl, const char *sep) { // Print equality constraints. @@ -324,7 +353,7 @@ void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, Out << nl << " $" << I.getKey() << " : "; bool isFirst = true; - GRState::IntSetTy::iterator J = I.getData().begin(), + ProgramState::IntSetTy::iterator J = I.getData().begin(), EJ = I.getData().end(); for ( ; J != EJ; ++J) { diff --git a/lib/StaticAnalyzer/Core/BasicStore.cpp b/lib/StaticAnalyzer/Core/BasicStore.cpp deleted file mode 100644 index 7c9f45a474c1..000000000000 --- a/lib/StaticAnalyzer/Core/BasicStore.cpp +++ /dev/null @@ -1,605 +0,0 @@ -//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the BasicStore and BasicStoreManager classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/DeclCXX.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/AnalysisContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; -using namespace ento; - -typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy; - -namespace { - -class BasicStoreSubRegionMap : public SubRegionMap { -public: - BasicStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* R, Visitor& V) const { - return true; // Do nothing. No subregions. - } -}; - -class BasicStoreManager : public StoreManager { - BindingsTy::Factory VBFactory; -public: - BasicStoreManager(GRStateManager& mgr) - : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} - - ~BasicStoreManager() {} - - SubRegionMap *getSubRegionMap(Store store) { - return new BasicStoreSubRegionMap(); - } - - SVal Retrieve(Store store, Loc loc, QualType T = QualType()); - - StoreRef invalidateRegion(Store store, const MemRegion *R, const Expr *E, - unsigned Count, InvalidatedSymbols &IS); - - StoreRef invalidateRegions(Store store, const MemRegion * const *Begin, - const MemRegion * const *End, const Expr *E, - unsigned Count, InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions); - - StoreRef scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St); - - StoreRef Bind(Store St, Loc loc, SVal V); - StoreRef Remove(Store St, Loc loc); - StoreRef getInitialStore(const LocationContext *InitLoc); - - StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr*, - const LocationContext*, SVal val) { - return StoreRef(store, *this); - } - - /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit - /// conversions between arrays and pointers. - SVal ArrayToPointer(Loc Array) { return Array; } - - /// removeDeadBindings - Scans a BasicStore of 'state' for dead values. - /// It updatees the GRState object in place with the values removed. - StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); - - void iterBindings(Store store, BindingsHandler& f); - - StoreRef BindDecl(Store store, const VarRegion *VR, SVal InitVal) { - return BindDeclInternal(store, VR, &InitVal); - } - - StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR) { - return BindDeclInternal(store, VR, 0); - } - - StoreRef BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); - - static inline BindingsTy GetBindings(Store store) { - return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - -private: - SVal LazyRetrieve(Store store, const TypedRegion *R); -}; - -} // end anonymous namespace - - -StoreManager* ento::CreateBasicStoreManager(GRStateManager& StMgr) { - return new BasicStoreManager(StMgr); -} - -static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { - bool foundPointer = false; - while (1) { - const PointerType *PT = T->getAs<PointerType>(); - if (!PT) { - if (!foundPointer) - return false; - - // intptr_t* or intptr_t**, etc? - if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) - return true; - - QualType X = C.getCanonicalType(T).getUnqualifiedType(); - return X == C.VoidTy; - } - - foundPointer = true; - T = PT->getPointeeType(); - } -} - -SVal BasicStoreManager::LazyRetrieve(Store store, const TypedRegion *R) { - const VarRegion *VR = dyn_cast<VarRegion>(R); - if (!VR) - return UnknownVal(); - - const VarDecl *VD = VR->getDecl(); - QualType T = VD->getType(); - - // Only handle simple types that we can symbolicate. - if (!SymbolManager::canSymbolicate(T) || !T->isScalarType()) - return UnknownVal(); - - // Globals and parameters start with symbolic values. - // Local variables initially are undefined. - - // Non-static globals may have had their values reset by invalidateRegions. - const MemSpaceRegion *MS = VR->getMemorySpace(); - if (isa<NonStaticGlobalSpaceRegion>(MS)) { - BindingsTy B = GetBindings(store); - // FIXME: Copy-and-pasted from RegionStore.cpp. - if (BindingsTy::data_type *Val = B.lookup(MS)) { - if (SymbolRef parentSym = Val->getAsSymbol()) - return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); - - if (Val->isZeroConstant()) - return svalBuilder.makeZeroVal(T); - - if (Val->isUnknownOrUndef()) - return *Val; - - assert(0 && "Unknown default value."); - } - } - - if (VR->hasGlobalsOrParametersStorage() || - isa<UnknownSpaceRegion>(VR->getMemorySpace())) - return svalBuilder.getRegionValueSymbolVal(R); - - return UndefinedVal(); -} - -SVal BasicStoreManager::Retrieve(Store store, Loc loc, QualType T) { - if (isa<UnknownVal>(loc)) - return UnknownVal(); - - assert(!isa<UndefinedVal>(loc)); - - switch (loc.getSubKind()) { - - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || - isa<CXXThisRegion>(R))) - return UnknownVal(); - - BindingsTy B = GetBindings(store); - BindingsTy::data_type *Val = B.lookup(R); - const TypedRegion *TR = cast<TypedRegion>(R); - - if (Val) - return CastRetrievedVal(*Val, TR, T); - - SVal V = LazyRetrieve(store, TR); - return V.isUnknownOrUndef() ? V : CastRetrievedVal(V, TR, T); - } - - case loc::ObjCPropRefKind: - case loc::ConcreteIntKind: - // Support direct accesses to memory. It's up to individual checkers - // to flag an error. - return UnknownVal(); - - default: - assert (false && "Invalid Loc."); - break; - } - - return UnknownVal(); -} - -StoreRef BasicStoreManager::Bind(Store store, Loc loc, SVal V) { - if (isa<loc::ConcreteInt>(loc)) - return StoreRef(store, *this); - - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - // Special case: a default symbol assigned to the NonStaticGlobalsSpaceRegion - // that is used to derive other symbols. - if (isa<NonStaticGlobalSpaceRegion>(R)) { - BindingsTy B = GetBindings(store); - return StoreRef(VBFactory.add(B, R, V).getRoot(), *this); - } - - // Special case: handle store of pointer values (Loc) to pointers via - // a cast to intXX_t*, void*, etc. This is needed to handle - // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. - if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V)) - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: Should check for index 0. - QualType T = ER->getLocationType(); - - if (isHigherOrderRawPtr(T, Ctx)) - R = ER->getSuperRegion(); - } - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || isa<CXXThisRegion>(R))) - return StoreRef(store, *this); - - const TypedRegion *TyR = cast<TypedRegion>(R); - - // Do not bind to arrays. We need to explicitly check for this so that - // we do not encounter any weirdness of trying to load/store from arrays. - if (TyR->isBoundable() && TyR->getValueType()->isArrayType()) - return StoreRef(store, *this); - - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) { - // Only convert 'V' to a location iff the underlying region type - // is a location as well. - // FIXME: We are allowing a store of an arbitrary location to - // a pointer. We may wish to flag a type error here if the types - // are incompatible. This may also cause lots of breakage - // elsewhere. Food for thought. - if (TyR->isBoundable() && Loc::isLocType(TyR->getValueType())) - V = X->getLoc(); - } - - BindingsTy B = GetBindings(store); - return StoreRef(V.isUnknown() - ? VBFactory.remove(B, R).getRoot() - : VBFactory.add(B, R, V).getRoot(), *this); -} - -StoreRef BasicStoreManager::Remove(Store store, Loc loc) { - switch (loc.getSubKind()) { - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || - isa<CXXThisRegion>(R))) - return StoreRef(store, *this); - - return StoreRef(VBFactory.remove(GetBindings(store), R).getRoot(), *this); - } - default: - assert ("Remove for given Loc type not yet implemented."); - return StoreRef(store, *this); - } -} - -StoreRef BasicStoreManager::removeDeadBindings(Store store, - const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) -{ - BindingsTy B = GetBindings(store); - typedef SVal::symbol_iterator symbol_iterator; - - // Iterate over the variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - if (const VarRegion *VR = dyn_cast<VarRegion>(I.getKey())) { - if (SymReaper.isLive(VR)) - RegionRoots.push_back(VR); - else - continue; - } - else if (isa<ObjCIvarRegion>(I.getKey()) || - isa<NonStaticGlobalSpaceRegion>(I.getKey()) || - isa<CXXThisRegion>(I.getKey())) - RegionRoots.push_back(I.getKey()); - else - continue; - - // Mark the bindings in the data as live. - SVal X = I.getData(); - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.markLive(*SI); - } - - // Scan for live variables and live symbols. - llvm::SmallPtrSet<const MemRegion*, 10> Marked; - - while (!RegionRoots.empty()) { - const MemRegion* MR = RegionRoots.back(); - RegionRoots.pop_back(); - - while (MR) { - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(MR)) { - SymReaper.markLive(SymR->getSymbol()); - break; - } - else if (isa<VarRegion>(MR) || isa<ObjCIvarRegion>(MR) || - isa<NonStaticGlobalSpaceRegion>(MR) || isa<CXXThisRegion>(MR)) { - if (Marked.count(MR)) - break; - - Marked.insert(MR); - SVal X = Retrieve(store, loc::MemRegionVal(MR)); - - // FIXME: We need to handle symbols nested in region definitions. - for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) - SymReaper.markLive(*SI); - - if (!isa<loc::MemRegionVal>(X)) - break; - - const loc::MemRegionVal& LVD = cast<loc::MemRegionVal>(X); - RegionRoots.push_back(LVD.getRegion()); - break; - } - else if (const SubRegion* R = dyn_cast<SubRegion>(MR)) - MR = R->getSuperRegion(); - else - break; - } - } - - // Remove dead variable bindings. - StoreRef newStore(store, *this); - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - const MemRegion* R = I.getKey(); - - if (!Marked.count(R)) { - newStore = Remove(newStore.getStore(), svalBuilder.makeLoc(R)); - SVal X = I.getData(); - - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.maybeDead(*SI); - } - } - - return newStore; -} - -StoreRef BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, - Store St) { - - StoreRef newStore(St, *this); - - for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); - CI != CE; ++CI) { - - if (!*CI) - continue; - - // Check if the statement is an ivar reference. We only - // care about self.ivar. - if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(*CI)) { - const Expr *Base = IV->getBase()->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Base)) { - if (DR->getDecl() == SelfDecl) { - const ObjCIvarRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), - SelfRegion); - SVal X = svalBuilder.getRegionValueSymbolVal(IVR); - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(IVR), X); - } - } - } - else - newStore = scanForIvars(*CI, SelfDecl, SelfRegion, newStore.getStore()); - } - - return newStore; -} - -StoreRef BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { - // The LiveVariables information already has a compilation of all VarDecls - // used in the function. Iterate through this set, and "symbolicate" - // any VarDecl whose value originally comes from outside the function. - typedef LiveVariables::AnalysisDataTy LVDataTy; - LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); - StoreRef St(VBFactory.getEmptyMap().getRoot(), *this); - - for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { - const NamedDecl* ND = I->first; - - // Handle implicit parameters. - if (const ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) { - const Decl& CD = *InitLoc->getDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) { - if (MD->getSelfDecl() == PD) { - // FIXME: Add type constraints (when they become available) to - // SelfRegion? (i.e., it implements MD->getClassInterface()). - const VarRegion *VR = MRMgr.getVarRegion(PD, InitLoc); - const MemRegion *SelfRegion = - svalBuilder.getRegionValueSymbolVal(VR).getAsRegion(); - assert(SelfRegion); - St = Bind(St.getStore(), svalBuilder.makeLoc(VR), - loc::MemRegionVal(SelfRegion)); - // Scan the method for ivar references. While this requires an - // entire AST scan, the cost should not be high in practice. - St = scanForIvars(MD->getBody(), PD, SelfRegion, St.getStore()); - } - } - } - } - - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(InitLoc->getDecl())) { - // For C++ non-static member variables, add a symbolic region for 'this' in - // the initial stack frame. - if (MD->isInstance()) { - QualType ThisT = MD->getThisType(StateMgr.getContext()); - MemRegionManager &RegMgr = svalBuilder.getRegionManager(); - const CXXThisRegion *ThisR = RegMgr.getCXXThisRegion(ThisT, InitLoc); - SVal ThisV = svalBuilder.getRegionValueSymbolVal(ThisR); - St = Bind(St.getStore(), svalBuilder.makeLoc(ThisR), ThisV); - } - } - - return St; -} - -StoreRef BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, - SVal* InitVal) { - - BasicValueFactory& BasicVals = StateMgr.getBasicVals(); - const VarDecl *VD = VR->getDecl(); - StoreRef newStore(store, *this); - - // BasicStore does not model arrays and structs. - if (VD->getType()->isArrayType() || VD->getType()->isStructureOrClassType()) - return newStore; - - if (VD->hasGlobalStorage()) { - // Handle variables with global storage: extern, static, PrivateExtern. - - // FIXME:: static variables may have an initializer, but the second time a - // function is called those values may not be current. Currently, a function - // will not be called more than once. - - // Static global variables should not be visited here. - assert(!(VD->getStorageClass() == SC_Static && - VD->isFileVarDecl())); - - // Process static variables. - if (VD->getStorageClass() == SC_Static) { - // C99: 6.7.8 Initialization - // If an object that has static storage duration is not initialized - // explicitly, then: - // -if it has pointer type, it is initialized to a null pointer; - // -if it has arithmetic type, it is initialized to (positive or - // unsigned) zero; - if (!InitVal) { - QualType T = VD->getType(); - if (Loc::isLocType(T)) - newStore = Bind(store, loc::MemRegionVal(VR), - loc::ConcreteInt(BasicVals.getValue(0, T))); - else if (T->isIntegerType() && T->isScalarType()) - newStore = Bind(store, loc::MemRegionVal(VR), - nonloc::ConcreteInt(BasicVals.getValue(0, T))); - } else { - newStore = Bind(store, loc::MemRegionVal(VR), *InitVal); - } - } - } else { - // Process local scalar variables. - QualType T = VD->getType(); - // BasicStore only supports scalars. - if ((T->isScalarType() || T->isReferenceType()) && - svalBuilder.getSymbolManager().canSymbolicate(T)) { - SVal V = InitVal ? *InitVal : UndefinedVal(); - newStore = Bind(store, loc::MemRegionVal(VR), V); - } - } - - return newStore; -} - -void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - BindingsTy B = GetBindings(store); - Out << "Variables:" << nl; - - bool isFirst = true; - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { - if (isFirst) - isFirst = false; - else - Out << nl; - - Out << ' ' << I.getKey() << " : " << I.getData(); - } -} - - -void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { - BindingsTy B = GetBindings(store); - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) - if (!f.HandleBinding(*this, store, I.getKey(), I.getData())) - return; - -} - -StoreManager::BindingsHandler::~BindingsHandler() {} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - - -StoreRef BasicStoreManager::invalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *End, - const Expr *E, unsigned Count, - InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions) { - StoreRef newStore(store, *this); - - if (invalidateGlobals) { - BindingsTy B = GetBindings(store); - for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) { - const MemRegion *R = I.getKey(); - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - newStore = invalidateRegion(newStore.getStore(), R, E, Count, IS); - } - } - - for ( ; I != End ; ++I) { - const MemRegion *R = *I; - // Don't invalidate globals twice. - if (invalidateGlobals) { - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - continue; - } - newStore = invalidateRegion(newStore.getStore(), *I, E, Count, IS); - if (Regions) - Regions->push_back(R); - } - - // FIXME: This is copy-and-paste from RegionStore.cpp. - if (invalidateGlobals) { - // Bind the non-static globals memory space to a new symbol that we will - // use to derive the bindings for all non-static globals. - const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(); - SVal V = - svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, E, - /* symbol type, doesn't matter */ Ctx.IntTy, - Count); - - newStore = Bind(newStore.getStore(), loc::MemRegionVal(GS), V); - if (Regions) - Regions->push_back(GS); - } - - return newStore; -} - - -StoreRef BasicStoreManager::invalidateRegion(Store store, - const MemRegion *R, - const Expr *E, - unsigned Count, - InvalidatedSymbols &IS) { - R = R->StripCasts(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return StoreRef(store, *this); - - BindingsTy B = GetBindings(store); - if (BindingsTy::data_type *Val = B.lookup(R)) { - if (SymbolRef Sym = Val->getAsSymbol()) - IS.insert(Sym); - } - - QualType T = cast<TypedRegion>(R)->getValueType(); - SVal V = svalBuilder.getConjuredSymbolVal(R, E, T, Count); - return Bind(store, loc::MemRegionVal(R), V); -} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 0ed4ff143171..fe96700772d6 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -27,7 +27,7 @@ void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, const StoreRef &store, - const TypedRegion *region) { + const TypedValueRegion *region) { ID.AddPointer(store.getStore()); ID.AddPointer(region); } @@ -70,7 +70,7 @@ BasicValueFactory::~BasicValueFactory() { const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy; X.Profile(ID); @@ -113,7 +113,7 @@ BasicValueFactory::getCompoundValData(QualType T, llvm::FoldingSetNodeID ID; CompoundValData::Profile(ID, T, Vals); - void* InsertPos; + void *InsertPos; CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); @@ -128,10 +128,10 @@ BasicValueFactory::getCompoundValData(QualType T, const LazyCompoundValData* BasicValueFactory::getLazyCompoundValData(const StoreRef &store, - const TypedRegion *region) { + const TypedValueRegion *region) { llvm::FoldingSetNodeID ID; LazyCompoundValData::Profile(ID, store, region); - void* InsertPos; + void *InsertPos; LazyCompoundValData *D = LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); @@ -243,7 +243,7 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; V.Profile(ID); ID.AddPointer((void*) Data); @@ -268,7 +268,7 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; V1.Profile(ID); V2.Profile(ID); diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp index ed52b6b012b7..74d761e1ecc1 100644 --- a/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -48,11 +48,11 @@ public: typedef llvm::ImmutableMap<CountKey, unsigned> CountMap; -static inline CountMap GetMap(void* D) { +static inline CountMap GetMap(void *D) { return CountMap(static_cast<CountMap::TreeTy*>(D)); } -static inline CountMap::Factory& GetFactory(void* F) { +static inline CountMap::Factory& GetFactory(void *F) { return *static_cast<CountMap::Factory*>(F); } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 8b5d383ed07f..fbbdb040e4f2 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -33,52 +33,31 @@ using namespace clang; using namespace ento; BugReporterVisitor::~BugReporterVisitor() {} -BugReporterContext::~BugReporterContext() { - for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) - if ((*I)->isOwnedByReporterContext()) delete *I; -} - -void BugReporterContext::addVisitor(BugReporterVisitor* visitor) { - if (!visitor) - return; - - llvm::FoldingSetNodeID ID; - visitor->Profile(ID); - void *InsertPos; - - if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { - delete visitor; - return; - } - - CallbacksSet.InsertNode(visitor, InsertPos); - Callbacks = F.add(visitor, Callbacks); -} //===----------------------------------------------------------------------===// // Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// -static inline const Stmt* GetStmt(const ProgramPoint &P) { +static inline const Stmt *GetStmt(const ProgramPoint &P) { if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P)) return SP->getStmt(); - else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) + else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) return BE->getSrc()->getTerminator(); return 0; } static inline const ExplodedNode* -GetPredecessorNode(const ExplodedNode* N) { +GetPredecessorNode(const ExplodedNode *N) { return N->pred_empty() ? NULL : *(N->pred_begin()); } static inline const ExplodedNode* -GetSuccessorNode(const ExplodedNode* N) { +GetSuccessorNode(const ExplodedNode *N) { return N->succ_empty() ? NULL : *(N->succ_begin()); } -static const Stmt* GetPreviousStmt(const ExplodedNode* N) { +static const Stmt *GetPreviousStmt(const ExplodedNode *N) { for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -86,7 +65,7 @@ static const Stmt* GetPreviousStmt(const ExplodedNode* N) { return 0; } -static const Stmt* GetNextStmt(const ExplodedNode* N) { +static const Stmt *GetNextStmt(const ExplodedNode *N) { for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) if (const Stmt *S = GetStmt(N->getLocation())) { // Check if the statement is '?' or '&&'/'||'. These are "merges", @@ -104,11 +83,6 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) { default: break; } - - // Some expressions don't have locations. - if (S->getLocStart().isInvalid()) - continue; - return S; } @@ -116,7 +90,7 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) { } static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode* N) { +GetCurrentOrPreviousStmt(const ExplodedNode *N) { if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -124,7 +98,7 @@ GetCurrentOrPreviousStmt(const ExplodedNode* N) { } static inline const Stmt* -GetCurrentOrNextStmt(const ExplodedNode* N) { +GetCurrentOrNextStmt(const ExplodedNode *N) { if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -145,7 +119,7 @@ public: NodeMapClosure(NodeBackMap *m) : M(*m) {} ~NodeMapClosure() {} - const ExplodedNode* getOriginalNode(const ExplodedNode* N) { + const ExplodedNode *getOriginalNode(const ExplodedNode *N) { NodeBackMap::iterator I = M.find(N); return I == M.end() ? 0 : I->second; } @@ -153,25 +127,29 @@ public: class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; - PathDiagnosticClient *PDC; + PathDiagnosticConsumer *PDC; llvm::OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, NodeBackMap *Backmap, - PathDiagnosticClient *pdc) + PathDiagnosticConsumer *pdc) : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) { - addVisitor(R); - } + R(r), PDC(pdc), NMC(Backmap) {} - PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); + PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N); + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N); + + BugReport *getBugReport() { return R; } Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } + const LocationContext* getLocationContext() { + return R->getErrorNode()->getLocationContext(); + } + ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); } const Stmt *getParent(const Stmt *S) { @@ -182,8 +160,8 @@ public: PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; + PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { + return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive; } bool supportsLogicalOpControlFlow() const { @@ -193,17 +171,17 @@ public: } // end anonymous namespace PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { +PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager()); + return PathDiagnosticLocation(S, getSourceManager(), getLocationContext()); - return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), - getSourceManager()); + return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), + getSourceManager()); } PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N) { +PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N) { // Slow, but probably doesn't matter. if (os.str().empty()) @@ -213,7 +191,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, if (Loc.asStmt()) os << "Execution continues on line " - << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) + << getSourceManager().getExpansionLineNumber(Loc.asLocation()) << '.'; else { os << "Execution jumps to the end of the "; @@ -253,9 +231,10 @@ static bool IsNested(const Stmt *S, ParentMap &PM) { PathDiagnosticLocation PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); + assert(S && "Null Stmt *passed to getEnclosingStmtLocation"); ParentMap &P = getParentMap(); SourceManager &SMgr = getSourceManager(); + const LocationContext *LC = getLocationContext(); while (IsNested(S, P)) { const Stmt *Parent = P.getParentIgnoreParens(S); @@ -267,44 +246,44 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { case Stmt::BinaryOperatorClass: { const BinaryOperator *B = cast<BinaryOperator>(Parent); if (B->isLogicalOp()) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; } case Stmt::CompoundStmtClass: case Stmt::StmtExprClass: - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::ChooseExprClass: // Similar to '?' if we are referring to condition, just have the edge // point to the entire choose expression. if (cast<ChooseExpr>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); else - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: // For '?', if we are referring to condition, just have the edge point // to the entire '?' expression. if (cast<AbstractConditionalOperator>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); else - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::DoStmtClass: - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::ForStmtClass: if (cast<ForStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::IfStmtClass: if (cast<IfStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::ObjCForCollectionStmtClass: if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::WhileStmtClass: if (cast<WhileStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; default: break; @@ -322,7 +301,7 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { switch (Parent->getStmtClass()) { case Stmt::ForStmtClass: case Stmt::ObjCForCollectionStmtClass: - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); default: break; } @@ -335,20 +314,20 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { if (const ForStmt *FS = dyn_cast_or_null<ForStmt>(P.getParentIgnoreParens(S))) { if (FS->getInit() == S) - return PathDiagnosticLocation(FS, SMgr); + return PathDiagnosticLocation(FS, SMgr, LC); } } - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); } //===----------------------------------------------------------------------===// // ScanNotableSymbols: closure-like callback for scanning Store bindings. //===----------------------------------------------------------------------===// -static const VarDecl* -GetMostRecentVarDeclBinding(const ExplodedNode* N, - GRStateManager& VMgr, SVal X) { +static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N, + ProgramStateManager& VMgr, + SVal X) { for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { @@ -357,7 +336,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N, if (!isa<PostStmt>(P)) continue; - const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); if (!DR) continue; @@ -367,7 +346,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N, if (X != Y) continue; - const VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()); + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) continue; @@ -383,19 +362,29 @@ class NotableSymbolHandler : public StoreManager::BindingsHandler { SymbolRef Sym; - const GRState* PrevSt; - const Stmt* S; - GRStateManager& VMgr; - const ExplodedNode* Pred; + const ProgramState *PrevSt; + const Stmt *S; + ProgramStateManager& VMgr; + const ExplodedNode *Pred; PathDiagnostic& PD; BugReporter& BR; public: - NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, - GRStateManager& vmgr, const ExplodedNode* pred, - PathDiagnostic& pd, BugReporter& br) - : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} + NotableSymbolHandler(SymbolRef sym, + const ProgramState *prevst, + const Stmt *s, + ProgramStateManager& vmgr, + const ExplodedNode *pred, + PathDiagnostic& pd, + BugReporter& br) + : Sym(sym), + PrevSt(prevst), + S(s), + VMgr(vmgr), + Pred(pred), + PD(pd), + BR(br) {} bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, SVal V) { @@ -422,14 +411,14 @@ public: return true; // What variable did we assign to? - DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts()); + DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts()); if (!DR) return true; VD = dyn_cast<VarDecl>(DR->getDecl()); } - else if (const DeclStmt* DS = dyn_cast<DeclStmt>(S)) { + else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { // FIXME: Eventually CFGs won't have DeclStmts. Right now we // assume that each DeclStmt has a single Decl. This invariant // holds by construction in the CFG. @@ -440,19 +429,20 @@ public: return true; // What is the most recently referenced variable with this binding? - const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); + const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); if (!MostRecent) return true; // Create the diagnostic. - FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); - if (Loc::isLocType(VD->getType())) { - std::string msg = "'" + std::string(VD->getNameAsString()) + - "' now aliases '" + MostRecent->getNameAsString() + "'"; - - PD.push_front(new PathDiagnosticEventPiece(L, msg)); + llvm::SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + os << '\'' << *VD << "' now aliases '" << *MostRecent << '\''; + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(S, BR.getSourceManager(), + Pred->getLocationContext()); + PD.push_front(new PathDiagnosticEventPiece(L, os.str())); } return true; @@ -460,20 +450,20 @@ public: }; } -static void HandleNotableSymbol(const ExplodedNode* N, - const Stmt* S, +static void HandleNotableSymbol(const ExplodedNode *N, + const Stmt *S, SymbolRef Sym, BugReporter& BR, PathDiagnostic& PD) { - const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); - const GRState* PrevSt = Pred ? Pred->getState() : 0; + const ExplodedNode *Pred = N->pred_empty() ? 0 : *N->pred_begin(); + const ProgramState *PrevSt = Pred ? Pred->getState() : 0; if (!PrevSt) return; // Look at the region bindings of the current state that map to the // specified symbol. Are any of them not in the previous state? - GRStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); + ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H); } @@ -483,13 +473,13 @@ class ScanNotableSymbols : public StoreManager::BindingsHandler { llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; - const ExplodedNode* N; - const Stmt* S; + const ExplodedNode *N; + const Stmt *S; GRBugReporter& BR; PathDiagnostic& PD; public: - ScanNotableSymbols(const ExplodedNode* n, const Stmt* s, + ScanNotableSymbols(const ExplodedNode *n, const Stmt *s, GRBugReporter& br, PathDiagnostic& pd) : N(n), S(s), BR(br), PD(pd) {} @@ -526,7 +516,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N) { SourceManager& SMgr = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() + const LocationContext *LC = PDB.getLocationContext(); + const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; @@ -534,15 +525,17 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock* Src = BE->getSrc(); - const CFGBlock* Dst = BE->getDst(); - const Stmt* T = Src->getTerminator(); + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + const CFGBlock *Src = BE->getSrc(); + const CFGBlock *Dst = BE->getDst(); + const Stmt *T = Src->getTerminator(); if (!T) continue; - FullSourceLoc Start(T->getLocStart(), SMgr); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createBegin(T, SMgr, + N->getLocationContext()); switch (T->getStmtClass()) { default: @@ -550,7 +543,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, case Stmt::GotoStmtClass: case Stmt::IndirectGotoStmtClass: { - const Stmt* S = GetNextStmt(N); + const Stmt *S = GetNextStmt(N); if (!S) continue; @@ -560,7 +553,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); os << "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; @@ -571,45 +564,45 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); - if (const Stmt* S = Dst->getLabel()) { - PathDiagnosticLocation End(S, SMgr); + if (const Stmt *S = Dst->getLabel()) { + PathDiagnosticLocation End(S, SMgr, LC); switch (S->getStmtClass()) { default: os << "No cases match in the switch statement. " "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; case Stmt::DefaultStmtClass: os << "Control jumps to the 'default' case at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; case Stmt::CaseStmtClass: { os << "Control jumps to 'case "; - const CaseStmt* Case = cast<CaseStmt>(S); - const Expr* LHS = Case->getLHS()->IgnoreParenCasts(); + const CaseStmt *Case = cast<CaseStmt>(S); + const Expr *LHS = Case->getLHS()->IgnoreParenCasts(); // Determine if it is an enum. bool GetRawInt = true; - if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) { + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) { // FIXME: Maybe this should be an assertion. Are there cases // were it is not an EnumConstantDecl? - const EnumConstantDecl* D = + const EnumConstantDecl *D = dyn_cast<EnumConstantDecl>(DR->getDecl()); if (D) { GetRawInt = false; - os << D; + os << *D; } } if (GetRawInt) - os << LHS->EvaluateAsInt(PDB.getASTContext()); + os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); os << ":' at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; } } @@ -673,14 +666,15 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (*(Src->succ_begin()+1) == Dst) { os << "false"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PathDiagnosticLocation End(B->getLHS(), SMgr, LC); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createOperatorLoc(B, SMgr); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); @@ -692,15 +686,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (*(Src->succ_begin()+1) == Dst) { os << "false"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PathDiagnosticLocation End(B->getLHS(), SMgr, LC); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createOperatorLoc(B, SMgr); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } @@ -781,14 +776,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } if (NextNode) { - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) + // Add diagnostic pieces from custom visitors. + BugReport *R = PDB.getBugReport(); + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) PD.push_front(p); } } - if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) { + if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { // Scan the region bindings, and see if a "notable" symbol has a new // lval binding. ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); @@ -882,11 +879,11 @@ class EdgeBuilder { } if (S != Original) - L = PathDiagnosticLocation(S, L.getManager()); + L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext()); } if (firstCharOnly) - L = PathDiagnosticLocation(L.asLocation()); + L = PathDiagnosticLocation::createSingleLocation(L); return L; } @@ -915,17 +912,14 @@ public: ~EdgeBuilder() { while (!CLocs.empty()) popLocation(); - + // Finally, add an initial edge from the start location of the first // statement (if it doesn't already exist). - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const CompoundStmt *CS = - dyn_cast_or_null<CompoundStmt>(PDB.getCodeDecl().getBody())) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getLocStart(); - rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); - } - + PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( + PDB.getLocationContext(), + PDB.getSourceManager()); + if (L.isValid()) + rawAddEdge(L); } void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); @@ -974,15 +968,15 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, SourceRange ContaineeR = Containee.asRange(); SourceManager &SM = PDB.getSourceManager(); - SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); - SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); - SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); - SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); + SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin()); + SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd()); + SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin()); + SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd()); - unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); - unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); - unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); - unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); + unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg); + unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd); + unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg); + unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd); assert(ContainerBegLine <= ContainerEndLine); assert(ContaineeBegLine <= ContaineeEndLine); @@ -990,11 +984,11 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, return (ContainerBegLine <= ContaineeBegLine && ContainerEndLine >= ContaineeEndLine && (ContainerBegLine != ContaineeBegLine || - SM.getInstantiationColumnNumber(ContainerRBeg) <= - SM.getInstantiationColumnNumber(ContaineeRBeg)) && + SM.getExpansionColumnNumber(ContainerRBeg) <= + SM.getExpansionColumnNumber(ContaineeRBeg)) && (ContainerEndLine != ContaineeEndLine || - SM.getInstantiationColumnNumber(ContainerREnd) >= - SM.getInstantiationColumnNumber(ContainerREnd))); + SM.getExpansionColumnNumber(ContainerREnd) >= + SM.getExpansionColumnNumber(ContainerREnd))); } void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { @@ -1010,8 +1004,8 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { return; // FIXME: Ignore intra-macro edges for now. - if (NewLocClean.asLocation().getInstantiationLoc() == - PrevLocClean.asLocation().getInstantiationLoc()) + if (NewLocClean.asLocation().getExpansionLoc() == + PrevLocClean.asLocation().getExpansionLoc()) return; PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); @@ -1099,7 +1093,7 @@ void EdgeBuilder::addContext(const Stmt *S) { if (!S) return; - PathDiagnosticLocation L(S, PDB.getSourceManager()); + PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext()); while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1124,8 +1118,9 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N) { EdgeBuilder EB(PD, PDB); + const SourceManager& SM = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); + const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; NextNode = GetPredecessorNode(N); @@ -1139,7 +1134,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, PDB.getSourceManager()); + PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext()); const CompoundStmt *CS = NULL; if (!Term) { @@ -1157,9 +1152,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PD.push_front(p); if (CS) { - PathDiagnosticLocation BL(CS->getRBracLoc(), - PDB.getSourceManager()); - BL = PathDiagnosticLocation(BL.asLocation()); + PathDiagnosticLocation BL = + PathDiagnosticLocation::createEndBrace(CS, SM); EB.addEdge(BL); } } @@ -1188,9 +1182,11 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, if (!NextNode) continue; - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { + // Add pieces from custom visitors. + BugReport *R = PDB.getBugReport(); + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { const PathDiagnosticLocation &Loc = p->getLocation(); EB.addEdge(Loc, true); PD.push_front(p); @@ -1211,14 +1207,58 @@ void BugType::FlushReports(BugReporter &BR) {} //===----------------------------------------------------------------------===// // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// -BugReport::~BugReport() {} -RangedBugReport::~RangedBugReport() {} -const Stmt* BugReport::getStmt() const { +void BugReport::addVisitor(BugReporterVisitor* visitor) { + if (!visitor) + return; + + llvm::FoldingSetNodeID ID; + visitor->Profile(ID); + void *InsertPos; + + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { + delete visitor; + return; + } + + CallbacksSet.InsertNode(visitor, InsertPos); + Callbacks = F.add(visitor, Callbacks); +} + +BugReport::~BugReport() { + for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) { + delete *I; + } +} + +void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddPointer(&BT); + hash.AddString(Description); + if (Location.isValid()) { + Location.Profile(hash); + } else { + assert(ErrorNode); + hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); + } + + for (SmallVectorImpl<SourceRange>::const_iterator I = + Ranges.begin(), E = Ranges.end(); I != E; ++I) { + const SourceRange range = *I; + if (!range.isValid()) + continue; + hash.AddInteger(range.getBegin().getRawEncoding()); + hash.AddInteger(range.getEnd().getRawEncoding()); + } +} + +const Stmt *BugReport::getStmt() const { + if (!ErrorNode) + return 0; + ProgramPoint ProgP = ErrorNode->getLocation(); const Stmt *S = NULL; - if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) { + if (BlockEntrance *BE = dyn_cast<BlockEntrance>(&ProgP)) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) S = GetPreviousStmt(ErrorNode); @@ -1229,61 +1269,47 @@ const Stmt* BugReport::getStmt() const { return S; } -PathDiagnosticPiece* -BugReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndPathNode) { - - const Stmt* S = getStmt(); - - if (!S) - return NULL; - - BugReport::ranges_iterator Beg, End; - llvm::tie(Beg, End) = getRanges(); - PathDiagnosticLocation L(S, BRC.getSourceManager()); - - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), - Beg == End); +std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator> +BugReport::getRanges() { + // If no custom ranges, add the range of the statement corresponding to + // the error node. + if (Ranges.empty()) { + if (const Expr *E = dyn_cast_or_null<Expr>(getStmt())) + addRange(E->getSourceRange()); + else + return std::make_pair(ranges_iterator(), ranges_iterator()); + } - for (; Beg != End; ++Beg) - P->addRange(*Beg); + // User-specified absence of range info. + if (Ranges.size() == 1 && !Ranges.begin()->isValid()) + return std::make_pair(ranges_iterator(), ranges_iterator()); - return P; + return std::make_pair(Ranges.begin(), Ranges.end()); } -std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator> -BugReport::getRanges() const { - if (const Expr* E = dyn_cast_or_null<Expr>(getStmt())) { - R = E->getSourceRange(); - assert(R.isValid()); - return std::make_pair(&R, &R+1); - } - else - return std::make_pair(ranges_iterator(), ranges_iterator()); -} +PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { + if (ErrorNode) { + assert(!Location.isValid() && + "Either Location or ErrorNode should be specified but not both."); + + if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) { + const LocationContext *LC = ErrorNode->getLocationContext(); -SourceLocation BugReport::getLocation() const { - if (ErrorNode) - if (const Stmt* S = GetCurrentOrPreviousStmt(ErrorNode)) { // For member expressions, return the location of the '.' or '->'. if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) - return ME->getMemberLoc(); + return PathDiagnosticLocation::createMemberLoc(ME, SM); // For binary operators, return the location of the operator. if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) - return B->getOperatorLoc(); + return PathDiagnosticLocation::createOperatorLoc(B, SM); - return S->getLocStart(); + return PathDiagnosticLocation::createBegin(S, SM, LC); } + } else { + assert(Location.isValid()); + return Location; + } - return FullSourceLoc(); -} - -PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext &BRC) { - return NULL; + return PathDiagnosticLocation(); } //===----------------------------------------------------------------------===// @@ -1299,10 +1325,19 @@ BugReporterData::~BugReporterData() {} ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } -GRStateManager& +ProgramStateManager& GRBugReporter::getStateManager() { return Eng.getStateManager(); } -BugReporter::~BugReporter() { FlushReports(); } +BugReporter::~BugReporter() { + FlushReports(); + + // Free the bug reports we are tracking. + typedef std::vector<BugReportEquivClass *> ContTy; + for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end(); + I != E; ++I) { + delete *I; + } +} void BugReporter::FlushReports() { if (BugTypes.isEmpty()) @@ -1312,10 +1347,10 @@ void BugReporter::FlushReports() { // warnings and new BugTypes. // FIXME: Only NSErrorChecker needs BugType's FlushReports. // Turn NSErrorChecker into a proper checker and remove this. - llvm::SmallVector<const BugType*, 16> bugTypes; + SmallVector<const BugType*, 16> bugTypes; for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) bugTypes.push_back(*I); - for (llvm::SmallVector<const BugType*, 16>::iterator + for (SmallVector<const BugType*, 16>::iterator I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); @@ -1344,7 +1379,7 @@ void BugReporter::FlushReports() { static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, std::pair<ExplodedNode*, unsigned> > MakeReportGraph(const ExplodedGraph* G, - llvm::SmallVectorImpl<const ExplodedNode*> &nodes) { + SmallVectorImpl<const ExplodedNode*> &nodes) { // Create the trimmed graph. It will contain the shortest paths from the // error nodes to the root. In the new graph we should only have one @@ -1390,10 +1425,10 @@ MakeReportGraph(const ExplodedGraph* G, llvm::DenseMap<const void*,unsigned> Visited; unsigned cnt = 0; - const ExplodedNode* Root = 0; + const ExplodedNode *Root = 0; while (!WS.empty()) { - const ExplodedNode* Node = WS.front(); + const ExplodedNode *Node = WS.front(); WS.pop(); if (Visited.find(Node) != Visited.end()) @@ -1426,7 +1461,7 @@ MakeReportGraph(const ExplodedGraph* G, // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); + ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState()); // Store the mapping to the original node. llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N); @@ -1495,7 +1530,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Determine the instantiation location, which is the location we group // related PathDiagnosticPieces. SourceLocation InstantiationLoc = Loc.isMacroID() ? - SM.getInstantiationLoc(Loc) : + SM.getExpansionLoc(Loc) : SourceLocation(); if (Loc.isFileID()) { @@ -1517,7 +1552,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { PathDiagnosticMacroPiece *MacroGroup = 0; SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? - SM.getInstantiationLoc(Loc) : + SM.getExpansionLoc(Loc) : SourceLocation(); // Walk the entire macro stack. @@ -1537,7 +1572,9 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { // Create a new macro group and add it to the stack. - PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); + PathDiagnosticMacroPiece *NewGroup = + new PathDiagnosticMacroPiece( + PathDiagnosticLocation::createSingleLocation(I->getLocation())); if (MacroGroup) MacroGroup->push_back(NewGroup); @@ -1569,11 +1606,11 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - llvm::SmallVectorImpl<BugReport *> &bugReports) { + SmallVectorImpl<BugReport *> &bugReports) { assert(!bugReports.empty()); - llvm::SmallVector<const ExplodedNode *, 10> errorNodes; - for (llvm::SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), + SmallVector<const ExplodedNode *, 10> errorNodes; + for (SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), E = bugReports.end(); I != E; ++I) { errorNodes.push_back((*I)->getErrorNode()); } @@ -1594,22 +1631,36 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); - - if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) - PD.push_back(Piece); + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), + getPathDiagnosticConsumer()); + + // Register additional node visitors. + R->addVisitor(new NilReceiverBRVisitor()); + R->addVisitor(new ConditionBRVisitor()); + + // Generate the very last diagnostic piece - the piece is visible before + // the trace is expanded. + PathDiagnosticPiece *LastPiece = 0; + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { + assert (!LastPiece && + "There can only be one final piece in a diagnostic."); + LastPiece = Piece; + } + } + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + if (LastPiece) + PD.push_back(LastPiece); else return; - // Register node visitors. - R->registerInitialVisitors(PDB, N); - bugreporter::registerNilReceiverVisitor(PDB); - switch (PDB.getGenerationScheme()) { - case PathDiagnosticClient::Extensive: + case PathDiagnosticConsumer::Extensive: GenerateExtensivePathDiagnostic(PD, PDB, N); break; - case PathDiagnosticClient::Minimal: + case PathDiagnosticConsumer::Minimal: GenerateMinimalPathDiagnostic(PD, PDB, N); break; } @@ -1633,6 +1684,7 @@ void BugReporter::EmitReport(BugReport* R) { if (!EQ) { EQ = new BugReportEquivClass(R); EQClasses.InsertNode(EQ, InsertPos); + EQClassesVector.push_back(EQ); } else EQ->AddReport(R); @@ -1655,7 +1707,7 @@ struct FRIEC_WLItem { static BugReport * FindReportInEquivalenceClass(BugReportEquivClass& EQ, - llvm::SmallVectorImpl<BugReport*> &bugReports) { + SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); @@ -1667,7 +1719,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode* N = I->getErrorNode(); + const ExplodedNode *N = I->getErrorNode(); if (N) { R = *I; bugReports.push_back(R); @@ -1691,9 +1743,8 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, if (!errorNode) continue; if (errorNode->isSink()) { - assert(false && + llvm_unreachable( "BugType::isSuppressSink() should not be 'true' for sink end nodes"); - return 0; } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { @@ -1706,7 +1757,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // At this point we know that 'N' is not a sink and it has at least one // successor. Use a DFS worklist to find a non-sink end-of-path node. typedef FRIEC_WLItem WLItem; - typedef llvm::SmallVector<WLItem, 10> DFSWorkList; + typedef SmallVector<WLItem, 10> DFSWorkList; llvm::DenseMap<const ExplodedNode *, unsigned> Visited; DFSWorkList WL; @@ -1763,11 +1814,8 @@ class DiagCacheItem : public llvm::FoldingSetNode { llvm::FoldingSetNodeID ID; public: DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - ID.AddString(R->getBugType().getName()); - ID.AddString(R->getBugType().getCategory()); - ID.AddString(R->getDescription()); - ID.AddInteger(R->getLocation().getRawEncoding()); - PD->Profile(ID); + R->Profile(ID); + PD->Profile(ID); } void Profile(llvm::FoldingSetNodeID &id) { @@ -1798,12 +1846,12 @@ static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { } void BugReporter::FlushReport(BugReportEquivClass& EQ) { - llvm::SmallVector<BugReport*, 10> bugReports; + SmallVector<BugReport*, 10> bugReports; BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); if (!exampleReport) return; - PathDiagnosticClient* PD = getPathDiagnosticClient(); + PathDiagnosticConsumer* PD = getPathDiagnosticConsumer(); // FIXME: Make sure we use the 'R' for the path that was actually used. // Probably doesn't make a difference in practice. @@ -1823,47 +1871,50 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { return; // Get the meta data. - std::pair<const char**, const char**> Meta = - exampleReport->getExtraDescriptiveText(); - for (const char** s = Meta.first; s != Meta.second; ++s) - D->addMeta(*s); + const BugReport::ExtraTextList &Meta = + exampleReport->getExtraText(); + for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), + e = Meta.end(); i != e; ++i) { + D->addMeta(*i); + } // Emit a summary diagnostic to the regular Diagnostics engine. BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = exampleReport->getRanges(); - Diagnostic &Diag = getDiagnostic(); - FullSourceLoc L(exampleReport->getLocation(), getSourceManager()); + DiagnosticsEngine &Diag = getDiagnostic(); // Search the description for '%', as that will be interpretted as a // format character by FormatDiagnostics. - llvm::StringRef desc = exampleReport->getShortDescription(); + StringRef desc = exampleReport->getShortDescription(); unsigned ErrorDiag; { llvm::SmallString<512> TmpStr; llvm::raw_svector_ostream Out(TmpStr); - for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) + for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) if (*I == '%') Out << "%%"; else Out << *I; Out.flush(); - ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); + ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); } { - DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); + DiagnosticBuilder diagBuilder = Diag.Report( + exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); for (BugReport::ranges_iterator I = Beg; I != End; ++I) diagBuilder << *I; } - // Emit a full diagnostic for the path if we have a PathDiagnosticClient. + // Emit a full diagnostic for the path if we have a PathDiagnosticConsumer. if (!PD) return; if (D->empty()) { - PathDiagnosticPiece* piece = - new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( + exampleReport->getLocation(getSourceManager()), + exampleReport->getDescription()); for ( ; Beg != End; ++Beg) piece->addRange(*Beg); D->push_back(piece); @@ -1872,27 +1923,26 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { PD->HandlePathDiagnostic(D.take()); } -void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, - SourceLocation Loc, +void BugReporter::EmitBasicReport(StringRef name, StringRef str, + PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); } -void BugReporter::EmitBasicReport(llvm::StringRef name, - llvm::StringRef category, - llvm::StringRef str, SourceLocation Loc, +void BugReporter::EmitBasicReport(StringRef name, + StringRef category, + StringRef str, PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(name, category); - FullSourceLoc L = getContext().getFullLoc(Loc); - RangedBugReport *R = new DiagBugReport(*BT, str, L); + BugReport *R = new BugReport(*BT, str, Loc); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); EmitReport(R); } -BugType *BugReporter::getBugTypeForName(llvm::StringRef name, - llvm::StringRef category) { +BugType *BugReporter::getBugTypeForName(StringRef name, + StringRef category) { llvm::SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << name << ":" << category; llvm::StringMapEntry<BugType *> & diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 8e31adead188..1abd8baef624 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -11,13 +11,15 @@ // enhance the diagnostics reported for a bug. // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; @@ -39,8 +41,6 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { return ME->getBase()->IgnoreParenCasts(); } else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { - // Retrieve the base for arrays since BasicStoreManager doesn't know how - // to reason about them. return AE->getBase(); } @@ -73,248 +73,255 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// -namespace { -class FindLastStoreBRVisitor : public BugReporterVisitor { - const MemRegion *R; - SVal V; - bool satisfied; - const ExplodedNode *StoreSite; -public: - FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false), StoreSite(0) {} - - virtual void Profile(llvm::FoldingSetNodeID &ID) const { - static int tag = 0; - ID.AddPointer(&tag); - ID.AddPointer(R); - ID.Add(V); +PathDiagnosticPiece* +BugReporterVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + return 0; +} + +PathDiagnosticPiece* +BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + const ProgramPoint &PP = EndPathNode->getLocation(); + PathDiagnosticLocation L; + + if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { + const CFGBlock *block = BE->getBlock(); + if (block->getBlockID() == 0) { + L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), + BRC.getSourceManager()); + } } - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { + if (!L.isValid()) { + const Stmt *S = BR.getStmt(); - if (satisfied) + if (!S) return NULL; - if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; + L = PathDiagnosticLocation(S, BRC.getSourceManager(), + PP.getLocationContext()); + } - for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = BR.getRanges(); - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - if (const PostStmt *P = Node->getLocationAs<PostStmt>()) - if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) - if (DS->getSingleDecl() == VR->getDecl()) { - Last = Node; - break; - } - } + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, + BR.getDescription(), + Beg == End); + for (; Beg != End; ++Beg) + P->addRange(*Beg); - if (Node->getState()->getSVal(R) != V) - break; - } + return P; +} - if (!Node || !Last) { - satisfied = true; - return NULL; + +void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddPointer(R); + ID.Add(V); +} + +PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + + if (satisfied) + return NULL; + + if (!StoreSite) { + const ExplodedNode *Node = N, *Last = NULL; + + for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + if (const PostStmt *P = Node->getLocationAs<PostStmt>()) + if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) + if (DS->getSingleDecl() == VR->getDecl()) { + Last = Node; + break; + } } - StoreSite = Last; + if (Node->getState()->getSVal(R) != V) + break; } - if (StoreSite != N) + if (!Node || !Last) { + satisfied = true; return NULL; + } - satisfied = true; - llvm::SmallString<256> sbuf; - llvm::raw_svector_ostream os(sbuf); + StoreSite = Last; + } - if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { - if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { + if (StoreSite != N) + return NULL; - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Variable '" << VR->getDecl() << "' "; - } - else - return NULL; - - if (isa<loc::ConcreteInt>(V)) { - bool b = false; - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType()->isObjCObjectPointerType()) { - os << "initialized to nil"; - b = true; - } - } - } + satisfied = true; + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); - if (!b) - os << "initialized to a null pointer value"; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); - } - else if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; - } - } + if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { + if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << "Variable '" << *VR->getDecl() << "' "; } - } + else + return NULL; - if (os.str().empty()) { if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { - os << "nil object reference stored to "; + os << "initialized to nil"; b = true; } } } if (!b) - os << "Null pointer value stored to "; - } - else if (V.isUndef()) { - os << "Uninitialized value stored to "; + os << "initialized to a null pointer value"; } else if (isa<nonloc::ConcreteInt>(V)) { - os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() - << " is assigned to "; + os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); } - else - return NULL; - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << '\'' << VR->getDecl() << '\''; + else if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } } - else - return NULL; } + } - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); + if (os.str().empty()) { + if (isa<loc::ConcreteInt>(V)) { + bool b = false; + if (R->isBoundable()) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "nil object reference stored to "; + b = true; + } + } + } - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); + if (!b) + os << "Null pointer value stored to "; } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); + else if (V.isUndef()) { + os << "Uninitialized value stored to "; } - - if (!S) + else if (isa<nonloc::ConcreteInt>(V)) { + os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() + << " is assigned to "; + } + else return NULL; - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << '\'' << *VR->getDecl() << '\''; + } + else + return NULL; } -}; - -static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, - SVal V) { - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); + // Construct a new PathDiagnosticPiece. + ProgramPoint P = N->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + if (!L.isValid()) + return NULL; + return new PathDiagnosticEventPiece(L, os.str()); } -class TrackConstraintBRVisitor : public BugReporterVisitor { - DefinedSVal Constraint; - const bool Assumption; - bool isSatisfied; -public: - TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} - - void Profile(llvm::FoldingSetNodeID &ID) const { - static int tag = 0; - ID.AddPointer(&tag); - ID.AddBoolean(Assumption); - ID.Add(Constraint); - } - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - if (isSatisfied) - return NULL; - - // Check if in the previous state it was feasible for this constraint - // to *not* be true. - if (PrevN->getState()->assume(Constraint, !Assumption)) { - - isSatisfied = true; - - // As a sanity check, make sure that the negation of the constraint - // was infeasible in the current state. If it is feasible, we somehow - // missed the transition point. - if (N->getState()->assume(Constraint, !Assumption)) - return NULL; - - // We found the transition point for the constraint. We now need to - // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); +void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddBoolean(Assumption); + ID.Add(Constraint); +} - if (isa<Loc>(Constraint)) { - os << "Assuming pointer value is "; - os << (Assumption ? "non-null" : "null"); - } +PathDiagnosticPiece * +TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + if (isSatisfied) + return NULL; - if (os.str().empty()) - return NULL; + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + if (PrevN->getState()->assume(Constraint, !Assumption)) { - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); + isSatisfied = true; - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); - } + // As a sanity check, make sure that the negation of the constraint + // was infeasible in the current state. If it is feasible, we somehow + // missed the transition point. + if (N->getState()->assume(Constraint, !Assumption)) + return NULL; - if (!S) - return NULL; + // We found the transition point for the constraint. We now need to + // pretty-print the constraint. (work-in-progress) + std::string sbuf; + llvm::raw_string_ostream os(sbuf); - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); + if (isa<Loc>(Constraint)) { + os << "Assuming pointer value is "; + os << (Assumption ? "non-null" : "null"); } - return NULL; + if (os.str().empty()) + return NULL; + + // Construct a new PathDiagnosticPiece. + ProgramPoint P = N->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + if (!L.isValid()) + return NULL; + return new PathDiagnosticEventPiece(L, os.str()); } -}; -} // end anonymous namespace -static void registerTrackConstraint(BugReporterContext& BRC, - DefinedSVal Constraint, - bool Assumption) { - BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); + return NULL; } -void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - - const Stmt *S = static_cast<const Stmt*>(data); - - if (!S) - return; +BugReporterVisitor * +bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, + const Stmt *S) { + if (!S || !N) + return 0; + + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + + // Walk through nodes until we get one that matches the statement + // exactly. + while (N) { + const ProgramPoint &pp = N->getLocation(); + if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { + if (ps->getStmt() == S) + break; + } + N = N->getFirstPred(); + } - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); + if (!N) + return 0; + + const ProgramState *state = N->getState(); // Walk through lvalue-to-rvalue conversions. if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { @@ -327,7 +334,7 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) || V.isUndef()) { - ::registerFindLastStore(BRC, R, V); + return new FindLastStoreBRVisitor(V, R); } } } @@ -347,94 +354,73 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, if (R) { assert(isa<SymbolicRegion>(R)); - registerTrackConstraint(BRC, loc::MemRegionVal(R), false); + return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); } } -} - -void bugreporter::registerFindLastStore(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - const MemRegion *R = static_cast<const MemRegion*>(data); + return 0; +} - if (!R) - return; +BugReporterVisitor * +FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, + const MemRegion *R) { + assert(R && "The memory region is null."); - const GRState *state = N->getState(); + const ProgramState *state = N->getState(); SVal V = state->getSVal(R); - if (V.isUnknown()) - return; + return 0; - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); + return new FindLastStoreBRVisitor(V, R); } -namespace { -class NilReceiverVisitor : public BugReporterVisitor { -public: - NilReceiverVisitor() {} - - void Profile(llvm::FoldingSetNodeID &ID) const { - static int x = 0; - ID.AddPointer(&x); - } - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - - const PostStmt *P = N->getLocationAs<PostStmt>(); - if (!P) - return 0; - const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); - if (!ME) - return 0; - const Expr *Receiver = ME->getInstanceReceiver(); - if (!Receiver) - return 0; - const GRState *state = N->getState(); - const SVal &V = state->getSVal(Receiver); - const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); - if (!DV) - return 0; - state = state->assume(*DV, true); - if (state) - return 0; - - // The receiver was nil, and hence the method was skipped. - // Register a BugReporterVisitor to issue a message telling us how - // the receiver was null. - bugreporter::registerTrackNullOrUndefValue(BRC, Receiver, N); - // Issue a message saying that the method was skipped. - PathDiagnosticLocation L(Receiver, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, "No method actually called " - "because the receiver is nil"); - } -}; -} // end anonymous namespace - -void bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) { - BRC.addVisitor(new NilReceiverVisitor()); +PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + const PostStmt *P = N->getLocationAs<PostStmt>(); + if (!P) + return 0; + const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); + if (!ME) + return 0; + const Expr *Receiver = ME->getInstanceReceiver(); + if (!Receiver) + return 0; + const ProgramState *state = N->getState(); + const SVal &V = state->getSVal(Receiver); + const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); + if (!DV) + return 0; + state = state->assume(*DV, true); + if (state) + return 0; + + // The receiver was nil, and hence the method was skipped. + // Register a BugReporterVisitor to issue a message telling us how + // the receiver was null. + BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver)); + // Issue a message saying that the method was skipped. + PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(L, "No method actually called " + "because the receiver is nil"); } -// Registers every VarDecl inside a Stmt with a last store vistor. -void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, - const void *stmt, - const ExplodedNode *N) { - const Stmt *S = static_cast<const Stmt *>(stmt); - +// Registers every VarDecl inside a Stmt with a last store visitor. +void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, + const Stmt *S) { + const ExplodedNode *N = BR.getErrorNode(); std::deque<const Stmt *> WorkList; - WorkList.push_back(S); while (!WorkList.empty()) { const Stmt *Head = WorkList.front(); WorkList.pop_front(); - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); + const ProgramState *state = N->getState(); + ProgramStateManager &StateMgr = state->getStateManager(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { @@ -445,7 +431,8 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, SVal V = state->getSVal(S); if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { - ::registerFindLastStore(BRC, R, V); + // Register a new visitor with the BugReport. + BR.addVisitor(new FindLastStoreBRVisitor(V, R)); } } } @@ -455,3 +442,248 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, WorkList.push_back(*I); } } + +//===----------------------------------------------------------------------===// +// Visitor that tries to report interesting diagnostics from conditions. +//===----------------------------------------------------------------------===// +PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *Prev, + BugReporterContext &BRC, + BugReport &BR) { + + const ProgramPoint &progPoint = N->getLocation(); + + const ProgramState *CurrentState = N->getState(); + const ProgramState *PrevState = Prev->getState(); + + // Compare the GDMs of the state, because that is where constraints + // are managed. Note that ensure that we only look at nodes that + // were generated by the analyzer engine proper, not checkers. + if (CurrentState->getGDM().getRoot() == + PrevState->getGDM().getRoot()) + return 0; + + // If an assumption was made on a branch, it should be caught + // here by looking at the state transition. + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) + return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC); + return 0; + } + + if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) { + // FIXME: Assuming that BugReporter is a GRBugReporter is a layering + // violation. + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = + cast<GRBugReporter>(BRC.getBugReporter()). + getEngine().getEagerlyAssumeTags(); + + const ProgramPointTag *tag = PS->getTag(); + if (tag == tags.first) + return VisitTrueTest(cast<Expr>(PS->getStmt()), true, + BRC, N->getLocationContext()); + if (tag == tags.second) + return VisitTrueTest(cast<Expr>(PS->getStmt()), false, + BRC, N->getLocationContext()); + + return 0; + } + + return 0; +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTerminator(const Stmt *Term, + const ExplodedNode *N, + const CFGBlock *srcBlk, + const CFGBlock *dstBlk, + BugReporterContext &BRC) { + const Expr *Cond = 0; + + switch (Term->getStmtClass()) { + default: + return 0; + case Stmt::IfStmtClass: + Cond = cast<IfStmt>(Term)->getCond(); + break; + case Stmt::ConditionalOperatorClass: + Cond = cast<ConditionalOperator>(Term)->getCond(); + break; + } + + assert(Cond); + assert(srcBlk->succ_size() == 2); + const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; + return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()), + tookTrue, BRC, N->getLocationContext()); +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + const Expr *Ex = Cond; + + while (true) { + Ex = Ex->IgnoreParens(); + switch (Ex->getStmtClass()) { + default: + return 0; + case Stmt::BinaryOperatorClass: + return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC); + case Stmt::DeclRefExprClass: + return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC); + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(Ex); + if (UO->getOpcode() == UO_LNot) { + tookTrue = !tookTrue; + Ex = UO->getSubExpr()->IgnoreParenNoopCasts(BRC.getASTContext()); + continue; + } + return 0; + } + } + } +} + +bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, + BugReporterContext &BRC) { + const Expr *OriginalExpr = Ex; + Ex = Ex->IgnoreParenCasts(); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { + const bool quotes = isa<VarDecl>(DR->getDecl()); + if (quotes) + Out << '\''; + Out << DR->getDecl()->getDeclName().getAsString(); + if (quotes) + Out << '\''; + return quotes; + } + + if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) { + QualType OriginalTy = OriginalExpr->getType(); + if (OriginalTy->isPointerType()) { + if (IL->getValue() == 0) { + Out << "null"; + return false; + } + } + else if (OriginalTy->isObjCObjectPointerType()) { + if (IL->getValue() == 0) { + Out << "nil"; + return false; + } + } + + Out << IL->getValue(); + return false; + } + + return false; +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + const BinaryOperator *BExpr, + const bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + bool shouldInvert = false; + + llvm::SmallString<128> LhsString, RhsString; + { + llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); + const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC); + const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC); + + shouldInvert = !isVarLHS && isVarRHS; + } + + if (LhsString.empty() || RhsString.empty()) + return 0; + + // Should we invert the strings if the LHS is not a variable name? + + llvm::SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; + + // Do we need to invert the opcode? + BinaryOperator::Opcode Op = BExpr->getOpcode(); + + if (shouldInvert) + switch (Op) { + default: break; + case BO_LT: Op = BO_GT; break; + case BO_GT: Op = BO_LT; break; + case BO_LE: Op = BO_GE; break; + case BO_GE: Op = BO_LE; break; + } + + if (!tookTrue) + switch (Op) { + case BO_EQ: Op = BO_NE; break; + case BO_NE: Op = BO_EQ; break; + case BO_LT: Op = BO_GE; break; + case BO_GT: Op = BO_LE; break; + case BO_LE: Op = BO_GT; break; + case BO_GE: Op = BO_LT; break; + default: + return 0; + } + + switch (BExpr->getOpcode()) { + case BO_EQ: + Out << "equal to "; + break; + case BO_NE: + Out << "not equal to "; + break; + default: + Out << BinaryOperator::getOpcodeStr(Op) << ' '; + break; + } + + Out << (shouldInvert ? LhsString : RhsString); + + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); + return new PathDiagnosticEventPiece(Loc, Out.str()); +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + const DeclRefExpr *DR, + const bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return 0; + + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << "Assuming '"; + VD->getDeclName().printName(Out); + Out << "' is "; + + QualType VDTy = VD->getType(); + + if (VDTy->isPointerType()) + Out << (tookTrue ? "non-null" : "null"); + else if (VDTy->isObjCObjectPointerType()) + Out << (tookTrue ? "non-nil" : "nil"); + else if (VDTy->isScalarType()) + Out << (tookTrue ? "not equal to 0" : "0"); + else + return 0; + + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); + return new PathDiagnosticEventPiece(Loc, Out.str()); +} diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp deleted file mode 100644 index bf5302920819..000000000000 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ /dev/null @@ -1,3689 +0,0 @@ -// CFRefCount.cpp - Transfer functions for tracking simple 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 defines the methods for CFRefCount, which implements -// a reference count checker for Core Foundation (Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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/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/ExprEngineBuilders.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.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> - -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 { -class GenericNodeBuilderRefCount { - StmtNodeBuilder *SNB; - const Stmt *S; - const void *tag; - EndOfFunctionNodeBuilder *ENB; -public: - GenericNodeBuilderRefCount(StmtNodeBuilder &snb, const Stmt *s, - const void *t) - : SNB(&snb), S(s), tag(t), ENB(0) {} - - GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &enb) - : SNB(0), S(0), tag(0), ENB(&enb) {} - - ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { - if (SNB) - return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), - state, Pred); - - assert(ENB); - return ENB->generateNode(state, Pred); - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Primitives used for constructing summaries for function/method calls. -//===----------------------------------------------------------------------===// - -/// ArgEffect is used to summarize a function/method call's effect on a -/// particular argument. -enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, - DecRefBridgedTransfered, - DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, SelfOwn, StopTracking }; - -namespace llvm { -template <> struct FoldingSetTrait<ArgEffect> { -static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { - ID.AddInteger((unsigned) X); -} -}; -} // end llvm namespace - -/// ArgEffects summarizes the effects of a function/method call on all of -/// its arguments. -typedef llvm::ImmutableMap<unsigned,ArgEffect> ArgEffects; - -namespace { - -/// RetEffect is used to summarize a function/method call's behavior with -/// respect to its return value. -class RetEffect { -public: - enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, - NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, - ReceiverAlias, - OwnedWhenTrackedReceiver }; - - enum ObjKind { CF, ObjC, AnyObj }; - -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) {} - -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; - } - - 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); - } - static RetEffect MakeNotOwned(ObjKind o) { - return RetEffect(NotOwnedSymbol, o); - } - static RetEffect MakeGCNotOwned() { - return RetEffect(GCNotOwnedSymbol, ObjC); - } - static RetEffect MakeARCNotOwned() { - return RetEffect(ARCNotOwnedSymbol, ObjC); - } - static RetEffect MakeNoRet() { - return RetEffect(NoRet); - } -}; - -//===----------------------------------------------------------------------===// -// Reference-counting logic (typestate + counts). -//===----------------------------------------------------------------------===// - -class RefVal { -public: - enum Kind { - Owned = 0, // Owning reference. - NotOwned, // Reference is not owned by still valid (not freed). - Released, // Object has been released. - ReturnedOwned, // Returned object passes ownership to caller. - ReturnedNotOwned, // Return object does not pass ownership to caller. - ERROR_START, - ErrorDeallocNotOwned, // -dealloc called on non-owned object. - ErrorDeallocGC, // Calling -dealloc with GC enabled. - ErrorUseAfterRelease, // Object used after released. - ErrorReleaseNotOwned, // Release of an object that was not owned. - ERROR_LEAK_START, - ErrorLeak, // A memory leak due to excessive reference counts. - ErrorLeakReturned, // A memory leak due to the returning method not having - // the correct naming conventions. - ErrorGCLeakReturned, - ErrorOverAutorelease, - ErrorReturnedNotOwned - }; - -private: - Kind kind; - RetEffect::ObjKind okind; - unsigned Cnt; - unsigned ACnt; - QualType T; - - RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) - : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} - -public: - Kind getKind() const { return kind; } - - RetEffect::ObjKind getObjKind() const { return okind; } - - unsigned getCount() const { return Cnt; } - unsigned getAutoreleaseCount() const { return ACnt; } - unsigned getCombinedCounts() const { return Cnt + ACnt; } - void clearCounts() { Cnt = 0; ACnt = 0; } - void setCount(unsigned i) { Cnt = i; } - void setAutoreleaseCount(unsigned i) { ACnt = i; } - - QualType getType() const { return T; } - - bool isOwned() const { - return getKind() == Owned; - } - - bool isNotOwned() const { - return getKind() == NotOwned; - } - - bool isReturnedOwned() const { - return getKind() == ReturnedOwned; - } - - bool isReturnedNotOwned() const { - return getKind() == ReturnedNotOwned; - } - - static RefVal makeOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 1) { - return RefVal(Owned, o, Count, 0, t); - } - - static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 0) { - return RefVal(NotOwned, o, Count, 0, t); - } - - // Comparison, profiling, and pretty-printing. - - bool operator==(const RefVal& X) const { - return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; - } - - RefVal operator-(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() - i, - getAutoreleaseCount(), getType()); - } - - RefVal operator+(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() + i, - getAutoreleaseCount(), getType()); - } - - RefVal operator^(Kind k) const { - return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), - getType()); - } - - RefVal autorelease() const { - return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, - getType()); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned) kind); - ID.AddInteger(Cnt); - ID.AddInteger(ACnt); - ID.Add(T); - } - - void print(llvm::raw_ostream& Out) const; -}; - -void RefVal::print(llvm::raw_ostream& Out) const { - if (!T.isNull()) - Out << "Tracked Type:" << T.getAsString() << '\n'; - - switch (getKind()) { - default: assert(false); - case Owned: { - Out << "Owned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case NotOwned: { - Out << "NotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedOwned: { - Out << "ReturnedOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedNotOwned: { - Out << "ReturnedNotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case Released: - Out << "Released"; - break; - - case ErrorDeallocGC: - Out << "-dealloc (GC)"; - break; - - case ErrorDeallocNotOwned: - Out << "-dealloc (not-owned)"; - break; - - case ErrorLeak: - Out << "Leaked"; - break; - - case ErrorLeakReturned: - Out << "Leaked (Bad naming)"; - break; - - case ErrorGCLeakReturned: - Out << "Leaked (GC-ed at return)"; - break; - - case ErrorUseAfterRelease: - Out << "Use-After-Release [ERROR]"; - break; - - case ErrorReleaseNotOwned: - Out << "Release of Not-Owned [ERROR]"; - break; - - case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; - break; - - case RefVal::ErrorReturnedNotOwned: - Out << "Non-owned object returned instead of owned"; - break; - } - - if (ACnt) { - Out << " [ARC +" << ACnt << ']'; - } -} -} //end anonymous namespace - -//===----------------------------------------------------------------------===// -// RefBindings - State used to track object reference counts. -//===----------------------------------------------------------------------===// - -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; - } - }; -} -} - -//===----------------------------------------------------------------------===// -// Summaries -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummary { - /// Args - an ordered vector of (index, ArgEffect) pairs, where index - /// specifies the argument (starting from 0). This can be sparsely - /// populated; arguments with no entry in Args use 'DefaultArgEffect'. - ArgEffects Args; - - /// DefaultArgEffect - The default ArgEffect to apply to arguments that - /// do not have an entry in Args. - ArgEffect DefaultArgEffect; - - /// Receiver - If this summary applies to an Objective-C message expression, - /// this is the effect applied to the state of the receiver. - 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. - 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) {} - - /// getArg - Return the argument effect on the argument specified by - /// idx (starting from 0). - ArgEffect getArg(unsigned idx) const { - if (const ArgEffect *AE = Args.lookup(idx)) - return *AE; - - return DefaultArgEffect; - } - - void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) { - Args = af.add(Args, idx, e); - } - - /// setDefaultArgEffect - Set the default argument effect. - void setDefaultArgEffect(ArgEffect E) { - DefaultArgEffect = E; - } - - /// getRetEffect - Returns the effect on the return value of the call. - RetEffect getRetEffect() const { return Ret; } - - /// 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; } - - /// 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; } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for constructing summaries. -//===----------------------------------------------------------------------===// - -namespace { -class ObjCSummaryKey { - IdentifierInfo* II; - Selector S; -public: - ObjCSummaryKey(IdentifierInfo* ii, Selector s) - : II(ii), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) - : II(d ? d->getIdentifier() : 0), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) - : II(d ? d->getIdentifier() : ii), S(s) {} - - ObjCSummaryKey(Selector s) - : II(0), S(s) {} - - IdentifierInfo* getIdentifier() const { return II; } - Selector getSelector() const { return S; } -}; -} - -namespace llvm { -template <> struct DenseMapInfo<ObjCSummaryKey> { - static inline ObjCSummaryKey getEmptyKey() { - return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(), - DenseMapInfo<Selector>::getEmptyKey()); - } - - static inline ObjCSummaryKey getTombstoneKey() { - return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(), - DenseMapInfo<Selector>::getTombstoneKey()); - } - - static unsigned getHashValue(const ObjCSummaryKey &V) { - return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier()) - & 0x88888888) - | (DenseMapInfo<Selector>::getHashValue(V.getSelector()) - & 0x55555555); - } - - static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { - return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(), - RHS.getIdentifier()) && - DenseMapInfo<Selector>::isEqual(LHS.getSelector(), - RHS.getSelector()); - } - -}; -template <> -struct isPodLike<ObjCSummaryKey> { static const bool value = true; }; -} // end llvm namespace - -namespace { -class ObjCSummaryCache { - typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy; - MapTy M; -public: - ObjCSummaryCache() {} - - 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) { - // Do a lookup with the (D,S) pair. If we find a match return - // the iterator. - ObjCSummaryKey K(D, S); - MapTy::iterator I = M.find(K); - - if (I != M.end() || !D) - return I->second; - - // Walk the super chain. If we find a hit with a parent, we'll end - // up returning that summary. We actually allow that key (null,S), as - // we cache summaries for the null ObjCInterfaceDecl* to allow us to - // 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()) { - if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) - break; - - if (!C) - return NULL; - } - - // Cache the summary with original key to make the next lookup faster - // and return the iterator. - RetainSummary *Summ = I->second; - M[K] = Summ; - return Summ; - } - - 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)); - - if (I == M.end()) - I = M.find(ObjCSummaryKey(S)); - - return I == M.end() ? NULL : I->second; - } - - RetainSummary*& operator[](ObjCSummaryKey K) { - return M[K]; - } - - RetainSummary*& operator[](Selector S) { - return M[ ObjCSummaryKey(S) ]; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for managing collections of summaries. -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummaryManager { - - //==-----------------------------------------------------------------==// - // Typedefs. - //==-----------------------------------------------------------------==// - - typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*> - FuncSummariesTy; - - typedef ObjCSummaryCache ObjCMethodSummariesTy; - - //==-----------------------------------------------------------------==// - // Data. - //==-----------------------------------------------------------------==// - - /// Ctx - The ASTContext object for the analyzed ASTs. - ASTContext& Ctx; - - /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier - /// "CFDictionaryCreate". - IdentifierInfo* CFDictionaryCreateII; - - /// GCEnabled - Records whether or not the analyzed code runs in GC mode. - const bool GCEnabled; - - /// Records whether or not the analyzed code runs in ARC mode. - const bool ARCEnabled; - - /// FuncSummaries - A map from FunctionDecls to summaries. - FuncSummariesTy FuncSummaries; - - /// ObjCClassMethodSummaries - A map from selectors (for instance methods) - /// to summaries. - ObjCMethodSummariesTy ObjCClassMethodSummaries; - - /// ObjCMethodSummaries - A map from selectors to summaries. - ObjCMethodSummariesTy ObjCMethodSummaries; - - /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, - /// and all other data used by the checker. - llvm::BumpPtrAllocator BPAlloc; - - /// AF - A factory for ArgEffects objects. - ArgEffects::Factory AF; - - /// ScratchArgs - A holding buffer for construct ArgEffects. - ArgEffects ScratchArgs; - - /// ObjCAllocRetE - Default return effect for methods returning Objective-C - /// objects. - RetEffect ObjCAllocRetE; - - /// ObjCInitRetE - Default return effect for init methods returning - /// Objective-C objects. - RetEffect ObjCInitRetE; - - RetainSummary DefaultSummary; - RetainSummary* StopSummary; - - //==-----------------------------------------------------------------==// - // Methods. - //==-----------------------------------------------------------------==// - - /// getArgEffects - Returns a persistent ArgEffects object based on the - /// data in ScratchArgs. - ArgEffects getArgEffects(); - - enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; - -public: - RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } - - RetainSummary *getDefaultSummary() { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - return new (Summ) RetainSummary(DefaultSummary); - } - - RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - - RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD); - RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD, - StringRef FName); - - RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape, - bool isEndPath = false); - - RetainSummary* getPersistentSummary(RetEffect RE, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape) { - return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); - } - - RetainSummary *getPersistentStopSummary() { - if (StopSummary) - return StopSummary; - - StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, StopTracking); - - return StopSummary; - } - - RetainSummary *getInitMethodSummary(QualType RetTy); - - void InitializeClassMethodSummaries(); - void InitializeMethodSummaries(); -private: - void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { - ObjCClassMethodSummaries[S] = Summ; - } - - void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { - ObjCMethodSummaries[S] = Summ; - } - - void addClassMethSummary(const char* Cls, const char* nullaryName, - 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) { - 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; - - while (const char* s = va_arg(argp, const char*)) - II.push_back(&Ctx.Idents.get(s)); - - return Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, - RetainSummary* Summ, va_list argp) { - Selector S = generateSelector(argp); - Summaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addInstMethSummary(const char* Cls, 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, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); - va_end(argp); - } - - void addClsMethSummary(IdentifierInfo *II, 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) - : Ctx(ctx), - CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), - GCEnabled(gcenabled), - ARCEnabled(usesARC), - AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), - ObjCAllocRetE(gcenabled - ? RetEffect::MakeGCNotOwned() - : (usesARC ? RetEffect::MakeARCNotOwned() - : RetEffect::MakeOwned(RetEffect::ObjC, true))), - ObjCInitRetE(gcenabled - ? RetEffect::MakeGCNotOwned() - : (usesARC ? RetEffect::MakeARCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver())), - DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */, - RetEffect::MakeNoRet() /* return effect */, - MayEscape, /* default argument effect */ - DoNothing /* receiver effect */), - StopSummary(0) { - - InitializeClassMethodSummaries(); - InitializeMethodSummaries(); - } - - ~RetainSummaryManager(); - - RetainSummary* getSummary(const FunctionDecl* FD); - - RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - const GRState *state, - const LocationContext *LC); - - 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); - - RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy); - - RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { - const ObjCInterfaceDecl *Class = 0; - if (!msg.isInstanceMessage()) - Class = msg.getReceiverInterface(); - - return getClassMethodSummary(msg.getSelector(), - Class? Class->getIdentifier() : 0, - Class, - msg.getMethodDecl(), msg.getType(Ctx)); - } - - /// getMethodSummary - This version of getMethodSummary is used to query - /// the summary for the current method being analyzed. - 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); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD); - - bool isGCEnabled() const { return GCEnabled; } - - bool isARCEnabled() const { return ARCEnabled; } - - bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } - - RetainSummary *copySummary(RetainSummary *OldSumm) { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(*OldSumm); - return Summ; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Implementation of checker data structures. -//===----------------------------------------------------------------------===// - -RetainSummaryManager::~RetainSummaryManager() {} - -ArgEffects RetainSummaryManager::getArgEffects() { - ArgEffects AE = ScratchArgs; - ScratchArgs = AF.getEmptyMap(); - return AE; -} - -RetainSummary* -RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff, - ArgEffect DefaultEff, - bool isEndPath) { - // Create the summary and return it. - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); - return Summ; -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static bool isRetain(const FunctionDecl* FD, StringRef FName) { - return FName.endswith("Retain"); -} - -static bool isRelease(const FunctionDecl* FD, StringRef FName) { - return FName.endswith("Release"); -} - -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; - - do { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) { - S = getPersistentStopSummary(); - break; - } - // For C++ methods, generate an implicit "stop" summary as well. We - // can relax this once we have a clear policy for C++ methods and - // ownership attributes. - if (isa<CXXMethodDecl>(FD)) { - S = getPersistentStopSummary(); - break; - } - - // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the - // function's type. - const FunctionType* FT = FD->getType()->getAs<FunctionType>(); - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - break; - - StringRef FName = II->getName(); - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - FName = FName.substr(FName.find_first_not_of('_')); - - // Inspect the result type. - QualType RetTy = FT->getResultType(); - - // FIXME: This should all be refactored into a chain of "summary lookup" - // filters. - assert(ScratchArgs.isEmpty()); - - if (FName == "pthread_create") { - // Part of: <rdar://problem/7299394>. This will be addressed - // better with IPA. - S = getPersistentStopSummary(); - } else if (FName == "NSMakeCollectable") { - // Handle: id NSMakeCollectable(CFTypeRef) - S = (RetTy->isObjCIdType()) - ? getUnarySummary(FT, cfmakecollectable) - : getPersistentStopSummary(); - } else if (FName == "IOBSDNameMatching" || - FName == "IOServiceMatching" || - FName == "IOServiceNameMatching" || - FName == "IORegistryEntryIDMatching" || - FName == "IOOpenFirmwarePathMatching") { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } else if (FName == "IOServiceGetMatchingService" || - FName == "IOServiceGetMatchingServices") { - // FIXES: <rdar://problem/6326900> - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.add(ScratchArgs, 1, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } else if (FName == "IOServiceAddNotification" || - FName == "IOServiceAddMatchingNotification") { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.add(ScratchArgs, 2, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } else if (FName == "CVPixelBufferCreateWithBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.add(ScratchArgs, 7, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } else if (FName == "CGBitmapContextCreateWithData") { - // FIXES: <rdar://problem/7358899> - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.add(ScratchArgs, 8, StopTracking); - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.add(ScratchArgs, 12, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - - // Did we get a summary? - if (S) - break; - - // Enable this code once the semantics of NSDeallocateObject are resolved - // for GC. <rdar://problem/6619988> -#if 0 - // Handle: NSDeallocateObject(id anObject); - // This method does allow 'nil' (although we don't check it now). - if (strcmp(FName, "NSDeallocateObject") == 0) { - return RetTy == Ctx.VoidTy - ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) - : getPersistentStopSummary(); - } -#endif - - if (RetTy->isPointerType()) { - // For CoreFoundation ('CF') types. - if (cocoa::isRefType(RetTy, "CF", FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else if (FName.find("MakeCollectable") != StringRef::npos) - S = getUnarySummary(FT, cfmakecollectable); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For CoreGraphics ('CG') types. - if (cocoa::isRefType(RetTy, "CG", FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For the Disk Arbitration API (DiskArbitration/DADisk.h) - if (cocoa::isRefType(RetTy, "DADisk") || - cocoa::isRefType(RetTy, "DADissenter") || - cocoa::isRefType(RetTy, "DASessionRef")) { - S = getCFCreateGetRuleSummary(FD, FName); - break; - } - - break; - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { - // Test for 'CGCF'. - FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); - - if (isRelease(FD, FName)) - S = getUnarySummary(FT, cfrelease); - else { - assert (ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape : DoNothing; - - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); - } - } - } - while (0); - - if (!S) - S = getDefaultSummary(); - - // Annotations override defaults. - assert(S); - updateSummaryFromAnnotations(*S, FD); - - FuncSummaries[FD] = S; - return S; -} - -RetainSummary* -RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, - StringRef FName) { - if (coreFoundation::followsCreateRule(FName)) - return getCFSummaryCreateRule(FD); - - return getCFSummaryGetRule(FD); -} - -RetainSummary* -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - UnaryFuncKind func) { - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); - if (!FTP || FTP->getNumArgs() != 1) - return getPersistentStopSummary(); - - assert (ScratchArgs.isEmpty()); - - 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(); - } -} - -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) { - assert (ScratchArgs.isEmpty()); - return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), - DoNothing, DoNothing); -} - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -RetainSummary* -RetainSummaryManager::getInitMethodSummary(QualType RetTy) { - assert(ScratchArgs.isEmpty()); - // 'init' methods conceptually return a newly allocated object and claim - // the receiver. - if (cocoa::isCocoaObjectRef(RetTy) || - coreFoundation::isCFObjectRef(RetTy)) - return getPersistentSummary(ObjCInitRetE, DecRefMsg); - - return getDefaultSummary(); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - // 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); - } - } - - QualType RetTy = FD->getResultType(); - - // Determine if there is a special return effect for this method. - if (cocoa::isCocoaObjectRef(RetTy)) { - if (FD->getAttr<NSReturnsRetainedAttr>()) { - Summ.setRetEffect(ObjCAllocRetE); - } - else if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - else if (FD->getAttr<NSReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); - } - else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } - } - else if (RetTy->getAs<PointerType>()) { - if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } - } -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - bool isTrackedLoc = false; - - // Effects on the receiver. - if (MD->getAttr<NSConsumesSelfAttr>()) { - if (!GCEnabled) - Summ.setReceiverEffect(DecRefMsg); - } - - // Effects on the parameters. - unsigned parm_idx = 0; - for (ObjCMethodDecl::param_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); - } - else if(pd->getAttr<CFConsumedAttr>()) { - Summ.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); - return; - } - if (MD->getAttr<NSReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); - return; - } - - isTrackedLoc = true; - } - - if (!isTrackedLoc) - isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL; - - if (isTrackedLoc) { - if (MD->getAttr<CFReturnsRetainedAttr>()) - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - else if (MD->getAttr<CFReturnsNotRetainedAttr>()) - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } -} - -RetainSummary* -RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy) { - - if (MD) { - // Scan the method decl for 'void*' arguments. These should be treated - // as 'StopTracking' because they are often used with delegates. - // Delegates are a frequent form of false positives with the retain - // count checker. - unsigned i = 0; - for (ObjCMethodDecl::param_iterator I = MD->param_begin(), - E = MD->param_end(); I != E; ++I, ++i) - if (ParmVarDecl *PD = *I) { - QualType Ty = Ctx.getCanonicalType(PD->getType()); - if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) - ScratchArgs = AF.add(ScratchArgs, i, StopTracking); - } - } - - // Any special effect for the receiver? - ArgEffect ReceiverEff = DoNothing; - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - const std::string &str = S.getAsString(); - assert(!str.empty()); - if (StrInStrNoCase(str, "delegate:") != StringRef::npos) - ReceiverEff = StopTracking; - } - - // Look for methods that return an owned object. - if (cocoa::isCocoaObjectRef(RetTy)) { - // EXPERIMENTAL: assume the Cocoa conventions for all objects returned - // by instance methods. - RetEffect E = cocoa::followsFundamentalRule(S, MD) - ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - // Look for methods that return an owned core foundation object. - if (coreFoundation::isCFObjectRef(RetTy)) { - RetEffect E = cocoa::followsFundamentalRule(S, MD) - ? RetEffect::MakeOwned(RetEffect::CF, true) - : RetEffect::MakeNotOwned(RetEffect::CF); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) - return getDefaultSummary(); - - return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, - const GRState *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; - - // FIXME: Is this really working as expected? There are cases where - // we just use the 'ID' from the message expression. - SVal receiverV; - - if (Receiver) { - receiverV = state->getSValAsScalarOrLoc(Receiver); - - // FIXME: Eventually replace the use of state->get<RefBindings> with - // a generic API for reasoning about the Objective-C types of symbolic - // objects. - if (SymbolRef Sym = receiverV.getAsLocSymbol()) - if (const RefVal *T = state->get<RefBindings>(Sym)) - if (const ObjCObjectPointerType* PT = - T->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - - // FIXME: this is a hack. This may or may not be the actual method - // that is called. - if (!ID) { - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - } - } else { - // FIXME: Hack for 'super'. - ID = msg.getReceiverInterface(); - } - - // 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(); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(Selector S, - IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - // Look up a summary in our summary cache. - RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - assert(ScratchArgs.isEmpty()); - - // "initXXX": pass-through for receiver. - if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule) - Summ = getInitMethodSummary(RetTy); - else - Summ = getCommonMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - - // Memoize the summary. - ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -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); - - if (!Summ) { - Summ = getCommonMethodSummary(MD, S, RetTy); - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - // Memoize the summary. - ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - assert(ScratchArgs.isEmpty()); - RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.add(ScratchArgs, 0, Autorelease); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, Autorelease)); - - // Create the summaries for [NSObject performSelector...]. We treat - // these as 'stop tracking' for the arguments because they are often - // 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); - IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", "inModes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", - "withObject", NULL); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - assert (ScratchArgs.isEmpty()); - - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); - RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); - - // Create the "retain" selector. - RetEffect E = RetEffect::MakeReceiverAlias(); - RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(E, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the "drain" selector. - Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); - addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(E, Autorelease); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // Specially handle NSAutoreleasePool. - addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(RetEffect::MakeReceiverAlias(), - NewAutoreleasePool)); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // 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(), - StopTracking, - StopTracking); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // Don't track allocated autorelease pools yet, as it is okay to prematurely - // 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); - addInstMethSummary("QCView", AllocSumm, - "createSnapshotImageOfType", NULL); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", NULL); - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", "format", "colorSpace", NULL); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", - "info", NULL); -} - -//===----------------------------------------------------------------------===// -// AutoreleaseBindings - State used to track objects in autorelease pools. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; -typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; -typedef llvm::ImmutableList<SymbolRef> ARStack; - -static int AutoRCIndex = 0; -static int AutoRBIndex = 0; - -namespace { class AutoreleasePoolContents {}; } -namespace { class AutoreleaseStack {}; } - -namespace clang { -namespace ento { -template<> struct GRStateTrait<AutoreleaseStack> - : public GRStatePartialTrait<ARStack> { - static inline void* GDMIndex() { return &AutoRBIndex; } -}; - -template<> struct GRStateTrait<AutoreleasePoolContents> - : public GRStatePartialTrait<ARPoolContents> { - static inline void* GDMIndex() { return &AutoRCIndex; } -}; -} // end GR namespace -} // end clang namespace - -static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { - ARStack stack = state->get<AutoreleaseStack>(); - return stack.isEmpty() ? SymbolRef() : stack.getHead(); -} - -static const GRState * SendAutorelease(const GRState *state, - ARCounts::Factory &F, SymbolRef sym) { - - SymbolRef pool = GetCurrentAutoreleasePool(state); - const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); - ARCounts newCnts(0); - - if (cnts) { - const unsigned *cnt = (*cnts).lookup(sym); - newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1); - } - else - newCnts = F.add(F.getEmptyMap(), sym, 1); - - return state->set<AutoreleasePoolContents>(pool, newCnts); -} - -//===----------------------------------------------------------------------===// -// 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 { - - //===-------------===// - // Bug Descriptions. // - //===-------------===// - - class CFRefBug : public BugType { - protected: - CFRefCount& TF; - - CFRefBug(CFRefCount* tf, llvm::StringRef name) - : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} - public: - - CFRefCount& getTF() { return TF; } - - // FIXME: Eventually remove. - 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") {} - - 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") {} - - const char* getDescription() const { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } - }; - - class DeallocGC : public CFRefBug { - public: - DeallocGC(CFRefCount *tf) - : CFRefBug(tf, "-dealloc called while using garbage collection") {} - - const char *getDescription() const { - return "-dealloc called while using garbage collection"; - } - }; - - class DeallocNotOwned : public CFRefBug { - public: - DeallocNotOwned(CFRefCount *tf) - : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const { - return "-dealloc sent to object that may be referenced elsewhere"; - } - }; - - class OverAutorelease : public CFRefBug { - public: - OverAutorelease(CFRefCount *tf) : - CFRefBug(tf, "Object sent -autorelease too many times") {} - - const char *getDescription() const { - return "Object sent -autorelease too many times"; - } - }; - - class ReturnedNotOwnedForOwned : public CFRefBug { - public: - ReturnedNotOwnedForOwned(CFRefCount *tf) : - CFRefBug(tf, "Method should return an owned object") {} - - const char *getDescription() const { - return "Object with a +0 retain count returned to caller where a +1 " - "(owning) retain count is expected"; - } - }; - - class Leak : public CFRefBug { - const bool isReturn; - protected: - Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) - : CFRefBug(tf, name), isReturn(isRet) {} - public: - - 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) {} - }; - - class LeakWithinFunction : public Leak { - public: - LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, false) {} - }; - - //===---------===// - // Bug Reports. // - //===---------===// - - class CFRefReport : public RangedBugReport { - protected: - SymbolRef Sym; - const CFRefCount &TF; - public: - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym) - : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} - - 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 std::pair<ranges_iterator, ranges_iterator> getRanges() const { - if (!getBugType().isLeak()) - return RangedBugReport::getRanges(); - else - return std::make_pair(ranges_iterator(), ranges_iterator()); - } - - SymbolRef getSymbol() const { return Sym; } - - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); - - std::pair<const char**,const char**> getExtraDescriptiveText(); - - PathDiagnosticPiece* VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC); - }; - - 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); - - SourceLocation getLocation() const { return AllocSite; } - }; -} // 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); - } -} - -static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V, - ArgEffect X) { - for (llvm::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) { - - if (!isa<PostStmt>(N->getLocation())) - return NULL; - - // Check if the type state has changed. - const GRState *PrevSt = PrevN->getState(); - const GRState *CurrSt = N->getState(); - - const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); - if (!CurrT) return NULL; - - const RefVal &CurrV = *CurrT; - const RefVal *PrevT = PrevSt->get<RefBindings>(Sym); - - // Create a string buffer to constain all the useful things we want - // to tell the user. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - // 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(); - - 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 << '\''; - else - os << "function call"; - } - else if (isa<ObjCMessageExpr>(S)) { - os << "Method"; - } else { - os << "Property"; - } - - if (CurrV.getObjKind() == RetEffect::CF) { - os << " returns a Core Foundation object with a "; - } - else { - assert (CurrV.getObjKind() == RetEffect::ObjC); - os << " returns an Objective-C object with a "; - } - - if (CurrV.isOwned()) { - os << "+1 retain count"; - - if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) { - assert(CurrV.getObjKind() == RetEffect::CF); - os << ". " - "Core Foundation objects are not automatically garbage collected."; - } - } - else { - assert (CurrV.isNotOwned()); - os << "+0 retain count"; - } - - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - 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; - - if (const RetainSummary *Summ = - TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { - // We only have summaries attached to nodes after evaluating CallExpr and - // ObjCMessageExprs. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { - // Iterate through the parameter expressions and see if the symbol - // was ever passed as an argument. - unsigned i = 0; - - for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end(); - AI!=AE; ++AI, ++i) { - - // Retrieve the value of the argument. Is it the symbol - // we are interested in? - if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym) - continue; - - // We have an argument. Get the effect! - AEffects.push_back(Summ->getArg(i)); - } - } - else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { - if (const Expr *receiver = ME->getInstanceReceiver()) - if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { - // The symbol we are tracking is the receiver. - AEffects.push_back(Summ->getReceiverEffect()); - } - } - } - - do { - // Get the previous type state. - RefVal PrevV = *PrevT; - - // Specially handle -dealloc. - if (!TF.isGCEnabled() && 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. - // This case is handled elsewhere. - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCombinedCounts() == 0); - os << "Object released by directly sending the '-dealloc' message"; - break; - } - } - - // Specially handle CFMakeCollectable and friends. - if (contains(AEffects, MakeCollectable)) { - // Get the name of the function. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - const std::string& FName = FD->getNameAsString(); - - if (TF.isGCEnabled()) { - // 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 - << "' decrements an object's retain count and registers the " - "object with the garbage collector. "; - - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCount() == 0); - os << "Since it now has a 0 retain count the object can be " - "automatically collected by the garbage collector."; - } - else - os << "An object must have a 0 retain count to be garbage collected. " - "After this call its retain count is +" << CurrV.getCount() - << '.'; - } - else - os << "When GC is not enabled a call to '" << FName - << "' has no effect on its argument."; - - // Nothing more to say. - break; - } - - // Determine if the typestate has changed. - if (!(PrevV == CurrV)) - switch (CurrV.getKind()) { - case RefVal::Owned: - case RefVal::NotOwned: - - if (PrevV.getCount() == CurrV.getCount()) { - // Did an autorelease message get sent? - if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) - return 0; - - assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); - os << "Object sent -autorelease message"; - break; - } - - if (PrevV.getCount() > CurrV.getCount()) - os << "Reference count decremented."; - else - os << "Reference count incremented."; - - if (unsigned Count = CurrV.getCount()) - os << " The object now has a +" << Count << " retain count."; - - if (PrevV.getKind() == RefVal::Released) { - assert(TF.isGCEnabled() && CurrV.getCount() > 0); - os << " The object is not eligible for garbage collection until the " - "retain count reaches 0 again."; - } - - break; - - case RefVal::Released: - os << "Object released."; - break; - - case RefVal::ReturnedOwned: - os << "Object returned to caller as an owning reference (single retain " - "count transferred to caller)"; - break; - - case RefVal::ReturnedNotOwned: - os << "Object returned to caller with a +0 retain count"; - break; - - default: - return NULL; - } - - // Emit any remaining diagnostics for the argument effects (if any). - for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(), - E=AEffects.end(); I != E; ++I) { - - // A bunch of things have alternate behavior under GC. - if (TF.isGCEnabled()) - switch (*I) { - default: break; - case Autorelease: - os << "In GC mode an 'autorelease' has no effect."; - continue; - case IncRefMsg: - os << "In GC mode the 'retain' message has no effect."; - continue; - case DecRefMsg: - os << "In GC mode the 'release' message has no effect."; - continue; - } - } - } while (0); - - 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()); - - // 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 (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { - P->addRange(Exp->getSourceRange()); - break; - } - - return P; -} - -namespace { - class FindUniqueBinding : - public StoreManager::BindingsHandler { - SymbolRef Sym; - const MemRegion* Binding; - bool First; - - public: - FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal val) { - - SymbolRef SymV = val.getAsSymbol(); - if (!SymV || SymV != Sym) - return true; - - if (Binding) { - First = false; - return false; - } - else - Binding = R; - - return true; - } - - operator bool() { return First && Binding; } - const MemRegion* getRegion() { return Binding; } - }; -} - -static std::pair<const ExplodedNode*,const MemRegion*> -GetAllocationSite(GRStateManager& 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 MemRegion* FirstBinding = 0; - - while (N) { - const GRState* St = N->getState(); - RefBindings B = St->get<RefBindings>(); - - if (!B.lookup(Sym)) - break; - - FindUniqueBinding FB(Sym); - StateMgr.iterBindings(St, FB); - if (FB) FirstBinding = FB.getRegion(); - - Last = N; - N = N->pred_empty() ? NULL : *(N->pred_begin()); - } - - return std::make_pair(Last, FirstBinding); -} - -PathDiagnosticPiece* -CFRefReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN) { - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - return RangedBugReport::getEndPath(BRC, EndN); -} - -PathDiagnosticPiece* -CFRefLeakReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN){ - - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - - // 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 MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = - GetAllocationSite(BRC.getStateManager(), EndN, Sym); - - SourceManager& SMgr = 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); - } - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Object leaked: "; - - if (FirstBinding) { - os << "object allocated and stored into '" - << FirstBinding->getString() << '\''; - } - else - os << "allocated object"; - - // Get the retain count. - const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); - - if (RV->getKind() == RefVal::ErrorLeakReturned) { - // FIXME: Per comments in rdar://6320065, "create" only applies to CF - // objects. Only "copy", "alloc", "retain" and "new" transfer ownership - // to the caller for NS objects. - const Decl *D = &EndN->getCodeDecl(); - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { - os << " is returned from a method whose name ('" - << MD->getSelector().getAsString() - << "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'." - " This violates the naming convention rules" - " given in the Memory Management Guide for Cocoa"; - } - else { - const FunctionDecl *FD = cast<FunctionDecl>(D); - os << " is return from a function whose name ('" - << FD->getNameAsString() - << "') does not contain 'Copy' or 'Create'. This violates the naming" - " convention rules given the Memory Management Guide for Core" - " Foundation"; - } - } - else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { - 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 " - "count since they expect the object to be managed by the garbage " - "collector"; - } - else - os << " is not referenced later in this execution path and has a retain " - "count of +" << RV->getCount(); - - return new PathDiagnosticEventPiece(L, os.str()); -} - -CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, - SymbolRef sym, ExprEngine& Eng) -: CFRefReport(D, tf, n, sym) { - - // Most bug reports are cached at the location where they occurred. - // With leaks, we want to unique them by the location where they were - // allocated, and only report a single path. To do this, we need to find - // the allocation site of a piece of tracked memory, which we do via a - // call to GetAllocationSite. This will walk the ExplodedGraph backwards. - // 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; - - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. - GetAllocationSite(Eng.getStateManager(), getErrorNode(), getSymbol()); - - // Get the SourceLocation for the allocation site. - ProgramPoint P = AllocNode->getLocation(); - AllocSite = cast<PostStmt>(P).getStmt()->getLocStart(); - - // 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); - os << "Potential leak "; - if (tf.isGCEnabled()) { - 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() << '\''; -} - -//===----------------------------------------------------------------------===// -// Main checker logic. -//===----------------------------------------------------------------------===// - -/// 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) { - 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 - /// more specific than id. - if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE)) - if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>()) - if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || - PT->isObjCClassType()) { - // At this point we know the return type of the message expression is - // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this - // is a call to a class method whose type we can resolve. In such - // cases, promote the return type to XXX* (where XXX is the class). - const ObjCInterfaceDecl *D = ME->getReceiverInterface(); - return !D ? RetTy : - Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D)); - } - - 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) { - - // 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; - - // 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 (hasErr) { - 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); - if (hasErr) { - ErrorRange = Receiver.getSourceRange(); - ErrorSym = Sym; - } - } - } - } - - // Process any errors. - if (hasErr) { - ProcessNonLeakError(Dst, Builder, Ex, ErrorRange, Pred, state, - hasErr, ErrorSym); - return; - } - - // Consult the summary for the return value. - 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) - 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; - } - - case RetEffect::ReceiverAlias: { - assert(Receiver); - SVal V = Receiver.getSValAsScalarOrLoc(state); - state = state->BindExpr(Ex, V, false); - break; - } - - case RetEffect::OwnedAllocatedSymbol: - case RetEffect::OwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); - - // Use the result type from callOrMsg as it automatically adjusts - // for methods/functions that return references. - QualType resultTy = callOrMsg.getResultType(Eng.getContext()); - state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), - resultTy)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); - - // FIXME: Add a flag to the checker where allocations are assumed to - // *not fail. -#if 0 - if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { - bool isFeasible; - state = state.assume(loc::SymbolVal(Sym), true, isFeasible); - assert(isFeasible && "Cannot assume fresh symbol is non-null."); - } -#endif - - break; - } - - 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()); - state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); - 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); - - // Annotate the edge with summary we used. - if (NewNode) SummaryLog[NewNode] = &Summ; -} - - -void CFRefCount::evalCall(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const CallExpr* CE, SVal L, - ExplodedNode* Pred) { - - 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); - else - Summ = Summaries.getDefaultSummary(); - } - else - Summ = Summaries.getDefaultSummary(); - - assert(Summ); - evalSummary(Dst, Eng, Builder, CE, - CallOrObjCMessage(CE, Builder.GetState(Pred)), - InstanceReceiver(), *Summ,L.getAsRegion(), - Pred, Builder.GetState(Pred)); -} - -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); -} - -namespace { -class StopTrackingCallback : public SymbolVisitor { - const GRState *state; -public: - StopTrackingCallback(const GRState *st) : state(st) {} - const GRState *getState() const { return state; } - - bool VisitSymbol(SymbolRef sym) { - state = state->remove<RefBindings>(sym); - return true; - } -}; -} // end anonymous namespace - - -void CFRefCount::evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) { - // Are we storing to something that causes the value to "escape"? - bool escapes = false; - - // 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(); - - if (!isa<loc::MemRegionVal>(location)) - escapes = true; - else { - const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion(); - escapes = !R->hasStackStorage(); - - 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()))); - } - } - - // 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. - B.MakeNode(state->scanReachableSymbols<StopTrackingCallback>(val).getState()); -} - - // Return statements. - -void CFRefCount::evalReturn(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const ReturnStmt* S, - ExplodedNode* Pred) { - - const Expr* RetE = S->getRetValue(); - if (!RetE) - return; - - const GRState *state = Builder.GetState(Pred); - SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); - - if (!Sym) - return; - - // Get the reference count binding (if any). - const RefVal* T = state->get<RefBindings>(Sym); - - if (!T) - return; - - // Change the reference count. - RefVal X = *T; - - switch (X.getKind()) { - case RefVal::Owned: { - unsigned cnt = X.getCount(); - assert (cnt > 0); - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - break; - } - - case RefVal::NotOwned: { - unsigned cnt = X.getCount(); - if (cnt) { - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - } - else { - X = X ^ RefVal::ReturnedNotOwned; - } - break; - } - - default: - return; - } - - // Update the binding. - state = state->set<RefBindings>(Sym, X); - Pred = Builder.MakeNode(Dst, S, Pred, state); - - // 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); - - // Did we cache out? - if (!Pred || stop) - return; - - // Get the updated binding. - T = state->get<RefBindings>(Sym); - assert(T); - X = *T; - - // Consult the summary of the enclosing method. - Decl const *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 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); - } -} - -void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst, - ExprEngine &Eng, - StmtNodeBuilder &Builder, - const ReturnStmt *S, - ExplodedNode *Pred, - RetEffect RE, RefVal X, - SymbolRef Sym, const GRState *state) { - // 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) { - // 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 - // method should return ownership unless it returns a CF object. - hasError = true; - X = X ^ RefVal::ErrorGCLeakReturned; - } - else if (!RE.isOwned()) { - // Either we are using GC and the returned object is a CF type - // or we aren't using GC. In either case, we expect that the - // enclosing method is expected to return ownership. - hasError = true; - X = X ^ RefVal::ErrorLeakReturned; - } - - 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); - if (N) { - CFRefReport *report = - new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this, - N, Sym, Eng); - BR->EmitReport(report); - } - } - } - return; - } - - 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); - } - } - } -} - -// Assumptions. - -const GRState* CFRefCount::evalAssume(const GRState *state, - SVal Cond, bool Assumption) { - - // FIXME: We may add to the interface of evalAssume the list of symbols - // whose assumptions have changed. For now we just iterate through the - // bindings and check if any of the tracked symbols are NULL. This isn't - // too bad since the number of symbols we will track in practice are - // probably small and evalAssume is only called at branches and a few - // other places. - RefBindings B = state->get<RefBindings>(); - - if (B.isEmpty()) - return state; - - bool changed = false; - RefBindings::Factory& RefBFactory = state->get_context<RefBindings>(); - - 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())) { - changed = true; - B = RefBFactory.remove(B, I.getKey()); - } - } - - if (changed) - state = state->set<RefBindings>(B); - - 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; - } - - // Handle all use-after-releases. - if (!isGCEnabled() && V.getKind() == RefVal::Released) { - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - return state->set<RefBindings>(sym, V); - } - - 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; - } - return state->set<RefBindings>(sym, V); -} - -//===----------------------------------------------------------------------===// -// 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) { - - 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?"); - unsigned Cnt = V.getCount(); - - // FIXME: Handle sending 'autorelease' to already released object. - - if (V.getKind() == RefVal::ReturnedOwned) - ++Cnt; - - if (ACnt <= Cnt) { - if (ACnt == Cnt) { - V.clearCounts(); - if (V.getKind() == RefVal::ReturnedOwned) - V = V ^ RefVal::ReturnedNotOwned; - else - V = V ^ RefVal::NotOwned; - } - else { - V.setCount(Cnt - ACnt); - V.setAutoreleaseCount(0); - } - state = state->set<RefBindings>(Sym, V); - ExplodedNode *N = Bd.MakeNode(state, Pred); - stop = (N == 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); - 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"; - - CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), - *this, N, Sym, os.str()); - BR->EmitReport(report); - } - - return std::make_pair((ExplodedNode*)0, state); -} - -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); - - if (!hasLeak) - return state->remove<RefBindings>(sid); - - Leaked.push_back(sid); - return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); -} - -ExplodedNode* -CFRefCount::ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl<SymbolRef> &Leaked, - GenericNodeBuilderRefCount &Builder, - ExprEngine& Eng, - ExplodedNode *Pred) { - - if (Leaked.empty()) - return Pred; - - // Generate an intermediate node representing the leak point. - ExplodedNode *N = Builder.MakeNode(state, Pred); - - if (N) { - for (llvm::SmallVectorImpl<SymbolRef>::iterator - I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - - CFRefBug *BT = static_cast<CFRefBug*>(Pred ? leakWithinFunction - : leakAtReturn); - assert(BT && "BugType not initialized."); - CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); - BR->EmitReport(report); - } - } - - return N; -} - -void CFRefCount::evalEndPath(ExprEngine& Eng, - EndOfFunctionNodeBuilder& Builder) { - - const GRState *state = Builder.getState(); - GenericNodeBuilderRefCount Bd(Builder); - RefBindings B = state->get<RefBindings>(); - ExplodedNode *Pred = 0; - - 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) - return; - } - - B = state->get<RefBindings>(); - llvm::SmallVector<SymbolRef, 10> Leaked; - - for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); - - ProcessLeaks(state, Leaked, Bd, Eng, Pred); -} - -void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - ExplodedNode* Pred, - const GRState* state, - SymbolReaper& SymReaper) { - const Stmt *S = Builder.getStmt(); - 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)){ - // 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) - return; - } - } - - B = state->get<RefBindings>(); - llvm::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); - } - - static unsigned LeakPPTag = 0; - { - GenericNodeBuilderRefCount Bd(Builder, S, &LeakPPTag); - Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); - } - - // Did we cache out? - if (!Pred) - return; - - // Now generate a new node that nukes the old bindings. - 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); - - 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); -} - -//===----------------------------------------------------------------------===// -// 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). -//===----------------------------------------------------------------------===// - -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 - -const GRState * -RetainReleaseChecker::checkRegionChanges(const GRState *state, - const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *begin, - const MemRegion * const *end) const { - if (!invalidated) - return state; - - 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; -} - -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; - - const GRState *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. - llvm::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 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; - - // 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; - } - - C.generateNode(state); -} - -//===----------------------------------------------------------------------===// -// Transfer function creation for external clients. -//===----------------------------------------------------------------------===// - -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>(); -} - -TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts) { - return new CFRefCount(Ctx, GCEnabled, lopts); -} diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 089a5cc39037..391a781ab09d 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -6,34 +6,36 @@ add_clang_library(clangStaticAnalyzerCore AggExprVisitor.cpp AnalysisManager.cpp BasicConstraintManager.cpp - BasicStore.cpp BasicValueFactory.cpp BlockCounter.cpp BugReporter.cpp BugReporterVisitors.cpp - CFRefCount.cpp - CXXExprEngine.cpp + Checker.cpp CheckerContext.cpp CheckerHelpers.cpp CheckerManager.cpp + CheckerRegistry.cpp CoreEngine.cpp Environment.cpp ExplodedGraph.cpp ExprEngine.cpp - FlatStore.cpp - GRState.cpp + ExprEngineC.cpp + ExprEngineCXX.cpp + ExprEngineCallAndReturn.cpp + ExprEngineObjC.cpp HTMLDiagnostics.cpp MemRegion.cpp ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp + ProgramState.cpp RangeConstraintManager.cpp RegionStore.cpp + SValBuilder.cpp + SVals.cpp SimpleConstraintManager.cpp SimpleSValBuilder.cpp Store.cpp - SValBuilder.cpp - SVals.cpp SymbolManager.cpp TextPathDiagnostics.cpp ) diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp new file mode 100644 index 000000000000..a3bf2c236f6e --- /dev/null +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -0,0 +1,22 @@ +//== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker, used to create and register checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +StringRef CheckerBase::getTagDescription() const { + // FIXME: We want to return the package + name of the checker here. + return "A Checker"; +} diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index f6fb8f256c01..5356edc752fa 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -23,8 +23,8 @@ CheckerContext::~CheckerContext() { // if we are building sinks or we generated a node and decided to not // add it as a transition. if (Dst.size() == size && !B.BuildSinks && !B.hasGeneratedNode) { - if (ST && ST != B.GetState(Pred)) { - static int autoTransitionTag = 0; + if (ST && ST != Pred->getState()) { + static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto"); addTransition(ST, &autoTransitionTag); } else diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index ba7c384e5c36..acacfb0e18c6 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -12,8 +12,9 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" @@ -33,7 +34,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const { !DeadSymbolsCheckers.empty() || !RegionChangesCheckers.empty() || !EvalAssumeCheckers.empty() || - !EvalCallCheckers.empty(); + !EvalCallCheckers.empty() || + !InlineCallCheckers.empty(); } void CheckerManager::finishedCheckerRegistration() { @@ -122,7 +124,7 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, namespace { struct CheckStmtContext { - typedef llvm::SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; + typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; bool IsPreVisit; const CheckersTy &Checkers; const Stmt *S; @@ -138,9 +140,12 @@ namespace { void runChecker(CheckerManager::CheckStmtFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { // FIXME: Remove respondsToCallback from CheckerContext; - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - IsPreVisit ? ProgramPoint::PreStmtKind : - ProgramPoint::PostStmtKind, 0, S); + ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(S, C); } }; @@ -174,10 +179,12 @@ namespace { void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - IsPreVisit ? ProgramPoint::PreStmtKind : - ProgramPoint::PostStmtKind, 0, - Msg.getOriginExpr()); + ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(Msg.getOriginExpr(), + K, Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(Msg, C); } }; @@ -214,10 +221,13 @@ namespace { void runChecker(CheckerManager::CheckLocationFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - IsLoad ? ProgramPoint::PreLoadKind : - ProgramPoint::PreStoreKind, 0, S); - checkFn(Loc, IsLoad, C); + ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + + checkFn(Loc, IsLoad, S, C); } }; } @@ -249,9 +259,12 @@ namespace { void runChecker(CheckerManager::CheckBindFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - ProgramPoint::PreStmtKind, 0, S); - checkFn(Loc, Val, C); + ProgramPoint::Kind K = ProgramPoint::PreStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + + checkFn(Loc, Val, S, C); } }; } @@ -293,7 +306,7 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *condition, } /// \brief Run checkers for live symbols. -void CheckerManager::runCheckersForLiveSymbols(const GRState *state, +void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state, SymbolReaper &SymReaper) { for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) LiveSymbolsCheckers[i](state, SymReaper); @@ -316,8 +329,11 @@ namespace { void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - ProgramPoint::PostPurgeDeadSymbolsKind, 0, S); + ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(SR, C); } }; @@ -334,7 +350,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, } /// \brief True if at least one checker wants to check region changes. -bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { +bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) if (RegionChangesCheckers[i].WantUpdateFn(state)) return true; @@ -343,24 +359,25 @@ bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { } /// \brief Run checkers for region changes. -const GRState * -CheckerManager::runCheckersForRegionChanges(const GRState *state, +const ProgramState * +CheckerManager::runCheckersForRegionChanges(const ProgramState *state, const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *Begin, - const MemRegion * const *End) { + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. if (!state) return NULL; - state = RegionChangesCheckers[i].CheckFn(state, invalidated, Begin, End); + state = RegionChangesCheckers[i].CheckFn(state, invalidated, + ExplicitRegions, Regions); } return state; } /// \brief Run checkers for handling assumptions on symbolic values. -const GRState * -CheckerManager::runCheckersForEvalAssume(const GRState *state, +const ProgramState * +CheckerManager::runCheckersForEvalAssume(const ProgramState *state, SVal Cond, bool Assumption) { for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), @@ -379,7 +396,9 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExprEngine &Eng, GraphExpander *defaultEval) { - if (EvalCallCheckers.empty() && defaultEval == 0) { + if (EvalCallCheckers.empty() && + InlineCallCheckers.empty() && + defaultEval == 0) { Dst.insert(Src); return; } @@ -389,13 +408,50 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred = *NI; bool anyEvaluated = false; + + // First, check if any of the InlineCall callbacks can evaluate the call. + assert(InlineCallCheckers.size() <= 1 && + "InlineCall is a special hacky callback to allow intrusive" + "evaluation of the call (which simulates inlining). It is " + "currently only used by OSAtomicChecker and should go away " + "at some point."); + for (std::vector<InlineCallFunc>::iterator + EI = InlineCallCheckers.begin(), EE = InlineCallCheckers.end(); + EI != EE; ++EI) { + ExplodedNodeSet checkDst; + bool evaluated = (*EI)(CE, Eng, Pred, checkDst); + assert(!(evaluated && anyEvaluated) + && "There are more than one checkers evaluating the call"); + if (evaluated) { + anyEvaluated = true; + Dst.insert(checkDst); +#ifdef NDEBUG + break; // on release don't check that no other checker also evals. +#endif + } + } + +#ifdef NDEBUG // on release don't check that no other checker also evals. + if (anyEvaluated) { + break; + } +#endif + + // Next, check if any of the EvalCall callbacks can evaluate the call. for (std::vector<EvalCallFunc>::iterator EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end(); EI != EE; ++EI) { ExplodedNodeSet checkDst; - CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, EI->Checker, - ProgramPoint::PostStmtKind, 0, CE); - bool evaluated = (*EI)(CE, C); + ProgramPoint::Kind K = ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K, + Pred->getLocationContext(), EI->Checker); + bool evaluated = false; + { // CheckerContext generates transitions(populates checkDest) on + // destruction, so introduce the scope to make sure it gets properly + // populated. + CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, L, 0); + evaluated = (*EI)(CE, C); + } assert(!(evaluated && anyEvaluated) && "There are more than one checkers evaluating the call"); if (evaluated) { @@ -407,6 +463,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } } + // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { if (defaultEval) defaultEval->expandGraph(Dst, Pred); @@ -425,6 +482,14 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( EndOfTranslationUnitCheckers[i](TU, mgr, BR); } +void CheckerManager::runCheckersForPrintState(raw_ostream &Out, + const ProgramState *State, + const char *NL, const char *Sep) { + for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator + I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I) + I->second->printState(Out, State, NL, Sep); +} + //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// @@ -504,6 +569,10 @@ void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { EvalCallCheckers.push_back(checkfn); } +void CheckerManager::_registerForInlineCall(InlineCallFunc checkfn) { + InlineCallCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEndOfTranslationUnit( CheckEndOfTranslationUnit checkfn) { EndOfTranslationUnitCheckers.push_back(checkfn); @@ -542,7 +611,4 @@ CheckerManager::~CheckerManager() { } // Anchor for the vtable. -CheckerProvider::~CheckerProvider() { } - -// Anchor for the vtable. GraphExpander::~GraphExpander() { } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp new file mode 100644 index 000000000000..13401acf3da9 --- /dev/null +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -0,0 +1,149 @@ +//===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" +#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" + +using namespace clang; +using namespace ento; + +static const char PackageSeparator = '.'; +typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; + + +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + +static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, + StringRef packageName) { + // Does the checker's full name have the package as a prefix? + if (!checker.FullName.startswith(packageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (checker.FullName.size() == packageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (checker.FullName[packageName.size()] == PackageSeparator) + return true; + + return false; +} + +static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, + const llvm::StringMap<size_t> &packageSizes, + CheckerOptInfo &opt, CheckerInfoSet &collected) { + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo packageInfo(NULL, opt.getName(), ""); + CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end(); + CheckerRegistry::CheckerInfoList::const_iterator i = + std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT); + + // If we didn't even find a possible package, give up. + if (i == e) + return; + + // If what we found doesn't actually start the package, give up. + if (!isInPackage(*i, opt.getName())) + return; + + // There is at least one checker in the package; claim the option. + opt.claim(); + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single checker. + size_t size = 1; + llvm::StringMap<size_t>::const_iterator packageSize = + packageSizes.find(opt.getName()); + if (packageSize != packageSizes.end()) + size = packageSize->getValue(); + + // Step through all the checkers in the package. + for (e = i+size; i != e; ++i) { + if (opt.isEnabled()) + collected.insert(&*i); + else + collected.erase(&*i); + } +} + +void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, + StringRef desc) { + Checkers.push_back(CheckerInfo(fn, name, desc)); + + // Record the presence of the checker in its packages. + StringRef packageName, leafName; + llvm::tie(packageName, leafName) = name.rsplit(PackageSeparator); + while (!leafName.empty()) { + Packages[packageName] += 1; + llvm::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, + SmallVectorImpl<CheckerOptInfo> &opts) const { + // Sort checkers for efficient collection. + std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers; + for (SmallVectorImpl<CheckerOptInfo>::iterator + i = opts.begin(), e = opts.end(); i != e; ++i) { + collectCheckers(Checkers, Packages, *i, enabledCheckers); + } + + // Initialize the CheckerManager with all enabled checkers. + for (CheckerInfoSet::iterator + i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) { + (*i)->Initialize(checkerMgr); + } +} + +void CheckerRegistry::printHelp(llvm::raw_ostream &out, + size_t maxNameChars) const { + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + + // FIXME: Print available packages. + + out << "CHECKERS:\n"; + + // Find the maximum option length. + size_t optionFieldWidth = 0; + for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); + i != e; ++i) { + // Limit the amount of padding we are willing to give up for alignment. + // Package.Name Description [Hidden] + size_t nameLength = i->FullName.size(); + if (nameLength <= maxNameChars) + optionFieldWidth = std::max(optionFieldWidth, nameLength); + } + + const size_t initialPad = 2; + for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); + i != e; ++i) { + out.indent(initialPad) << i->FullName; + + int pad = optionFieldWidth - i->FullName.size(); + + // Break on long option names. + if (pad < 0) { + out << '\n'; + pad = optionFieldWidth + initialPad; + } + out.indent(pad + 2) << i->Desc; + + out << '\n'; + } +} diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 34cd6e8884a0..525219846a93 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -17,22 +17,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/Index/TranslationUnit.h" #include "clang/AST/Expr.h" +#include "clang/AST/StmtCXX.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" - -using llvm::cast; -using llvm::isa; using namespace clang; using namespace ento; -// This should be removed in the future. -namespace clang { -namespace ento { -TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts); -} -} - //===----------------------------------------------------------------------===// // Worklist classes for exploration of reachable states. //===----------------------------------------------------------------------===// @@ -41,7 +31,7 @@ WorkList::Visitor::~Visitor() {} namespace { class DFS : public WorkList { - llvm::SmallVector<WorkListUnit,20> Stack; + SmallVector<WorkListUnit,20> Stack; public: virtual bool hasWork() const { return !Stack.empty(); @@ -59,7 +49,7 @@ public: } virtual bool visitItemsInWorkList(Visitor &V) { - for (llvm::SmallVectorImpl<WorkListUnit>::iterator + for (SmallVectorImpl<WorkListUnit>::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) { if (V.visit(*I)) return true; @@ -107,7 +97,7 @@ WorkList *WorkList::makeBFS() { return new BFS(); } namespace { class BFSBlockDFSContents : public WorkList { std::deque<WorkListUnit> Queue; - llvm::SmallVector<WorkListUnit,20> Stack; + SmallVector<WorkListUnit,20> Stack; public: virtual bool hasWork() const { return !Queue.empty() || !Stack.empty(); @@ -136,7 +126,7 @@ namespace { return U; } virtual bool visitItemsInWorkList(Visitor &V) { - for (llvm::SmallVectorImpl<WorkListUnit>::iterator + for (SmallVectorImpl<WorkListUnit>::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) { if (V.visit(*I)) return true; @@ -162,12 +152,12 @@ WorkList* WorkList::makeBFSBlockDFSContents() { /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, - const GRState *InitState) { + const ProgramState *InitState) { if (G->num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. - const CFGBlock* Entry = &(L->getCFG()->getEntry()); + const CFGBlock *Entry = &(L->getCFG()->getEntry()); assert (Entry->empty() && "Entry block must be empty."); @@ -176,7 +166,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, "Entry block must have 1 successor."); // Get the solitary successor. - const CFGBlock* Succ = *(Entry->succ_begin()); + const CFGBlock *Succ = *(Entry->succ_begin()); // Construct an edge representing the // starting location in the function. @@ -208,7 +198,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, WList->setBlockCounter(WU.getBlockCounter()); // Retrieve the node. - ExplodedNode* Node = WU.getNode(); + ExplodedNode *Node = WU.getNode(); // Dispatch on the location type. switch (Node->getLocation().getKind()) { @@ -246,11 +236,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, } void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, - unsigned Steps, - const GRState *InitState, - ExplodedNodeSet &Dst) { + unsigned Steps, + const ProgramState *InitState, + ExplodedNodeSet &Dst) { ExecuteWorkList(L, Steps, InitState); - for (llvm::SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), + for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), E = G->EndNodes.end(); I != E; ++I) { Dst.Add(*I); } @@ -268,9 +258,9 @@ void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { SubEng.processCallExit(Builder); } -void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { +void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { - const CFGBlock* Blk = L.getDst(); + const CFGBlock *Blk = L.getDst(); // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { @@ -305,15 +295,15 @@ void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { } } - for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator + for (SmallVectorImpl<ExplodedNode*>::const_iterator I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end(); I != E; ++I) { blocksExhausted.push_back(std::make_pair(L, *I)); } } -void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, - ExplodedNode* Pred) { +void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, + ExplodedNode *Pred) { // Increment the block counter. BlockCounter Counter = WList->getBlockCounter(); @@ -324,21 +314,19 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, // Process the entrance of the block. if (CFGElement E = L.getFirstElement()) { - StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, - SubEng.getStateManager()); + StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this); SubEng.processCFGElement(E, Builder); } else HandleBlockExit(L.getBlock(), Pred); } -void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { +void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { - if (const Stmt* Term = B->getTerminator()) { + if (const Stmt *Term = B->getTerminator()) { switch (Term->getStmtClass()) { default: - assert(false && "Analysis for this terminator not implemented."); - break; + llvm_unreachable("Analysis for this terminator not implemented."); case Stmt::BinaryOperatorClass: // '&&' and '||' HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); @@ -361,6 +349,10 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); return; + case Stmt::CXXForRangeStmtClass: + HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred); + return; + case Stmt::ForStmtClass: HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); return; @@ -422,34 +414,34 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { Pred->State, Pred); } -void CoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term, - const CFGBlock * B, ExplodedNode* Pred) { +void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, + const CFGBlock * B, ExplodedNode *Pred) { assert(B->succ_size() == 2); BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), Pred, this); SubEng.processBranch(Cond, Term, Builder); } -void CoreEngine::HandlePostStmt(const CFGBlock* B, unsigned StmtIdx, - ExplodedNode* Pred) { +void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, + ExplodedNode *Pred) { assert (!B->empty()); if (StmtIdx == B->size()) HandleBlockExit(B, Pred); else { - StmtNodeBuilder Builder(B, StmtIdx, Pred, this, - SubEng.getStateManager()); + StmtNodeBuilder Builder(B, StmtIdx, Pred, this); SubEng.processCFGElement((*B)[StmtIdx], Builder); } } /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. -void CoreEngine::generateNode(const ProgramPoint& Loc, - const GRState* State, ExplodedNode* Pred) { +void CoreEngine::generateNode(const ProgramPoint &Loc, + const ProgramState *State, + ExplodedNode *Pred) { bool IsNew; - ExplodedNode* Node = G->getNode(Loc, State, &IsNew); + ExplodedNode *Node = G->getNode(Loc, State, &IsNew); if (Pred) Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. @@ -463,7 +455,7 @@ void CoreEngine::generateNode(const ProgramPoint& Loc, } ExplodedNode * -GenericNodeBuilderImpl::generateNodeImpl(const GRState *state, +GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state, ExplodedNode *pred, ProgramPoint programPoint, bool asSink) { @@ -483,14 +475,14 @@ GenericNodeBuilderImpl::generateNodeImpl(const GRState *state, return 0; } -StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx, - ExplodedNode* N, CoreEngine* e, - GRStateManager &mgr) - : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), +StmtNodeBuilder::StmtNodeBuilder(const CFGBlock *b, + unsigned idx, + ExplodedNode *N, + CoreEngine* e) + : Eng(*e), B(*b), Idx(idx), Pred(N), PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false), PointKind(ProgramPoint::PostStmtKind), Tag(0) { Deferred.insert(N); - CleanedState = Pred->getState(); } StmtNodeBuilder::~StmtNodeBuilder() { @@ -499,7 +491,7 @@ StmtNodeBuilder::~StmtNodeBuilder() { GenerateAutoTransition(*I); } -void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { +void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { assert (!N->isSink()); // Check if this node entered a callee. @@ -526,18 +518,20 @@ void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { } bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew); Succ->addPredecessor(N, *Eng.G); if (IsNew) Eng.WList->enqueue(Succ, &B, Idx+1); } -ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, - ExplodedNode* Pred, const GRState* St, - ProgramPoint::Kind K) { +ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst, + const Stmt *S, + ExplodedNode *Pred, + const ProgramState *St, + ProgramPoint::Kind K) { - ExplodedNode* N = generateNode(S, St, Pred, K); + ExplodedNode *N = generateNode(S, St, Pred, K); if (N) { if (BuildSinks) @@ -549,46 +543,24 @@ ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, return N; } -static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, - const LocationContext *LC, const void *tag){ - switch (K) { - default: - assert(false && "Unhandled ProgramPoint kind"); - case ProgramPoint::PreStmtKind: - return PreStmt(S, LC, tag); - case ProgramPoint::PostStmtKind: - return PostStmt(S, LC, tag); - case ProgramPoint::PreLoadKind: - return PreLoad(S, LC, tag); - case ProgramPoint::PostLoadKind: - return PostLoad(S, LC, tag); - case ProgramPoint::PreStoreKind: - return PreStore(S, LC, tag); - case ProgramPoint::PostStoreKind: - return PostStore(S, LC, tag); - case ProgramPoint::PostLValueKind: - return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); - } -} - ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, - ExplodedNode* Pred, - ProgramPoint::Kind K, - const void *tag) { +StmtNodeBuilder::generateNodeInternal(const Stmt *S, + const ProgramState *state, + ExplodedNode *Pred, + ProgramPoint::Kind K, + const ProgramPointTag *tag) { - const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); return generateNodeInternal(L, state, Pred); } ExplodedNode* StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const GRState* State, - ExplodedNode* Pred) { + const ProgramState *State, + ExplodedNode *Pred) { bool IsNew; - ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); + ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew); N->addPredecessor(Pred, *Eng.G); Deferred.erase(Pred); @@ -601,11 +573,11 @@ StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, } // This function generate a new ExplodedNode but not a new branch(block edge). -ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition, - const GRState* State) { +ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition, + const ProgramState *State) { bool IsNew; - ExplodedNode* Succ + ExplodedNode *Succ = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State, &IsNew); @@ -619,8 +591,8 @@ ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition, return NULL; } -ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, - bool branch) { +ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State, + bool branch) { // If the branch has been marked infeasible we should not generate a node. if (!isFeasible(branch)) @@ -628,7 +600,7 @@ ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, bool IsNew; - ExplodedNode* Succ = + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), State, &IsNew); @@ -657,11 +629,12 @@ BranchNodeBuilder::~BranchNodeBuilder() { ExplodedNode* -IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, - bool isSink) { +IndirectGotoNodeBuilder::generateNode(const iterator &I, + const ProgramState *St, + bool isSink) { bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); @@ -681,34 +654,38 @@ IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, ExplodedNode* -SwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ +SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, + const ProgramState *St) { bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), + St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { Eng.WList->enqueue(Succ); return Succ; } - return NULL; } ExplodedNode* -SwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { - +SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, + bool isSink) { // Get the block for the default case. - assert (Src->succ_rbegin() != Src->succ_rend()); - CFGBlock* DefaultBlock = *Src->succ_rbegin(); + assert(Src->succ_rbegin() != Src->succ_rend()); + CFGBlock *DefaultBlock = *Src->succ_rbegin(); + // Sanity check for default blocks that are unreachable and not caught + // by earlier stages. + if (!DefaultBlock) + return NULL; + bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, + Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (IsNew) { @@ -735,12 +712,13 @@ EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() { } ExplodedNode* -EndOfFunctionNodeBuilder::generateNode(const GRState* State, - ExplodedNode* P, const void *tag) { +EndOfFunctionNodeBuilder::generateNode(const ProgramState *State, + ExplodedNode *P, + const ProgramPointTag *tag) { hasGeneratedNode = true; bool IsNew; - ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, + ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B, Pred->getLocationContext(), tag ? tag : Tag), State, &IsNew); @@ -754,7 +732,7 @@ EndOfFunctionNodeBuilder::generateNode(const GRState* State, return NULL; } -void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) { +void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) { hasGeneratedNode = true; // Create a CallExit node and enqueue it. const StackFrameContext *LocCtx @@ -773,7 +751,7 @@ void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) { } -void CallEnterNodeBuilder::generateNode(const GRState *state) { +void CallEnterNodeBuilder::generateNode(const ProgramState *state) { // Check if the callee is in the same translation unit. if (CalleeCtx->getTranslationUnit() != Pred->getLocationContext()->getTranslationUnit()) { @@ -787,30 +765,13 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) { // Create a new AnalysisManager with components of the callee's // TranslationUnit. - // The Diagnostic is actually shared when we create ASTUnits from AST files. - AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), - OldMgr.getLangOptions(), - OldMgr.getPathDiagnosticClient(), - OldMgr.getStoreManagerCreator(), - OldMgr.getConstraintManagerCreator(), - OldMgr.getCheckerManager(), - OldMgr.getIndexer(), - OldMgr.getMaxNodes(), OldMgr.getMaxVisit(), - OldMgr.shouldVisualizeGraphviz(), - OldMgr.shouldVisualizeUbigraph(), - OldMgr.shouldPurgeDead(), - OldMgr.shouldEagerlyAssume(), - OldMgr.shouldTrimGraph(), - OldMgr.shouldInlineCall(), - OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(), - OldMgr.getAnalysisContextManager().getAddImplicitDtors(), - OldMgr.getAnalysisContextManager().getAddInitializers(), - OldMgr.shouldEagerlyTrimExplodedGraph()); - llvm::OwningPtr<TransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(), - /* GCEnabled */ false, - AMgr.getLangOptions())); + // The Diagnostic is actually shared when we create ASTUnits from AST files. + AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), OldMgr); + // Create the new engine. - ExprEngine NewEng(AMgr, TF.take()); + // FIXME: This cast isn't really safe. + bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled(); + ExprEngine NewEng(AMgr, GCEnabled); // Create the new LocationContext. AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), @@ -823,8 +784,8 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) { OldLocCtx->getIndex()); // Now create an initial state for the new engine. - const GRState *NewState = NewEng.getStateManager().MarshalState(state, - NewLocCtx); + const ProgramState *NewState = + NewEng.getStateManager().MarshalState(state, NewLocCtx); ExplodedNodeSet ReturnNodes; NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), NewState, ReturnNodes); @@ -850,7 +811,7 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) { Eng.WList->enqueue(Node); } -void CallExitNodeBuilder::generateNode(const GRState *state) { +void CallExitNodeBuilder::generateNode(const ProgramState *state) { // Get the callee's location context. const StackFrameContext *LocCtx = cast<StackFrameContext>(Pred->getLocationContext()); diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 3961c7b95259..e1b982c08cfc 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -11,14 +11,15 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprObjC.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; using namespace ento; -SVal Environment::lookupExpr(const Stmt* E) const { +SVal Environment::lookupExpr(const Stmt *E) const { const SVal* X = ExprBindings.lookup(E); if (X) { SVal V = *X; @@ -83,9 +84,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, case Stmt::CXXBindTemporaryExprClass: E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); continue; - case Stmt::MaterializeTemporaryExprClass: - E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); - continue; + case Stmt::ObjCPropertyRefExprClass: + return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E)); + // Handle all other Stmt* using a lookup. default: break; @@ -129,18 +130,6 @@ public: }; } // end anonymous namespace -static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) { - const LocationContext *ParentLC = LC->getParent(); - while (ParentLC) { - CFG &C = *ParentLC->getCFG(); - if (C.isBlkExpr(E)) - return true; - ParentLC = ParentLC->getParent(); - } - - return false; -} - // In addition to mapping from Stmt * - > SVals in the Environment, we also // maintain a mapping from Stmt * -> SVals (locations) that were used during // a load and store. @@ -158,24 +147,27 @@ static inline bool IsLocation(const Stmt *S) { Environment EnvironmentManager::removeDeadBindings(Environment Env, SymbolReaper &SymReaper, - const GRState *ST, - llvm::SmallVectorImpl<const MemRegion*> &DRoots) { - - CFG &C = *SymReaper.getLocationContext()->getCFG(); + const ProgramState *ST) { // We construct a new Environment object entirely, as this is cheaper than // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); - llvm::SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations; + SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations; + + MarkLiveCallback CB(SymReaper); + ScanReachableSymbols RSScaner(ST, CB); + + llvm::ImmutableMapRef<const Stmt*,SVal> + EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), + F.getTreeFactory()); // Iterate over the block-expr bindings. for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { const Stmt *BlkExpr = I.getKey(); - // For recorded locations (used when evaluating loads and stores), we // consider them live only when their associated normal expression is // also live. @@ -185,41 +177,20 @@ EnvironmentManager::removeDeadBindings(Environment Env, deferredLocations.push_back(std::make_pair(BlkExpr, I.getData())); continue; } - const SVal &X = I.getData(); - // Block-level expressions in callers are assumed always live. - if (isBlockExprInCallers(BlkExpr, SymReaper.getLocationContext())) { - NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); - - if (isa<loc::MemRegionVal>(X)) { - const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion(); - DRoots.push_back(R); - } - - // Mark all symbols in the block expr's value live. - MarkLiveCallback cb(SymReaper); - ST->scanReachableSymbols(X, cb); - continue; - } - - // Not a block-level expression? - if (!C.isBlkExpr(BlkExpr)) - continue; - if (SymReaper.isLive(BlkExpr)) { // Copy the binding to the new map. - NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); + EBMapRef = EBMapRef.add(BlkExpr, X); // If the block expr's value is a memory region, then mark that region. if (isa<loc::MemRegionVal>(X)) { - const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion(); - DRoots.push_back(R); + const MemRegion *R = cast<loc::MemRegionVal>(X).getRegion(); + SymReaper.markLive(R); } // Mark all symbols in the block expr's value live. - MarkLiveCallback cb(SymReaper); - ST->scanReachableSymbols(X, cb); + RSScaner.scan(X); continue; } @@ -228,17 +199,18 @@ EnvironmentManager::removeDeadBindings(Environment Env, // beginning of itself, but we need its UndefinedVal to determine its // SVal. if (X.isUndef() && cast<UndefinedVal>(X).getData()) - NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); + EBMapRef = EBMapRef.add(BlkExpr, X); } // Go through he deferred locations and add them to the new environment if // the correspond Stmt* is in the map as well. - for (llvm::SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator + for (SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1); - if (NewEnv.ExprBindings.lookup(S)) - NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, I->first, I->second); + if (EBMapRef.lookup(S)) + EBMapRef = EBMapRef.add(I->first, I->second); } + NewEnv.ExprBindings = EBMapRef.asImmutableMap(); return NewEnv; } diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index fa16fead9f1d..5762a21600e4 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" @@ -93,18 +93,17 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { ProgramPoint progPoint = node->getLocation(); if (!isa<PostStmt>(progPoint)) continue; - // Condition 4. PostStmt ps = cast<PostStmt>(progPoint); - if (ps.getTag() || isa<PostStmtCustom>(ps)) + if (ps.getTag()) continue; if (isa<BinaryOperator>(ps.getStmt())) continue; // Conditions 5, 6, and 7. - const GRState *state = node->getState(); - const GRState *pred_state = pred->getState(); + const ProgramState *state = node->getState(); + const ProgramState *pred_state = pred->getState(); if (state->store != pred_state->store || state->GDM != pred_state->GDM || progPoint.getLocationContext() != pred->getLocationContext()) continue; @@ -134,11 +133,11 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { // ExplodedNode. //===----------------------------------------------------------------------===// -static inline BumpVector<ExplodedNode*>& getVector(void* P) { +static inline BumpVector<ExplodedNode*>& getVector(void *P) { return *reinterpret_cast<BumpVector<ExplodedNode*>*>(P); } -void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) { +void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) { assert (!V->isSink()); Preds.addNode(V, G); V->Succs.addNode(this, G); @@ -153,12 +152,12 @@ void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) { assert(getKind() == Size1); } -void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) { +void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) { assert((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0); assert(!getFlag()); if (getKind() == Size1) { - if (ExplodedNode* NOld = getNode()) { + if (ExplodedNode *NOld = getNode()) { BumpVectorContext &Ctx = G.getNodeAllocator(); BumpVector<ExplodedNode*> *V = G.getAllocator().Allocate<BumpVector<ExplodedNode*> >(); @@ -215,11 +214,11 @@ ExplodedNode** ExplodedNode::NodeGroup::end() const { } } -ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L, - const GRState* State, bool* IsNew) { +ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, + const ProgramState *State, bool* IsNew) { // Profile 'State' to determine if we already have an existing node. llvm::FoldingSetNodeID profile; - void* InsertPos = 0; + void *InsertPos = 0; NodeTy::Profile(profile, L, State); NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); @@ -285,7 +284,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty; Pass2Ty& Pass2 = M->M; - llvm::SmallVector<const ExplodedNode*, 10> WL1, WL2; + SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { @@ -326,7 +325,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, // ===- Pass 2 (forward DFS to construct the new graph) -=== while (!WL2.empty()) { - const ExplodedNode* N = WL2.back(); + const ExplodedNode *N = WL2.back(); WL2.pop_back(); // Skip this node if we have already processed it. @@ -335,7 +334,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. - ExplodedNode* NewN = G->getNode(N->getLocation(), N->State, NULL); + ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, NULL); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. @@ -383,7 +382,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, } ExplodedNode* -InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { +InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const { llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = M.find(N); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index ffe5f0b6cdf2..ac9cf0b08d44 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" @@ -35,25 +36,13 @@ using namespace clang; using namespace ento; -using llvm::dyn_cast; -using llvm::dyn_cast_or_null; -using llvm::cast; using llvm::APSInt; -namespace { - // Trait class for recording returned expression in the state. - struct ReturnExpr { - static int TagInt; - typedef const Stmt *data_type; - }; - int ReturnExpr::TagInt; -} - //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// -static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { +static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) { IdentifierInfo* II = &Ctx.Idents.get(name); return Ctx.Selectors.getSelector(0, &II); } @@ -62,7 +51,7 @@ static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { // Engine construction and deletion. //===----------------------------------------------------------------------===// -ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) +ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled) : AMgr(mgr), Engine(*this), G(Engine.getGraph()), @@ -75,11 +64,7 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) EntryNode(NULL), currentStmt(NULL), NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), RaiseSel(GetNullarySelector("raise", getContext())), - BR(mgr, *this), TF(tf) { - - // FIXME: Eventually remove the TF object entirely. - TF->RegisterChecks(*this); - TF->RegisterPrinters(getStateManager().Printers); + ObjCGCEnabled(gcEnabled), BR(mgr, *this) { if (mgr.shouldEagerlyTrimExplodedGraph()) { // Enable eager node reclaimation when constructing the ExplodedGraph. @@ -96,8 +81,8 @@ ExprEngine::~ExprEngine() { // Utility methods. //===----------------------------------------------------------------------===// -const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) { - const GRState *state = StateMgr.getInitialState(InitLoc); +const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) { + const ProgramState *state = StateMgr.getInitialState(InitLoc); // Preconditions. @@ -132,7 +117,7 @@ const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) { if (!Constraint) break; - if (const GRState *newState = state->assume(*Constraint, true)) + if (const ProgramState *newState = state->assume(*Constraint, true)) state = newState; break; @@ -162,11 +147,11 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) { SVal calleeV = callOrMessage.getFunctionCallee(); if (const FunctionTextRegion *codeR = - llvm::dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) { + dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) { const FunctionDecl *fd = codeR->getDecl(); if (const IdentifierInfo *ii = fd->getIdentifier()) { - llvm::StringRef fname = ii->getName(); + StringRef fname = ii->getName(); if (fname == "strlen") return false; } @@ -183,28 +168,27 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const /// evalAssume - Called by ConstraintManager. Used to call checker-specific /// logic for handling assumptions on symbolic values. -const GRState *ExprEngine::processAssume(const GRState *state, SVal cond, - bool assumption) { - state = getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); - - // If the state is infeasible at this point, bail out. - if (!state) - return NULL; - - return TF->evalAssume(state, cond, assumption); +const ProgramState *ExprEngine::processAssume(const ProgramState *state, + SVal cond, bool assumption) { + return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } -bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) { +bool ExprEngine::wantsRegionChangeUpdate(const ProgramState *state) { return getCheckerManager().wantsRegionChangeUpdate(state); } -const GRState * -ExprEngine::processRegionChanges(const GRState *state, +const ProgramState * +ExprEngine::processRegionChanges(const ProgramState *state, const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *Begin, - const MemRegion * const *End) { + ArrayRef<const MemRegion *> Explicits, + ArrayRef<const MemRegion *> Regions) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, - Begin, End); + Explicits, Regions); +} + +void ExprEngine::printState(raw_ostream &Out, const ProgramState *State, + const char *NL, const char *Sep) { + getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } void ExprEngine::processEndWorklist(bool hasWorkRemaining) { @@ -217,7 +201,7 @@ void ExprEngine::processCFGElement(const CFGElement E, case CFGElement::Invalid: llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(E.getAs<CFGStmt>()->getStmt(), builder); + ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), builder); return; case CFGElement::Initializer: ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), builder); @@ -232,9 +216,12 @@ void ExprEngine::processCFGElement(const CFGElement E, } void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { + // TODO: Use RAII to remove the unnecessary, tagged nodes. + //RegisterCreatedNodes registerCreatedNodes(getGraph()); + // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - // Recycle any unused states in the GRStateManager. + // Recycle any unused states in the ProgramStateManager. StateMgr.recycleUnusedStates(); currentStmt = S.getStmt(); @@ -242,74 +229,93 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { currentStmt->getLocStart(), "Error evaluating statement"); + // A tag to track convenience transitions, which can be removed at cleanup. + static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); Builder = &builder; EntryNode = builder.getPredecessor(); + const ProgramState *EntryState = EntryNode->getState(); + CleanedState = EntryState; + ExplodedNode *CleanedNode = 0; + // Create the cleaned state. const LocationContext *LC = EntryNode->getLocationContext(); - SymbolReaper SymReaper(LC, currentStmt, SymMgr); + SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - if (AMgr.shouldPurgeDead()) { - const GRState *St = EntryNode->getState(); - getCheckerManager().runCheckersForLiveSymbols(St, SymReaper); + if (AMgr.getPurgeMode() != PurgeNone) { + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); const StackFrameContext *SFC = LC->getCurrentStackFrame(); - CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper); - } else { - CleanedState = EntryNode->getState(); + + // Create a state in which dead bindings are removed from the environment + // and the store. TODO: The function should just return new env and store, + // not a new state. + CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); } // Process any special transfer function for dead symbols. ExplodedNodeSet Tmp; + if (!SymReaper.hasDeadSymbols()) { + // Generate a CleanedNode that has the environment and store cleaned + // up. Since no symbols are dead, we can optimize and not clean out + // the constraint manager. + CleanedNode = + Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag); + Tmp.Add(CleanedNode); - if (!SymReaper.hasDeadSymbols()) - Tmp.Add(EntryNode); - else { + } else { SaveAndRestore<bool> OldSink(Builder->BuildSinks); SaveOr OldHasGen(Builder->hasGeneratedNode); SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); Builder->PurgingDeadSymbols = true; - // FIXME: This should soon be removed. - ExplodedNodeSet Tmp2; - getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode, - CleanedState, SymReaper); - - getCheckerManager().runCheckersForDeadSymbols(Tmp, Tmp2, + // Call checkers with the non-cleaned state so that they could query the + // values of the soon to be dead symbols. + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode, SymReaper, currentStmt, *this); - if (!Builder->BuildSinks && !Builder->hasGeneratedNode) - Tmp.Add(EntryNode); - } + // For each node in CheckedSet, generate CleanedNodes that have the + // environment, the store, and the constraints cleaned up but have the + // user-supplied states as the predecessors. + for (ExplodedNodeSet::const_iterator + I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { + const ProgramState *CheckerState = (*I)->getState(); - bool HasAutoGenerated = false; + // The constraint manager has not been cleaned up yet, so clean up now. + CheckerState = getConstraintManager().removeDeadBindings(CheckerState, + SymReaper); - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - ExplodedNodeSet Dst; + assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) && + "Checkers are not allowed to modify the Environment as a part of " + "checkDeadSymbols processing."); + assert(StateMgr.haveEqualStores(CheckerState, EntryState) && + "Checkers are not allowed to modify the Store as a part of " + "checkDeadSymbols processing."); - // Set the cleaned state. - Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); + // Create a state based on CleanedState with CheckerState GDM and + // generate a transition to that state. + const ProgramState *CleanedCheckerSt = + StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); + ExplodedNode *CleanedNode = Builder->generateNode(currentStmt, + CleanedCheckerSt, *I, + &cleanupTag); + Tmp.Add(CleanedNode); + } + } + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + // TODO: Remove Dest set, it's no longer needed. + ExplodedNodeSet Dst; // Visit the statement. Visit(currentStmt, *I, Dst); - - // Do we need to auto-generate a node? We only need to do this to generate - // a node with a "cleaned" state; CoreEngine will actually handle - // auto-transitions for other cases. - if (Dst.size() == 1 && *Dst.begin() == EntryNode - && !Builder->hasGeneratedNode && !HasAutoGenerated) { - HasAutoGenerated = true; - builder.generateNode(currentStmt, GetState(EntryNode), *I); - } } // NULL out these variables to cleanup. CleanedState = NULL; EntryNode = NULL; - currentStmt = 0; - Builder = NULL; } @@ -334,7 +340,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){ ExplodedNode *Pred = *I; - const GRState *state = Pred->getState(); + const ProgramState *state = Pred->getState(); const FieldDecl *FD = BMI->getAnyMember(); @@ -391,7 +397,7 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, StmtNodeBuilder &builder) { ExplodedNode *pred = builder.getPredecessor(); - const GRState *state = pred->getState(); + const ProgramState *state = pred->getState(); const VarDecl *varDecl = dtor.getVarDecl(); QualType varType = varDecl->getType(); @@ -422,8 +428,8 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, StmtNodeBuilder &builder) { } -void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { +void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); @@ -447,9 +453,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: - case Stmt::CXXForRangeStmtClass: case Stmt::CXXPseudoDestructorExprClass: - case Stmt::CXXTemporaryObjectExprClass: case Stmt::CXXThrowExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: @@ -472,7 +476,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, { SaveAndRestore<bool> OldSink(Builder->BuildSinks); Builder->BuildSinks = true; - const ExplodedNode *node = MakeNode(Dst, S, Pred, GetState(Pred)); + const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState()); Engine.addAbortedBlock(node, Builder->getBlock()); break; } @@ -495,6 +499,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CaseStmtClass: case Stmt::CompoundStmtClass: case Stmt::ContinueStmtClass: + case Stmt::CXXForRangeStmtClass: case Stmt::DefaultStmtClass: case Stmt::DoStmtClass: case Stmt::ForStmtClass: @@ -511,7 +516,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); MakeNode(Dst, S, Pred, state); break; @@ -522,11 +527,12 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; case Stmt::ObjCPropertyRefExprClass: - VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst); + // Implicitly handled by Environment::getSVal(). + Dst.Add(Pred); break; case Stmt::ImplicitValueInitExprClass: { - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); SVal val = svalBuilder.makeZeroVal(ty); MakeNode(Dst, S, Pred, state->BindExpr(S, val)); @@ -558,6 +564,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: + case Stmt::AtomicExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need @@ -597,7 +604,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; } else if (B->getOpcode() == BO_Comma) { - const GRState* state = GetState(Pred); + const ProgramState *state = Pred->getState(); MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); break; } @@ -621,6 +628,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; } + case Stmt::CXXTemporaryObjectExprClass: case Stmt::CXXConstructExprClass: { const CXXConstructExpr *C = cast<CXXConstructExpr>(S); // For block-level CXXConstructExpr, we don't have a destination region. @@ -644,7 +652,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr - const ChooseExpr* C = cast<ChooseExpr>(S); + const ChooseExpr *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); break; } @@ -687,7 +695,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { - const CastExpr* C = cast<CastExpr>(S); + const CastExpr *C = cast<CastExpr>(S); // Handle the previsit checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); @@ -708,7 +716,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, const MaterializeTemporaryExpr *Materialize = cast<MaterializeTemporaryExpr>(S); if (!Materialize->getType()->isRecordType()) - CreateCXXTemporaryObject(Materialize->GetTemporaryExpr(), Pred, Dst); + CreateCXXTemporaryObject(Materialize, Pred, Dst); else Visit(Materialize->GetTemporaryExpr(), Pred, Dst); break; @@ -730,7 +738,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; case Stmt::ObjCMessageExprClass: - VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst); + VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); break; case Stmt::ObjCAtThrowStmtClass: { @@ -738,7 +746,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, // an abort. SaveAndRestore<bool> OldSink(Builder->BuildSinks); Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, GetState(Pred)); + MakeNode(Dst, S, Pred, Pred->getState()); break; } @@ -756,7 +764,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; case Stmt::StmtExprClass: { - const StmtExpr* SE = cast<StmtExpr>(S); + const StmtExpr *SE = cast<StmtExpr>(S); if (SE->getSubStmt()->body_empty()) { // Empty statement expression. @@ -766,8 +774,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; } - if (Expr* LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { - const GRState* state = GetState(Pred); + if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { + const ProgramState *state = Pred->getState(); MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); } else @@ -777,7 +785,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, } case Stmt::StringLiteralClass: { - const GRState* state = GetState(Pred); + const ProgramState *state = Pred->getState(); SVal V = state->getLValue(cast<StringLiteral>(S)); MakeNode(Dst, S, Pred, state->BindExpr(S, V)); return; @@ -811,8 +819,7 @@ void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, if (nodeBuilder.getBlockCounter().getNumVisited( pred->getLocationContext()->getCurrentStackFrame(), block->getBlockID()) >= AMgr.getMaxVisit()) { - - static int tag = 0; + static SimpleProgramPointTag tag("ExprEngine : Block count exceeded"); nodeBuilder.generateNode(pred->getState(), pred, &tag, true); } } @@ -821,11 +828,12 @@ void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, // Generic node creation. //===----------------------------------------------------------------------===// -ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, - ExplodedNode* Pred, const GRState* St, - ProgramPoint::Kind K, const void *tag) { +ExplodedNode *ExprEngine::MakeNode(ExplodedNodeSet &Dst, const Stmt *S, + ExplodedNode *Pred, const ProgramState *St, + ProgramPoint::Kind K, + const ProgramPointTag *tag) { assert (Builder && "StmtNodeBuilder not present."); - SaveAndRestore<const void*> OldTag(Builder->Tag); + SaveAndRestore<const ProgramPointTag*> OldTag(Builder->Tag); Builder->Tag = tag; return Builder->MakeNode(Dst, S, Pred, St, K); } @@ -834,8 +842,8 @@ ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, // Branch processing. //===----------------------------------------------------------------------===// -const GRState* ExprEngine::MarkBranch(const GRState* state, - const Stmt* Terminator, +const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, + const Stmt *Terminator, bool branchTaken) { switch (Terminator->getStmtClass()) { @@ -855,7 +863,7 @@ const GRState* ExprEngine::MarkBranch(const GRState* state, // For ||, if we take the false branch, then the value of the whole // expression is that of the RHS expression. - const Expr* Ex = (Op == BO_LAnd && branchTaken) || + const Expr *Ex = (Op == BO_LAnd && branchTaken) || (Op == BO_LOr && !branchTaken) ? B->getRHS() : B->getLHS(); @@ -870,7 +878,7 @@ const GRState* ExprEngine::MarkBranch(const GRState* state, // For ?, if branchTaken == true then the value is either the LHS or // the condition itself. (GNU extension). - const Expr* Ex; + const Expr *Ex; if (branchTaken) Ex = C->getTrueExpr(); @@ -882,9 +890,9 @@ const GRState* ExprEngine::MarkBranch(const GRState* state, case Stmt::ChooseExprClass: { // ?: - const ChooseExpr* C = cast<ChooseExpr>(Terminator); + const ChooseExpr *C = cast<ChooseExpr>(Terminator); - const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS(); return state->BindExpr(C, UndefinedVal(Ex)); } } @@ -895,8 +903,10 @@ const GRState* ExprEngine::MarkBranch(const GRState* state, /// integers that promote their values (which are currently not tracked well). /// This function returns the SVal bound to Condition->IgnoreCasts if all the // cast(s) did was sign-extend the original value. -static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, - const Stmt* Condition, ASTContext& Ctx) { +static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, + const ProgramState *state, + const Stmt *Condition, + ASTContext &Ctx) { const Expr *Ex = dyn_cast<Expr>(Condition); if (!Ex) @@ -929,7 +939,7 @@ static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, return state->getSVal(Ex); } -void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, +void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, BranchNodeBuilder& builder) { // Check for NULL conditions; e.g. "for(;;)" @@ -948,7 +958,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, if (!builder.isFeasible(true) && !builder.isFeasible(false)) return; - const GRState* PrevState = builder.getState(); + const ProgramState *PrevState = builder.getState(); SVal X = PrevState->getSVal(Condition); if (X.isUnknownOrUndef()) { @@ -980,7 +990,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, // Process the true branch. if (builder.isFeasible(true)) { - if (const GRState *state = PrevState->assume(V, true)) + if (const ProgramState *state = PrevState->assume(V, true)) builder.generateNode(MarkBranch(state, Term, true), true); else builder.markInfeasible(true); @@ -988,7 +998,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, // Process the false branch. if (builder.isFeasible(false)) { - if (const GRState *state = PrevState->assume(V, false)) + if (const ProgramState *state = PrevState->assume(V, false)) builder.generateNode(MarkBranch(state, Term, false), false); else builder.markInfeasible(false); @@ -999,7 +1009,7 @@ void ExprEngine::processBranch(const Stmt* Condition, const Stmt* Term, /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { - const GRState *state = builder.getState(); + const ProgramState *state = builder.getState(); SVal V = state->getSVal(builder.getTarget()); // Three possibilities: @@ -1021,8 +1031,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { } } - assert(false && "No block with label."); - return; + llvm_unreachable("No block with label."); } if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { @@ -1040,31 +1049,9 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { builder.generateNode(I, state); } - -void ExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L, - const Expr* R, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - assert(Ex == currentStmt && - Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(Ex); - - assert (X.isUndef()); - - const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); - assert(SE); - X = state->getSVal(SE); - - // Make sure that we invalidate the previous binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); -} - /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { - getTF().evalEndPath(*this, builder); StateMgr.EndPath(builder.getState()); getCheckerManager().runCheckersForEndPath(builder, *this); } @@ -1073,8 +1060,8 @@ void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { typedef SwitchNodeBuilder::iterator iterator; - const GRState* state = builder.getState(); - const Expr* CondE = builder.getCondition(); + const ProgramState *state = builder.getState(); + const Expr *CondE = builder.getCondition(); SVal CondV_untested = state->getSVal(CondE); if (CondV_untested.isUndef()) { @@ -1086,7 +1073,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { } DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); - const GRState *DefaultSt = state; + const ProgramState *DefaultSt = state; iterator I = builder.begin(), EI = builder.end(); bool defaultIsFeasible = I == EI; @@ -1096,28 +1083,16 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { if (!I.getBlock()) continue; - const CaseStmt* Case = I.getCase(); + const CaseStmt *Case = I.getCase(); // Evaluate the LHS of the case value. - Expr::EvalResult V1; - bool b = Case->getLHS()->Evaluate(V1, getContext()); - - // Sanity checks. These go away in Release builds. - assert(b && V1.Val.isInt() && !V1.HasSideEffects - && "Case condition must evaluate to an integer constant."); - (void)b; // silence unused variable warning - assert(V1.Val.getInt().getBitWidth() == - getContext().getTypeSize(CondE->getType())); + llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); + assert(V1.getBitWidth() == getContext().getTypeSize(CondE->getType())); // Get the RHS of the case, if it exists. - Expr::EvalResult V2; - - if (const Expr* E = Case->getRHS()) { - b = E->Evaluate(V2, getContext()); - assert(b && V2.Val.isInt() && !V2.HasSideEffects - && "Case condition must evaluate to an integer constant."); - (void)b; // silence unused variable warning - } + llvm::APSInt V2; + if (const Expr *E = Case->getRHS()) + V2 = E->EvaluateKnownConstInt(getContext()); else V2 = V1; @@ -1126,12 +1101,12 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // This should be easy once we have "ranges" for NonLVals. do { - nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); + nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1)); DefinedOrUnknownSVal Res = svalBuilder.evalEQ(DefaultSt ? DefaultSt : state, CondV, CaseVal); // Now "assume" that the case matches. - if (const GRState* stateNew = state->assume(Res, true)) { + if (const ProgramState *stateNew = state->assume(Res, true)) { builder.generateCaseStmtNode(I, stateNew); // If CondV evaluates to a constant, then we know that this @@ -1144,7 +1119,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // Now "assume" that the case doesn't match. Add this state // to the default state (if it is feasible). if (DefaultSt) { - if (const GRState *stateNew = DefaultSt->assume(Res, false)) { + if (const ProgramState *stateNew = DefaultSt->assume(Res, false)) { defaultIsFeasible = true; DefaultSt = stateNew; } @@ -1155,11 +1130,11 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { } // Concretize the next value in the range. - if (V1.Val.getInt() == V2.Val.getInt()) + if (V1 == V2) break; - ++V1.Val.getInt(); - assert (V1.Val.getInt() <= V2.Val.getInt()); + ++V1; + assert (V1 <= V2); } while (true); } @@ -1184,120 +1159,16 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { builder.generateDefaultCaseNode(DefaultSt); } -void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) { - const GRState *state = B.getState()->enterStackFrame(B.getCalleeContext()); - B.generateNode(state); -} - -void ExprEngine::processCallExit(CallExitNodeBuilder &B) { - const GRState *state = B.getState(); - const ExplodedNode *Pred = B.getPredecessor(); - const StackFrameContext *calleeCtx = - cast<StackFrameContext>(Pred->getLocationContext()); - const Stmt *CE = calleeCtx->getCallSite(); - - // If the callee returns an expression, bind its value to CallExpr. - const Stmt *ReturnedExpr = state->get<ReturnExpr>(); - if (ReturnedExpr) { - SVal RetVal = state->getSVal(ReturnedExpr); - state = state->BindExpr(CE, RetVal); - // Clear the return expr GDM. - state = state->remove<ReturnExpr>(); - } - - // Bind the constructed object value to CXXConstructExpr. - if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { - const CXXThisRegion *ThisR = - getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); - - SVal ThisV = state->getSVal(ThisR); - // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, ThisV); - } - - B.generateNode(state); -} - -//===----------------------------------------------------------------------===// -// Transfer functions: logical operations ('&&', '||'). -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - assert(B->getOpcode() == BO_LAnd || - B->getOpcode() == BO_LOr); - - assert(B==currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(B); - assert(X.isUndef()); - - const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); - assert(Ex); - - if (Ex == B->getRHS()) { - X = state->getSVal(Ex); - - // Handle undefined values. - if (X.isUndef()) { - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - return; - } - - DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); - - // We took the RHS. Because the value of the '&&' or '||' expression must - // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 - // or 1. Alternatively, we could take a lazy approach, and calculate this - // value later when necessary. We don't have the machinery in place for - // this right now, and since most logical expressions are used for branches, - // the payoff is not likely to be large. Instead, we do eager evaluation. - if (const GRState *newState = state->assume(XD, true)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); - - if (const GRState *newState = state->assume(XD, false)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); - } - else { - // We took the LHS expression. Depending on whether we are '&&' or - // '||' we know what the value of the expression is via properties of - // the short-circuiting. - X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, - B->getType()); - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - } -} - //===----------------------------------------------------------------------===// // Transfer functions: Loads and stores. //===----------------------------------------------------------------------===// -void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - ExplodedNodeSet Tmp; - - CanQualType T = getContext().getCanonicalType(BE->getType()); - SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, - Pred->getLocationContext()); - - MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), - ProgramPoint::PostLValueKind); - - // Post-visit the BlockExpr. - getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); -} - void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); - if (const VarDecl* VD = dyn_cast<VarDecl>(D)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { assert(Ex->isLValue()); SVal V = state->getLValue(VD, Pred->getLocationContext()); @@ -1314,13 +1185,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ProgramPoint::PostLValueKind); return; } - if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) { + if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { assert(!Ex->isLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); return; } - if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { SVal V = svalBuilder.getFunctionPointer(FD); MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), ProgramPoint::PostLValueKind); @@ -1331,124 +1202,93 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, } /// VisitArraySubscriptExpr - Transfer function for array accesses -void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* A, - ExplodedNode* Pred, - ExplodedNodeSet& Dst){ +void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, + ExplodedNode *Pred, + ExplodedNodeSet &Dst){ - const Expr* Base = A->getBase()->IgnoreParens(); - const Expr* Idx = A->getIdx()->IgnoreParens(); + const Expr *Base = A->getBase()->IgnoreParens(); + const Expr *Idx = A->getIdx()->IgnoreParens(); - // Evaluate the base. - ExplodedNodeSet Tmp; - Visit(Base, Pred, Tmp); - - for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { - ExplodedNodeSet Tmp2; - Visit(Idx, *I1, Tmp2); // Evaluate the index. - ExplodedNodeSet Tmp3; - getCheckerManager().runCheckersForPreStmt(Tmp3, Tmp2, A, *this); - - for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { - const GRState* state = GetState(*I2); - SVal V = state->getLValue(A->getType(), state->getSVal(Idx), - state->getSVal(Base)); - assert(A->isLValue()); - MakeNode(Dst, A, *I2, state->BindExpr(A, V), ProgramPoint::PostLValueKind); - } + + ExplodedNodeSet checkerPreStmt; + getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); + + for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(), + ei = checkerPreStmt.end(); it != ei; ++it) { + const ProgramState *state = (*it)->getState(); + SVal V = state->getLValue(A->getType(), state->getSVal(Idx), + state->getSVal(Base)); + assert(A->isLValue()); + MakeNode(Dst, A, *it, state->BindExpr(A, V), ProgramPoint::PostLValueKind); } } /// VisitMemberExpr - Transfer function for member expressions. -void ExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - Expr *baseExpr = M->getBase()->IgnoreParens(); - ExplodedNodeSet dstBase; - Visit(baseExpr, Pred, dstBase); +void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { - FieldDecl *field = dyn_cast<FieldDecl>(M->getMemberDecl()); + Decl *member = M->getMemberDecl(); + if (VarDecl *VD = dyn_cast<VarDecl>(member)) { + assert(M->isLValue()); + VisitCommonDeclRefExpr(M, VD, Pred, Dst); + return; + } + + FieldDecl *field = dyn_cast<FieldDecl>(member); if (!field) // FIXME: skipping member expressions for non-fields return; - for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); - I != E; ++I) { - const GRState* state = GetState(*I); - SVal baseExprVal = state->getSVal(baseExpr); - if (isa<nonloc::LazyCompoundVal>(baseExprVal) || - isa<nonloc::CompoundVal>(baseExprVal) || - // FIXME: This can originate by conjuring a symbol for an unknown - // temporary struct object, see test/Analysis/fields.c: - // (p = getit()).x - isa<nonloc::SymbolVal>(baseExprVal)) { - MakeNode(Dst, M, *I, state->BindExpr(M, UnknownVal())); - continue; - } + Expr *baseExpr = M->getBase()->IgnoreParens(); + const ProgramState *state = Pred->getState(); + SVal baseExprVal = state->getSVal(baseExpr); + if (isa<nonloc::LazyCompoundVal>(baseExprVal) || + isa<nonloc::CompoundVal>(baseExprVal) || + // FIXME: This can originate by conjuring a symbol for an unknown + // temporary struct object, see test/Analysis/fields.c: + // (p = getit()).x + isa<nonloc::SymbolVal>(baseExprVal)) { + MakeNode(Dst, M, Pred, state->BindExpr(M, UnknownVal())); + return; + } - // FIXME: Should we insert some assumption logic in here to determine - // if "Base" is a valid piece of memory? Before we put this assumption - // later when using FieldOffset lvals (which we no longer have). + // FIXME: Should we insert some assumption logic in here to determine + // if "Base" is a valid piece of memory? Before we put this assumption + // later when using FieldOffset lvals (which we no longer have). - // For all other cases, compute an lvalue. - SVal L = state->getLValue(field, baseExprVal); - if (M->isLValue()) - MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); - else - evalLoad(Dst, M, *I, state, L); - } + // For all other cases, compute an lvalue. + SVal L = state->getLValue(field, baseExprVal); + if (M->isLValue()) + MakeNode(Dst, M, Pred, state->BindExpr(M, L), ProgramPoint::PostLValueKind); + else + evalLoad(Dst, M, Pred, state, L); } /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore and (soon) VisitDeclStmt, and others. -void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE, - ExplodedNode* Pred, const GRState* state, - SVal location, SVal Val, bool atDeclInit) { - +void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, + ExplodedNode *Pred, + SVal location, SVal Val, bool atDeclInit) { // Do a previsit of the bind. - ExplodedNodeSet CheckedSet, Src; - Src.Add(Pred); - getCheckerManager().runCheckersForBind(CheckedSet, Src, location, Val, StoreE, - *this); + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, + StoreE, *this); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { - if (Pred != *I) - state = GetState(*I); - - const GRState* newState = 0; + const ProgramState *state = (*I)->getState(); if (atDeclInit) { const VarRegion *VR = cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion()); - newState = state->bindDecl(VR, Val); - } - else { - if (location.isUnknown()) { - // We know that the new state will be the same as the old state since - // the location of the binding is "unknown". Consequently, there - // is no reason to just create a new node. - newState = state; - } - else { - // We are binding to a value other than 'unknown'. Perform the binding - // using the StoreManager. - newState = state->bindLoc(cast<Loc>(location), Val); - } + state = state->bindDecl(VR, Val); + } else { + state = state->bindLoc(location, Val); } - // The next thing to do is check if the TransferFuncs object wants to - // update the state based on the new binding. If the GRTransferFunc object - // doesn't do anything, just auto-propagate the current state. - - // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE' - // is non-NULL. Checkers typically care about - - StmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, - true); - - getTF().evalBind(BuilderRef, location, Val); + MakeNode(Dst, StoreE, *I, state); } } @@ -1460,11 +1300,11 @@ void ExprEngine::evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE, /// @param state The current simulation state /// @param location The location to store the value /// @param Val The value to be stored -void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE, - const Expr* LocationE, - ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - const void *tag) { +void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, + const Expr *LocationE, + ExplodedNode *Pred, + const ProgramState *state, SVal location, SVal Val, + const ProgramPointTag *tag) { assert(Builder && "StmtNodeBuilder must be defined."); @@ -1474,9 +1314,8 @@ void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE, if (isa<loc::ObjCPropRef>(location)) { loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - ExplodedNodeSet src = Pred; return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(), - StoreE, Val), src, Dst); + StoreE, Val), Pred, Dst); } // Evaluate the location (checks for bad dereferences). @@ -1493,38 +1332,38 @@ void ExprEngine::evalStore(ExplodedNodeSet& Dst, const Expr *AssignE, ProgramPoint::PostStoreKind); for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - evalBind(Dst, StoreE, *NI, GetState(*NI), location, Val); + evalBind(Dst, StoreE, *NI, location, Val); } -void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { +void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex, + ExplodedNode *Pred, + const ProgramState *state, SVal location, + const ProgramPointTag *tag, QualType LoadTy) { assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); if (isa<loc::ObjCPropRef>(location)) { loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - ExplodedNodeSet src = Pred; return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex), - src, Dst); + Pred, Dst); } // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the // referenced value. - if (const TypedRegion *TR = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + if (const TypedValueRegion *TR = + dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { QualType ValTy = TR->getValueType(); if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { - static int loadReferenceTag = 0; + static SimpleProgramPointTag + loadReferenceTag("ExprEngine : Load Reference"); ExplodedNodeSet Tmp; evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, getContext().getPointerType(RT->getPointeeType())); // Perform the load from the referenced value. for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { - state = GetState(*I); + state = (*I)->getState(); location = state->getSVal(Ex); evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); } @@ -1535,10 +1374,10 @@ void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex, evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); } -void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { +void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *Ex, + ExplodedNode *Pred, + const ProgramState *state, SVal location, + const ProgramPointTag *tag, QualType LoadTy) { // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; @@ -1554,7 +1393,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, // Proceed with the load. for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - state = GetState(*NI); + state = (*NI)->getState(); if (location.isUnknown()) { // This is important. We must nuke the old binding. @@ -1572,9 +1411,9 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, } void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, bool isLoad) { + ExplodedNode *Pred, + const ProgramState *state, SVal location, + const ProgramPointTag *tag, bool isLoad) { // Early checks for performance reason. if (location.isUnknown()) { Dst.Add(Pred); @@ -1582,7 +1421,7 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, } ExplodedNodeSet Src; - if (Builder->GetState(Pred) == state) { + if (Pred->getState() == state) { Src.Add(Pred); } else { // Associate this new state with an ExplodedNode. @@ -1593,7 +1432,11 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, // "p = 0" is not noted as "Null pointer value stored to 'p'" but // instead "int *p" is noted as // "Variable 'p' initialized to a null pointer value" - ExplodedNode *N = Builder->generateNode(S, state, Pred, this); + + // FIXME: why is 'tag' not used instead of etag? + static SimpleProgramPointTag etag("ExprEngine: Location"); + + ExplodedNode *N = Builder->generateNode(S, state, Pred, &etag); Src.Add(N ? N : Pred); } getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S, @@ -1611,7 +1454,7 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, // cases as well. #if 0 - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); @@ -1627,7 +1470,7 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, case Stmt::CXXOperatorCallExprClass: { const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE); methodDecl = - llvm::dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl()); + dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl()); break; } case Stmt::CXXMemberCallExprClass: { @@ -1676,126 +1519,18 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, #endif } -void ExprEngine::VisitCallExpr(const CallExpr* CE, ExplodedNode* Pred, - ExplodedNodeSet& dst) { - - // Determine the type of function we're calling (if available). - const FunctionProtoType *Proto = NULL; - QualType FnType = CE->getCallee()->IgnoreParens()->getType(); - if (const PointerType *FnTypePtr = FnType->getAs<PointerType>()) - Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>(); - - // Should the first argument be evaluated as an lvalue? - bool firstArgumentAsLvalue = false; - switch (CE->getStmtClass()) { - case Stmt::CXXOperatorCallExprClass: - firstArgumentAsLvalue = true; - break; - default: - break; - } - - // Evaluate the arguments. - ExplodedNodeSet dstArgsEvaluated; - evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, dstArgsEvaluated, - firstArgumentAsLvalue); - - // Evaluate the callee. - ExplodedNodeSet dstCalleeEvaluated; - evalCallee(CE, dstArgsEvaluated, dstCalleeEvaluated); - - // Perform the previsit of the CallExpr. - ExplodedNodeSet dstPreVisit; - getCheckerManager().runCheckersForPreStmt(dstPreVisit, dstCalleeEvaluated, - CE, *this); - - // Now evaluate the call itself. - class DefaultEval : public GraphExpander { - ExprEngine &Eng; - const CallExpr *CE; - public: - - DefaultEval(ExprEngine &eng, const CallExpr *ce) - : Eng(eng), CE(ce) {} - virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - // Should we inline the call? - if (Eng.getAnalysisManager().shouldInlineCall() && - Eng.InlineCall(Dst, CE, Pred)) { - return; - } - - StmtNodeBuilder &Builder = Eng.getBuilder(); - assert(&Builder && "StmtNodeBuilder must be defined."); - - // Dispatch to the plug-in transfer function. - unsigned oldSize = Dst.size(); - SaveOr OldHasGen(Builder.hasGeneratedNode); - - // Dispatch to transfer function logic to handle the call itself. - const Expr* Callee = CE->getCallee()->IgnoreParens(); - const GRState* state = Eng.GetState(Pred); - SVal L = state->getSVal(Callee); - Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred); - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder.BuildSinks && Dst.size() == oldSize && - !Builder.hasGeneratedNode) - Eng.MakeNode(Dst, CE, Pred, state); - } - }; - - // Finally, evaluate the function call. We try each of the checkers - // to see if the can evaluate the function call. - ExplodedNodeSet dstCallEvaluated; - DefaultEval defEval(*this, CE); - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, - dstPreVisit, - CE, *this, &defEval); - - // Finally, perform the post-condition check of the CallExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, - *this); -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C dot-syntax to access a property. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Ex, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - ExplodedNodeSet dstBase; - - // Visit the receiver (if any). - if (Ex->isObjectReceiver()) - Visit(Ex->getBase(), Pred, dstBase); - else - dstBase = Pred; - - ExplodedNodeSet dstPropRef; - - // Using the base, compute the lvalue of the instance variable. - for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); - I!=E; ++I) { - ExplodedNode *nodeBase = *I; - const GRState *state = GetState(nodeBase); - MakeNode(dstPropRef, Ex, *I, state->BindExpr(Ex, loc::ObjCPropRef(Ex))); - } - - Dst.insert(dstPropRef); +std::pair<const ProgramPointTag *, const ProgramPointTag*> +ExprEngine::getEagerlyAssumeTags() { + static SimpleProgramPointTag + EagerlyAssumeTrue("ExprEngine : Eagerly Assume True"), + EagerlyAssumeFalse("ExprEngine : Eagerly Assume False"); + return std::make_pair(&EagerlyAssumeTrue, &EagerlyAssumeFalse); } -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -static std::pair<const void*,const void*> EagerlyAssumeTag - = std::pair<const void*,const void*>(&EagerlyAssumeTag,static_cast<void*>(0)); - void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex) { + + for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; @@ -1808,25 +1543,24 @@ void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, continue; } - const GRState* state = GetState(Pred); + const ProgramState *state = Pred->getState(); SVal V = state->getSVal(Ex); if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) { + const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = + getEagerlyAssumeTags(); + // First assume that the condition is true. - if (const GRState *stateTrue = state->assume(*SEV, true)) { - stateTrue = stateTrue->BindExpr(Ex, - svalBuilder.makeIntVal(1U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, - &EagerlyAssumeTag, Pred->getLocationContext()), - stateTrue, Pred)); + if (const ProgramState *StateTrue = state->assume(*SEV, true)) { + SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); + StateTrue = StateTrue->BindExpr(Ex, Val); + Dst.Add(Builder->generateNode(Ex, StateTrue, Pred, tags.first)); } // Next, assume that the condition is false. - if (const GRState *stateFalse = state->assume(*SEV, false)) { - stateFalse = stateFalse->BindExpr(Ex, - svalBuilder.makeIntVal(0U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, - Pred->getLocationContext()), - stateFalse, Pred)); + if (const ProgramState *StateFalse = state->assume(*SEV, false)) { + SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); + StateFalse = StateFalse->BindExpr(Ex, Val); + Dst.Add(Builder->generateNode(Ex, StateFalse, Pred, tags.second)); } } else @@ -1834,954 +1568,15 @@ void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, } } -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C @synchronized. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - // The mutex expression is a CFGElement, so we don't need to explicitly - // visit it since it will already be processed. - - // Pre-visit the ObjCAtSynchronizedStmt. - ExplodedNodeSet Tmp; - Tmp.Add(Pred); - getCheckerManager().runCheckersForPreStmt(Dst, Tmp, S, *this); -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* Ex, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - // Visit the base expression, which is needed for computing the lvalue - // of the ivar. - ExplodedNodeSet dstBase; - const Expr *baseExpr = Ex->getBase(); - Visit(baseExpr, Pred, dstBase); - - ExplodedNodeSet dstIvar; - - // Using the base, compute the lvalue of the instance variable. - for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); - I!=E; ++I) { - ExplodedNode *nodeBase = *I; - const GRState *state = GetState(nodeBase); - SVal baseVal = state->getSVal(baseExpr); - SVal location = state->getLValue(Ex->getDecl(), baseVal); - MakeNode(dstIvar, Ex, *I, state->BindExpr(Ex, location)); - } - - // Perform the post-condition check of the ObjCIvarRefExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this); -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C fast enumeration 'for' statements. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - // ObjCForCollectionStmts are processed in two places. This method - // handles the case where an ObjCForCollectionStmt* occurs as one of the - // statements within a basic block. This transfer function does two things: - // - // (1) binds the next container value to 'element'. This creates a new - // node in the ExplodedGraph. - // - // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating - // whether or not the container has any more elements. This value - // will be tested in ProcessBranch. We need to explicitly bind - // this value because a container can contain nil elements. - // - // FIXME: Eventually this logic should actually do dispatches to - // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). - // This will require simulating a temporary NSFastEnumerationState, either - // through an SVal or through the use of MemRegions. This value can - // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop - // terminates we reclaim the temporary (it goes out of scope) and we - // we can test if the SVal is 0 or if the MemRegion is null (depending - // on what approach we take). - // - // For now: simulate (1) by assigning either a symbol or nil if the - // container is empty. Thus this transfer function will by default - // result in state splitting. - - const Stmt* elem = S->getElement(); - SVal ElementV; - - if (const DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { - const VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl()); - assert (ElemD->getInit() == 0); - ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); - VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); - return; - } - - ExplodedNodeSet Tmp; - Visit(cast<Expr>(elem), Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); - } -} - -void ExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst, - SVal ElementV) { - - // Check if the location we are writing back to is a null pointer. - const Stmt* elem = S->getElement(); - ExplodedNodeSet Tmp; - evalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); - - if (Tmp.empty()) - return; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - Pred = *NI; - const GRState *state = GetState(Pred); - - // Handle the case where the container still has elements. - SVal TrueV = svalBuilder.makeTruthVal(1); - const GRState *hasElems = state->BindExpr(S, TrueV); - - // Handle the case where the container has no elements. - SVal FalseV = svalBuilder.makeTruthVal(0); - const GRState *noElems = state->BindExpr(S, FalseV); - - if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV)) - if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) { - // FIXME: The proper thing to do is to really iterate over the - // container. We will do this with dispatch logic to the store. - // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(); - assert(Loc::isLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); - SVal V = svalBuilder.makeLoc(Sym); - hasElems = hasElems->bindLoc(ElementV, V); - - // Bind the location to 'nil' on the false branch. - SVal nilV = svalBuilder.makeIntVal(0, T); - noElems = noElems->bindLoc(ElementV, nilV); - } - - // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C message expressions. -//===----------------------------------------------------------------------===// - -namespace { -class ObjCMsgWLItem { -public: - ObjCMessageExpr::const_arg_iterator I; - ExplodedNode *N; - - ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n) - : I(i), N(n) {} -}; -} // end anonymous namespace - -void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, - ExplodedNode* Pred, - ExplodedNodeSet& Dst){ - - // Create a worklist to process both the arguments. - llvm::SmallVector<ObjCMsgWLItem, 20> WL; - - // But first evaluate the receiver (if any). - ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); - if (const Expr *Receiver = ME->getInstanceReceiver()) { - ExplodedNodeSet Tmp; - Visit(Receiver, Pred, Tmp); - - if (Tmp.empty()) - return; - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) - WL.push_back(ObjCMsgWLItem(AI, *I)); - } - else - WL.push_back(ObjCMsgWLItem(AI, Pred)); - - // Evaluate the arguments. - ExplodedNodeSet ArgsEvaluated; - while (!WL.empty()) { - ObjCMsgWLItem Item = WL.back(); - WL.pop_back(); - - if (Item.I == AE) { - ArgsEvaluated.insert(Item.N); - continue; - } - - // Evaluate the subexpression. - ExplodedNodeSet Tmp; - - // FIXME: [Objective-C++] handle arguments that are references - Visit(*Item.I, Item.N, Tmp); - - // Enqueue evaluating the next argument on the worklist. - ++(Item.I); - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - WL.push_back(ObjCMsgWLItem(Item.I, *NI)); - } - - // Now that the arguments are processed, handle the ObjC message. - VisitObjCMessage(ME, ArgsEvaluated, Dst); -} - -void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, - ExplodedNodeSet &Src, ExplodedNodeSet& Dst) { - - // Handle the previsits checks. - ExplodedNodeSet DstPrevisit; - getCheckerManager().runCheckersForPreObjCMessage(DstPrevisit, Src, msg,*this); - - // Proceed with evaluate the message expression. - ExplodedNodeSet dstEval; - - for (ExplodedNodeSet::iterator DI = DstPrevisit.begin(), - DE = DstPrevisit.end(); DI != DE; ++DI) { - - ExplodedNode *Pred = *DI; - bool RaisesException = false; - unsigned oldSize = dstEval.size(); - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); - - if (const Expr *Receiver = msg.getInstanceReceiver()) { - const GRState *state = GetState(Pred); - SVal recVal = state->getSVal(Receiver); - if (!recVal.isUndef()) { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - - const GRState *notNilState, *nilState; - llvm::tie(notNilState, nilState) = state->assume(receiverVal); - - // There are three cases: can be nil or non-nil, must be nil, must be - // non-nil. We ignore must be nil, and merge the rest two into non-nil. - if (nilState && !notNilState) { - dstEval.insert(Pred); - continue; - } - - // Check if the "raise" message was sent. - assert(notNilState); - if (msg.getSelector() == RaiseSel) - RaisesException = true; - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, notNilState); - } - } - else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { - IdentifierInfo* ClsName = Iface->getIdentifier(); - Selector S = msg.getSelector(); - - // Check for special instance methods. - if (!NSExceptionII) { - ASTContext& Ctx = getContext(); - NSExceptionII = &Ctx.Idents.get("NSException"); - } - - if (ClsName == NSExceptionII) { - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. - if (!NSExceptionInstanceRaiseSelectors) { - ASTContext& Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = - new Selector[NUM_RAISE_SELECTORS]; - llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; - unsigned idx = 0; - - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; - break; - } - } - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, Builder->GetState(Pred)); - } - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && dstEval.size() == oldSize && - !Builder->hasGeneratedNode) - MakeNode(dstEval, msg.getOriginExpr(), Pred, GetState(Pred)); - } - - // Finally, perform the post-condition check of the ObjCMessageExpr and store - // the created nodes in 'Dst'. - getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Miscellaneous statements. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - ExplodedNodeSet S1; - Visit(Ex, Pred, S1); - ExplodedNodeSet S2; - getCheckerManager().runCheckersForPreStmt(S2, S1, CastE, *this); - - if (CastE->getCastKind() == CK_LValueToRValue || - CastE->getCastKind() == CK_GetObjCProperty) { - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I!=E; ++I) { - ExplodedNode *subExprNode = *I; - const GRState *state = GetState(subExprNode); - evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); - } - return; - } - - // All other casts. - QualType T = CastE->getType(); - QualType ExTy = Ex->getType(); - - if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) - T = ExCast->getTypeAsWritten(); - - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - Pred = *I; - - switch (CastE->getCastKind()) { - case CK_LValueToRValue: - assert(false && "LValueToRValue casts handled earlier."); - case CK_GetObjCProperty: - assert(false && "GetObjCProperty casts handled earlier."); - case CK_ToVoid: - Dst.Add(Pred); - continue; - // The analyzer doesn't do anything special with these casts, - // since it understands retain/release semantics already. - case CK_ObjCProduceObject: - case CK_ObjCConsumeObject: - case CK_ObjCReclaimReturnedObject: // Fall-through. - // True no-ops. - case CK_NoOp: - case CK_FunctionToPointerDecay: { - // Copy the SVal of Ex to CastE. - const GRState *state = GetState(Pred); - SVal V = state->getSVal(Ex); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); - continue; - } - case CK_Dependent: - case CK_ArrayToPointerDecay: - case CK_BitCast: - case CK_LValueBitCast: - case CK_IntegralCast: - case CK_NullToPointer: - case CK_IntegralToPointer: - case CK_PointerToIntegral: - case CK_PointerToBoolean: - case CK_IntegralToBoolean: - case CK_IntegralToFloating: - case CK_FloatingToIntegral: - case CK_FloatingToBoolean: - case CK_FloatingCast: - case CK_FloatingRealToComplex: - case CK_FloatingComplexToReal: - case CK_FloatingComplexToBoolean: - case CK_FloatingComplexCast: - case CK_FloatingComplexToIntegralComplex: - case CK_IntegralRealToComplex: - case CK_IntegralComplexToReal: - case CK_IntegralComplexToBoolean: - case CK_IntegralComplexCast: - case CK_IntegralComplexToFloatingComplex: - case CK_AnyPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_ObjCObjectLValueCast: { - // Delegate to SValBuilder to process. - const GRState* state = GetState(Pred); - SVal V = state->getSVal(Ex); - V = svalBuilder.evalCast(V, T, ExTy); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); - continue; - } - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: { - // For DerivedToBase cast, delegate to the store manager. - const GRState *state = GetState(Pred); - SVal val = state->getSVal(Ex); - val = getStoreManager().evalDerivedToBase(val, T); - state = state->BindExpr(CastE, val); - MakeNode(Dst, CastE, Pred, state); - continue; - } - // Various C++ casts that are not handled yet. - case CK_Dynamic: - case CK_ToUnion: - case CK_BaseToDerived: - case CK_NullToMemberPointer: - case CK_BaseToDerivedMemberPointer: - case CK_DerivedToBaseMemberPointer: - case CK_UserDefinedConversion: - case CK_ConstructorConversion: - case CK_VectorSplat: - case CK_MemberPointerToBoolean: { - // Recover some path-sensitivty by conjuring a new value. - QualType resultType = CastE->getType(); - if (CastE->isLValue()) - resultType = getContext().getPointerType(resultType); - - SVal result = - svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType, - Builder->getCurrentBlockCount()); - - const GRState *state = GetState(Pred)->BindExpr(CastE, result); - MakeNode(Dst, CastE, Pred, state); - continue; - } - } - } -} - -void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - const InitListExpr* ILE - = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); - ExplodedNodeSet Tmp; - Visit(ILE, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { - const GRState* state = GetState(*I); - SVal ILV = state->getSVal(ILE); - const LocationContext *LC = (*I)->getLocationContext(); - state = state->bindCompoundLiteral(CL, LC, ILV); - - if (CL->isLValue()) { - MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); - } - else - MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); - } -} - -void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, - ExplodedNodeSet& Dst) { - - // The CFG has one DeclStmt per Decl. - const Decl* D = *DS->decl_begin(); - - if (!D || !isa<VarDecl>(D)) - return; - - const VarDecl* VD = dyn_cast<VarDecl>(D); - const Expr* InitEx = VD->getInit(); - - // FIXME: static variables may have an initializer, but the second - // time a function is called those values may not be current. - ExplodedNodeSet Tmp; - - if (InitEx) - Visit(InitEx, Pred, Tmp); - else - Tmp.Add(Pred); - - ExplodedNodeSet Tmp2; - getCheckerManager().runCheckersForPreStmt(Tmp2, Tmp, DS, *this); - - for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { - ExplodedNode *N = *I; - const GRState *state = GetState(N); - - // Decls without InitExpr are not initialized explicitly. - const LocationContext *LC = N->getLocationContext(); - - if (InitEx) { - SVal InitVal = state->getSVal(InitEx); - - // We bound the temp obj region to the CXXConstructExpr. Now recover - // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && - !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ - InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); - assert(isa<nonloc::LazyCompoundVal>(InitVal)); - } - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if ((InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) && - !VD->getType()->isReferenceType()) { - InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); - } - - evalBind(Dst, DS, *I, state, - loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); - } - else { - state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); - MakeNode(Dst, DS, *I, state); - } - } -} - -namespace { - // This class is used by VisitInitListExpr as an item in a worklist - // for processing the values contained in an InitListExpr. -class InitListWLItem { -public: - llvm::ImmutableList<SVal> Vals; - ExplodedNode* N; - InitListExpr::const_reverse_iterator Itr; - - InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals, - InitListExpr::const_reverse_iterator itr) - : Vals(vals), N(n), Itr(itr) {} -}; -} - - -void ExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - const GRState* state = GetState(Pred); - QualType T = getContext().getCanonicalType(E->getType()); - unsigned NumInitElements = E->getNumInits(); - - if (T->isArrayType() || T->isRecordType() || T->isVectorType()) { - llvm::ImmutableList<SVal> StartVals = getBasicVals().getEmptySValList(); - - // Handle base case where the initializer has no elements. - // e.g: static int* myArray[] = {}; - if (NumInitElements == 0) { - SVal V = svalBuilder.makeCompoundVal(T, StartVals); - MakeNode(Dst, E, Pred, state->BindExpr(E, V)); - return; - } - - // Create a worklist to process the initializers. - llvm::SmallVector<InitListWLItem, 10> WorkList; - WorkList.reserve(NumInitElements); - WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); - InitListExpr::const_reverse_iterator ItrEnd = E->rend(); - assert(!(E->rbegin() == E->rend())); - - // Process the worklist until it is empty. - while (!WorkList.empty()) { - InitListWLItem X = WorkList.back(); - WorkList.pop_back(); - - ExplodedNodeSet Tmp; - Visit(*X.Itr, X.N, Tmp); - - InitListExpr::const_reverse_iterator NewItr = X.Itr + 1; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { - // Get the last initializer value. - state = GetState(*NI); - SVal InitV = state->getSVal(cast<Expr>(*X.Itr)); - - // Construct the new list of values by prepending the new value to - // the already constructed list. - llvm::ImmutableList<SVal> NewVals = - getBasicVals().consVals(InitV, X.Vals); - - if (NewItr == ItrEnd) { - // Now we have a list holding all init values. Make CompoundValData. - SVal V = svalBuilder.makeCompoundVal(T, NewVals); - - // Make final state and node. - MakeNode(Dst, E, *NI, state->BindExpr(E, V)); - } - else { - // Still some initializer values to go. Push them onto the worklist. - WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); - } - } - } - - return; - } - - if (Loc::isLocType(T) || T->isIntegerType()) { - assert (E->getNumInits() == 1); - ExplodedNodeSet Tmp; - const Expr* Init = E->getInit(0); - Visit(Init, Pred, Tmp); - for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { - state = GetState(*I); - MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); - } - return; - } - - assert(0 && "unprocessed InitListExpr type"); -} - -/// VisitUnaryExprOrTypeTraitExpr - Transfer function for sizeof(type). -void ExprEngine::VisitUnaryExprOrTypeTraitExpr( - const UnaryExprOrTypeTraitExpr* Ex, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - QualType T = Ex->getTypeOfArgument(); - - if (Ex->getKind() == UETT_SizeOf) { - if (!T->isIncompleteType() && !T->isConstantSizeType()) { - assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); - - // FIXME: Add support for VLA type arguments, not just VLA expressions. - // When that happens, we should probably refactor VLASizeChecker's code. - if (Ex->isArgumentType()) { - Dst.Add(Pred); - return; - } - - // Get the size by getting the extent of the sub-expression. - // First, visit the sub-expression to find its region. - const Expr *Arg = Ex->getArgumentExpr(); - ExplodedNodeSet Tmp; - Visit(Arg, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - const MemRegion *MR = state->getSVal(Arg).getAsRegion(); - - // If the subexpression can't be resolved to a region, we don't know - // anything about its size. Just leave the state as is and continue. - if (!MR) { - Dst.Add(*I); - continue; - } - - // The result is the extent of the VLA. - SVal Extent = cast<SubRegion>(MR)->getExtent(svalBuilder); - MakeNode(Dst, Ex, *I, state->BindExpr(Ex, Extent)); - } - - return; - } - else if (T->getAs<ObjCObjectType>()) { - // Some code tries to take the sizeof an ObjCObjectType, relying that - // the compiler has laid out its representation. Just report Unknown - // for these. - Dst.Add(Pred); - return; - } - } - - Expr::EvalResult Result; - Ex->Evaluate(Result, getContext()); - CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue()); - - MakeNode(Dst, Ex, Pred, - GetState(Pred)->BindExpr(Ex, - svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()))); -} - -void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - Expr::EvalResult Res; - if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); - assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); - assert(OOE->getType()->isIntegerType()); - assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); - SVal X = svalBuilder.makeIntVal(IV); - MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X)); - return; - } - // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); -} - -void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - switch (U->getOpcode()) { - - default: - break; - - case UO_Real: { - const Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UO_Real is an identity operation. - assert (U->getType() == Ex->getType()); - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UO_Imag: { - - const Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UO_Imag returns 0. - const GRState* state = GetState(*I); - SVal X = svalBuilder.makeZeroVal(Ex->getType()); - MakeNode(Dst, U, *I, state->BindExpr(U, X)); - } - - return; - } - - case UO_Plus: - assert(!U->isLValue()); - // FALL-THROUGH. - case UO_Deref: - case UO_AddrOf: - case UO_Extension: { - - // Unary "+" is a no-op, similar to a parentheses. We still have places - // where it may be a block-level expression, so we need to - // generate an extra node that just propagates the value of the - // subexpression. - - const Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UO_LNot: - case UO_Minus: - case UO_Not: { - assert (!U->isLValue()); - const Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - - // Get the value of the subexpression. - SVal V = state->getSVal(Ex); - - if (V.isUnknownOrUndef()) { - MakeNode(Dst, U, *I, state->BindExpr(U, V)); - continue; - } - -// QualType DstT = getContext().getCanonicalType(U->getType()); -// QualType SrcT = getContext().getCanonicalType(Ex->getType()); -// -// if (DstT != SrcT) // Perform promotions. -// V = evalCast(V, DstT); -// -// if (V.isUnknownOrUndef()) { -// MakeNode(Dst, U, *I, BindExpr(St, U, V)); -// continue; -// } - - switch (U->getOpcode()) { - default: - assert(false && "Invalid Opcode."); - break; - - case UO_Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalComplement(cast<NonLoc>(V))); - break; - - case UO_Minus: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalMinus(cast<NonLoc>(V))); - break; - - case UO_LNot: - - // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." - // - // Note: technically we do "E == 0", but this is the same in the - // transfer functions as "0 == E". - SVal Result; - - if (isa<Loc>(V)) { - Loc X = svalBuilder.makeNull(); - Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, - U->getType()); - } - else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, - U->getType()); - } - - state = state->BindExpr(U, Result); - - break; - } - - MakeNode(Dst, U, *I, state); - } - - return; - } - } - - // Handle ++ and -- (both pre- and post-increment). - assert (U->isIncrementDecrementOp()); - ExplodedNodeSet Tmp; - const Expr* Ex = U->getSubExpr()->IgnoreParens(); - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - - const GRState* state = GetState(*I); - SVal loc = state->getSVal(Ex); - - // Perform a load. - ExplodedNodeSet Tmp2; - evalLoad(Tmp2, Ex, *I, state, loc); - - for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { - - state = GetState(*I2); - SVal V2_untested = state->getSVal(Ex); - - // Propagate unknown and undefined values. - if (V2_untested.isUnknownOrUndef()) { - MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); - continue; - } - DefinedSVal V2 = cast<DefinedSVal>(V2_untested); - - // Handle all other values. - BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add - : BO_Sub; - - // If the UnaryOperator has non-location type, use its type to create the - // constant value. If the UnaryOperator has location type, create the - // constant with int type and pointer width. - SVal RHS; - - if (U->getType()->isAnyPointerType()) - RHS = svalBuilder.makeArrayIndex(1); - else - RHS = svalBuilder.makeIntVal(1, U->getType()); - - SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); - - // Conjure a new symbol if necessary to recover precision. - if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ - DefinedOrUnknownSVal SymVal = - svalBuilder.getConjuredSymbolVal(NULL, Ex, - Builder->getCurrentBlockCount()); - Result = SymVal; - - // If the value is a location, ++/-- should always preserve - // non-nullness. Check if the original value was non-null, and if so - // propagate that constraint. - if (Loc::isLocType(U->getType())) { - DefinedOrUnknownSVal Constraint = - svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); - - if (!state->assume(Constraint, true)) { - // It isn't feasible for the original value to be null. - // Propagate this constraint. - Constraint = svalBuilder.evalEQ(state, SymVal, - svalBuilder.makeZeroVal(U->getType())); - - - state = state->assume(Constraint, false); - assert(state); - } - } - } - - // Since the lvalue-to-rvalue conversion is explicit in the AST, - // we bind an l-value if the operator is prefix and an lvalue (in C++). - if (U->isLValue()) - state = state->BindExpr(U, loc); - else - state = state->BindExpr(U, U->isPostfix() ? V2 : Result); - - // Perform the store. - evalStore(Dst, NULL, U, *I2, state, loc, Result); - } - } -} - -void ExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { +void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); } -void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A, +void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt *A, AsmStmt::const_outputs_iterator I, AsmStmt::const_outputs_iterator E, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { + ExplodedNode *Pred, ExplodedNodeSet &Dst) { if (I == E) { VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); return; @@ -2795,11 +1590,11 @@ void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A, VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); } -void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, +void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt *A, AsmStmt::const_inputs_iterator I, AsmStmt::const_inputs_iterator E, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { if (I == E) { // We have processed both the inputs and the outputs. All of the outputs @@ -2809,7 +1604,7 @@ void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, // which interprets the inline asm and stores proper results in the // outputs. - const GRState* state = GetState(Pred); + const ProgramState *state = Pred->getState(); for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), OE = A->end_outputs(); OI != OE; ++OI) { @@ -2834,198 +1629,6 @@ void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); } -void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - ExplodedNodeSet Src; - if (const Expr *RetE = RS->getRetValue()) { - // Record the returned expression in the state. It will be used in - // processCallExit to bind the return value to the call expr. - { - static int tag = 0; - const GRState *state = GetState(Pred); - state = state->set<ReturnExpr>(RetE); - Pred = Builder->generateNode(RetE, state, Pred, &tag); - } - // We may get a NULL Pred because we generated a cached node. - if (Pred) - Visit(RetE, Pred, Src); - } - else { - Src.Add(Pred); - } - - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForPreStmt(CheckedSet, Src, RS, *this); - - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - - assert(Builder && "StmtNodeBuilder must be defined."); - - Pred = *I; - unsigned size = Dst.size(); - - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); - - getTF().evalReturn(Dst, *this, *Builder, RS, Pred); - - // Handle the case where no nodes where generated. - if (!Builder->BuildSinks && Dst.size() == size && - !Builder->hasGeneratedNode) - MakeNode(Dst, RS, Pred, GetState(Pred)); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Binary operators. -//===----------------------------------------------------------------------===// - -void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - ExplodedNodeSet Tmp1; - Expr* LHS = B->getLHS()->IgnoreParens(); - Expr* RHS = B->getRHS()->IgnoreParens(); - - Visit(LHS, Pred, Tmp1); - ExplodedNodeSet Tmp3; - - for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { - SVal LeftV = GetState(*I1)->getSVal(LHS); - ExplodedNodeSet Tmp2; - Visit(RHS, *I1, Tmp2); - - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForPreStmt(CheckedSet, Tmp2, B, *this); - - // With both the LHS and RHS evaluated, process the operation itself. - - for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); - I2 != E2; ++I2) { - - const GRState *state = GetState(*I2); - SVal RightV = state->getSVal(RHS); - - BinaryOperator::Opcode Op = B->getOpcode(); - - if (Op == BO_Assign) { - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV)) - { - unsigned Count = Builder->getCurrentBlockCount(); - RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); - } - - SVal ExprVal = B->isLValue() ? LeftV : RightV; - - // Simulate the effects of a "store": bind the value of the RHS - // to the L-Value represented by the LHS. - evalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); - continue; - } - - if (!B->isAssignmentOp()) { - // Process non-assignments except commas or short-circuited - // logical expressions (LAnd and LOr). - SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); - - if (Result.isUnknown()) { - MakeNode(Tmp3, B, *I2, state); - continue; - } - - state = state->BindExpr(B, Result); - - MakeNode(Tmp3, B, *I2, state); - continue; - } - - assert (B->isCompoundAssignmentOp()); - - switch (Op) { - default: - assert(0 && "Invalid opcode for compound assignment."); - case BO_MulAssign: Op = BO_Mul; break; - case BO_DivAssign: Op = BO_Div; break; - case BO_RemAssign: Op = BO_Rem; break; - case BO_AddAssign: Op = BO_Add; break; - case BO_SubAssign: Op = BO_Sub; break; - case BO_ShlAssign: Op = BO_Shl; break; - case BO_ShrAssign: Op = BO_Shr; break; - case BO_AndAssign: Op = BO_And; break; - case BO_XorAssign: Op = BO_Xor; break; - case BO_OrAssign: Op = BO_Or; break; - } - - // Perform a load (the LHS). This performs the checks for - // null dereferences, and so on. - ExplodedNodeSet Tmp4; - SVal location = state->getSVal(LHS); - evalLoad(Tmp4, LHS, *I2, state, location); - - for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; - ++I4) { - state = GetState(*I4); - SVal V = state->getSVal(LHS); - - // Get the computation type. - QualType CTy = - cast<CompoundAssignOperator>(B)->getComputationResultType(); - CTy = getContext().getCanonicalType(CTy); - - QualType CLHSTy = - cast<CompoundAssignOperator>(B)->getComputationLHSType(); - CLHSTy = getContext().getCanonicalType(CLHSTy); - - QualType LTy = getContext().getCanonicalType(LHS->getType()); - - // Promote LHS. - V = svalBuilder.evalCast(V, CLHSTy, LTy); - - // Compute the result of the operation. - SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy), - B->getType(), CTy); - - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - - SVal LHSVal; - - if (Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) { - - unsigned Count = Builder->getCurrentBlockCount(); - - // The symbolic value is actually for the type of the left-hand side - // expression, not the computation type, as this is the value the - // LValue on the LHS will bind to. - LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); - - // However, we need to convert the symbol to the computation type. - Result = svalBuilder.evalCast(LHSVal, CTy, LTy); - } - else { - // The left-hand side may bind to a different value then the - // computation type. - LHSVal = svalBuilder.evalCast(Result, LTy, CTy); - } - - // In C++, assignment and compound assignment operators return an - // lvalue. - if (B->isLValue()) - state = state->BindExpr(B, location); - else - state = state->BindExpr(B, Result); - - evalStore(Tmp3, B, LHS, *I4, state, location, LHSVal); - } - } - } - - getCheckerManager().runCheckersForPostStmt(Dst, Tmp3, B, *this); -} //===----------------------------------------------------------------------===// // Visualization. @@ -3044,7 +1647,7 @@ struct DOTGraphTraits<ExplodedNode*> : // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. - static std::string getNodeAttributes(const ExplodedNode* N, void*) { + static std::string getNodeAttributes(const ExplodedNode *N, void*) { #if 0 // FIXME: Replace with a general scheme to tell if the node is @@ -3065,7 +1668,7 @@ struct DOTGraphTraits<ExplodedNode*> : return ""; } - static std::string getNodeLabel(const ExplodedNode* N, void*){ + static std::string getNodeLabel(const ExplodedNode *N, void*){ std::string sbuf; llvm::raw_string_ostream Out(sbuf); @@ -3093,7 +1696,7 @@ struct DOTGraphTraits<ExplodedNode*> : default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { - const Stmt* S = L->getStmt(); + const Stmt *S = L->getStmt(); SourceLocation SLoc = S->getLocStart(); Out << S->getStmtClassName() << ' ' << (void*) S << ' '; @@ -3102,9 +1705,9 @@ struct DOTGraphTraits<ExplodedNode*> : if (SLoc.isFileID()) { Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << GraphPrintSourceManager->getExpansionLineNumber(SLoc) << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) + << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) << "\\l"; } @@ -3141,11 +1744,11 @@ struct DOTGraphTraits<ExplodedNode*> : break; } - const BlockEdge& E = cast<BlockEdge>(Loc); + const BlockEdge &E = cast<BlockEdge>(Loc); Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" << E.getDst()->getBlockID() << ')'; - if (const Stmt* T = E.getSrc()->getTerminator()) { + if (const Stmt *T = E.getSrc()->getTerminator()) { SourceLocation SLoc = T->getLocStart(); @@ -3155,21 +1758,21 @@ struct DOTGraphTraits<ExplodedNode*> : if (SLoc.isFileID()) { Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << GraphPrintSourceManager->getExpansionLineNumber(SLoc) << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); + << GraphPrintSourceManager->getExpansionColumnNumber(SLoc); } if (isa<SwitchStmt>(T)) { - const Stmt* Label = E.getDst()->getLabel(); + const Stmt *Label = E.getDst()->getLabel(); if (Label) { - if (const CaseStmt* C = dyn_cast<CaseStmt>(Label)) { + if (const CaseStmt *C = dyn_cast<CaseStmt>(Label)) { Out << "\\lcase "; LangOptions LO; // FIXME. C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); - if (const Stmt* RHS = C->getRHS()) { + if (const Stmt *RHS = C->getRHS()) { Out << " .. "; RHS->printPretty(Out, 0, PrintingPolicy(LO)); } @@ -3208,11 +1811,17 @@ struct DOTGraphTraits<ExplodedNode*> : } } - const GRState *state = N->getState(); + const ProgramState *state = N->getState(); Out << "\\|StateID: " << (void*) state << " NodeID: " << (void*) N << "\\|"; state->printDOT(Out, *N->getLocationContext()->getCFG()); - Out << "\\l"; + + Out << "\\l"; + + if (const ProgramPointTag *tag = Loc.getTag()) { + Out << "\\|Tag: " << tag->getTagDescription(); + Out << "\\l"; + } return Out.str(); } }; @@ -3221,7 +1830,7 @@ struct DOTGraphTraits<ExplodedNode*> : #ifndef NDEBUG template <typename ITERATOR> -ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } +ExplodedNode *GetGraphNode(ITERATOR I) { return *I; } template <> ExplodedNode* GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator> diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp new file mode 100644 index 000000000000..68ccc59ac952 --- /dev/null +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -0,0 +1,752 @@ +//=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for C expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/Support/SaveAndRestore.h" + +using namespace clang; +using namespace ento; +using llvm::APSInt; + +void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + Expr *LHS = B->getLHS()->IgnoreParens(); + Expr *RHS = B->getRHS()->IgnoreParens(); + + // FIXME: Prechecks eventually go in ::Visit(). + ExplodedNodeSet CheckedSet; + ExplodedNodeSet Tmp2; + getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this); + + // With both the LHS and RHS evaluated, process the operation itself. + for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end(); + it != ei; ++it) { + + const ProgramState *state = (*it)->getState(); + SVal LeftV = state->getSVal(LHS); + SVal RightV = state->getSVal(RHS); + + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op == BO_Assign) { + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + if (RightV.isUnknown() || + !getConstraintManager().canReasonAbout(RightV)) { + unsigned Count = Builder->getCurrentBlockCount(); + RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); + } + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + SVal ExprVal = B->isLValue() ? LeftV : RightV; + evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, ExprVal), LeftV, RightV); + continue; + } + + if (!B->isAssignmentOp()) { + // Process non-assignments except commas or short-circuited + // logical expressions (LAnd and LOr). + SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + if (Result.isUnknown()) { + MakeNode(Tmp2, B, *it, state); + continue; + } + + state = state->BindExpr(B, Result); + MakeNode(Tmp2, B, *it, state); + continue; + } + + assert (B->isCompoundAssignmentOp()); + + switch (Op) { + default: + llvm_unreachable("Invalid opcode for compound assignment."); + case BO_MulAssign: Op = BO_Mul; break; + case BO_DivAssign: Op = BO_Div; break; + case BO_RemAssign: Op = BO_Rem; break; + case BO_AddAssign: Op = BO_Add; break; + case BO_SubAssign: Op = BO_Sub; break; + case BO_ShlAssign: Op = BO_Shl; break; + case BO_ShrAssign: Op = BO_Shr; break; + case BO_AndAssign: Op = BO_And; break; + case BO_XorAssign: Op = BO_Xor; break; + case BO_OrAssign: Op = BO_Or; break; + } + + // Perform a load (the LHS). This performs the checks for + // null dereferences, and so on. + ExplodedNodeSet Tmp; + SVal location = LeftV; + evalLoad(Tmp, LHS, *it, state, location); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; + ++I) { + + state = (*I)->getState(); + SVal V = state->getSVal(LHS); + + // Get the computation type. + QualType CTy = + cast<CompoundAssignOperator>(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = + cast<CompoundAssignOperator>(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CLHSTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + + // Promote LHS. + V = svalBuilder.evalCast(V, CLHSTy, LTy); + + // Compute the result of the operation. + SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy), + B->getType(), CTy); + + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + + SVal LHSVal; + + if (Result.isUnknown() || + !getConstraintManager().canReasonAbout(Result)) { + + unsigned Count = Builder->getCurrentBlockCount(); + + // The symbolic value is actually for the type of the left-hand side + // expression, not the computation type, as this is the value the + // LValue on the LHS will bind to. + LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, + Count); + + // However, we need to convert the symbol to the computation type. + Result = svalBuilder.evalCast(LHSVal, CTy, LTy); + } + else { + // The left-hand side may bind to a different value then the + // computation type. + LHSVal = svalBuilder.evalCast(Result, LTy, CTy); + } + + // In C++, assignment and compound assignment operators return an + // lvalue. + if (B->isLValue()) + state = state->BindExpr(B, location); + else + state = state->BindExpr(B, Result); + + evalStore(Tmp2, B, LHS, *I, state, location, LHSVal); + } + } + + // FIXME: postvisits eventually go in ::Visit() + getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this); +} + +void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + CanQualType T = getContext().getCanonicalType(BE->getType()); + SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, + Pred->getLocationContext()); + + ExplodedNodeSet Tmp; + MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V), + ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); +} + +void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + + ExplodedNodeSet dstPreStmt; + getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this); + + if (CastE->getCastKind() == CK_LValueToRValue || + CastE->getCastKind() == CK_GetObjCProperty) { + for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); + I!=E; ++I) { + ExplodedNode *subExprNode = *I; + const ProgramState *state = subExprNode->getState(); + evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); + } + return; + } + + // All other casts. + QualType T = CastE->getType(); + QualType ExTy = Ex->getType(); + + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) + T = ExCast->getTypeAsWritten(); + + for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); + I != E; ++I) { + + Pred = *I; + + switch (CastE->getCastKind()) { + case CK_LValueToRValue: + llvm_unreachable("LValueToRValue casts handled earlier."); + case CK_GetObjCProperty: + llvm_unreachable("GetObjCProperty casts handled earlier."); + case CK_ToVoid: + Dst.Add(Pred); + continue; + // The analyzer doesn't do anything special with these casts, + // since it understands retain/release semantics already. + case CK_ARCProduceObject: + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: + case CK_ARCExtendBlockObject: // Fall-through. + // True no-ops. + case CK_NoOp: + case CK_FunctionToPointerDecay: { + // Copy the SVal of Ex to CastE. + const ProgramState *state = Pred->getState(); + SVal V = state->getSVal(Ex); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, Pred, state); + continue; + } + case CK_Dependent: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_LValueBitCast: + case CK_IntegralCast: + case CK_NullToPointer: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_PointerToBoolean: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_FloatingRealToComplex: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: { + // Delegate to SValBuilder to process. + const ProgramState *state = Pred->getState(); + SVal V = state->getSVal(Ex); + V = svalBuilder.evalCast(V, T, ExTy); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, Pred, state); + continue; + } + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + // For DerivedToBase cast, delegate to the store manager. + const ProgramState *state = Pred->getState(); + SVal val = state->getSVal(Ex); + val = getStoreManager().evalDerivedToBase(val, T); + state = state->BindExpr(CastE, val); + MakeNode(Dst, CastE, Pred, state); + continue; + } + // Various C++ casts that are not handled yet. + case CK_Dynamic: + case CK_ToUnion: + case CK_BaseToDerived: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_UserDefinedConversion: + case CK_ConstructorConversion: + case CK_VectorSplat: + case CK_MemberPointerToBoolean: { + // Recover some path-sensitivty by conjuring a new value. + QualType resultType = CastE->getType(); + if (CastE->isLValue()) + resultType = getContext().getPointerType(resultType); + + SVal result = + svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType, + Builder->getCurrentBlockCount()); + + const ProgramState *state = Pred->getState()->BindExpr(CastE, result); + MakeNode(Dst, CastE, Pred, state); + continue; + } + } + } +} + +void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const InitListExpr *ILE + = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); + + const ProgramState *state = Pred->getState(); + SVal ILV = state->getSVal(ILE); + const LocationContext *LC = Pred->getLocationContext(); + state = state->bindCompoundLiteral(CL, LC, ILV); + + if (CL->isLValue()) + MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC))); + else + MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV)); +} + +void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // FIXME: static variables may have an initializer, but the second + // time a function is called those values may not be current. + // This may need to be reflected in the CFG. + + // Assumption: The CFG has one DeclStmt per Decl. + const Decl *D = *DS->decl_begin(); + + if (!D || !isa<VarDecl>(D)) + return; + + // FIXME: all pre/post visits should eventually be handled by ::Visit(). + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); + + const VarDecl *VD = dyn_cast<VarDecl>(D); + + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I!=E; ++I) { + ExplodedNode *N = *I; + const ProgramState *state = N->getState(); + + // Decls without InitExpr are not initialized explicitly. + const LocationContext *LC = N->getLocationContext(); + + if (const Expr *InitEx = VD->getInit()) { + SVal InitVal = state->getSVal(InitEx); + + // We bound the temp obj region to the CXXConstructExpr. Now recover + // the lazy compound value when the variable is not a reference. + if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ + InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); + assert(isa<nonloc::LazyCompoundVal>(InitVal)); + } + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if ((InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) && + !VD->getType()->isReferenceType()) { + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + evalBind(Dst, DS, N, state->getLValue(VD, LC), InitVal, true); + } + else { + MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC))); + } + } +} + +void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + assert(B->getOpcode() == BO_LAnd || + B->getOpcode() == BO_LOr); + + const ProgramState *state = Pred->getState(); + SVal X = state->getSVal(B); + assert(X.isUndef()); + + const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); + assert(Ex); + + if (Ex == B->getRHS()) { + X = state->getSVal(Ex); + + // Handle undefined values. + if (X.isUndef()) { + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + return; + } + + DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); + + // We took the RHS. Because the value of the '&&' or '||' expression must + // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 + // or 1. Alternatively, we could take a lazy approach, and calculate this + // value later when necessary. We don't have the machinery in place for + // this right now, and since most logical expressions are used for branches, + // the payoff is not likely to be large. Instead, we do eager evaluation. + if (const ProgramState *newState = state->assume(XD, true)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); + + if (const ProgramState *newState = state->assume(XD, false)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); + } + else { + // We took the LHS expression. Depending on whether we are '&&' or + // '||' we know what the value of the expression is via properties of + // the short-circuiting. + X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, + B->getType()); + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + } +} + +void ExprEngine::VisitInitListExpr(const InitListExpr *IE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + const ProgramState *state = Pred->getState(); + QualType T = getContext().getCanonicalType(IE->getType()); + unsigned NumInitElements = IE->getNumInits(); + + if (T->isArrayType() || T->isRecordType() || T->isVectorType()) { + llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); + + // Handle base case where the initializer has no elements. + // e.g: static int* myArray[] = {}; + if (NumInitElements == 0) { + SVal V = svalBuilder.makeCompoundVal(T, vals); + MakeNode(Dst, IE, Pred, state->BindExpr(IE, V)); + return; + } + + for (InitListExpr::const_reverse_iterator it = IE->rbegin(), + ei = IE->rend(); it != ei; ++it) { + vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals); + } + + MakeNode(Dst, IE, Pred, + state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals))); + return; + } + + if (Loc::isLocType(T) || T->isIntegerType()) { + assert(IE->getNumInits() == 1); + const Expr *initEx = IE->getInit(0); + MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx))); + return; + } + + llvm_unreachable("unprocessed InitListExpr type"); +} + +void ExprEngine::VisitGuardedExpr(const Expr *Ex, + const Expr *L, + const Expr *R, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + const ProgramState *state = Pred->getState(); + SVal X = state->getSVal(Ex); + assert (X.isUndef()); + const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); + assert(SE); + X = state->getSVal(SE); + + // Make sure that we invalidate the previous binding. + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); +} + +void ExprEngine:: +VisitOffsetOfExpr(const OffsetOfExpr *OOE, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + Expr::EvalResult Res; + if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); + assert(OOE->getType()->isIntegerType()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); + SVal X = svalBuilder.makeIntVal(IV); + MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X)); + return; + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. + Dst.Add(Pred); +} + + +void ExprEngine:: +VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + QualType T = Ex->getTypeOfArgument(); + + if (Ex->getKind() == UETT_SizeOf) { + if (!T->isIncompleteType() && !T->isConstantSizeType()) { + assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); + + // FIXME: Add support for VLA type arguments and VLA expressions. + // When that happens, we should probably refactor VLASizeChecker's code. + Dst.Add(Pred); + return; + } + else if (T->getAs<ObjCObjectType>()) { + // Some code tries to take the sizeof an ObjCObjectType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + Dst.Add(Pred); + return; + } + } + + Expr::EvalResult Result; + Ex->Evaluate(Result, getContext()); + CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue()); + + const ProgramState *state = Pred->getState(); + state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(), + Ex->getType())); + MakeNode(Dst, Ex, Pred, state); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + switch (U->getOpcode()) { + default: + break; + case UO_Real: { + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + const ProgramState *state = (*I)->getState(); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_Imag: { + + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Imag returns 0. + const ProgramState *state = (*I)->getState(); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + MakeNode(Dst, U, *I, state->BindExpr(U, X)); + } + + return; + } + + case UO_Plus: + assert(!U->isLValue()); + // FALL-THROUGH. + case UO_Deref: + case UO_AddrOf: + case UO_Extension: { + + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const ProgramState *state = (*I)->getState(); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_LNot: + case UO_Minus: + case UO_Not: { + assert (!U->isLValue()); + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const ProgramState *state = (*I)->getState(); + + // Get the value of the subexpression. + SVal V = state->getSVal(Ex); + + if (V.isUnknownOrUndef()) { + MakeNode(Dst, U, *I, state->BindExpr(U, V)); + continue; + } + + switch (U->getOpcode()) { + default: + llvm_unreachable("Invalid Opcode."); + + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalComplement(cast<NonLoc>(V))); + break; + + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalMinus(cast<NonLoc>(V))); + break; + + case UO_LNot: + + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + + if (isa<Loc>(V)) { + Loc X = svalBuilder.makeNull(); + Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + U->getType()); + } + + state = state->BindExpr(U, Result); + + break; + } + + MakeNode(Dst, U, *I, state); + } + + return; + } + } + + // Handle ++ and -- (both pre- and post-increment). + assert (U->isIncrementDecrementOp()); + ExplodedNodeSet Tmp; + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + + const ProgramState *state = (*I)->getState(); + SVal loc = state->getSVal(Ex); + + // Perform a load. + ExplodedNodeSet Tmp2; + evalLoad(Tmp2, Ex, *I, state, loc); + + for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { + + state = (*I2)->getState(); + SVal V2_untested = state->getSVal(Ex); + + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); + continue; + } + DefinedSVal V2 = cast<DefinedSVal>(V2_untested); + + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add + : BO_Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeArrayIndex(1); + else + RHS = svalBuilder.makeIntVal(1, U->getType()); + + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ + DefinedOrUnknownSVal SymVal = + svalBuilder.getConjuredSymbolVal(NULL, Ex, + Builder->getCurrentBlockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::isLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); + + if (!state->assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = svalBuilder.evalEQ(state, SymVal, + svalBuilder.makeZeroVal(U->getType())); + + + state = state->assume(Constraint, false); + assert(state); + } + } + } + + // Since the lvalue-to-rvalue conversion is explicit in the AST, + // we bind an l-value if the operator is prefix and an lvalue (in C++). + if (U->isLValue()) + state = state->BindExpr(U, loc); + else + state = state->BindExpr(U, U->isPostfix() ? V2 : Result); + + // Perform the store. + evalStore(Dst, NULL, U, *I2, state, loc, Result); + } + } +} diff --git a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index ef7bc2016cdf..acb007490ee1 100644 --- a/lib/StaticAnalyzer/Core/CXXExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1,4 +1,4 @@ -//===- GRCXXExprEngine.cpp - C++ expr evaluation engine ---------*- C++ -*-===// +//===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclCXX.h" using namespace clang; @@ -36,7 +37,7 @@ void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE, bool FstArgAsLValue) { - llvm::SmallVector<CallExprWLItem, 20> WorkList; + SmallVector<CallExprWLItem, 20> WorkList; WorkList.reserve(AE - AI); WorkList.push_back(CallExprWLItem(AI, Pred)); @@ -103,24 +104,22 @@ const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, getCXXThisRegion(decl->getThisType(getContext()), frameCtx); } -void ExprEngine::CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - const GRState *state = GetState(*I); +void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens(); + const ProgramState *state = Pred->getState(); - // Bind the temporary object to the value of the expression. Then bind - // the expression to the location of the object. - SVal V = state->getSVal(Ex); + // Bind the temporary object to the value of the expression. Then bind + // the expression to the location of the object. + SVal V = state->getSVal(tempExpr); - const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(Ex, - Pred->getLocationContext()); + const MemRegion *R = + svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, + Pred->getLocationContext()); - state = state->bindLoc(loc::MemRegionVal(R), V); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R))); - } + state = state->bindLoc(loc::MemRegionVal(R), V); + MakeNode(Dst, ME, Pred, state->BindExpr(ME, loc::MemRegionVal(R))); } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, @@ -186,7 +185,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), NE = argsEvaluated.end(); NI != NE; ++NI) { - const GRState *state = GetState(*NI); + const ProgramState *state = (*NI)->getState(); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); @@ -197,17 +196,6 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, #endif // Default semantics: invalidate all regions passed as arguments. - llvm::SmallVector<const MemRegion*, 10> regionsToInvalidate; - - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned blockCount = Builder->getCurrentBlockCount(); - - // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate - // global variables. ExplodedNodeSet destCall; for (ExplodedNodeSet::iterator @@ -215,25 +203,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, i != e; ++i) { ExplodedNode *Pred = *i; - const GRState *state = GetState(Pred); + const LocationContext *LC = Pred->getLocationContext(); + const ProgramState *state = Pred->getState(); - // Accumulate list of regions that are invalidated. - for (CXXConstructExpr::const_arg_iterator - ai = E->arg_begin(), ae = E->arg_end(); - ai != ae; ++ai) - { - SVal val = state->getSVal(*ai); - if (const MemRegion *region = val.getAsRegion()) - regionsToInvalidate.push_back(region); - } - - // Invalidate the regions. - state = state->invalidateRegions(regionsToInvalidate.data(), - regionsToInvalidate.data() + - regionsToInvalidate.size(), - E, blockCount, 0, - /* invalidateGlobals = */ true); - + state = invalidateArguments(state, CallOrObjCMessage(E, state), LC); Builder->MakeNode(destCall, E, Pred, state); } @@ -258,7 +231,7 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, CallEnter PP(S, SFC, Pred->getLocationContext()); - const GRState *state = Pred->getState(); + const ProgramState *state = Pred->getState(); state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); ExplodedNode *N = Builder->generateNode(PP, state, Pred); if (N) @@ -279,7 +252,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); MakeNode(Dst, CNE, Pred, state); return; @@ -298,12 +271,12 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), E = argsEvaluated.end(); I != E; ++I) { - const GRState *state = GetState(*I); + const ProgramState *state = (*I)->getState(); // Accumulate list of regions that are invalidated. // FIXME: Eventually we should unify the logic for constructor // processing in one place. - llvm::SmallVector<const MemRegion*, 10> regionsToInvalidate; + SmallVector<const MemRegion*, 10> regionsToInvalidate; for (CXXNewExpr::const_arg_iterator ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end(); ai != ae; ++ai) @@ -316,17 +289,13 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (ObjTy->isRecordType()) { regionsToInvalidate.push_back(EleReg); // Invalidate the regions. - state = state->invalidateRegions(regionsToInvalidate.data(), - regionsToInvalidate.data() + - regionsToInvalidate.size(), + state = state->invalidateRegions(regionsToInvalidate, CNE, blockCount, 0, /* invalidateGlobals = */ true); } else { // Invalidate the regions. - state = state->invalidateRegions(regionsToInvalidate.data(), - regionsToInvalidate.data() + - regionsToInvalidate.size(), + state = state->invalidateRegions(regionsToInvalidate, CNE, blockCount, 0, /* invalidateGlobals = */ true); @@ -351,7 +320,7 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, Visit(CDE->getArgument(), Pred, Argevaluated); for (ExplodedNodeSet::iterator I = Argevaluated.begin(), E = Argevaluated.end(); I != E; ++I) { - const GRState *state = GetState(*I); + const ProgramState *state = (*I)->getState(); MakeNode(Dst, CDE, *I, state); } } @@ -364,7 +333,7 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, getContext().getCanonicalType(TE->getType()), Pred->getLocationContext()); - const GRState *state = GetState(Pred); + const ProgramState *state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp new file mode 100644 index 000000000000..6d377b959136 --- /dev/null +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -0,0 +1,253 @@ +//=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for calls and returns. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Analysis/Support/SaveAndRestore.h" + +using namespace clang; +using namespace ento; + +namespace { + // Trait class for recording returned expression in the state. + struct ReturnExpr { + static int TagInt; + typedef const Stmt *data_type; + }; + int ReturnExpr::TagInt; +} + +void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) { + const ProgramState *state = + B.getState()->enterStackFrame(B.getCalleeContext()); + B.generateNode(state); +} + +void ExprEngine::processCallExit(CallExitNodeBuilder &B) { + const ProgramState *state = B.getState(); + const ExplodedNode *Pred = B.getPredecessor(); + const StackFrameContext *calleeCtx = + cast<StackFrameContext>(Pred->getLocationContext()); + const Stmt *CE = calleeCtx->getCallSite(); + + // If the callee returns an expression, bind its value to CallExpr. + const Stmt *ReturnedExpr = state->get<ReturnExpr>(); + if (ReturnedExpr) { + SVal RetVal = state->getSVal(ReturnedExpr); + state = state->BindExpr(CE, RetVal); + // Clear the return expr GDM. + state = state->remove<ReturnExpr>(); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { + const CXXThisRegion *ThisR = + getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); + + SVal ThisV = state->getSVal(ThisR); + // Always bind the region to the CXXConstructExpr. + state = state->BindExpr(CCE, ThisV); + } + + B.generateNode(state); +} + +const ProgramState * +ExprEngine::invalidateArguments(const ProgramState *State, + const CallOrObjCMessage &Call, + const LocationContext *LC) { + SmallVector<const MemRegion *, 8> RegionsToInvalidate; + + if (Call.isObjCMessage()) { + // Invalidate all instance variables of the receiver of an ObjC message. + // FIXME: We should be able to do better with inter-procedural analysis. + if (const MemRegion *MR = Call.getInstanceMessageReceiver(LC).getAsRegion()) + RegionsToInvalidate.push_back(MR); + + } else if (Call.isCXXCall()) { + // Invalidate all instance variables for the callee of a C++ method call. + // FIXME: We should be able to do better with inter-procedural analysis. + // FIXME: We can probably do better for const versus non-const methods. + if (const MemRegion *Callee = Call.getCXXCallee().getAsRegion()) + RegionsToInvalidate.push_back(Callee); + + } else if (Call.isFunctionCall()) { + // Block calls invalidate all captured-by-reference values. + if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) { + if (isa<BlockDataRegion>(Callee)) + RegionsToInvalidate.push_back(Callee); + } + } + + for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) { + SVal V = Call.getArgSVal(idx); + + // If we are passing a location wrapped as an integer, unwrap it and + // invalidate the values referred by the location. + if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) + V = Wrapped->getLoc(); + else if (!isa<Loc>(V)) + continue; + + if (const MemRegion *R = V.getAsRegion()) { + // Invalidate the value of the variable passed by reference. + + // Are we dealing with an ElementRegion? If the element type is + // a basic integer type (e.g., char, int) and the underying region + // is a variable region then strip off the ElementRegion. + // FIXME: We really need to think about this for the general case + // as sometimes we are reasoning about arrays and other times + // about (char*), etc., is just a form of passing raw bytes. + // e.g., void *p = alloca(); foo((char*)p); + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + // Checking for 'integral type' is probably too promiscuous, but + // we'll leave it in for now until we have a systematic way of + // handling all of these cases. Eventually we need to come up + // with an interface to StoreManager so that this logic can be + // approriately delegated to the respective StoreManagers while + // still allowing us to do checker-specific logic (e.g., + // invalidating reference counts), probably via callbacks. + if (ER->getElementType()->isIntegralOrEnumerationType()) { + const MemRegion *superReg = ER->getSuperRegion(); + if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || + isa<ObjCIvarRegion>(superReg)) + R = cast<TypedRegion>(superReg); + } + // FIXME: What about layers of ElementRegions? + } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + } else { + // Nuke all other arguments passed by reference. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + State = State->unbindLoc(cast<Loc>(V)); + } + } + + // Invalidate designated regions using the batch invalidation API. + + // FIXME: We can have collisions on the conjured symbol if the + // expression *I also creates conjured symbols. We probably want + // to identify conjured symbols by an expression pair: the enclosing + // expression (the context) and the expression itself. This should + // disambiguate conjured symbols. + assert(Builder && "Invalidating arguments outside of a statement context"); + unsigned Count = Builder->getCurrentBlockCount(); + StoreManager::InvalidatedSymbols IS; + + // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate + // global variables. + return State->invalidateRegions(RegionsToInvalidate, + Call.getOriginExpr(), Count, + &IS, doesInvalidateGlobals(Call)); + +} + +void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, + ExplodedNodeSet &dst) { + // Perform the previsit of the CallExpr. + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); + + // Now evaluate the call itself. + class DefaultEval : public GraphExpander { + ExprEngine &Eng; + const CallExpr *CE; + public: + + DefaultEval(ExprEngine &eng, const CallExpr *ce) + : Eng(eng), CE(ce) {} + virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { + // Should we inline the call? + if (Eng.getAnalysisManager().shouldInlineCall() && + Eng.InlineCall(Dst, CE, Pred)) { + return; + } + + // First handle the return value. + StmtNodeBuilder &Builder = Eng.getBuilder(); + assert(&Builder && "StmtNodeBuilder must be defined."); + + // Get the callee. + const Expr *Callee = CE->getCallee()->IgnoreParens(); + const ProgramState *state = Pred->getState(); + SVal L = state->getSVal(Callee); + + // Figure out the result type. We do this dance to handle references. + QualType ResultTy; + if (const FunctionDecl *FD = L.getAsFunctionDecl()) + ResultTy = FD->getResultType(); + else + ResultTy = CE->getType(); + + if (CE->isLValue()) + ResultTy = Eng.getContext().getPointerType(ResultTy); + + // Conjure a symbol value to use as the result. + SValBuilder &SVB = Eng.getSValBuilder(); + unsigned Count = Builder.getCurrentBlockCount(); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count); + + // Generate a new state with the return value set. + state = state->BindExpr(CE, RetVal); + + // Invalidate the arguments. + const LocationContext *LC = Pred->getLocationContext(); + state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC); + + // And make the result node. + Eng.MakeNode(Dst, CE, Pred, state); + } + }; + + // Finally, evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet dstCallEvaluated; + DefaultEval defEval(*this, CE); + getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, + dstPreVisit, + CE, *this, &defEval); + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, + *this); +} + +void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet Src; + if (const Expr *RetE = RS->getRetValue()) { + // Record the returned expression in the state. It will be used in + // processCallExit to bind the return value to the call expr. + { + static SimpleProgramPointTag tag("ExprEngine: ReturnStmt"); + const ProgramState *state = Pred->getState(); + state = state->set<ReturnExpr>(RetE); + Pred = Builder->generateNode(RetE, state, Pred, &tag); + } + // We may get a NULL Pred because we generated a cached node. + if (Pred) + Visit(RetE, Pred, Src); + } + else { + Src.Add(Pred); + } + + getCheckerManager().runCheckersForPreStmt(Dst, Src, RS, *this); +} diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp new file mode 100644 index 000000000000..e0560fdfe460 --- /dev/null +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -0,0 +1,279 @@ +//=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for Objective-C expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/Analysis/Support/SaveAndRestore.h" + +using namespace clang; +using namespace ento; + +void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + const ProgramState *state = Pred->getState(); + SVal baseVal = state->getSVal(Ex->getBase()); + SVal location = state->getLValue(Ex->getDecl(), baseVal); + + ExplodedNodeSet dstIvar; + MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location)); + + // Perform the post-condition check of the ObjCIvarRefExpr and store + // the created nodes in 'Dst'. + getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this); +} + +void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this); +} + +void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + const Stmt *elem = S->getElement(); + const ProgramState *state = Pred->getState(); + SVal elementV; + + if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { + const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); + assert(elemD->getInit() == 0); + elementV = state->getLValue(elemD, Pred->getLocationContext()); + } + else { + elementV = state->getSVal(elem); + } + + ExplodedNodeSet dstLocation; + evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false); + + if (dstLocation.empty()) + return; + + for (ExplodedNodeSet::iterator NI = dstLocation.begin(), + NE = dstLocation.end(); NI!=NE; ++NI) { + Pred = *NI; + const ProgramState *state = Pred->getState(); + + // Handle the case where the container still has elements. + SVal TrueV = svalBuilder.makeTruthVal(1); + const ProgramState *hasElems = state->BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = svalBuilder.makeTruthVal(0); + const ProgramState *noElems = state->BindExpr(S, FalseV); + + if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV)) + if (const TypedValueRegion *R = + dyn_cast<TypedValueRegion>(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(); + assert(Loc::isLocType(T)); + unsigned Count = Builder->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + SVal V = svalBuilder.makeLoc(Sym); + hasElems = hasElems->bindLoc(elementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = svalBuilder.makeIntVal(0, T); + noElems = noElems->bindLoc(elementV, nilV); + } + + // Create the new nodes. + MakeNode(Dst, S, Pred, hasElems); + MakeNode(Dst, S, Pred, noElems); + } +} + +void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // Handle the previsits checks. + ExplodedNodeSet dstPrevisit; + getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, + msg, *this); + + // Proceed with evaluate the message expression. + ExplodedNodeSet dstEval; + + for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(), + DE = dstPrevisit.end(); DI != DE; ++DI) { + + ExplodedNode *Pred = *DI; + bool RaisesException = false; + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->hasGeneratedNode); + + if (const Expr *Receiver = msg.getInstanceReceiver()) { + const ProgramState *state = Pred->getState(); + SVal recVal = state->getSVal(Receiver); + if (!recVal.isUndef()) { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); + + const ProgramState *notNilState, *nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // There are three cases: can be nil or non-nil, must be nil, must be + // non-nil. We ignore must be nil, and merge the rest two into non-nil. + if (nilState && !notNilState) { + dstEval.insert(Pred); + continue; + } + + // Check if the "raise" message was sent. + assert(notNilState); + if (msg.getSelector() == RaiseSel) + RaisesException = true; + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessage(dstEval, msg, Pred, notNilState); + } + } + else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { + IdentifierInfo* ClsName = Iface->getIdentifier(); + Selector S = msg.getSelector(); + + // Check for special instance methods. + if (!NSExceptionII) { + ASTContext &Ctx = getContext(); + NSExceptionII = &Ctx.Idents.get("NSException"); + } + + if (ClsName == NSExceptionII) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext &Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format::arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessage(dstEval, msg, Pred, Pred->getState()); + } + + assert(Builder->BuildSinks || Builder->hasGeneratedNode); + } + + // Finally, perform the post-condition check of the ObjCMessageExpr and store + // the created nodes in 'Dst'. + getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); +} + +void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, + ExplodedNode *Pred, + const ProgramState *state) { + assert (Builder && "StmtNodeBuilder must be defined."); + + // First handle the return value. + SVal ReturnValue = UnknownVal(); + + // Some method families have known return values. + switch (msg.getMethodFamily()) { + default: + break; + case OMF_autorelease: + case OMF_retain: + case OMF_self: { + // These methods return their receivers. + const Expr *ReceiverE = msg.getInstanceReceiver(); + if (ReceiverE) + ReturnValue = state->getSVal(ReceiverE); + break; + } + } + + // If we failed to figure out the return value, use a conjured value instead. + if (ReturnValue.isUnknown()) { + SValBuilder &SVB = getSValBuilder(); + QualType ResultTy = msg.getResultType(getContext()); + unsigned Count = Builder->getCurrentBlockCount(); + const Expr *CurrentE = cast<Expr>(currentStmt); + ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count); + } + + // Bind the return value. + state = state->BindExpr(currentStmt, ReturnValue); + + // Invalidate the arguments (and the receiver) + const LocationContext *LC = Pred->getLocationContext(); + state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC); + + // And create the new node. + MakeNode(Dst, msg.getOriginExpr(), Pred, state); +} + diff --git a/lib/StaticAnalyzer/Core/FlatStore.cpp b/lib/StaticAnalyzer/Core/FlatStore.cpp deleted file mode 100644 index ca867aebded4..000000000000 --- a/lib/StaticAnalyzer/Core/FlatStore.cpp +++ /dev/null @@ -1,217 +0,0 @@ -//=== FlatStore.cpp - Flat region-based store model -------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "llvm/ADT/ImmutableIntervalMap.h" -#include "llvm/Support/ErrorHandling.h" - -using namespace clang; -using namespace ento; -using llvm::Interval; - -// The actual store type. -typedef llvm::ImmutableIntervalMap<SVal> BindingVal; -typedef llvm::ImmutableMap<const MemRegion *, BindingVal> RegionBindings; - -namespace { -class FlatStoreManager : public StoreManager { - RegionBindings::Factory RBFactory; - BindingVal::Factory BVFactory; - -public: - FlatStoreManager(GRStateManager &mgr) - : StoreManager(mgr), - RBFactory(mgr.getAllocator()), - BVFactory(mgr.getAllocator()) {} - - SVal Retrieve(Store store, Loc L, QualType T); - StoreRef Bind(Store store, Loc L, SVal val); - StoreRef Remove(Store St, Loc L); - StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr* cl, - const LocationContext *LC, SVal v); - - StoreRef getInitialStore(const LocationContext *InitLoc) { - return StoreRef(RBFactory.getEmptyMap().getRoot(), *this); - } - - SubRegionMap *getSubRegionMap(Store store) { - return 0; - } - - SVal ArrayToPointer(Loc Array); - StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots){ - return StoreRef(store, *this); - } - - StoreRef BindDecl(Store store, const VarRegion *VR, SVal initVal); - - StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR); - - typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; - - StoreRef invalidateRegions(Store store, const MemRegion * const *I, - const MemRegion * const *E, const Expr *Ex, - unsigned Count, InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions); - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - void iterBindings(Store store, BindingsHandler& f); - -private: - static RegionBindings getRegionBindings(Store store) { - return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); - } - - class RegionInterval { - public: - const MemRegion *R; - Interval I; - RegionInterval(const MemRegion *r, int64_t s, int64_t e) : R(r), I(s, e){} - }; - - RegionInterval RegionToInterval(const MemRegion *R); - - SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T); -}; -} // end anonymous namespace - -StoreManager *ento::CreateFlatStoreManager(GRStateManager &StMgr) { - return new FlatStoreManager(StMgr); -} - -SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) { - // For access to concrete addresses, return UnknownVal. Checks - // for null dereferences (and similar errors) are done by checkers, not - // the Store. - // FIXME: We can consider lazily symbolicating such memory, but we really - // should defer this when we can reason easily about symbolicating arrays - // of bytes. - if (isa<loc::ConcreteInt>(L)) { - return UnknownVal(); - } - if (!isa<loc::MemRegionVal>(L)) { - return UnknownVal(); - } - - const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); - RegionInterval RI = RegionToInterval(R); - // FIXME: FlatStore should handle regions with unknown intervals. - if (!RI.R) - return UnknownVal(); - - RegionBindings B = getRegionBindings(store); - const BindingVal *BV = B.lookup(RI.R); - if (BV) { - const SVal *V = BVFactory.lookup(*BV, RI.I); - if (V) - return *V; - else - return RetrieveRegionWithNoBinding(R, T); - } - return RetrieveRegionWithNoBinding(R, T); -} - -SVal FlatStoreManager::RetrieveRegionWithNoBinding(const MemRegion *R, - QualType T) { - if (R->hasStackNonParametersStorage()) - return UndefinedVal(); - else - return svalBuilder.getRegionValueSymbolVal(cast<TypedRegion>(R)); -} - -StoreRef FlatStoreManager::Bind(Store store, Loc L, SVal val) { - const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); - RegionBindings B = getRegionBindings(store); - const BindingVal *V = B.lookup(R); - - BindingVal BV = BVFactory.getEmptyMap(); - if (V) - BV = *V; - - RegionInterval RI = RegionToInterval(R); - // FIXME: FlatStore should handle regions with unknown intervals. - if (!RI.R) - return StoreRef(B.getRoot(), *this); - BV = BVFactory.add(BV, RI.I, val); - B = RBFactory.add(B, RI.R, BV); - return StoreRef(B.getRoot(), *this); -} - -StoreRef FlatStoreManager::Remove(Store store, Loc L) { - return StoreRef(store, *this); -} - -StoreRef FlatStoreManager::BindCompoundLiteral(Store store, - const CompoundLiteralExpr* cl, - const LocationContext *LC, - SVal v) { - return StoreRef(store, *this); -} - -SVal FlatStoreManager::ArrayToPointer(Loc Array) { - return Array; -} - -StoreRef FlatStoreManager::BindDecl(Store store, const VarRegion *VR, - SVal initVal) { - return Bind(store, svalBuilder.makeLoc(VR), initVal); -} - -StoreRef FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR){ - return StoreRef(store, *this); -} - -StoreRef FlatStoreManager::invalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *E, - const Expr *Ex, unsigned Count, - InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions) { - assert(false && "Not implemented"); - return StoreRef(store, *this); -} - -void FlatStoreManager::print(Store store, llvm::raw_ostream& Out, - const char* nl, const char *sep) { -} - -void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) { -} - -FlatStoreManager::RegionInterval -FlatStoreManager::RegionToInterval(const MemRegion *R) { - switch (R->getKind()) { - case MemRegion::VarRegionKind: { - QualType T = cast<VarRegion>(R)->getValueType(); - int64_t Size = Ctx.getTypeSize(T); - return RegionInterval(R, 0, Size-1); - } - - case MemRegion::ElementRegionKind: - case MemRegion::FieldRegionKind: { - RegionOffset Offset = R->getAsOffset(); - // We cannot compute offset for all regions, for example, elements - // with symbolic offsets. - if (!Offset.getRegion()) - return RegionInterval(0, 0, 0); - int64_t Start = Offset.getOffset(); - int64_t Size = Ctx.getTypeSize(cast<TypedRegion>(R)->getValueType()); - return RegionInterval(Offset.getRegion(), Start, Start+Size); - } - - default: - llvm_unreachable("Region kind unhandled."); - return RegionInterval(0, 0, 0); - } -} diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 1ebc28c044fd..0c4e4277e4d6 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -35,7 +35,7 @@ using namespace ento; namespace { -class HTMLDiagnostics : public PathDiagnosticClient { +class HTMLDiagnostics : public PathDiagnosticConsumer { llvm::sys::Path Directory, FilePrefix; bool createdDir, noDir; const Preprocessor &PP; @@ -45,15 +45,15 @@ public: virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } - virtual void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade); + virtual void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - virtual void HandlePathDiagnostic(const PathDiagnostic* D); + virtual void HandlePathDiagnosticImpl(const PathDiagnostic* D); - virtual llvm::StringRef getName() const { + virtual StringRef getName() const { return "HTMLDiagnostics"; } - unsigned ProcessMacroPiece(llvm::raw_ostream& os, + unsigned ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num); @@ -65,7 +65,7 @@ public: const char *HighlightEnd = "</span>"); void ReportDiag(const PathDiagnostic& D, - llvm::SmallVectorImpl<std::string> *FilesMade); + SmallVectorImpl<std::string> *FilesMade); }; } // end anonymous namespace @@ -78,8 +78,8 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, FilePrefix.appendComponent("report"); } -PathDiagnosticClient* -ento::createHTMLDiagnosticClient(const std::string& prefix, +PathDiagnosticConsumer* +ento::createHTMLDiagnosticConsumer(const std::string& prefix, const Preprocessor &PP) { return new HTMLDiagnostics(prefix, PP); } @@ -88,7 +88,7 @@ ento::createHTMLDiagnosticClient(const std::string& prefix, // Report processing. //===----------------------------------------------------------------------===// -void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { +void HTMLDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { if (!D) return; @@ -102,7 +102,7 @@ void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { } void -HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade) +HTMLDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { while (!BatchedDiags.empty()) { const PathDiagnostic* D = BatchedDiags.back(); @@ -115,7 +115,7 @@ HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade) } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, - llvm::SmallVectorImpl<std::string> *FilesMade){ + SmallVectorImpl<std::string> *FilesMade){ // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; @@ -143,7 +143,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, // Verify that the entire path is from the same FileID. for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) { - FullSourceLoc L = I->getLocation().asLocation().getInstantiationLoc(); + FullSourceLoc L = I->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); @@ -154,12 +154,12 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), RE=I->ranges_end(); RI!=RE; ++RI) { - SourceLocation L = SMgr.getInstantiationLoc(RI->getBegin()); + SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? - L = SMgr.getInstantiationLoc(RI->getEnd()); + L = SMgr.getExpansionLoc(RI->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? @@ -221,9 +221,9 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, << html::EscapeText(Entry->getName()) << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>" "<a href=\"#EndPath\">line " - << (*D.rbegin()).getLocation().asLocation().getInstantiationLineNumber() + << (*D.rbegin()).getLocation().asLocation().getExpansionLineNumber() << ", column " - << (*D.rbegin()).getLocation().asLocation().getInstantiationColumnNumber() + << (*D.rbegin()).getLocation().asLocation().getExpansionColumnNumber() << "</a></td></tr>\n" "<tr><td class=\"rowname\">Description:</td><td>" << D.getDescription() << "</td></tr>\n"; @@ -261,7 +261,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- BUGLINE " - << D.back()->getLocation().asLocation().getInstantiationLineNumber() + << D.back()->getLocation().asLocation().getExpansionLineNumber() << " -->\n"; os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n"; @@ -324,7 +324,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, SourceManager &SM = R.getSourceMgr(); assert(&Pos.getManager() == &SM && "SourceManagers are different!"); - std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedInstantiationLoc(Pos); + std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos); if (LPosInfo.first != BugFileID) return; @@ -335,7 +335,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Compute the column number. Rewind from the current position to the start // of the line. unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second); - const char *TokInstantiationPtr =Pos.getInstantiationLoc().getCharacterData(); + const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData(); const char *LineStart = TokInstantiationPtr-ColNo; // Compute LineEnd. @@ -441,9 +441,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Get the name of the macro by relexing it. { - FullSourceLoc L = MP->getLocation().asLocation().getInstantiationLoc(); + FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); - llvm::StringRef BufferInfo = L.getBufferData(); + StringRef BufferInfo = L.getBufferData(); const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(), MacroName, BufferInfo.end()); @@ -474,7 +474,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Insert the new html. unsigned DisplayPos = LineEnd - FileStart; SourceLocation Loc = - SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos); + SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos); R.InsertTextBefore(Loc, os.str()); @@ -504,7 +504,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, #endif } -static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) { +static void EmitAlphaCounter(raw_ostream &os, unsigned n) { unsigned x = n % ('z' - 'a'); n /= 'z' - 'a'; @@ -514,7 +514,7 @@ static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) { os << char('a' + x); } -unsigned HTMLDiagnostics::ProcessMacroPiece(llvm::raw_ostream& os, +unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { @@ -549,11 +549,11 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, SourceManager &SM = R.getSourceMgr(); const LangOptions &LangOpts = R.getLangOpts(); - SourceLocation InstantiationStart = SM.getInstantiationLoc(Range.getBegin()); - unsigned StartLineNo = SM.getInstantiationLineNumber(InstantiationStart); + SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin()); + unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart); - SourceLocation InstantiationEnd = SM.getInstantiationLoc(Range.getEnd()); - unsigned EndLineNo = SM.getInstantiationLineNumber(InstantiationEnd); + SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd()); + unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd); if (EndLineNo < StartLineNo) return; @@ -563,7 +563,7 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, return; // Compute the column number of the end. - unsigned EndColNo = SM.getInstantiationColumnNumber(InstantiationEnd); + unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd); unsigned OldEndColNo = EndColNo; if (EndColNo) { @@ -575,7 +575,7 @@ void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, // selected range. SourceLocation E = - InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo); + InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo); html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd); } diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index d9e884a4b208..6f92da8f3e5f 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -38,7 +38,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; + void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); @@ -56,7 +56,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const MemRegion *superRegion) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; + void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); @@ -77,7 +77,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; + void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); @@ -96,7 +96,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; + void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); @@ -115,7 +115,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); - void* InsertPos; + void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); @@ -178,7 +178,7 @@ const StackFrameContext *VarRegion::getStackFrame() const { //===----------------------------------------------------------------------===// DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const { - ASTContext& Ctx = svalBuilder.getContext(); + ASTContext &Ctx = svalBuilder.getContext(); QualType T = getDesugaredValueType(Ctx); if (isa<VariableArrayType>(T)) @@ -250,7 +250,7 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, } void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const Expr* Ex, unsigned cnt, + const Expr *Ex, unsigned cnt, const MemRegion *) { ID.AddInteger((unsigned) AllocaRegionKind); ID.AddPointer(Ex); @@ -266,7 +266,7 @@ void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { } void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const CompoundLiteralExpr* CL, + const CompoundLiteralExpr *CL, const MemRegion* superRegion) { ID.AddInteger((unsigned) CompoundLiteralRegionKind); ID.AddPointer(CL); @@ -285,7 +285,7 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); } -void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, +void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k) { ID.AddInteger((unsigned) k); ID.AddPointer(D); @@ -398,81 +398,82 @@ std::string MemRegion::getString() const { return os.str(); } -void MemRegion::dumpToStream(llvm::raw_ostream& os) const { +void MemRegion::dumpToStream(raw_ostream &os) const { os << "<Unknown Region>"; } -void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { +void AllocaRegion::dumpToStream(raw_ostream &os) const { os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; } -void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { +void FunctionTextRegion::dumpToStream(raw_ostream &os) const { os << "code{" << getDecl()->getDeclName().getAsString() << '}'; } -void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { +void BlockTextRegion::dumpToStream(raw_ostream &os) const { os << "block_code{" << (void*) this << '}'; } -void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { +void BlockDataRegion::dumpToStream(raw_ostream &os) const { os << "block_data{" << BC << '}'; } -void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { +void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { // FIXME: More elaborate pretty-printing. os << "{ " << (void*) CL << " }"; } -void CXXTempObjectRegion::dumpToStream(llvm::raw_ostream &os) const { - os << "temp_object"; +void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { + os << "temp_object{" << getValueType().getAsString() << ',' + << (void*) Ex << '}'; } -void CXXBaseObjectRegion::dumpToStream(llvm::raw_ostream &os) const { +void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { os << "base " << decl->getName(); } -void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const { +void CXXThisRegion::dumpToStream(raw_ostream &os) const { os << "this"; } -void ElementRegion::dumpToStream(llvm::raw_ostream& os) const { +void ElementRegion::dumpToStream(raw_ostream &os) const { os << "element{" << superRegion << ',' << Index << ',' << getElementType().getAsString() << '}'; } -void FieldRegion::dumpToStream(llvm::raw_ostream& os) const { - os << superRegion << "->" << getDecl(); +void FieldRegion::dumpToStream(raw_ostream &os) const { + os << superRegion << "->" << *getDecl(); } -void NonStaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const { +void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "NonStaticGlobalSpaceRegion"; } -void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "ivar{" << superRegion << ',' << getDecl() << '}'; +void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { + os << "ivar{" << superRegion << ',' << *getDecl() << '}'; } -void StringRegion::dumpToStream(llvm::raw_ostream& os) const { +void StringRegion::dumpToStream(raw_ostream &os) const { Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); } -void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const { +void SymbolicRegion::dumpToStream(raw_ostream &os) const { os << "SymRegion{" << sym << '}'; } -void VarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << cast<VarDecl>(D); +void VarRegion::dumpToStream(raw_ostream &os) const { + os << *cast<VarDecl>(D); } void RegionRawOffset::dump() const { dumpToStream(llvm::errs()); } -void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { +void RegionRawOffset::dumpToStream(raw_ostream &os) const { os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}'; } -void StaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const { +void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } @@ -631,7 +632,7 @@ MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, } const CompoundLiteralRegion* -MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, const LocationContext *LC) { const MemRegion *sReg = 0; @@ -650,14 +651,14 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, const ElementRegion* MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, const MemRegion* superRegion, - ASTContext& Ctx){ + ASTContext &Ctx){ QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType(); llvm::FoldingSetNodeID ID; ElementRegion::ProfileRegion(ID, T, Idx, superRegion); - void* InsertPos; + void *InsertPos; MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); ElementRegion* R = cast_or_null<ElementRegion>(data); @@ -688,13 +689,13 @@ const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { } const FieldRegion* -MemRegionManager::getFieldRegion(const FieldDecl* d, +MemRegionManager::getFieldRegion(const FieldDecl *d, const MemRegion* superRegion){ return getSubRegion<FieldRegion>(d, superRegion); } const ObjCIvarRegion* -MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, +MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, const MemRegion* superRegion) { return getSubRegion<ObjCIvarRegion>(d, superRegion); } @@ -724,7 +725,7 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy, } const AllocaRegion* -MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, +MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, const LocationContext *LC) { const StackFrameContext *STC = LC->getCurrentStackFrame(); assert(STC); @@ -896,7 +897,7 @@ RegionOffset MemRegion::getAsOffset() const { case FieldRegionKind: { const FieldRegion *FR = cast<FieldRegion>(R); const RecordDecl *RD = FR->getDecl()->getParent(); - if (!RD->isDefinition()) + if (!RD->isCompleteDefinition()) // We cannot compute offset for incomplete type. return RegionOffset(0); // Get the field number. diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp index c00060047498..0974fe877ac2 100644 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp @@ -112,18 +112,22 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { QualType resultTy; bool isLVal = false; - if (CallE) { - isLVal = CallE->isLValue(); - const Expr *Callee = CallE->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl()) - resultTy = FD->getResultType(); - else - resultTy = CallE->getType(); - } - else { + if (isObjCMessage()) { isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) && Msg.getOriginExpr()->isLValue(); resultTy = Msg.getResultType(ctx); + } else if (const CXXConstructExpr *Ctor = + CallE.dyn_cast<const CXXConstructExpr *>()) { + resultTy = Ctor->getType(); + } else { + const CallExpr *FunctionCall = CallE.get<const CallExpr *>(); + + isLVal = FunctionCall->isLValue(); + const Expr *Callee = FunctionCall->getCallee(); + if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl()) + resultTy = FD->getResultType(); + else + resultTy = FunctionCall->getType(); } if (isLVal) @@ -132,25 +136,29 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { return resultTy; } -SVal CallOrObjCMessage::getArgSValAsScalarOrLoc(unsigned i) const { - assert(i < getNumArgs()); - if (CallE) return State->getSValAsScalarOrLoc(CallE->getArg(i)); - QualType argT = Msg.getArgType(i); - if (Loc::isLocType(argT) || argT->isIntegerType()) - return Msg.getArgSVal(i, State); - return UnknownVal(); -} - SVal CallOrObjCMessage::getFunctionCallee() const { assert(isFunctionCall()); assert(!isCXXCall()); - const Expr *callee = CallE->getCallee()->IgnoreParenCasts(); - return State->getSVal(callee); + const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens(); + return State->getSVal(Fun); } SVal CallOrObjCMessage::getCXXCallee() const { assert(isCXXCall()); + const CallExpr *ActualCall = CallE.get<const CallExpr *>(); const Expr *callee = - cast<CXXMemberCallExpr>(CallE)->getImplicitObjectArgument(); - return State->getSVal(callee); + cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument(); + + // FIXME: Will eventually need to cope with member pointers. This is + // a limitation in getImplicitObjectArgument(). + if (!callee) + return UnknownVal(); + + return State->getSVal(callee); +} + +SVal +CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { + assert(isObjCMessage()); + return Msg.getInstanceReceiverSVal(State, LC); } diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 872bbfe9e160..3a879030da11 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -12,17 +12,16 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/ParentMap.h" #include "clang/AST/StmtCXX.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/Casting.h" using namespace clang; using namespace ento; -using llvm::dyn_cast; -using llvm::isa; bool PathDiagnosticMacroPiece::containsEvent() const { for (const_iterator I = begin(), E = end(); I!=E; ++I) { @@ -37,14 +36,14 @@ bool PathDiagnosticMacroPiece::containsEvent() const { return false; } -static llvm::StringRef StripTrailingDots(llvm::StringRef s) { - for (llvm::StringRef::size_type i = s.size(); i != 0; --i) +static StringRef StripTrailingDots(StringRef s) { + for (StringRef::size_type i = s.size(); i != 0; --i) if (s[i - 1] != '.') return s.substr(0, i); return ""; } -PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s, +PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint) : str(StripTrailingDots(s)), kind(k), Hint(hint) {} @@ -76,55 +75,161 @@ void PathDiagnostic::resetPath(bool deletePieces) { } -PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, - llvm::StringRef category) +PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, + StringRef category) : Size(0), BugType(StripTrailingDots(bugtype)), Desc(StripTrailingDots(desc)), Category(StripTrailingDots(category)) {} -void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, - const DiagnosticInfo &Info) { - // Default implementation (Warnings/errors count). - DiagnosticClient::HandleDiagnostic(DiagLevel, Info); +void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) { + // For now this simply forwards to HandlePathDiagnosticImpl. In the future + // we can use this indirection to control for multi-threaded access to + // the PathDiagnosticConsumer from multiple bug reporters. + HandlePathDiagnosticImpl(D); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticLocation methods. +//===----------------------------------------------------------------------===// + +static SourceLocation getValidSourceLocation(const Stmt* S, + LocationOrAnalysisContext LAC) { + SourceLocation L = S->getLocStart(); + assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext should " + "be passed to PathDiagnosticLocation upon creation."); + + // S might be a temporary statement that does not have a location in the + // source code, so find an enclosing statement and use it's location. + if (!L.isValid()) { + + ParentMap *PM = 0; + if (LAC.is<const LocationContext*>()) + PM = &LAC.get<const LocationContext*>()->getParentMap(); + else + PM = &LAC.get<AnalysisContext*>()->getParentMap(); + + while (!L.isValid()) { + S = PM->getParent(S); + L = S->getLocStart(); + } + } + + return L; +} - // Create a PathDiagnostic with a single piece. +PathDiagnosticLocation + PathDiagnosticLocation::createBegin(const Decl *D, + const SourceManager &SM) { + return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK); +} - PathDiagnostic* D = new PathDiagnostic(); +PathDiagnosticLocation + PathDiagnosticLocation::createBegin(const Stmt *S, + const SourceManager &SM, + LocationOrAnalysisContext LAC) { + return PathDiagnosticLocation(getValidSourceLocation(S, LAC), + SM, SingleLocK); +} - const char *LevelStr; - switch (DiagLevel) { - default: - case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); - case Diagnostic::Note: LevelStr = "note: "; break; - case Diagnostic::Warning: LevelStr = "warning: "; break; - case Diagnostic::Error: LevelStr = "error: "; break; - case Diagnostic::Fatal: LevelStr = "fatal error: "; break; +PathDiagnosticLocation + PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, + const SourceManager &SM) { + return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation + PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, + const SourceManager &SM) { + return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation + PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, + const SourceManager &SM) { + SourceLocation L = CS->getLBracLoc(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation + PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, + const SourceManager &SM) { + SourceLocation L = CS->getRBracLoc(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation + PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, + const SourceManager &SM) { + // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. + if (const CompoundStmt *CS = + dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) + if (!CS->body_empty()) { + SourceLocation Loc = (*CS->body_begin())->getLocStart(); + return PathDiagnosticLocation(Loc, SM, SingleLocK); + } + + return PathDiagnosticLocation(); +} + +PathDiagnosticLocation + PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, + const SourceManager &SM) { + SourceLocation L = LC->getDecl()->getBodyRBrace(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation + PathDiagnosticLocation::create(const ProgramPoint& P, + const SourceManager &SMng) { + + const Stmt* S = 0; + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + const CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } + else if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + S = PS->getStmt(); } - llvm::SmallString<100> StrC; - StrC += LevelStr; - Info.FormatDiagnostic(StrC); + return PathDiagnosticLocation(S, SMng, P.getLocationContext()); - PathDiagnosticPiece *P = - new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(), - Info.getSourceManager()), - StrC.str()); + if (!S) + return PathDiagnosticLocation(); +} - for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) - P->addRange(Info.getRange(i).getAsRange()); - for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) - P->addFixItHint(Info.getFixItHint(i)); - D->push_front(P); +PathDiagnosticLocation + PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, + const SourceManager &SM) { + assert(N && "Cannot create a location with a null node."); + + const ExplodedNode *NI = N; + + while (NI) { + ProgramPoint P = NI->getLocation(); + const LocationContext *LC = P.getLocationContext(); + if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) + return PathDiagnosticLocation(PS->getStmt(), SM, LC); + else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + const Stmt *Term = BE->getSrc()->getTerminator(); + assert(Term); + return PathDiagnosticLocation(Term, SM, LC); + } + NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); + } - HandlePathDiagnostic(D); + return createDeclEnd(N->getLocationContext(), SM); } -//===----------------------------------------------------------------------===// -// PathDiagnosticLocation methods. -//===----------------------------------------------------------------------===// +PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( + const PathDiagnosticLocation &PDL) { + FullSourceLoc L = PDL.asLocation(); + return PathDiagnosticLocation(L, L.getManager(), SingleLocK); +} -FullSourceLoc PathDiagnosticLocation::asLocation() const { +FullSourceLoc + PathDiagnosticLocation::genLocation(SourceLocation L, + LocationOrAnalysisContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. @@ -133,21 +238,23 @@ FullSourceLoc PathDiagnosticLocation::asLocation() const { case RangeK: break; case StmtK: - return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM)); + return FullSourceLoc(getValidSourceLocation(S, LAC), + const_cast<SourceManager&>(*SM)); case DeclK: return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); } - return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM)); + return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); } -PathDiagnosticRange PathDiagnosticLocation::asRange() const { +PathDiagnosticRange + PathDiagnosticLocation::genRange(LocationOrAnalysisContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. switch (K) { case SingleLocK: - return PathDiagnosticRange(R, true); + return PathDiagnosticRange(SourceRange(Loc,Loc), true); case RangeK: break; case StmtK: { @@ -176,12 +283,14 @@ PathDiagnosticRange PathDiagnosticLocation::asRange() const { case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: case Stmt::ObjCForCollectionStmtClass: { - SourceLocation L = S->getLocStart(); + SourceLocation L = getValidSourceLocation(S, LAC); return SourceRange(L, L); } } - - return S->getSourceRange(); + SourceRange R = S->getSourceRange(); + if (R.isValid()) + return R; + break; } case DeclK: if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) @@ -196,19 +305,16 @@ PathDiagnosticRange PathDiagnosticLocation::asRange() const { } } - return R; + return SourceRange(Loc,Loc); } void PathDiagnosticLocation::flatten() { if (K == StmtK) { - R = asRange(); K = RangeK; S = 0; D = 0; } else if (K == DeclK) { - SourceLocation L = D->getLocation(); - R = SourceRange(L, L); K = SingleLocK; S = 0; D = 0; @@ -220,22 +326,9 @@ void PathDiagnosticLocation::flatten() { //===----------------------------------------------------------------------===// void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) K); - switch (K) { - case RangeK: - ID.AddInteger(R.getBegin().getRawEncoding()); - ID.AddInteger(R.getEnd().getRawEncoding()); - break; - case SingleLocK: - ID.AddInteger(R.getBegin().getRawEncoding()); - break; - case StmtK: - ID.Add(S); - break; - case DeclK: - ID.Add(D); - break; - } + ID.AddInteger(Range.getBegin().getRawEncoding()); + ID.AddInteger(Range.getEnd().getRawEncoding()); + ID.AddInteger(Loc.getRawEncoding()); return; } diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index fbbbd46ac149..5ae95c61f819 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" @@ -22,14 +22,9 @@ #include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; -using llvm::cast; typedef llvm::DenseMap<FileID, unsigned> FIDMap; -namespace clang { - class Preprocessor; -} - namespace { struct CompareDiagnostics { // Compare if 'X' is "<" than 'Y'. @@ -43,16 +38,16 @@ struct CompareDiagnostics { return false; // Next, compare by bug type. - llvm::StringRef XBugType = X->getBugType(); - llvm::StringRef YBugType = Y->getBugType(); + StringRef XBugType = X->getBugType(); + StringRef YBugType = Y->getBugType(); if (XBugType < YBugType) return true; if (XBugType != YBugType) return false; // Next, compare by bug description. - llvm::StringRef XDesc = X->getDescription(); - llvm::StringRef YDesc = Y->getDescription(); + StringRef XDesc = X->getDescription(); + StringRef YDesc = Y->getDescription(); if (XDesc < YDesc) return true; if (XDesc != YDesc) @@ -65,23 +60,23 @@ struct CompareDiagnostics { } namespace { - class PlistDiagnostics : public PathDiagnosticClient { + class PlistDiagnostics : public PathDiagnosticConsumer { std::vector<const PathDiagnostic*> BatchedDiags; const std::string OutputFile; const LangOptions &LangOpts; - llvm::OwningPtr<PathDiagnosticClient> SubPD; + llvm::OwningPtr<PathDiagnosticConsumer> SubPD; bool flushed; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, - PathDiagnosticClient *subPD); + PathDiagnosticConsumer *subPD); ~PlistDiagnostics() { FlushDiagnostics(NULL); } - void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade); + void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - void HandlePathDiagnostic(const PathDiagnostic* D); + void HandlePathDiagnosticImpl(const PathDiagnostic* D); - virtual llvm::StringRef getName() const { + virtual StringRef getName() const { return "PlistDiagnostics"; } @@ -94,27 +89,27 @@ namespace { PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, - PathDiagnosticClient *subPD) + PathDiagnosticConsumer *subPD) : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} -PathDiagnosticClient* -ento::createPlistDiagnosticClient(const std::string& s, const Preprocessor &PP, - PathDiagnosticClient *subPD) { +PathDiagnosticConsumer* +ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, + PathDiagnosticConsumer *subPD) { return new PlistDiagnostics(s, PP.getLangOptions(), subPD); } -PathDiagnosticClient::PathGenerationScheme +PathDiagnosticConsumer::PathGenerationScheme PlistDiagnostics::getGenerationScheme() const { - if (const PathDiagnosticClient *PD = SubPD.get()) + if (const PathDiagnosticConsumer *PD = SubPD.get()) return PD->getGenerationScheme(); return Extensive; } -static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V, +static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, const SourceManager* SM, SourceLocation L) { - FileID FID = SM->getFileID(SM->getInstantiationLoc(L)); + FileID FID = SM->getFileID(SM->getExpansionLoc(L)); FIDMap::iterator I = FIDs.find(FID); if (I != FIDs.end()) return; FIDs[FID] = V.size(); @@ -123,23 +118,23 @@ static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V, static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, SourceLocation L) { - FileID FID = SM.getFileID(SM.getInstantiationLoc(L)); + FileID FID = SM.getFileID(SM.getExpansionLoc(L)); FIDMap::const_iterator I = FIDs.find(FID); assert(I != FIDs.end()); return I->second; } -static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) { +static raw_ostream &Indent(raw_ostream &o, const unsigned indent) { for (unsigned i = 0; i < indent; ++i) o << ' '; return o; } -static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, +static void EmitLocation(raw_ostream &o, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation L, const FIDMap &FM, unsigned indent, bool extend = false) { - FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM)); + FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM)); // Add in the length of the token, so that we cover multi-char tokens. unsigned offset = @@ -147,22 +142,22 @@ static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, Indent(o, indent) << "<dict>\n"; Indent(o, indent) << " <key>line</key><integer>" - << Loc.getInstantiationLineNumber() << "</integer>\n"; + << Loc.getExpansionLineNumber() << "</integer>\n"; Indent(o, indent) << " <key>col</key><integer>" - << Loc.getInstantiationColumnNumber() + offset << "</integer>\n"; + << Loc.getExpansionColumnNumber() + offset << "</integer>\n"; Indent(o, indent) << " <key>file</key><integer>" << GetFID(FM, SM, Loc) << "</integer>\n"; Indent(o, indent) << "</dict>\n"; } -static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, +static void EmitLocation(raw_ostream &o, const SourceManager &SM, const LangOptions &LangOpts, const PathDiagnosticLocation &L, const FIDMap& FM, unsigned indent, bool extend = false) { EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); } -static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM, +static void EmitRange(raw_ostream &o, const SourceManager &SM, const LangOptions &LangOpts, PathDiagnosticRange R, const FIDMap &FM, unsigned indent) { @@ -172,7 +167,7 @@ static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM, Indent(o, indent) << "</array>\n"; } -static llvm::raw_ostream& EmitString(llvm::raw_ostream& o, +static raw_ostream &EmitString(raw_ostream &o, const std::string& s) { o << "<string>"; for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { @@ -190,7 +185,7 @@ static llvm::raw_ostream& EmitString(llvm::raw_ostream& o, return o; } -static void ReportControlFlow(llvm::raw_ostream& o, +static void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, const FIDMap& FM, const SourceManager &SM, @@ -233,7 +228,7 @@ static void ReportControlFlow(llvm::raw_ostream& o, Indent(o, indent) << "</dict>\n"; } -static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P, +static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, @@ -280,7 +275,7 @@ static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P, Indent(o, indent); o << "</dict>\n"; } -static void ReportMacro(llvm::raw_ostream& o, +static void ReportMacro(raw_ostream &o, const PathDiagnosticMacroPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, @@ -304,7 +299,7 @@ static void ReportMacro(llvm::raw_ostream& o, } } -static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P, +static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { @@ -326,7 +321,7 @@ static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P, } } -void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { +void PlistDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { if (!D) return; @@ -342,7 +337,7 @@ void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { BatchedDiags.push_back(D); } -void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> +void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { if (flushed) @@ -358,7 +353,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; - llvm::SmallVector<FileID, 10> Fids; + SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; if (!BatchedDiags.empty()) @@ -401,7 +396,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> " <key>files</key>\n" " <array>\n"; - for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); + for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); I!=E; ++I) { o << " "; EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; @@ -444,7 +439,7 @@ void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> // Output the diagnostic to the sub-diagnostic client, if any. if (SubPD) { SubPD->HandlePathDiagnostic(OwnedD.take()); - llvm::SmallVector<std::string, 1> SubFilesMade; + SmallVector<std::string, 1> SubFilesMade; SubPD->FlushDiagnostics(SubFilesMade); if (!SubFilesMade.empty()) { diff --git a/lib/StaticAnalyzer/Core/GRState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 0f6ff1ef588c..73788cc42efb 100644 --- a/lib/StaticAnalyzer/Core/GRState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -1,4 +1,4 @@ -//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=// +//= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--= // // The LLVM Compiler Infrastructure // @@ -7,15 +7,14 @@ // //===----------------------------------------------------------------------===// // -// This file implements GRState and GRStateManager. +// This file implements ProgramState and ProgramStateManager. // //===----------------------------------------------------------------------===// #include "clang/Analysis/CFG.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -25,7 +24,7 @@ using namespace ento; // FIXME: Move this elsewhere. ConstraintManager::~ConstraintManager() {} -GRState::GRState(GRStateManager *mgr, const Environment& env, +ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env, StoreRef st, GenericDataMap gdm) : stateMgr(mgr), Env(env), @@ -35,7 +34,7 @@ GRState::GRState(GRStateManager *mgr, const Environment& env, stateMgr->getStoreManager().incrementReferenceCount(store); } -GRState::GRState(const GRState& RHS) +ProgramState::ProgramState(const ProgramState &RHS) : llvm::FoldingSetNode(), stateMgr(RHS.stateMgr), Env(RHS.Env), @@ -45,23 +44,19 @@ GRState::GRState(const GRState& RHS) stateMgr->getStoreManager().incrementReferenceCount(store); } -GRState::~GRState() { +ProgramState::~ProgramState() { if (store) stateMgr->getStoreManager().decrementReferenceCount(store); } -GRStateManager::~GRStateManager() { - for (std::vector<GRState::Printer*>::iterator I=Printers.begin(), - E=Printers.end(); I!=E; ++I) - delete *I; - +ProgramStateManager::~ProgramStateManager() { for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); I!=E; ++I) I->second.second(I->second.first); } -const GRState* -GRStateManager::removeDeadBindings(const GRState* state, +const ProgramState* +ProgramStateManager::removeDeadBindings(const ProgramState *state, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { @@ -71,23 +66,23 @@ GRStateManager::removeDeadBindings(const GRState* state, // those around. This code more than likely can be made faster, and the // frequency of which this method is called should be experimented with // for optimum performance. - llvm::SmallVector<const MemRegion*, 10> RegionRoots; - GRState NewState = *state; + ProgramState NewState = *state; - NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, - state, RegionRoots); + NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, state); // Clean up the store. - NewState.setStore(StoreMgr->removeDeadBindings(NewState.getStore(), LCtx, - SymReaper, RegionRoots)); - state = getPersistentState(NewState); - return ConstraintMgr->removeDeadBindings(state, SymReaper); + StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx, + SymReaper); + NewState.setStore(newStore); + SymReaper.setReapedStore(newStore); + + return getPersistentState(NewState); } -const GRState *GRStateManager::MarshalState(const GRState *state, +const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state, const StackFrameContext *InitLoc) { // make up an empty state for now. - GRState State(this, + ProgramState State(this, EnvMgr.getInitialEnvironment(), StoreMgr->getInitialStore(InitLoc), GDMFactory.getEmptyMap()); @@ -95,7 +90,7 @@ const GRState *GRStateManager::MarshalState(const GRState *state, return getPersistentState(State); } -const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL, +const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, const LocationContext *LC, SVal V) const { const StoreRef &newStore = @@ -103,21 +98,21 @@ const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL, return makeWithStore(newStore); } -const GRState *GRState::bindDecl(const VarRegion* VR, SVal IVal) const { +const ProgramState *ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDecl(getStore(), VR, IVal); return makeWithStore(newStore); } -const GRState *GRState::bindDeclWithNoInit(const VarRegion* VR) const { +const ProgramState *ProgramState::bindDeclWithNoInit(const VarRegion* VR) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDeclWithNoInit(getStore(), VR); return makeWithStore(newStore); } -const GRState *GRState::bindLoc(Loc LV, SVal V) const { - GRStateManager &Mgr = getStateManager(); - const GRState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), +const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const { + ProgramStateManager &Mgr = getStateManager(); + const ProgramState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine()) @@ -126,56 +121,53 @@ const GRState *GRState::bindLoc(Loc LV, SVal V) const { return newState; } -const GRState *GRState::bindDefault(SVal loc, SVal V) const { - GRStateManager &Mgr = getStateManager(); +const ProgramState *ProgramState::bindDefault(SVal loc, SVal V) const { + ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); - const GRState *new_state = makeWithStore(newStore); + const ProgramState *new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? Mgr.getOwningEngine()->processRegionChange(new_state, R) : new_state; } -const GRState *GRState::invalidateRegions(const MemRegion * const *Begin, - const MemRegion * const *End, - const Expr *E, unsigned Count, - StoreManager::InvalidatedSymbols *IS, - bool invalidateGlobals) const { +const ProgramState * +ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, + const Expr *E, unsigned Count, + StoreManager::InvalidatedSymbols *IS, + bool invalidateGlobals) const { if (!IS) { StoreManager::InvalidatedSymbols invalidated; - return invalidateRegionsImpl(Begin, End, E, Count, - invalidated, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, + invalidated, invalidateGlobals); } - return invalidateRegionsImpl(Begin, End, E, Count, *IS, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, *IS, invalidateGlobals); } -const GRState * -GRState::invalidateRegionsImpl(const MemRegion * const *Begin, - const MemRegion * const *End, - const Expr *E, unsigned Count, - StoreManager::InvalidatedSymbols &IS, - bool invalidateGlobals) const { - GRStateManager &Mgr = getStateManager(); +const ProgramState * +ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, + const Expr *E, unsigned Count, + StoreManager::InvalidatedSymbols &IS, + bool invalidateGlobals) const { + ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); if (Eng && Eng->wantsRegionChangeUpdate(this)) { - StoreManager::InvalidatedRegions Regions; + StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Begin, End, E, Count, IS, - invalidateGlobals, &Regions); - const GRState *newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, &IS, - &Regions.front(), - &Regions.back()+1); + = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, + invalidateGlobals, &Invalidated); + const ProgramState *newState = makeWithStore(newStore); + return Eng->processRegionChanges(newState, &IS, Regions, Invalidated); } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Begin, End, E, Count, IS, + Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, invalidateGlobals, NULL); return makeWithStore(newStore); } -const GRState *GRState::unbindLoc(Loc LV) const { +const ProgramState *ProgramState::unbindLoc(Loc LV) const { assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); Store OldStore = getStore(); @@ -187,20 +179,20 @@ const GRState *GRState::unbindLoc(Loc LV) const { return makeWithStore(newStore); } -const GRState *GRState::enterStackFrame(const StackFrameContext *frame) const { +const ProgramState *ProgramState::enterStackFrame(const StackFrameContext *frame) const { const StoreRef &new_store = getStateManager().StoreMgr->enterStackFrame(this, frame); return makeWithStore(new_store); } -SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { +SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { // We only want to do fetches from regions that we can actually bind // values. For example, SymbolicRegions of type 'id<...>' cannot // have direct bindings (but their can be bindings on their subregions). if (!R->isBoundable()) return UnknownVal(); - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { QualType T = TR->getValueType(); if (Loc::isLocType(T) || T->isIntegerType()) return getSVal(R); @@ -209,7 +201,7 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { return UnknownVal(); } -SVal GRState::getSVal(Loc location, QualType T) const { +SVal ProgramState::getSVal(Loc location, QualType T) const { SVal V = getRawSVal(cast<Loc>(location), T); // If 'V' is a symbolic value that is *perfectly* constrained to @@ -246,18 +238,18 @@ SVal GRState::getSVal(Loc location, QualType T) const { return V; } -const GRState *GRState::BindExpr(const Stmt* S, SVal V, bool Invalidate) const{ +const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidate) const{ Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V, Invalidate); if (NewEnv == Env) return this; - GRState NewSt = *this; + ProgramState NewSt = *this; NewSt.Env = NewEnv; return getStateManager().getPersistentState(NewSt); } -const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location, +const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal location, SVal V) const { Environment NewEnv = getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V); @@ -265,12 +257,12 @@ const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location, if (NewEnv == Env) return this; - GRState NewSt = *this; + ProgramState NewSt = *this; NewSt.Env = NewEnv; return getStateManager().getPersistentState(NewSt); } -const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx, +const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, bool Assumption) const { if (Idx.isUnknown() || UpperBound.isUnknown()) @@ -279,7 +271,7 @@ const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx, // Build an expression for 0 <= Idx < UpperBound. // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed. // FIXME: This should probably be part of SValBuilder. - GRStateManager &SM = getStateManager(); + ProgramStateManager &SM = getStateManager(); SValBuilder &svalBuilder = SM.getSValBuilder(); ASTContext &Ctx = svalBuilder.getContext(); @@ -315,8 +307,8 @@ const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx, return CM.assume(this, cast<DefinedSVal>(inBound), Assumption); } -const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { - GRState State(this, +const ProgramState *ProgramStateManager::getInitialState(const LocationContext *InitLoc) { + ProgramState State(this, EnvMgr.getInitialEnvironment(), StoreMgr->getInitialStore(InitLoc), GDMFactory.getEmptyMap()); @@ -324,49 +316,57 @@ const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { return getPersistentState(State); } -void GRStateManager::recycleUnusedStates() { - for (std::vector<GRState*>::iterator i = recentlyAllocatedStates.begin(), +void ProgramStateManager::recycleUnusedStates() { + for (std::vector<ProgramState*>::iterator i = recentlyAllocatedStates.begin(), e = recentlyAllocatedStates.end(); i != e; ++i) { - GRState *state = *i; + ProgramState *state = *i; if (state->referencedByExplodedNode()) continue; StateSet.RemoveNode(state); freeStates.push_back(state); - state->~GRState(); + state->~ProgramState(); } recentlyAllocatedStates.clear(); } -const GRState* GRStateManager::getPersistentState(GRState& State) { +const ProgramState *ProgramStateManager::getPersistentStateWithGDM( + const ProgramState *FromState, + const ProgramState *GDMState) { + ProgramState NewState = *FromState; + NewState.GDM = GDMState->GDM; + return getPersistentState(NewState); +} + +const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) { llvm::FoldingSetNodeID ID; State.Profile(ID); - void* InsertPos; + void *InsertPos; - if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) + if (ProgramState *I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) return I; - GRState *newState = 0; + ProgramState *newState = 0; if (!freeStates.empty()) { newState = freeStates.back(); freeStates.pop_back(); } else { - newState = (GRState*) Alloc.Allocate<GRState>(); + newState = (ProgramState*) Alloc.Allocate<ProgramState>(); } - new (newState) GRState(State); + new (newState) ProgramState(State); StateSet.InsertNode(newState, InsertPos); recentlyAllocatedStates.push_back(newState); return newState; } -const GRState* GRState::makeWithStore(const StoreRef &store) const { - GRState NewSt = *this; +const ProgramState *ProgramState::makeWithStore(const StoreRef &store) const { + ProgramState NewSt = *this; NewSt.setStore(store); return getStateManager().getPersistentState(NewSt); } -void GRState::setStore(const StoreRef &newStore) { +void ProgramState::setStore(const StoreRef &newStore) { Store newStoreStore = newStore.getStore(); if (newStoreStore) stateMgr->getStoreManager().incrementReferenceCount(newStoreStore); @@ -384,11 +384,11 @@ static bool IsEnvLoc(const Stmt *S) { return (bool) (((uintptr_t) S) & 0x1); } -void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, - const char* sep) const { +void ProgramState::print(raw_ostream &Out, CFG &C, + const char *NL, const char *Sep) const { // Print the store. - GRStateManager &Mgr = getStateManager(); - Mgr.getStoreManager().print(getStore(), Out, nl, sep); + ProgramStateManager &Mgr = getStateManager(); + Mgr.getStoreManager().print(getStore(), Out, NL, Sep); // Print Subexpression bindings. bool isFirst = true; @@ -399,10 +399,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, continue; if (isFirst) { - Out << nl << nl << "Sub-Expressions:" << nl; + Out << NL << NL << "Sub-Expressions:" << NL; isFirst = false; + } else { + Out << NL; } - else { Out << nl; } Out << " (" << (void*) I.getKey() << ") "; LangOptions LO; // FIXME. @@ -418,10 +419,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, continue; if (isFirst) { - Out << nl << nl << "Block-level Expressions:" << nl; + Out << NL << NL << "Block-level Expressions:" << NL; isFirst = false; + } else { + Out << NL; } - else { Out << nl; } Out << " (" << (void*) I.getKey() << ") "; LangOptions LO; // FIXME. @@ -437,10 +439,11 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, continue; if (isFirst) { - Out << nl << nl << "Load/store locations:" << nl; + Out << NL << NL << "Load/store locations:" << NL; isFirst = false; + } else { + Out << NL; } - else { Out << nl; } const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1)); @@ -450,20 +453,17 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, Out << " : " << I.getData(); } - Mgr.getConstraintManager().print(this, Out, nl, sep); + Mgr.getConstraintManager().print(this, Out, NL, Sep); // Print checker-specific data. - for (std::vector<Printer*>::iterator I = Mgr.Printers.begin(), - E = Mgr.Printers.end(); I != E; ++I) { - (*I)->Print(Out, this, nl, sep); - } + Mgr.getOwningEngine()->printState(Out, this, NL, Sep); } -void GRState::printDOT(llvm::raw_ostream& Out, CFG &C) const { +void ProgramState::printDOT(raw_ostream &Out, CFG &C) const { print(Out, C, "\\l", "\\|"); } -void GRState::printStdErr(CFG &C) const { +void ProgramState::printStdErr(CFG &C) const { print(llvm::errs(), C); } @@ -471,13 +471,13 @@ void GRState::printStdErr(CFG &C) const { // Generic Data Map. //===----------------------------------------------------------------------===// -void* const* GRState::FindGDM(void* K) const { +void *const* ProgramState::FindGDM(void *K) const { return GDM.lookup(K); } void* -GRStateManager::FindGDMContext(void* K, - void* (*CreateContext)(llvm::BumpPtrAllocator&), +ProgramStateManager::FindGDMContext(void *K, + void *(*CreateContext)(llvm::BumpPtrAllocator&), void (*DeleteContext)(void*)) { std::pair<void*, void (*)(void*)>& p = GDMContexts[K]; @@ -489,58 +489,30 @@ GRStateManager::FindGDMContext(void* K, return p.first; } -const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ - GRState::GenericDataMap M1 = St->getGDM(); - GRState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); +const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Key, void *Data){ + ProgramState::GenericDataMap M1 = St->getGDM(); + ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); if (M1 == M2) return St; - GRState NewSt = *St; + ProgramState NewSt = *St; NewSt.GDM = M2; return getPersistentState(NewSt); } -const GRState *GRStateManager::removeGDM(const GRState *state, void *Key) { - GRState::GenericDataMap OldM = state->getGDM(); - GRState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); +const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, void *Key) { + ProgramState::GenericDataMap OldM = state->getGDM(); + ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); if (NewM == OldM) return state; - GRState NewState = *state; + ProgramState NewState = *state; NewState.GDM = NewM; return getPersistentState(NewState); } -//===----------------------------------------------------------------------===// -// Utility. -//===----------------------------------------------------------------------===// - -namespace { -class ScanReachableSymbols : public SubRegionMap::Visitor { - typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy; - - VisitedRegionsTy visited; - const GRState *state; - SymbolVisitor &visitor; - llvm::OwningPtr<SubRegionMap> SRM; -public: - - ScanReachableSymbols(const GRState *st, SymbolVisitor& v) - : state(st), visitor(v) {} - - bool scan(nonloc::CompoundVal val); - bool scan(SVal val); - bool scan(const MemRegion *R); - - // From SubRegionMap::Visitor. - bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) { - return scan(SubRegion); - } -}; -} - bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) if (!scan(*I)) @@ -549,6 +521,33 @@ bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { return true; } +bool ScanReachableSymbols::scan(const SymExpr *sym) { + unsigned &isVisited = visited[sym]; + if (isVisited) + return true; + isVisited = 1; + + if (const SymbolData *sData = dyn_cast<SymbolData>(sym)) + if (!visitor.VisitSymbol(sData)) + return false; + + switch (sym->getKind()) { + case SymExpr::RegionValueKind: + case SymExpr::ConjuredKind: + case SymExpr::DerivedKind: + case SymExpr::ExtentKind: + case SymExpr::MetadataKind: + break; + case SymExpr::SymIntKind: + return scan(cast<SymIntExpr>(sym)->getLHS()); + case SymExpr::SymSymKind: { + const SymSymExpr *x = cast<SymSymExpr>(sym); + return scan(x->getLHS()) && scan(x->getRHS()); + } + } + return true; +} + bool ScanReachableSymbols::scan(SVal val) { if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) return scan(X->getRegion()); @@ -557,7 +556,10 @@ bool ScanReachableSymbols::scan(SVal val) { return scan(X->getLoc()); if (SymbolRef Sym = val.getAsSymbol()) - return visitor.VisitSymbol(Sym); + return scan(Sym); + + if (const SymExpr *Sym = val.getAsSymbolicExpression()) + return scan(Sym); if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val)) return scan(*X); @@ -566,10 +568,13 @@ bool ScanReachableSymbols::scan(SVal val) { } bool ScanReachableSymbols::scan(const MemRegion *R) { - if (isa<MemSpaceRegion>(R) || visited.count(R)) + if (isa<MemSpaceRegion>(R)) return true; - - visited.insert(R); + + unsigned &isVisited = visited[R]; + if (isVisited) + return true; + isVisited = 1; // If this is a symbolic region, visit the symbol for the region. if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) @@ -593,12 +598,12 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { return SRM->iterSubRegions(R, *this); } -bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { +bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { ScanReachableSymbols S(this, visitor); return S.scan(val); } -bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, +bool ProgramState::scanReachableSymbols(const SVal *I, const SVal *E, SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); for ( ; I != E; ++I) { @@ -608,7 +613,7 @@ bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, return true; } -bool GRState::scanReachableSymbols(const MemRegion * const *I, +bool ProgramState::scanReachableSymbols(const MemRegion * const *I, const MemRegion * const *E, SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 389fff5ed593..9337788535ab 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -8,14 +8,13 @@ //===----------------------------------------------------------------------===// // // This file defines RangeConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. +// equality and inequality constraints on symbolic values of ProgramState. // //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" @@ -170,7 +169,7 @@ public: return newRanges; } - void print(llvm::raw_ostream &os) const { + void print(raw_ostream &os) const { bool isFirst = true; os << "{ "; for (iterator i = begin(), e = end(); i != e; ++i) { @@ -196,55 +195,55 @@ typedef llvm::ImmutableMap<SymbolRef,RangeSet> ConstraintRangeTy; namespace clang { namespace ento { template<> -struct GRStateTrait<ConstraintRange> - : public GRStatePartialTrait<ConstraintRangeTy> { - static inline void* GDMIndex() { return &ConstraintRangeIndex; } +struct ProgramStateTrait<ConstraintRange> + : public ProgramStatePartialTrait<ConstraintRangeTy> { + static inline void *GDMIndex() { return &ConstraintRangeIndex; } }; } } namespace { class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(const GRState *state, SymbolRef sym); + RangeSet GetRange(const ProgramState *state, SymbolRef sym); public: RangeConstraintManager(SubEngine &subengine) : SimpleConstraintManager(subengine) {} - const GRState *assumeSymNE(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const GRState *assumeSymEQ(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const GRState *assumeSymLT(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const GRState *assumeSymGT(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const GRState *assumeSymGE(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const GRState *assumeSymLE(const GRState* state, SymbolRef sym, + const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; + const llvm::APSInt* getSymVal(const ProgramState *St, SymbolRef sym) const; // FIXME: Refactor into SimpleConstraintManager? - bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const { + bool isEqual(const ProgramState *St, SymbolRef sym, const llvm::APSInt& V) const { const llvm::APSInt *i = getSymVal(St, sym); return i ? *i == V : false; } - const GRState* removeDeadBindings(const GRState* St, SymbolReaper& SymReaper); + const ProgramState *removeDeadBindings(const ProgramState *St, SymbolReaper& SymReaper); - void print(const GRState* St, llvm::raw_ostream& Out, + void print(const ProgramState *St, raw_ostream &Out, const char* nl, const char *sep); private: @@ -253,12 +252,12 @@ private: } // end anonymous namespace -ConstraintManager* ento::CreateRangeConstraintManager(GRStateManager&, +ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&, SubEngine &subeng) { return new RangeConstraintManager(subeng); } -const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, +const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St, SymbolRef sym) const { const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym); return T ? T->getConcreteValue() : NULL; @@ -266,8 +265,8 @@ const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -RangeConstraintManager::removeDeadBindings(const GRState* state, +const ProgramState* +RangeConstraintManager::removeDeadBindings(const ProgramState *state, SymbolReaper& SymReaper) { ConstraintRangeTy CR = state->get<ConstraintRange>(); @@ -283,7 +282,7 @@ RangeConstraintManager::removeDeadBindings(const GRState* state, } RangeSet -RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { +RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) { if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym)) return *V; @@ -306,8 +305,8 @@ RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { // As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, // UINT_MAX, 0, 1, and 2. -const GRState* -RangeConstraintManager::assumeSymNE(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -323,8 +322,8 @@ RangeConstraintManager::assumeSymNE(const GRState* state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const GRState* -RangeConstraintManager::assumeSymEQ(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { // [Int-Adjustment, Int-Adjustment] @@ -334,8 +333,8 @@ RangeConstraintManager::assumeSymEQ(const GRState* state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const GRState* -RangeConstraintManager::assumeSymLT(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -355,8 +354,8 @@ RangeConstraintManager::assumeSymLT(const GRState* state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const GRState* -RangeConstraintManager::assumeSymGT(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -376,8 +375,8 @@ RangeConstraintManager::assumeSymGT(const GRState* state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const GRState* -RangeConstraintManager::assumeSymGE(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -398,8 +397,8 @@ RangeConstraintManager::assumeSymGE(const GRState* state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const GRState* -RangeConstraintManager::assumeSymLE(const GRState* state, SymbolRef sym, +const ProgramState* +RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -424,7 +423,7 @@ RangeConstraintManager::assumeSymLE(const GRState* state, SymbolRef sym, // Pretty-printing. //===------------------------------------------------------------------------===/ -void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out, +void RangeConstraintManager::print(const ProgramState *St, raw_ostream &Out, const char* nl, const char *sep) { ConstraintRangeTy Ranges = St->get<ConstraintRange>(); diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 23dd6416a8f1..4b76cf1a3deb 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -20,8 +20,8 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" @@ -94,7 +94,7 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) { namespace llvm { static inline - llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { + raw_ostream &operator<<(raw_ostream &os, BindingKey K) { os << '(' << K.getRegion() << ',' << K.getOffset() << ',' << (K.isDirect() ? "direct" : "default") << ')'; @@ -157,7 +157,7 @@ public: return false; } - void process(llvm::SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R); + void process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R); ~RegionStoreSubRegionMap() {} @@ -183,7 +183,7 @@ public: }; void -RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL, +RegionStoreSubRegionMap::process(SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R) { const MemRegion *superR = R->getSuperRegion(); if (add(superR, R)) @@ -196,7 +196,7 @@ class RegionStoreManager : public StoreManager { RegionBindings::Factory RBFactory; public: - RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f) + RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) : StoreManager(mgr), Features(f), RBFactory(mgr.getAllocator()) {} @@ -236,13 +236,11 @@ public: // Binding values to regions. //===-------------------------------------------------------------------===// - StoreRef invalidateRegions(Store store, - const MemRegion * const *Begin, - const MemRegion * const *End, + StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, InvalidatedSymbols &IS, bool invalidateGlobals, - InvalidatedRegions *Regions); + InvalidatedRegions *Invalidated); public: // Made public for helper classes. @@ -278,7 +276,7 @@ public: // Part of public interface to class. return StoreRef(addBinding(B, R, BindingKey::Default, V).getRootWithoutRetain(), *this); } - StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr* CL, + StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr *CL, const LocationContext *LC, SVal V); StoreRef BindDecl(Store store, const VarRegion *VR, SVal InitVal); @@ -288,9 +286,9 @@ public: // Part of public interface to class. } /// BindStruct - Bind a compound value to a structure. - StoreRef BindStruct(Store store, const TypedRegion* R, SVal V); + StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V); - StoreRef BindArray(Store store, const TypedRegion* R, SVal V); + StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V); /// KillStruct - Set the entire struct to unknown. StoreRef KillStruct(Store store, const TypedRegion* R, SVal DefaultVal); @@ -307,6 +305,8 @@ public: // Part of public interface to class. void decrementReferenceCount(Store store) { GetRegionBindings(store).manualRelease(); } + + bool includedInBindings(Store store, const MemRegion *region) const; //===------------------------------------------------------------------===// // Loading values from regions. @@ -333,9 +333,9 @@ public: // Part of public interface to class. SVal RetrieveVar(Store store, const VarRegion *R); - SVal RetrieveLazySymbol(const TypedRegion *R); + SVal RetrieveLazySymbol(const TypedValueRegion *R); - SVal RetrieveFieldOrElementCommon(Store store, const TypedRegion *R, + SVal RetrieveFieldOrElementCommon(Store store, const TypedValueRegion *R, QualType Ty, const MemRegion *superR); SVal RetrieveLazyBinding(const MemRegion *lazyBindingRegion, @@ -346,15 +346,16 @@ public: // Part of public interface to class. /// struct s x, y; /// x = y; /// y's value is retrieved by this method. - SVal RetrieveStruct(Store store, const TypedRegion* R); + SVal RetrieveStruct(Store store, const TypedValueRegion* R); - SVal RetrieveArray(Store store, const TypedRegion* R); + SVal RetrieveArray(Store store, const TypedValueRegion* R); /// Used to lazily generate derived symbols for bindings that are defined /// implicitly by default bindings in a super region. Optional<SVal> RetrieveDerivedDefaultValue(RegionBindings B, const MemRegion *superR, - const TypedRegion *R, QualType Ty); + const TypedValueRegion *R, + QualType Ty); /// Get the state and region whose binding this region R corresponds to. std::pair<Store, const MemRegion*> @@ -371,17 +372,17 @@ public: // Part of public interface to class. /// removeDeadBindings - Scans the RegionStore of 'state' for dead values. /// It returns a new Store with these values removed. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); + SymbolReaper& SymReaper); - StoreRef enterStackFrame(const GRState *state, const StackFrameContext *frame); + StoreRef enterStackFrame(const ProgramState *state, + const StackFrameContext *frame); //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// // FIXME: This method will soon be eliminated; see the note in Store.h. - DefinedOrUnknownSVal getSizeInElements(const GRState *state, + DefinedOrUnknownSVal getSizeInElements(const ProgramState *state, const MemRegion* R, QualType EleTy); //===------------------------------------------------------------------===// @@ -392,7 +393,7 @@ public: // Part of public interface to class. return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); } - void print(Store store, llvm::raw_ostream& Out, const char* nl, + void print(Store store, raw_ostream &Out, const char* nl, const char *sep); void iterBindings(Store store, BindingsHandler& f) { @@ -416,12 +417,12 @@ public: // Part of public interface to class. // RegionStore creation. //===----------------------------------------------------------------------===// -StoreManager *ento::CreateRegionStoreManager(GRStateManager& StMgr) { +StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) { RegionStoreFeatures F = maximal_features_tag(); return new RegionStoreManager(StMgr, F); } -StoreManager *ento::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { +StoreManager *ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); return new RegionStoreManager(StMgr, F); @@ -433,7 +434,7 @@ RegionStoreManager::getRegionStoreSubRegionMap(Store store) { RegionBindings B = GetRegionBindings(store); RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); - llvm::SmallVector<const SubRegion*, 10> WL; + SmallVector<const SubRegion*, 10> WL; for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) @@ -461,7 +462,7 @@ protected: typedef BumpVector<BindingKey> RegionCluster; typedef llvm::DenseMap<const MemRegion *, RegionCluster *> ClusterMap; llvm::DenseMap<const RegionCluster*, unsigned> Visited; - typedef llvm::SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10> + typedef SmallVector<std::pair<const MemRegion *, RegionCluster*>, 10> WorkList; BumpVectorContext BVC; @@ -477,7 +478,7 @@ protected: const bool includeGlobals; public: - ClusterAnalysis(RegionStoreManager &rm, GRStateManager &StateMgr, + ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, RegionBindings b, const bool includeGlobals) : RM(rm), Ctx(StateMgr.getContext()), svalBuilder(StateMgr.getSValBuilder()), @@ -590,7 +591,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> StoreManager::InvalidatedRegions *Regions; public: invalidateRegionsWorker(RegionStoreManager &rm, - GRStateManager &stateMgr, + ProgramStateManager &stateMgr, RegionBindings b, const Expr *ex, unsigned count, StoreManager::InvalidatedSymbols &is, @@ -681,7 +682,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { if (!baseR->isBoundable()) return; - const TypedRegion *TR = cast<TypedRegion>(baseR); + const TypedValueRegion *TR = cast<TypedValueRegion>(baseR); QualType T = TR->getValueType(); // Invalidate the binding. @@ -718,21 +719,21 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { } StoreRef RegionStoreManager::invalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *E, + ArrayRef<const MemRegion *> Regions, const Expr *Ex, unsigned Count, InvalidatedSymbols &IS, bool invalidateGlobals, - InvalidatedRegions *Regions) { + InvalidatedRegions *Invalidated) { invalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), - Ex, Count, IS, Regions, invalidateGlobals); + Ex, Count, IS, Invalidated, invalidateGlobals); // Scan the bindings and generate the clusters. W.GenerateClusters(); - // Add I .. E to the worklist. - for ( ; I != E; ++I) + // Add the regions to the worklist. + for (ArrayRef<const MemRegion *>::iterator + I = Regions.begin(), E = Regions.end(); I != E; ++I) W.AddToWorkList(*I); W.RunWorkList(); @@ -752,8 +753,8 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Even if there are no bindings in the global scope, we still need to // record that we touched it. - if (Regions) - Regions->push_back(GS); + if (Invalidated) + Invalidated->push_back(GS); } return StoreRef(B.getRootWithoutRetain(), *this); @@ -763,7 +764,7 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Extents for regions. //===----------------------------------------------------------------------===// -DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, +DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const ProgramState *state, const MemRegion *R, QualType EleTy) { SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder); @@ -803,7 +804,7 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { return UnknownVal(); const MemRegion* R = cast<loc::MemRegionVal>(&Array)->getRegion(); - const TypedRegion* ArrayR = dyn_cast<TypedRegion>(R); + const TypedValueRegion* ArrayR = dyn_cast<TypedValueRegion>(R); if (!ArrayR) return UnknownVal(); @@ -852,7 +853,7 @@ Optional<SVal> RegionStoreManager::getDirectBinding(RegionBindings B, Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, const MemRegion *R) { if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) if (TR->getValueType()->isUnionType()) return UnknownVal(); @@ -890,13 +891,12 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { } if (isa<CodeTextRegion>(MR)) { - assert(0 && "Why load from a code text region?"); - return UnknownVal(); + llvm_unreachable("Why load from a code text region?"); } // FIXME: Perhaps this method should just take a 'const MemRegion*' argument // instead of 'Loc', and have the other Loc cases handled at a higher level. - const TypedRegion *R = cast<TypedRegion>(MR); + const TypedValueRegion *R = cast<TypedValueRegion>(MR); QualType RTy = R->getValueType(); // FIXME: We should eventually handle funny addressing. e.g.: @@ -1042,6 +1042,10 @@ SVal RegionStoreManager::RetrieveElement(Store store, SVal Idx = R->getIndex(); if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { int64_t i = CI->getValue().getSExtValue(); + // Abort on string underrun. This can be possible by arbitrary + // clients of RetrieveElement(). + if (i < 0) + return UndefinedVal(); int64_t byteLength = Str->getByteLength(); // Technically, only i == byteLength is guaranteed to be null. // However, such overflows should be caught before reaching this point; @@ -1068,7 +1072,8 @@ SVal RegionStoreManager::RetrieveElement(Store store, if (!O.getRegion()) return UnknownVal(); - if (const TypedRegion *baseR = dyn_cast_or_null<TypedRegion>(O.getRegion())) { + if (const TypedValueRegion *baseR = + dyn_cast_or_null<TypedValueRegion>(O.getRegion())) { QualType baseT = baseR->getValueType(); if (baseT->isScalarType()) { QualType elemT = R->getElementType(); @@ -1106,7 +1111,7 @@ SVal RegionStoreManager::RetrieveField(Store store, Optional<SVal> RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, const MemRegion *superR, - const TypedRegion *R, + const TypedValueRegion *R, QualType Ty) { if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { @@ -1124,7 +1129,7 @@ RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, if (isa<nonloc::LazyCompoundVal>(val)) return Optional<SVal>(); - assert(0 && "Unknown default value"); + llvm_unreachable("Unknown default value"); } return Optional<SVal>(); @@ -1140,7 +1145,7 @@ SVal RegionStoreManager::RetrieveLazyBinding(const MemRegion *lazyBindingRegion, } SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, - const TypedRegion *R, + const TypedValueRegion *R, QualType Ty, const MemRegion *superR) { @@ -1175,7 +1180,8 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. - if (const TypedRegion *typedSuperR = dyn_cast<TypedRegion>(superR)) { + if (const TypedValueRegion *typedSuperR = + dyn_cast<TypedValueRegion>(superR)) { if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); } @@ -1264,22 +1270,41 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { return UndefinedVal(); } -SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) { +SVal RegionStoreManager::RetrieveLazySymbol(const TypedValueRegion *R) { // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) { +SVal RegionStoreManager::RetrieveStruct(Store store, + const TypedValueRegion* R) { QualType T = R->getValueType(); assert(T->isStructureOrClassType()); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } -SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) { +SVal RegionStoreManager::RetrieveArray(Store store, + const TypedValueRegion * R) { assert(Ctx.getAsConstantArrayType(R->getValueType())); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } +bool RegionStoreManager::includedInBindings(Store store, + const MemRegion *region) const { + RegionBindings B = GetRegionBindings(store); + region = region->getBaseRegion(); + + for (RegionBindings::iterator it = B.begin(), ei = B.end(); it != ei; ++it) { + const BindingKey &K = it.getKey(); + if (region == K.getRegion()) + return true; + const SVal &D = it.getData(); + if (const MemRegion *r = D.getAsRegion()) + if (r == region) + return true; + } + return false; +} + //===----------------------------------------------------------------------===// // Binding values to regions. //===----------------------------------------------------------------------===// @@ -1302,14 +1327,14 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); // Check if the region is a struct region. - if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) + if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) if (TR->getValueType()->isStructureOrClassType()) return BindStruct(store, TR, V); if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { if (ER->getIndex().isZeroConstant()) { - if (const TypedRegion *superR = - dyn_cast<TypedRegion>(ER->getSuperRegion())) { + if (const TypedValueRegion *superR = + dyn_cast<TypedValueRegion>(ER->getSuperRegion())) { QualType superTy = superR->getValueType(); // For now, just invalidate the fields of the struct/union/class. // This is for test rdar_test_7185607 in misc-ps-region-store.m. @@ -1389,7 +1414,7 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, V).getRootWithoutRetain(), *this); } -StoreRef RegionStoreManager::BindArray(Store store, const TypedRegion* R, +StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, SVal Init) { const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); @@ -1448,7 +1473,7 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedRegion* R, return newStore; } -StoreRef RegionStoreManager::BindStruct(Store store, const TypedRegion* R, +StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, SVal V) { if (!Features.supportsFields()) @@ -1458,9 +1483,9 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedRegion* R, assert(T->isStructureOrClassType()); const RecordType* RT = T->getAs<RecordType>(); - RecordDecl* RD = RT->getDecl(); + RecordDecl *RD = RT->getDecl(); - if (!RD->isDefinition()) + if (!RD->isCompleteDefinition()) return StoreRef(store, *this); // Handle lazy compound values. @@ -1611,12 +1636,12 @@ RegionBindings RegionStoreManager::removeBinding(RegionBindings B, namespace { class removeDeadBindingsWorker : public ClusterAnalysis<removeDeadBindingsWorker> { - llvm::SmallVector<const SymbolicRegion*, 12> Postponed; + SmallVector<const SymbolicRegion*, 12> Postponed; SymbolReaper &SymReaper; const StackFrameContext *CurrentLCtx; public: - removeDeadBindingsWorker(RegionStoreManager &rm, GRStateManager &stateMgr, + removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, RegionBindings b, SymbolReaper &symReaper, const StackFrameContext *LCtx) : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, @@ -1736,7 +1761,7 @@ bool removeDeadBindingsWorker::UpdatePostponed() { // having done a scan. bool changed = false; - for (llvm::SmallVectorImpl<const SymbolicRegion*>::iterator + for (SmallVectorImpl<const SymbolicRegion*>::iterator I = Postponed.begin(), E = Postponed.end() ; I != E ; ++I) { if (const SymbolicRegion *SR = cast_or_null<SymbolicRegion>(*I)) { if (SymReaper.isLive(SR->getSymbol())) { @@ -1751,17 +1776,16 @@ bool removeDeadBindingsWorker::UpdatePostponed() { StoreRef RegionStoreManager::removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) -{ + SymbolReaper& SymReaper) { RegionBindings B = GetRegionBindings(store); removeDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); W.GenerateClusters(); // Enqueue the region roots onto the worklist. - for (llvm::SmallVectorImpl<const MemRegion*>::iterator I=RegionRoots.begin(), - E=RegionRoots.end(); I!=E; ++I) + for (SymbolReaper::region_iterator I = SymReaper.region_begin(), + E = SymReaper.region_end(); I != E; ++I) { W.AddToWorkList(*I); + } do W.RunWorkList(); while (W.UpdatePostponed()); @@ -1792,7 +1816,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, } -StoreRef RegionStoreManager::enterStackFrame(const GRState *state, +StoreRef RegionStoreManager::enterStackFrame(const ProgramState *state, const StackFrameContext *frame) { FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(), @@ -1831,7 +1855,7 @@ StoreRef RegionStoreManager::enterStackFrame(const GRState *state, // Utility methods. //===----------------------------------------------------------------------===// -void RegionStoreManager::print(Store store, llvm::raw_ostream& OS, +void RegionStoreManager::print(Store store, raw_ostream &OS, const char* nl, const char *sep) { RegionBindings B = GetRegionBindings(store); OS << "Store (direct and default bindings):" << nl; diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 71f2b4abb9c3..ebf7ae2fd4b1 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -15,7 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" using namespace clang; @@ -70,7 +70,7 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { } DefinedOrUnknownSVal -SValBuilder::getRegionValueSymbolVal(const TypedRegion* region) { +SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { QualType T = region->getValueType(); if (!SymbolManager::canSymbolicate(T)) @@ -133,7 +133,7 @@ DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, DefinedOrUnknownSVal SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, - const TypedRegion *region) { + const TypedValueRegion *region) { QualType T = region->getValueType(); if (!SymbolManager::canSymbolicate(T)) @@ -147,7 +147,7 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, return nonloc::SymbolVal(sym); } -DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl* func) { +DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { return loc::MemRegionVal(MemMgr.getFunctionTextRegion(func)); } @@ -162,7 +162,7 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, //===----------------------------------------------------------------------===// -SVal SValBuilder::evalBinOp(const GRState *state, BinaryOperator::Opcode op, +SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type) { if (lhs.isUndef() || rhs.isUndef()) @@ -190,7 +190,7 @@ SVal SValBuilder::evalBinOp(const GRState *state, BinaryOperator::Opcode op, return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type); } -DefinedOrUnknownSVal SValBuilder::evalEQ(const GRState *state, +DefinedOrUnknownSVal SValBuilder::evalEQ(const ProgramState *state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs, @@ -213,8 +213,13 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { return UnknownVal(); // Check for casts from integers to integers. - if (castTy->isIntegerType() && originalTy->isIntegerType()) - return evalCastFromNonLoc(cast<NonLoc>(val), castTy); + if (castTy->isIntegerType() && originalTy->isIntegerType()) { + if (isa<Loc>(val)) + // This can be a cast to ObjC property of type int. + return evalCastFromLoc(cast<Loc>(val), castTy); + else + return evalCastFromNonLoc(cast<NonLoc>(val), castTy); + } // Check for casts from pointers to integers. if (castTy->isIntegerType() && Loc::isLocType(originalTy)) diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 4614e349dece..b5980b96938a 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -12,14 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/IdentifierTable.h" - using namespace clang; using namespace ento; -using llvm::dyn_cast; -using llvm::cast; using llvm::APSInt; //===----------------------------------------------------------------------===// @@ -146,7 +143,7 @@ SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { while (!isa<SymbolData>(itr.back())) expand(); } -SVal::symbol_iterator& SVal::symbol_iterator::operator++() { +SVal::symbol_iterator &SVal::symbol_iterator::operator++() { assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); assert(isa<SymbolData>(itr.back())); itr.pop_back(); @@ -174,7 +171,7 @@ void SVal::symbol_iterator::expand() { return; } - assert(false && "unhandled expansion case"); + llvm_unreachable("unhandled expansion case"); } const void *nonloc::LazyCompoundVal::getStore() const { @@ -270,7 +267,7 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, void SVal::dump() const { dumpToStream(llvm::errs()); } -void SVal::dumpToStream(llvm::raw_ostream& os) const { +void SVal::dumpToStream(raw_ostream &os) const { switch (getBaseKind()) { case UnknownKind: os << "Unknown"; @@ -289,7 +286,7 @@ void SVal::dumpToStream(llvm::raw_ostream& os) const { } } -void NonLoc::dumpToStream(llvm::raw_ostream& os) const { +void NonLoc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case nonloc::ConcreteIntKind: { const nonloc::ConcreteInt& C = *cast<nonloc::ConcreteInt>(this); @@ -344,7 +341,7 @@ void NonLoc::dumpToStream(llvm::raw_ostream& os) const { } } -void Loc::dumpToStream(llvm::raw_ostream& os) const { +void Loc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case loc::ConcreteIntKind: os << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() << " (Loc)"; @@ -372,7 +369,6 @@ void Loc::dumpToStream(llvm::raw_ostream& os) const { break; } default: - assert(false && "Pretty-printing not implemented for this Loc."); - break; + llvm_unreachable("Pretty-printing not implemented for this Loc."); } } diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 20762e07dd22..79d8b8bf9de3 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -14,7 +14,7 @@ #include "SimpleConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" namespace clang { @@ -56,7 +56,7 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { return true; } -const GRState *SimpleConstraintManager::assume(const GRState *state, +const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, DefinedSVal Cond, bool Assumption) { if (isa<NonLoc>(Cond)) @@ -65,13 +65,13 @@ const GRState *SimpleConstraintManager::assume(const GRState *state, return assume(state, cast<Loc>(Cond), Assumption); } -const GRState *SimpleConstraintManager::assume(const GRState *state, Loc cond, +const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, Loc cond, bool assumption) { state = assumeAux(state, cond, assumption); return SU.processAssume(state, cond, assumption); } -const GRState *SimpleConstraintManager::assumeAux(const GRState *state, +const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, Loc Cond, bool Assumption) { BasicValueFactory &BasicVals = state->getBasicVals(); @@ -113,7 +113,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state, } // end switch } -const GRState *SimpleConstraintManager::assume(const GRState *state, +const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, NonLoc cond, bool assumption) { state = assumeAux(state, cond, assumption); @@ -125,7 +125,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { // the only place it's used. (This code was copied from SimpleSValBuilder.cpp.) switch (op) { default: - assert(false && "Invalid opcode."); + llvm_unreachable("Invalid opcode."); case BO_LT: return BO_GE; case BO_GT: return BO_LE; case BO_LE: return BO_GT; @@ -135,7 +135,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } } -const GRState *SimpleConstraintManager::assumeAux(const GRState *state, +const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption) { @@ -152,7 +152,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state, switch (Cond.getSubKind()) { default: - assert(false && "'Assume' not implemented for this NonLoc"); + llvm_unreachable("'Assume' not implemented for this NonLoc"); case nonloc::SymbolValKind: { nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond); @@ -202,7 +202,7 @@ const GRState *SimpleConstraintManager::assumeAux(const GRState *state, } // end switch } -const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state, +const ProgramState *SimpleConstraintManager::assumeSymRel(const ProgramState *state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int) { @@ -256,7 +256,7 @@ const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state, // be of the same type as the symbol, which is not always correct. Really the // comparisons should be performed using the Int's type, then mapped back to // the symbol's range of values. - GRStateManager &StateMgr = state->getStateManager(); + ProgramStateManager &StateMgr = state->getStateManager(); ASTContext &Ctx = StateMgr.getContext(); QualType T = Sym->getType(Ctx); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index a2952afa7351..d4295d42d95e 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -15,7 +15,7 @@ #define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" namespace clang { @@ -33,14 +33,14 @@ public: bool canReasonAbout(SVal X) const; - const GRState *assume(const GRState *state, DefinedSVal Cond, + const ProgramState *assume(const ProgramState *state, DefinedSVal Cond, bool Assumption); - const GRState *assume(const GRState *state, Loc Cond, bool Assumption); + const ProgramState *assume(const ProgramState *state, Loc Cond, bool Assumption); - const GRState *assume(const GRState *state, NonLoc Cond, bool Assumption); + const ProgramState *assume(const ProgramState *state, NonLoc Cond, bool Assumption); - const GRState *assumeSymRel(const GRState *state, + const ProgramState *assumeSymRel(const ProgramState *state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int); @@ -53,27 +53,27 @@ protected: // Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison // operation for the method being invoked. - virtual const GRState *assumeSymNE(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const GRState *assumeSymEQ(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const GRState *assumeSymLT(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const GRState *assumeSymGT(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const GRState *assumeSymLE(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const GRState *assumeSymGE(const GRState *state, SymbolRef sym, + virtual const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; @@ -81,9 +81,9 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// - const GRState *assumeAux(const GRState *state, Loc Cond,bool Assumption); + const ProgramState *assumeAux(const ProgramState *state, Loc Cond,bool Assumption); - const GRState *assumeAux(const GRState *state, NonLoc Cond, bool Assumption); + const ProgramState *assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption); }; } // end GR namespace diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 80c18a335fde..bd63ecf775d3 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; using namespace ento; @@ -25,22 +25,22 @@ protected: public: SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, - GRStateManager &stateMgr) + ProgramStateManager &stateMgr) : SValBuilder(alloc, context, stateMgr) {} virtual ~SimpleSValBuilder() {} virtual SVal evalMinus(NonLoc val); virtual SVal evalComplement(NonLoc val); - virtual SVal evalBinOpNN(const GRState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal evalBinOpLL(const GRState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy); - virtual SVal evalBinOpLN(const GRState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy); /// getKnownValue - evaluates a given SVal. If the SVal has only one possible /// (integer) value, that value is returned. Otherwise, returns NULL. - virtual const llvm::APSInt *getKnownValue(const GRState *state, SVal V); + virtual const llvm::APSInt *getKnownValue(const ProgramState *state, SVal V); SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, QualType resultTy); @@ -49,7 +49,7 @@ public: SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, - GRStateManager &stateMgr) { + ProgramStateManager &stateMgr) { return new SimpleSValBuilder(alloc, context, stateMgr); } @@ -171,7 +171,7 @@ SVal SimpleSValBuilder::evalComplement(NonLoc X) { static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { switch (op) { default: - assert(false && "Invalid opcode."); + llvm_unreachable("Invalid opcode."); case BO_LT: return BO_GE; case BO_GT: return BO_LE; case BO_LE: return BO_GT; @@ -184,7 +184,7 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { switch (op) { default: - assert(false && "Invalid opcode."); + llvm_unreachable("Invalid opcode."); case BO_LT: return BO_GT; case BO_GT: return BO_LT; case BO_LE: return BO_GE; @@ -270,7 +270,7 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, return makeNonLoc(LHS, op, RHS, resultTy); } -SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, +SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) { @@ -347,8 +347,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, break; case BO_LAnd: case BO_LOr: - assert(false && "Logical operators handled by branching logic."); - return UnknownVal(); + llvm_unreachable("Logical operators handled by branching logic."); case BO_Assign: case BO_MulAssign: case BO_DivAssign: @@ -361,12 +360,10 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, case BO_XorAssign: case BO_OrAssign: case BO_Comma: - assert(false && "'=' and ',' operators handled by ExprEngine."); - return UnknownVal(); + llvm_unreachable("'=' and ',' operators handled by ExprEngine."); case BO_PtrMemD: case BO_PtrMemI: - assert(false && "Pointer arithmetic not handled here."); - return UnknownVal(); + llvm_unreachable("Pointer arithmetic not handled here."); case BO_LT: case BO_GT: case BO_LE: @@ -539,7 +536,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, } // FIXME: all this logic will change if/when we have MemRegion::getLocation(). -SVal SimpleSValBuilder::evalBinOpLL(const GRState *state, +SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy) { @@ -556,8 +553,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state, if (lhs == rhs) { switch (op) { default: - assert(false && "Unimplemented operation for two identical values"); - return UnknownVal(); + llvm_unreachable("Unimplemented operation for two identical values"); case BO_Sub: return makeZeroVal(resultTy); case BO_EQ: @@ -573,8 +569,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state, switch (lhs.getSubKind()) { default: - assert(false && "Ordering not implemented for this Loc."); - return UnknownVal(); + llvm_unreachable("Ordering not implemented for this Loc."); case loc::GotoLabelKind: // The only thing we know about labels is that they're non-null. @@ -827,7 +822,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state, return makeTruthVal(!leftFirst, resultTy); } - assert(false && "Fields not found in parent record's definition"); + llvm_unreachable("Fields not found in parent record's definition"); } // If we get here, we have no way of comparing the regions. @@ -836,7 +831,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const GRState *state, } } -SVal SimpleSValBuilder::evalBinOpLN(const GRState *state, +SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) { @@ -930,7 +925,7 @@ SVal SimpleSValBuilder::evalBinOpLN(const GRState *state, return UnknownVal(); } -const llvm::APSInt *SimpleSValBuilder::getKnownValue(const GRState *state, +const llvm::APSInt *SimpleSValBuilder::getKnownValue(const ProgramState *state, SVal V) { if (V.isUnknownOrUndef()) return NULL; diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index b936738009ec..48a6f4f60f8a 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,17 +12,17 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" using namespace clang; using namespace ento; -StoreManager::StoreManager(GRStateManager &stateMgr) +StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(const GRState *state, +StoreRef StoreManager::enterStackFrame(const ProgramState *state, const StackFrameContext *frame) { return StoreRef(state->getStore(), *this); } @@ -57,7 +57,7 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R, const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) { - ASTContext& Ctx = StateMgr.getContext(); + ASTContext &Ctx = StateMgr.getContext(); // Handle casts to Objective-C objects. if (CastToTy->isObjCObjectPointerType()) @@ -87,7 +87,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) // Handle casts from compatible types. if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); if (CanonPointeeTy == ObjTy) return R; @@ -103,8 +103,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::UnknownSpaceRegionKind: case MemRegion::NonStaticGlobalSpaceRegionKind: case MemRegion::StaticGlobalSpaceRegionKind: { - assert(0 && "Invalid region cast"); - break; + llvm_unreachable("Invalid region cast"); } case MemRegion::FunctionTextRegionKind: @@ -157,7 +156,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) // Edge case: we are at 0 bytes off the beginning of baseR. We // check to see if type we are casting to is the same as the base // region. If so, just return the base region. - if (const TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(baseR)) { QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); if (CanonPointeeTy == ObjTy) @@ -203,15 +202,14 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) } } - assert(0 && "unreachable"); - return 0; + llvm_unreachable("unreachable"); } /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted /// as another region. -SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, +SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, QualType castTy, bool performTestOnly) { if (castTy.isNull()) @@ -237,7 +235,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, return V; } -SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { +SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { if (Base.isUnknownOrUndef()) return Base; @@ -261,8 +259,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { return Base; default: - assert(0 && "Unhandled Base."); - return Base; + llvm_unreachable("Unhandled Base."); } // NOTE: We must have this check first because ObjCIvarDecl is a subclass @@ -336,3 +333,6 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, Ctx)); } + +StoreManager::BindingsHandler::~BindingsHandler() {} + diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index c1ca1cfcdc07..b843ab1a903b 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -24,11 +25,10 @@ void SymExpr::dump() const { dumpToStream(llvm::errs()); } -static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { +static void print(raw_ostream &os, BinaryOperator::Opcode Op) { switch (Op) { default: - assert(false && "operator printing not implemented"); - break; + llvm_unreachable("operator printing not implemented"); case BO_Mul: os << '*' ; break; case BO_Div: os << '/' ; break; case BO_Rem: os << '%' ; break; @@ -48,7 +48,7 @@ static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { } } -void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { +void SymIntExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); os << ") "; @@ -57,7 +57,7 @@ void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { if (getRHS().isUnsigned()) os << 'U'; } -void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { +void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); os << ") "; @@ -66,33 +66,33 @@ void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { os << ')'; } -void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { +void SymbolConjured::dumpToStream(raw_ostream &os) const { os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; } -void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { +void SymbolDerived::dumpToStream(raw_ostream &os) const { os << "derived_$" << getSymbolID() << '{' << getParentSymbol() << ',' << getRegion() << '}'; } -void SymbolExtent::dumpToStream(llvm::raw_ostream& os) const { +void SymbolExtent::dumpToStream(raw_ostream &os) const { os << "extent_$" << getSymbolID() << '{' << getRegion() << '}'; } -void SymbolMetadata::dumpToStream(llvm::raw_ostream& os) const { +void SymbolMetadata::dumpToStream(raw_ostream &os) const { os << "meta_$" << getSymbolID() << '{' << getRegion() << ',' << T.getAsString() << '}'; } -void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { +void SymbolRegionValue::dumpToStream(raw_ostream &os) const { os << "reg_$" << getSymbolID() << "<" << R << ">"; } const SymbolRegionValue* -SymbolManager::getRegionValueSymbol(const TypedRegion* R) { +SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; SymbolRegionValue::Profile(profile, R); - void* InsertPos; + void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>(); @@ -105,12 +105,12 @@ SymbolManager::getRegionValueSymbol(const TypedRegion* R) { } const SymbolConjured* -SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, - const void* SymbolTag) { +SymbolManager::getConjuredSymbol(const Stmt *E, QualType T, unsigned Count, + const void *SymbolTag) { llvm::FoldingSetNodeID profile; SymbolConjured::Profile(profile, E, T, Count, SymbolTag); - void* InsertPos; + void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>(); @@ -124,11 +124,11 @@ SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, const SymbolDerived* SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, - const TypedRegion *R) { + const TypedValueRegion *R) { llvm::FoldingSetNodeID profile; SymbolDerived::Profile(profile, parentSymbol, R); - void* InsertPos; + void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>(); @@ -144,7 +144,7 @@ const SymbolExtent* SymbolManager::getExtentSymbol(const SubRegion *R) { llvm::FoldingSetNodeID profile; SymbolExtent::Profile(profile, R); - void* InsertPos; + void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolExtent>(); @@ -157,12 +157,12 @@ SymbolManager::getExtentSymbol(const SubRegion *R) { } const SymbolMetadata* -SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt* S, QualType T, - unsigned Count, const void* SymbolTag) { +SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, + unsigned Count, const void *SymbolTag) { llvm::FoldingSetNodeID profile; SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag); - void* InsertPos; + void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>(); @@ -214,11 +214,11 @@ QualType SymbolConjured::getType(ASTContext&) const { return T; } -QualType SymbolDerived::getType(ASTContext& Ctx) const { +QualType SymbolDerived::getType(ASTContext &Ctx) const { return R->getValueType(); } -QualType SymbolExtent::getType(ASTContext& Ctx) const { +QualType SymbolExtent::getType(ASTContext &Ctx) const { return Ctx.getSizeType(); } @@ -226,11 +226,17 @@ QualType SymbolMetadata::getType(ASTContext&) const { return T; } -QualType SymbolRegionValue::getType(ASTContext& C) const { +QualType SymbolRegionValue::getType(ASTContext &C) const { return R->getValueType(); } -SymbolManager::~SymbolManager() {} +SymbolManager::~SymbolManager() { + for (SymbolDependTy::const_iterator I = SymbolDependencies.begin(), + E = SymbolDependencies.end(); I != E; ++I) { + delete I->second; + } + +} bool SymbolManager::canSymbolicate(QualType T) { T = T.getCanonicalType(); @@ -247,9 +253,53 @@ bool SymbolManager::canSymbolicate(QualType T) { return false; } +void SymbolManager::addSymbolDependency(const SymbolRef Primary, + const SymbolRef Dependent) { + SymbolDependTy::iterator I = SymbolDependencies.find(Primary); + SymbolRefSmallVectorTy *dependencies = 0; + if (I == SymbolDependencies.end()) { + dependencies = new SymbolRefSmallVectorTy(); + SymbolDependencies[Primary] = dependencies; + } else { + dependencies = I->second; + } + dependencies->push_back(Dependent); +} + +const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols( + const SymbolRef Primary) { + SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary); + if (I == SymbolDependencies.end()) + return 0; + return I->second; +} + +void SymbolReaper::markDependentsLive(SymbolRef sym) { + // Do not mark dependents more then once. + SymbolMapTy::iterator LI = TheLiving.find(sym); + assert(LI != TheLiving.end() && "The primary symbol is not live."); + if (LI->second == HaveMarkedDependents) + return; + LI->second = HaveMarkedDependents; + + if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) { + for (SymbolRefSmallVectorTy::const_iterator I = Deps->begin(), + E = Deps->end(); I != E; ++I) { + if (TheLiving.find(*I) != TheLiving.end()) + continue; + markLive(*I); + } + } +} + void SymbolReaper::markLive(SymbolRef sym) { - TheLiving.insert(sym); + TheLiving[sym] = NotProcessed; TheDead.erase(sym); + markDependentsLive(sym); +} + +void SymbolReaper::markLive(const MemRegion *region) { + RegionRoots.insert(region); } void SymbolReaper::markInUse(SymbolRef sym) { @@ -265,14 +315,17 @@ bool SymbolReaper::maybeDead(SymbolRef sym) { return true; } -static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) { +bool SymbolReaper::isLiveRegion(const MemRegion *MR) { + if (RegionRoots.count(MR)) + return true; + MR = MR->getBaseRegion(); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) - return Reaper.isLive(SR->getSymbol()); + return isLive(SR->getSymbol()); if (const VarRegion *VR = dyn_cast<VarRegion>(MR)) - return Reaper.isLive(VR); + return isLive(VR, true); // FIXME: This is a gross over-approximation. What we really need is a way to // tell if anything still refers to this region. Unlike SymbolicRegions, @@ -291,8 +344,10 @@ static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) { } bool SymbolReaper::isLive(SymbolRef sym) { - if (TheLiving.count(sym)) + if (TheLiving.count(sym)) { + markDependentsLive(sym); return true; + } if (const SymbolDerived *derived = dyn_cast<SymbolDerived>(sym)) { if (isLive(derived->getParentSymbol())) { @@ -303,7 +358,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { } if (const SymbolExtent *extent = dyn_cast<SymbolExtent>(sym)) { - if (IsLiveRegion(*this, extent->getRegion())) { + if (isLiveRegion(extent->getRegion())) { markLive(sym); return true; } @@ -312,7 +367,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { if (const SymbolMetadata *metadata = dyn_cast<SymbolMetadata>(sym)) { if (MetadataInUse.count(sym)) { - if (IsLiveRegion(*this, metadata->getRegion())) { + if (isLiveRegion(metadata->getRegion())) { markLive(sym); MetadataInUse.erase(sym); return true; @@ -326,18 +381,38 @@ bool SymbolReaper::isLive(SymbolRef sym) { return isa<SymbolRegionValue>(sym); } -bool SymbolReaper::isLive(const Stmt* ExprVal) const { - return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> - isLive(Loc, ExprVal); +bool SymbolReaper::isLive(const Stmt *ExprVal) const { + return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); } -bool SymbolReaper::isLive(const VarRegion *VR) const { +bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ const StackFrameContext *VarContext = VR->getStackFrame(); const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); - if (VarContext == CurrentContext) - return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> - isLive(Loc, VR->getDecl()); + if (VarContext == CurrentContext) { + if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl())) + return true; + + if (!includeStoreBindings) + return false; + + unsigned &cachedQuery = + const_cast<SymbolReaper*>(this)->includedRegionCache[VR]; + + if (cachedQuery) { + return cachedQuery == 1; + } + + // Query the store to see if the region occurs in any live bindings. + if (Store store = reapedStore.getStore()) { + bool hasRegion = + reapedStore.getStoreManager().includedInBindings(store, VR); + cachedQuery = hasRegion ? 1 : 2; + return hasRegion; + } + + return false; + } return VarContext->isParentOf(CurrentContext); } diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index 230b6a1087dc..3543f7ff13dc 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/raw_ostream.h" @@ -23,19 +23,19 @@ namespace { /// \brief Simple path diagnostic client used for outputting as diagnostic notes /// the sequence of events. -class TextPathDiagnostics : public PathDiagnosticClient { +class TextPathDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; - Diagnostic &Diag; + DiagnosticsEngine &Diag; public: - TextPathDiagnostics(const std::string& output, Diagnostic &diag) + TextPathDiagnostics(const std::string& output, DiagnosticsEngine &diag) : OutputFile(output), Diag(diag) {} - void HandlePathDiagnostic(const PathDiagnostic* D); + void HandlePathDiagnosticImpl(const PathDiagnostic* D); - void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade) { } + void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { } - virtual llvm::StringRef getName() const { + virtual StringRef getName() const { return "TextPathDiagnostics"; } @@ -47,13 +47,13 @@ public: } // end anonymous namespace -PathDiagnosticClient* -ento::createTextPathDiagnosticClient(const std::string& out, +PathDiagnosticConsumer* +ento::createTextPathDiagnosticConsumer(const std::string& out, const Preprocessor &PP) { return new TextPathDiagnostics(out, PP.getDiagnostics()); } -void TextPathDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { +void TextPathDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { if (!D) return; |