Index: include/clang/Analysis/AnalysisContext.h =================================================================== --- include/clang/Analysis/AnalysisContext.h +++ include/clang/Analysis/AnalysisContext.h @@ -33,6 +33,7 @@ class PseudoConstantAnalysis; class LocationContextManager; class StackFrameContext; +class ScopeContext; class BlockInvocationContext; class AnalysisDeclContextManager; class LocationContext; @@ -83,6 +84,7 @@ llvm::BumpPtrAllocator A; llvm::DenseMap *ReferencedBlockVars; + llvm::DenseMap *DeclaredVars; void *ManagedAnalyses; @@ -163,10 +165,14 @@ PseudoConstantAnalysis *getPseudoConstantAnalysis(); typedef const VarDecl * const * referenced_decls_iterator; + typedef const VarDecl * const * declared_vars_iterator; std::pair getReferencedBlockVars(const BlockDecl *BD); + std::pair + getDeclaredVars(const Stmt *Scope); + /// Return the ImplicitParamDecl* associated with 'self' if this /// AnalysisDeclContext wraps an ObjCMethodDecl. Returns NULL otherwise. const ImplicitParamDecl *getSelfDecl() const; @@ -175,6 +181,9 @@ const Stmt *S, const CFGBlock *Blk, unsigned Idx, unsigned Cnt); + const ScopeContext *getScope(LocationContext const *Parent, const Stmt *S, + unsigned BlockCount); + const BlockInvocationContext * getBlockInvocationContext(const LocationContext *parent, const BlockDecl *BD, @@ -315,20 +324,29 @@ class ScopeContext : public LocationContext { const Stmt *Enter; + // The block count to discriminate between scopes of the same statement. + unsigned BlockCount; friend class LocationContextManager; - ScopeContext(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s) - : LocationContext(Scope, ctx, parent), Enter(s) {} + ScopeContext(AnalysisDeclContext *ctx, const LocationContext *Parent, + const Stmt *S, unsigned BlockCount) + : LocationContext(Scope, ctx, Parent), Enter(S), BlockCount(BlockCount) { + assert(Parent); + assert(S); + } public: ~ScopeContext() {} + const Stmt *getTriggerStmt() const { return Enter; } + void Profile(llvm::FoldingSetNodeID &ID); static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, - const LocationContext *parent, const Stmt *s) { + const LocationContext *parent, const Stmt *s, + unsigned BlockCount) { ProfileCommon(ID, Scope, ctx, parent, s); + ID.AddInteger(BlockCount); } static bool classof(const LocationContext *Ctx) { @@ -382,7 +400,7 @@ const ScopeContext *getScope(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s); + const Stmt *s, unsigned BlockCount); const BlockInvocationContext * getBlockInvocationContext(AnalysisDeclContext *ctx, Index: include/clang/Analysis/ProgramPoint.h =================================================================== --- include/clang/Analysis/ProgramPoint.h +++ include/clang/Analysis/ProgramPoint.h @@ -60,6 +60,8 @@ PostImplicitCallKind, MinImplicitCallKind = PreImplicitCallKind, MaxImplicitCallKind = PostImplicitCallKind, + ScopeEnterKind, + ScopeExitKind, EpsilonKind}; private: @@ -556,6 +558,52 @@ } }; +/// Represents a point where a new scope is being entered. +class ScopeEnter : public ProgramPoint { +public: + ScopeEnter(const Stmt *TriggerStmt, const ScopeContext *ScopeCtx, + const LocationContext *CurrCtx) + : ProgramPoint(TriggerStmt, CurrCtx, ScopeEnterKind, ScopeCtx) {} + + const Stmt *getTriggerStmt() const { + return static_cast(getData1()); + } + + const LocationContext *getParentContext() const { + return static_cast(getData2()); + } + +private: + ScopeEnter() {} + friend class ProgramPoint; + static bool isKind(const ProgramPoint &Location) { + return Location.getKind() == ScopeEnterKind; + } +}; + +/// Represents a point where a scope is being exited. +class ScopeExit : public ProgramPoint { +public: + ScopeExit(const Stmt *TriggerStmt, const ScopeContext *ScopeCtx, + const LocationContext *TargetCtx) + : ProgramPoint(TriggerStmt, ScopeCtx, ScopeExitKind, TargetCtx) {} + + const Stmt *getTriggerStmt() const { + return static_cast(getData1()); + } + + const LocationContext *getScopeContext() const { + return static_cast(getData2()); + } + +private: + ScopeExit() {} + friend class ProgramPoint; + static bool isKind(const ProgramPoint &Location) { + return Location.getKind() == ScopeExitKind; + } +}; + /// Represents a point when we begin processing an inlined call. /// CallEnter uses the caller's location context. class CallEnter : public ProgramPoint { Index: include/clang/StaticAnalyzer/Core/Checker.h =================================================================== --- include/clang/StaticAnalyzer/Core/Checker.h +++ include/clang/StaticAnalyzer/Core/Checker.h @@ -196,6 +196,34 @@ } }; +class ScopeEnter { + template + static void _checkScopeEnter(void *checker, CheckerContext &C) { + ((const CHECKER *)checker)->checkScopeEnter(C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForScopeEnter( + CheckerManager::CheckScopeFunc(checker, _checkScopeEnter)); + } +}; + +class ScopeExit { + template + static void _checkScopeExit(void *checker, CheckerContext &C) { + ((const CHECKER *)checker)->checkScopeExit(C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForScopeExit( + CheckerManager::CheckScopeFunc(checker, _checkScopeExit)); + } +}; + class Location { template static void _checkLocation(void *checker, Index: include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/CheckerManager.h +++ include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -293,6 +293,27 @@ const CallEvent &Call, ExprEngine &Eng, bool wasInlined = false); + /// \brief Run checkers for entering scope. + void runCheckersForScopeEnter(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + ProgramPoint PP, ExprEngine &Eng) { + runCheckersForScope(/*isEnter=*/true, Dst, Src, PP, Eng); + } + + /// \brief Run checkers for exiting scope. + void runCheckersForScopeExit(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + ProgramPoint PP, + ExprEngine &Eng) { + runCheckersForScope(/*isEnter=*/false, Dst, Src, PP, Eng); + } + + /// \brief Run checkers for scope enter/exit event. + void runCheckersForScope(bool IsEnter, ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + ProgramPoint PP, + ExprEngine &Eng); + /// \brief Run checkers for load/store of a location. void runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -467,7 +488,9 @@ typedef CheckerFn CheckCallFunc; - + + typedef CheckerFn CheckScopeFunc; + typedef CheckerFn @@ -538,6 +561,9 @@ void _registerForPreCall(CheckCallFunc checkfn); void _registerForPostCall(CheckCallFunc checkfn); + void _registerForScopeEnter(CheckScopeFunc checkfn); + void _registerForScopeExit(CheckScopeFunc checkfn); + void _registerForLocation(CheckLocationFunc checkfn); void _registerForBind(CheckBindFunc checkfn); @@ -647,6 +673,9 @@ std::vector PreCallCheckers; std::vector PostCallCheckers; + std::vector ScopeEnterCheckers; + std::vector ScopeExitCheckers; + std::vector LocationCheckers; std::vector BindCheckers; Index: include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -31,8 +31,8 @@ /// This allows the environment to manage context-sensitive bindings, /// which is essentially for modeling recursive function analysis, among /// other things. -class EnvironmentEntry : public std::pair { +class EnvironmentEntry + : public std::pair { public: EnvironmentEntry(const Stmt *s, const LocationContext *L); Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -201,6 +201,10 @@ void ProcessInitializer(const CFGInitializer I, ExplodedNode *Pred); + void ProcessScopeEnter(const CFGScopeBegin SC, ExplodedNode *Pred); + + void ProcessScopeExit(const CFGScopeEnd SE, ExplodedNode *Pred); + void ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred); void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, Index: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -404,6 +404,67 @@ } }; +class ScopeLocalSpaceRegion : public MemSpaceRegion { + const ScopeContext *ScopeCtx; + void *DeclaredVars; + + virtual void anchor(); + friend class MemRegionManager; + ScopeLocalSpaceRegion(MemRegionManager *Mgr, const ScopeContext *ScopeCtx) + : MemSpaceRegion(Mgr, ScopeLocalSpaceRegionKind), + ScopeCtx(ScopeCtx), + DeclaredVars(nullptr) {} + + void LazyInitializeDeclaredVars(); + const VarRegion *getScopedRegion(const VarDecl *VD); + +public: + const ScopeContext *getScopeContext() const { return ScopeCtx; } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + class declared_vars_iterator { + const MemRegion *const *MR; + public: + explicit declared_vars_iterator(const MemRegion *const *MR) : MR(MR) {} + + const VarRegion *getDeclaredRegion() const { + return cast(*MR); + } + + const VarRegion *operator *() const { + return getDeclaredRegion(); + } + + const VarRegion *operator ->() const { + return getDeclaredRegion(); + } + + bool operator==(const declared_vars_iterator &I) const { + assert((MR == nullptr) == (I.MR == nullptr)); + return I.MR == MR; + } + + bool operator!=(const declared_vars_iterator &I) const { + assert((MR == nullptr) == (I.MR == nullptr)); + return I.MR != MR; + } + declared_vars_iterator &operator++() { + ++MR; + return *this; + } + }; + + declared_vars_iterator declared_vars_begin() const; + declared_vars_iterator declared_vars_end() const; + + void dumpToStream(raw_ostream &os) const; + + static bool classof(const MemRegion *R) { + return R->getKind() == ScopeLocalSpaceRegionKind; + } +}; + /// SubRegion - A region that subsets another larger region. Most regions /// are subclasses of SubRegion. @@ -1124,17 +1185,24 @@ GlobalImmutableSpaceRegion *ImmutableGlobals; - llvm::DenseMap + llvm::DenseMap StackLocalsSpaceRegions; llvm::DenseMap StackArgumentsSpaceRegions; llvm::DenseMap StaticsGlobalSpaceRegions; + llvm::DenseMap + ScopeLocalSpaceRegions; HeapSpaceRegion *heap; UnknownSpaceRegion *unknown; CodeSpaceRegion *code; + llvm::PointerUnion + getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD); + public: MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator& a) : C(c), A(a), InternalGlobals(0), SystemGlobals(0), ImmutableGlobals(0), @@ -1156,6 +1224,11 @@ const StackArgumentsSpaceRegion * getStackArgumentsRegion(const StackFrameContext *STC); + /// getScopeLocalSpaceRegion - Retrieve the memory region associated with + /// the given scope. + const ScopeLocalSpaceRegion * + getScopeLocalSpaceRegion(const ScopeContext *ScopeCtx); + /// getGlobalsRegion - Retrieve the memory region associated with /// global variables. const GlobalsSpaceRegion *getGlobalsRegion( Index: include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def +++ include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -52,6 +52,7 @@ REGION(StackLocalsSpaceRegion, StackSpaceRegion) REGION_RANGE(STACK_MEMSPACES, StackArgumentsSpaceRegionKind, StackLocalsSpaceRegionKind) + REGION(ScopeLocalSpaceRegion, MemSpaceRegion) REGION(UnknownSpaceRegion, MemSpaceRegion) REGION_RANGE(MEMSPACES, CodeSpaceRegionKind, UnknownSpaceRegionKind) Index: include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -674,7 +674,7 @@ RegionSetTy RegionRoots; - const StackFrameContext *LCtx; + const LocationContext *LCtx; const Stmt *Loc; SymbolManager& SymMgr; StoreRef reapedStore; @@ -688,7 +688,7 @@ /// considered live. /// If the stack frame context is NULL, everything on stack is considered /// dead. - SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, SymbolManager& symmgr, + SymbolReaper(const LocationContext *Ctx, const Stmt *s, SymbolManager& symmgr, StoreManager &storeMgr) : LCtx(Ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {} Index: lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- lib/Analysis/AnalysisDeclContext.cpp +++ lib/Analysis/AnalysisDeclContext.cpp @@ -45,6 +45,7 @@ builtCFG(false), builtCompleteCFG(false), ReferencedBlockVars(0), + DeclaredVars(nullptr), ManagedAnalyses(0) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; @@ -58,6 +59,7 @@ builtCFG(false), builtCompleteCFG(false), ReferencedBlockVars(0), + DeclaredVars(nullptr), ManagedAnalyses(0) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; @@ -279,6 +281,12 @@ Cnt); } +const ScopeContext * +AnalysisDeclContext::getScope(const LocationContext *Parent, const Stmt *S, + unsigned BlockCount) { + return getLocationContextManager().getScope(this, Parent, S, BlockCount); +} + const BlockInvocationContext * AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, const clang::BlockDecl *BD, @@ -329,7 +337,7 @@ } void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAnalysisDeclContext(), getParent(), Enter); + Profile(ID, getAnalysisDeclContext(), getParent(), Enter, BlockCount); } void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { @@ -376,8 +384,17 @@ const ScopeContext * LocationContextManager::getScope(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s) { - return getLocationContext(ctx, parent, s); + const Stmt *s, unsigned BlockCount) { + llvm::FoldingSetNodeID ID; + ScopeContext::Profile(ID, ctx, parent, s, BlockCount); + void *InsertPos; + ScopeContext *L = + cast_or_null(Contexts.FindNodeOrInsertPos(ID, InsertPos)); + if (!L) { + L = new ScopeContext(ctx, parent, s, BlockCount); + Contexts.InsertNode(L, InsertPos); + } + return L; } const BlockInvocationContext * @@ -537,6 +554,52 @@ return BV; } +static DeclVec *LazyInitializeDeclaredVars(const Stmt *Scope, void *&Vec, + llvm::BumpPtrAllocator &A) { + if (Vec) + return (DeclVec*) Vec; + + BumpVectorContext BC(A); + DeclVec *Declared = (DeclVec*) A.Allocate(); + new (Declared) DeclVec(BC, 10); + + if (const IfStmt *IfS = dyn_cast(Scope)) { + if (const VarDecl *VD = IfS->getConditionVariable()) + Declared->push_back(VD, BC); + + } else if (const WhileStmt *WS = dyn_cast(Scope)) { + if (const VarDecl *VD = WS->getConditionVariable()) + Declared->push_back(VD, BC); + + } else if (const SwitchStmt *SS = dyn_cast(Scope)) { + if (const VarDecl *VD = SS->getConditionVariable()) + Declared->push_back(VD, BC); + + } else if (const ForStmt *FS = dyn_cast(Scope)) { + if (const VarDecl *VD = FS->getConditionVariable()) + Declared->push_back(VD, BC); + + } else if (const CXXForRangeStmt *CFR = dyn_cast(Scope)) { + if (const VarDecl *VD = CFR->getLoopVariable()) + Declared->push_back(VD, BC); + + } else if (const CXXCatchStmt *CS = dyn_cast(Scope)) { + if (const VarDecl *VD = CS->getExceptionDecl()) + Declared->push_back(VD, BC); + + } else { + for (const Stmt *Child : Scope->children()) + if (const DeclStmt *DS = dyn_cast(Child)) + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), + E = DS->decl_end(); I != E; ++I) + if (const VarDecl *VD = dyn_cast(*I)) + Declared->push_back(VD, BC); + } + + Vec = Declared; + return Declared; +} + std::pair AnalysisDeclContext::getReferencedBlockVars(const BlockDecl *BD) { @@ -547,6 +610,16 @@ return std::make_pair(V->begin(), V->end()); } +std::pair +AnalysisDeclContext::getDeclaredVars(const Stmt *Scope) { + if (!DeclaredVars) + DeclaredVars = new llvm::DenseMap(); + + DeclVec *V = LazyInitializeDeclaredVars(Scope, (*DeclaredVars)[Scope], A); + return std::make_pair(V->begin(), V->end()); +} + ManagedAnalysis *&AnalysisDeclContext::getAnalysisImpl(const void *tag) { if (!ManagedAnalyses) ManagedAnalyses = new ManagedAnalysisMap(); Index: lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -44,6 +44,8 @@ check::Location, check::Bind, check::DeadSymbols, + check::ScopeEnter, + check::ScopeExit, check::EndFunction, check::EndAnalysis, check::EndOfTranslationUnit, @@ -154,6 +156,22 @@ /// check::DeadSymbols void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {} + /// \brief Called when a scope is being entered. + /// + /// This callback may be used by the checkers to determine variables that + /// become alive in some scope and perform necessary actions. + /// + /// check::ScopeEnter + void checkScopeEnter(CheckerContext &C) const {} + + /// \brief Called when a scope is being leaved. + /// + /// This callback may be used by the checkers to determine variables whose + /// lifetime ends. + /// + /// check::ScopeExit + void checkScopeExit(CheckerContext &C) const {} + /// \brief Called when the analyzer core reaches the end of a /// function being analyzed. /// Index: lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -89,7 +89,8 @@ os << " stack allocated memory"; os << " for the predicate value. Using such transient memory for " "the predicate is potentially dangerous."; - if (isa(R) && isa(R->getMemorySpace())) + if (isa(R) && (isa(R->getMemorySpace()) || + isa(R->getMemorySpace()))) os << " Perhaps you intended to declare the variable as 'static'?"; BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N); Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1172,6 +1172,22 @@ return true; } + if (isa(MS)) { + const VarRegion *VR = dyn_cast(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) + os << "the address of the scope-local variable '" << VD->getName() + << "'"; + else + os << "the address of a scope-local variable"; + return true; + } + if (isa(MS)) { const VarRegion *VR = dyn_cast(MR); const VarDecl *VD; Index: lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -176,7 +176,8 @@ os << " stack allocated memory"; os << " for the \"control\" value. Using such transient memory for " "the control value is potentially dangerous."; - if (isa(R) && isa(R->getMemorySpace())) + if (isa(R) && (isa(R->getMemorySpace()) || + isa(R->getMemorySpace()))) os << " Perhaps you intended to declare the variable as 'static'?"; LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -434,7 +434,9 @@ const MemSpaceRegion *VarSpace = VR->getMemorySpace(); const StackSpaceRegion *FrameSpace = dyn_cast(VarSpace); - if (!FrameSpace) { + const ScopeLocalSpaceRegion *ScopeSpace = + dyn_cast(VarSpace); + if (!FrameSpace && !ScopeSpace) { // If we ever directly evaluate global DeclStmts, this assertion will be // invalid, but this still seems preferable to silently accepting an // initialization that may be for a path-sensitive variable. @@ -444,7 +446,9 @@ assert(VR->getDecl()->hasLocalStorage()); const LocationContext *LCtx = N->getLocationContext(); - return FrameSpace->getStackFrame() == LCtx->getCurrentStackFrame(); + return FrameSpace + ? FrameSpace->getStackFrame() == LCtx->getCurrentStackFrame() + : ScopeSpace->getScopeContext() == LCtx; } PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, Index: lib/StaticAnalyzer/Core/CheckerManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/CheckerManager.cpp +++ lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -273,6 +273,41 @@ } namespace { + struct CheckScopeContext { + typedef std::vector CheckersTy; + bool IsEnter; + const CheckersTy &Checkers; + ProgramPoint PP; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckScopeContext(bool IsEnter, const CheckersTy &Checkers, + ProgramPoint PP, ExprEngine &Eng) + : IsEnter(IsEnter), Checkers(Checkers), PP(PP), Eng(Eng) {} + + void runChecker(CheckerManager::CheckScopeFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + CheckerContext C(Bldr, Eng, Pred, PP.withTag(checkFn.Checker)); + checkFn(C); + } + }; +} + +/// \brief Run checkers for entering/leaving a scope. +void CheckerManager::runCheckersForScope(bool IsEnter, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + ProgramPoint PP, + ExprEngine &Eng) { + CheckScopeContext C(IsEnter, + IsEnter ? ScopeEnterCheckers : ScopeExitCheckers, + PP, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { struct CheckLocationContext { typedef std::vector CheckersTy; const CheckersTy &Checkers; @@ -694,6 +729,14 @@ PostCallCheckers.push_back(checkfn); } +void CheckerManager::_registerForScopeEnter(CheckScopeFunc checkfn) { + ScopeEnterCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForScopeExit(CheckScopeFunc checkfn) { + ScopeExitCheckers.push_back(checkfn); +} + void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { LocationCheckers.push_back(checkfn); } Index: lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/CoreEngine.cpp +++ lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -271,7 +271,9 @@ assert(Loc.getAs() || Loc.getAs() || Loc.getAs() || - Loc.getAs()); + Loc.getAs() || + Loc.getAs() || + Loc.getAs()); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -525,7 +527,9 @@ // Do not create extra nodes. Move to the next CFG element. if (N->getLocation().getAs() || - N->getLocation().getAs()) { + N->getLocation().getAs() || + N->getLocation().getAs() || + N->getLocation().getAs()) { WList->enqueue(N, Block, Idx+1); return; } Index: lib/StaticAnalyzer/Core/Environment.cpp =================================================================== --- lib/StaticAnalyzer/Core/Environment.cpp +++ lib/StaticAnalyzer/Core/Environment.cpp @@ -52,9 +52,8 @@ } EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L) - : std::pair(ignoreTransparentExprs(S), - L ? L->getCurrentStackFrame() : 0) {} + : std::pair( + ignoreTransparentExprs(S), L) {} SVal Environment::lookupExpr(const EnvironmentEntry &E) const { const SVal* X = ExprBindings.lookup(E); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -296,6 +296,12 @@ case CFGElement::Initializer: ProcessInitializer(E.castAs().getInitializer(), Pred); return; + case CFGElement::ScopeBegin: + ProcessScopeEnter(E.castAs(), Pred); + return; + case CFGElement::ScopeEnd: + ProcessScopeExit(E.castAs(), Pred); + return; case CFGElement::AutomaticObjectDtor: case CFGElement::DeleteDtor: case CFGElement::BaseDtor: @@ -361,7 +367,7 @@ } const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : 0; - SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); + SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); @@ -530,6 +536,66 @@ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } + +void ExprEngine::ProcessScopeEnter(const CFGScopeBegin SC, ExplodedNode *Pred) { + const Stmt *TriggerStmt = SC.getTriggerStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + TriggerStmt->getLocStart(), + "Error evaluating scope enter"); + + const LocationContext *ParentCtx = Pred->getLocationContext(); + // We don't clean up dead bindings here. + const StackFrameContext *StackFrame = ParentCtx->getCurrentStackFrame(); + + // Construct a new scope frame for the statement. + AnalysisDeclContext *ADC = AMgr.getAnalysisDeclContext(StackFrame->getDecl()); + const ScopeContext *ScopeCtx = ADC->getScope(ParentCtx, TriggerStmt, + currBldrCtx->blockCount()); + + ScopeEnter Enter(TriggerStmt, ScopeCtx, ParentCtx); + ExplodedNodeSet Src(Pred); + ExplodedNodeSet Tmp, Dst; + getCheckerManager().runCheckersForScopeEnter(Tmp, Src, Enter, *this); + if (Tmp.empty()) + Tmp.Add(Pred); + + NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); + for (auto *Node : Tmp) + Bldr.generateNode(Enter, Node->getState(), Node); + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessScopeExit(const CFGScopeEnd SE, ExplodedNode *Pred) { + const Stmt *TriggerStmt = SE.getTriggerStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + TriggerStmt->getLocEnd(), + "Error evaluating scope exit"); + + // Construct a new scope frame for the statement. + const ScopeContext *ScopeCtx = cast(Pred->getLocationContext()); + + const LocationContext *TargetCtx = isa(SE.getTriggerStmt()) + ? ScopeCtx->getCurrentStackFrame() + : ScopeCtx->getParent(); + + ScopeExit Exit(TriggerStmt, ScopeCtx, TargetCtx); + ExplodedNodeSet Src(Pred); + ExplodedNodeSet Tmp, Dst; + getCheckerManager().runCheckersForScopeExit(Tmp, Src, Exit, *this); + if (Tmp.empty()) + Tmp.Add(Pred); + + NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); + for (auto *Node : Tmp) + Bldr.generateNode(Exit, Node->getState(), Node); + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + + void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNode *Pred) { ExplodedNodeSet Dst; @@ -2425,6 +2491,20 @@ break; } + case ProgramPoint::ScopeEnterKind: { + const Stmt *Trigger = Loc.castAs().getTriggerStmt(); + Out <<"ScopeEnter: " << Trigger->getStmtClassName() << ' ' << + (const void *)Trigger; + break; + } + + case ProgramPoint::ScopeExitKind: { + const Stmt *Trigger = Loc.castAs().getTriggerStmt(); + Out <<"ScopeExit: " << Trigger->getStmtClassName() << ' ' << + (const void *)Trigger; + break; + } + default: { const Stmt *S = Loc.castAs().getStmt(); Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -221,12 +221,11 @@ PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); - - // The parent context might not be a stack frame, so make sure we - // look up the first enclosing stack frame. - const StackFrameContext *callerCtx = - calleeCtx->getParent()->getCurrentStackFrame(); - + + const LocationContext *CallerCtx = calleeCtx->getParent(); + if (isa(CallerCtx)) + CallerCtx = CallerCtx->getParent(); + const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. @@ -259,7 +258,7 @@ } } - state = state->BindExpr(CE, callerCtx, V); + state = state->BindExpr(CE, CallerCtx, V); } // Bind the constructed object value to CXXConstructExpr. @@ -272,7 +271,7 @@ if (isTemporaryPRValue(CCE, ThisV)) ThisV = state->getSVal(ThisV.castAs()); - state = state->BindExpr(CCE, callerCtx, ThisV); + state = state->BindExpr(CCE, CallerCtx, ThisV); } } @@ -310,7 +309,7 @@ // Step 4: Generate the CallExit and leave the callee's context. // CleanedNodes -> CEENode - CallExitEnd Loc(calleeCtx, callerCtx); + CallExitEnd Loc(calleeCtx, CallerCtx); bool isNew; ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); @@ -432,13 +431,12 @@ assert(D); const LocationContext *CurLC = Pred->getLocationContext(); - const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); - const LocationContext *ParentOfCallee = CallerSFC; + const LocationContext *ParentOfCallee = CurLC; if (Call.getKind() == CE_Block) { const BlockDataRegion *BR = cast(Call).getBlockRegion(); assert(BR && "If we have the block definition we should have its region"); AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); - ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + ParentOfCallee = BlockCtx->getBlockInvocationContext(CurLC, cast(D), BR); } Index: lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- lib/StaticAnalyzer/Core/MemRegion.cpp +++ lib/StaticAnalyzer/Core/MemRegion.cpp @@ -172,8 +172,15 @@ } const StackFrameContext *VarRegion::getStackFrame() const { - const StackSpaceRegion *SSR = dyn_cast(getMemorySpace()); - return SSR ? SSR->getStackFrame() : NULL; + const MemSpaceRegion *Space = getMemorySpace(); + if (const StackSpaceRegion *StackSpace = dyn_cast(Space)) + return StackSpace->getStackFrame(); + + else if (const ScopeLocalSpaceRegion *ScopeSpace = + dyn_cast(Space)) + return ScopeSpace->getScopeContext()->getCurrentStackFrame(); + + return nullptr; } //===----------------------------------------------------------------------===// @@ -259,6 +266,10 @@ ID.AddPointer(getCodeRegion()); } +void ScopeLocalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(ScopeCtx); +} + void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const StringLiteral* Str, const MemRegion* superRegion) { @@ -427,6 +438,7 @@ void GlobalsSpaceRegion::anchor() { } void HeapSpaceRegion::anchor() { } void UnknownSpaceRegion::anchor() { } +void ScopeLocalSpaceRegion::anchor() { } void StackLocalsSpaceRegion::anchor() { } void StackArgumentsSpaceRegion::anchor() { } void TypedRegion::anchor() { } @@ -567,6 +579,10 @@ os << "StackLocalsSpaceRegion"; } +void ScopeLocalSpaceRegion::dumpToStream(raw_ostream &OS) const { + OS << "ScopeLocalSpaceRegion"; +} + bool MemRegion::canPrintPretty() const { return canPrintPrettyAsExpr(); } @@ -687,6 +703,19 @@ return R; } +const ScopeLocalSpaceRegion * +MemRegionManager::getScopeLocalSpaceRegion(const ScopeContext *ScopeCtx) { + assert(ScopeCtx); + ScopeLocalSpaceRegion *&R = ScopeLocalSpaceRegions[ScopeCtx]; + + if (R) + return R; + + R = A.Allocate(); + new (R) ScopeLocalSpaceRegion(this, ScopeCtx); + return R; +} + const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion(MemRegion::Kind K, const CodeTextRegion *CR) { @@ -734,12 +763,12 @@ } /// Look through a chain of LocationContexts to either find the -/// StackFrameContext that matches a DeclContext, or find a VarRegion -/// for a variable captured by a block. -static llvm::PointerUnion -getStackOrCaptureRegionForDeclContext(const LocationContext *LC, - const DeclContext *DC, - const VarDecl *VD) { +/// StackFrameContext that matches a DeclContext, +/// or a ScopeContext where a variable was declared, +/// or find a VarRegion for a variable captured by a block. +llvm::PointerUnion +MemRegionManager::getStackOrCaptureRegionForDeclContext( + const LocationContext *LC, const DeclContext *DC, const VarDecl *VD) { while (LC) { if (const StackFrameContext *SFC = dyn_cast(LC)) { if (cast(SFC->getDecl()) == DC) @@ -758,6 +787,14 @@ return cast(I.getCapturedRegion()); } } + if (const ScopeContext *ScopeCtx = dyn_cast(LC)) { + const ScopeLocalSpaceRegion *Space = getScopeLocalSpaceRegion(ScopeCtx); + for (auto I = Space->declared_vars_begin(), + E = Space->declared_vars_end(); I != E; ++I) { + if (I->getDecl() == VD) + return *I; + } + } LC = LC->getParent(); } @@ -793,16 +830,14 @@ // Finally handle static locals. } else { - // FIXME: Once we implement scope handling, we will need to properly lookup - // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); llvm::PointerUnion V = getStackOrCaptureRegionForDeclContext(LC, DC, D); if (V.is()) return V.get(); - - const StackFrameContext *STC = V.get(); + + const StackFrameContext *STC = V.get(); if (!STC) sReg = getUnknownRegion(); @@ -810,9 +845,10 @@ if (D->hasLocalStorage()) { sReg = isa(D) || isa(D) ? static_cast(getStackArgumentsRegion(STC)) + // TODO: assert if 'cfg-scope-info=true'. : static_cast(getStackLocalsRegion(STC)); - } - else { + + } else { assert(D->isStaticLocal()); const Decl *STCD = STC->getDecl(); if (isa(STCD) || isa(STCD)) @@ -834,8 +870,8 @@ STC->getAnalysisDeclContext()); sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, BTR); - } - else { + + } else { sReg = getGlobalsRegion(); } } @@ -1042,11 +1078,13 @@ } bool MemRegion::hasStackStorage() const { - return isa(getMemorySpace()); + const MemSpaceRegion *MS = getMemorySpace(); + return isa(MS) || isa(MS); } bool MemRegion::hasStackNonParametersStorage() const { - return isa(getMemorySpace()); + const MemSpaceRegion *MS = getMemorySpace(); + return isa(MS) || isa(MS); } bool MemRegion::hasStackParametersStorage() const { @@ -1192,6 +1230,7 @@ case HeapSpaceRegionKind: case UnknownSpaceRegionKind: case StaticGlobalSpaceRegionKind: + case ScopeLocalSpaceRegionKind: case GlobalInternalSpaceRegionKind: case GlobalSystemSpaceRegionKind: case GlobalImmutableSpaceRegionKind: @@ -1455,6 +1494,69 @@ } //===----------------------------------------------------------------------===// +// ScopeLocalRegionSpace +//===----------------------------------------------------------------------===// + +const VarRegion *ScopeLocalSpaceRegion::getScopedRegion(const VarDecl *VD) { + MemRegionManager &MemMgr = *getMemRegionManager(); + + return VD->hasLocalStorage() ? MemMgr.getVarRegion(VD, this) + : MemMgr.getVarRegion(VD, ScopeCtx); +} + +void ScopeLocalSpaceRegion::LazyInitializeDeclaredVars() { + if (DeclaredVars) + return; + + AnalysisDeclContext *ADC = ScopeCtx->getAnalysisDeclContext(); + AnalysisDeclContext::declared_vars_iterator I, E; + llvm::tie(I, E) = ADC->getDeclaredVars(ScopeCtx->getTriggerStmt()); + + if (I == E) { + DeclaredVars = (void *)0x1; + return; + } + + MemRegionManager &MemMgr = *getMemRegionManager(); + llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); + BumpVectorContext BC(A); + + typedef BumpVector VarVec; + VarVec *BV = (VarVec*) A.Allocate(); + new (BV) VarVec(BC, E - I); + VarVec *BVOriginal = (VarVec*) A.Allocate(); + new (BVOriginal) VarVec(BC, E - I); + + for ( ; I != E; ++I) { + const VarRegion *VR = getScopedRegion(*I); + assert(VR); + BV->push_back(VR, BC); + } + + DeclaredVars = BV; +} + +ScopeLocalSpaceRegion::declared_vars_iterator +ScopeLocalSpaceRegion::declared_vars_begin() const { + const_cast(this)->LazyInitializeDeclaredVars(); + + BumpVector *Vec = + static_cast *>(DeclaredVars); + + return declared_vars_iterator(Vec == (void *)0x1 ? nullptr : Vec->begin()); +} + +ScopeLocalSpaceRegion::declared_vars_iterator +ScopeLocalSpaceRegion::declared_vars_end() const { + const_cast(this)->LazyInitializeDeclaredVars(); + + BumpVector *Vec = + static_cast *>(DeclaredVars); + + return declared_vars_iterator(Vec == (void *)0x1 ? nullptr : Vec->end()); +} + +//===----------------------------------------------------------------------===// // RegionAndSymbolInvalidationTraits //===----------------------------------------------------------------------===// Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -554,6 +554,14 @@ return PathDiagnosticLocation(Init.getInitializer()->getInit(), SM, CallerCtx); } + case CFGElement::ScopeBegin: { + const Stmt *TriggerStmt = Source.castAs().getTriggerStmt(); + return PathDiagnosticLocation(TriggerStmt, SM, CallerCtx); + } + case CFGElement::ScopeEnd: { + const Stmt *TriggerStmt = Source.castAs().getTriggerStmt(); + return PathDiagnosticLocation::createEnd(TriggerStmt, SM, CallerCtx); + } case CFGElement::AutomaticObjectDtor: { const CFGAutomaticObjDtor &Dtor = Source.castAs(); return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), @@ -694,6 +702,10 @@ return CEE->getCalleeContext()->getCallSite(); if (Optional PIPP = P.getAs()) return PIPP->getInitializer()->getInit(); + if (Optional SE = P.getAs()) + return SE->getTriggerStmt(); + if (Optional SEnt = P.getAs()) + return SEnt->getTriggerStmt(); return 0; } @@ -746,7 +758,7 @@ if (const BinaryOperator *B = dyn_cast(S)) return PathDiagnosticLocation::createOperatorLoc(B, SM); - if (P.getAs()) + if (P.getAs() || P.getAs()) return PathDiagnosticLocation::createEnd(S, SM, LC); if (S->getLocStart().isValid()) Index: lib/StaticAnalyzer/Core/Store.cpp =================================================================== --- lib/StaticAnalyzer/Core/Store.cpp +++ lib/StaticAnalyzer/Core/Store.cpp @@ -106,6 +106,7 @@ case MemRegion::HeapSpaceRegionKind: case MemRegion::UnknownSpaceRegionKind: case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::ScopeLocalSpaceRegionKind: case MemRegion::GlobalInternalSpaceRegionKind: case MemRegion::GlobalSystemSpaceRegionKind: case MemRegion::GlobalImmutableSpaceRegionKind: { Index: test/Analysis/scope-context.cpp =================================================================== --- /dev/null +++ test/Analysis/scope-context.cpp @@ -0,0 +1,24 @@ +// RUN: clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify -analyzer-config cfg-scope-info=true %s + +void clang_analyzer_eval(bool); + +void testEq() { + void *a[2]; + for (int i = 0; i < 2; ++i) { + int x[1]; + a[i] = &x[0]; + } + clang_analyzer_eval(a[0] == a[1]); // expected-warning{{FALSE}} +} + +void testBreak() { + for (int i = 0; i < 3; ++i) { + { + int unused; + break; + } + } + { + int unused; + } +}