//===- AnalysisDeclContext.h - Context for path sensitivity -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file /// This file defines AnalysisDeclContext, a class that manages the analysis /// context data for context sensitive and path sensitive analysis. /// It also defines the helper classes to model entering, leaving or inlining /// function calls. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_ANALYSISDECLCONTEXT_H #define LLVM_CLANG_ANALYSIS_ANALYSISDECLCONTEXT_H #include "clang/AST/DeclBase.h" #include "clang/Analysis/BodyFarm.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CodeInjector.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Allocator.h" #include #include namespace clang { class AnalysisDeclContextManager; class ASTContext; class BlockDecl; class BlockInvocationContext; class CFGReverseBlockReachabilityAnalysis; class CFGStmtMap; class ImplicitParamDecl; class LocationContext; class LocationContextManager; class ParentMap; class StackFrameContext; class Stmt; class VarDecl; /// The base class of a hierarchy of objects representing analyses tied /// to AnalysisDeclContext. class ManagedAnalysis { protected: ManagedAnalysis() = default; public: virtual ~ManagedAnalysis(); // Subclasses need to implement: // // static const void *getTag(); // // Which returns a fixed pointer address to distinguish classes of // analysis objects. They also need to implement: // // static [Derived*] create(AnalysisDeclContext &Ctx); // // which creates the analysis object given an AnalysisDeclContext. }; /// AnalysisDeclContext contains the context data for the function, method /// or block under analysis. class AnalysisDeclContext { // Backpoint to the AnalysisManager object that created this // AnalysisDeclContext. This may be null. AnalysisDeclContextManager *ADCMgr; const Decl *const D; std::unique_ptr cfg, completeCFG; std::unique_ptr cfgStmtMap; CFG::BuildOptions cfgBuildOptions; CFG::BuildOptions::ForcedBlkExprs *forcedBlkExprs = nullptr; bool builtCFG = false; bool builtCompleteCFG = false; std::unique_ptr PM; std::unique_ptr CFA; llvm::BumpPtrAllocator A; llvm::DenseMap *ReferencedBlockVars = nullptr; void *ManagedAnalyses = nullptr; public: AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *D); AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *D, const CFG::BuildOptions &BuildOptions); ~AnalysisDeclContext(); ASTContext &getASTContext() const { return D->getASTContext(); } const Decl *getDecl() const { return D; } AnalysisDeclContextManager *getManager() const { return ADCMgr; } CFG::BuildOptions &getCFGBuildOptions() { return cfgBuildOptions; } const CFG::BuildOptions &getCFGBuildOptions() const { return cfgBuildOptions; } /// \returns Whether we are adding exception handling edges from CallExprs. /// If this is false, then try/catch statements and blocks reachable from them /// can appear to be dead in the CFG, analysis passes must cope with that. bool getAddEHEdges() const { return cfgBuildOptions.AddEHEdges; } bool getUseUnoptimizedCFG() const { return !cfgBuildOptions.PruneTriviallyFalseEdges; } bool getAddImplicitDtors() const { return cfgBuildOptions.AddImplicitDtors; } bool getAddInitializers() const { return cfgBuildOptions.AddInitializers; } void registerForcedBlockExpression(const Stmt *stmt); const CFGBlock *getBlockForRegisteredExpression(const Stmt *stmt); /// \returns The body of the stored Decl \c D. Stmt *getBody() const; /// \copydoc AnalysisDeclContext::getBody() /// \param[out] IsAutosynthesized Specifies if the body is auto-generated /// by the BodyFarm. Stmt *getBody(bool &IsAutosynthesized) const; /// \returns Whether the body of the Decl \c D is generated by the BodyFarm. /// /// \note The lookup is not free. We are going to call getBody behind /// the scenes. /// \sa getBody bool isBodyAutosynthesized() const; /// \returns Whether the body of the Decl \c D is generated by the BodyFarm /// from a model file. /// /// \note The lookup is not free. We are going to call getBody behind /// the scenes. /// \sa getBody bool isBodyAutosynthesizedFromModelFile() const; CFG *getCFG(); CFGStmtMap *getCFGStmtMap(); CFGReverseBlockReachabilityAnalysis *getCFGReachablityAnalysis(); /// \returns A version of the CFG without any edges pruned. CFG *getUnoptimizedCFG(); void dumpCFG(bool ShowColors); /// \returns Whether we have built a CFG for this analysis context. /// /// \note This doesn't correspond to whether or not a valid CFG exists, it /// corresponds to whether we *attempted* to build one. bool isCFGBuilt() const { return builtCFG; } ParentMap &getParentMap(); using referenced_decls_iterator = const VarDecl *const *; llvm::iterator_range getReferencedBlockVars(const BlockDecl *BD); /// \returns The ImplicitParamDecl associated with \c self if this /// AnalysisDeclContext wraps an ObjCMethodDecl or nullptr otherwise. const ImplicitParamDecl *getSelfDecl() const; /// \copydoc LocationContextManager::getStackFrame() const StackFrameContext *getStackFrame(LocationContext const *ParentLC, const Stmt *S, const CFGBlock *Blk, unsigned BlockCount, unsigned Index); /// \copydoc LocationContextManager::getBlockInvocationContext() const BlockInvocationContext * getBlockInvocationContext(const LocationContext *ParentLC, const BlockDecl *BD, const void *Data); /// \returns The specified analysis object, lazily running the analysis if /// necessary or nullptr if the analysis could not run. template T *getAnalysis() { const void *tag = T::getTag(); std::unique_ptr &data = getAnalysisImpl(tag); if (!data) data = T::create(*this); return static_cast(data.get()); } /// \returns Whether the root namespace of \p D is the \c std C++ namespace. static bool isInStdNamespace(const Decl *D); static std::string getFunctionName(const Decl *D); private: std::unique_ptr &getAnalysisImpl(const void *tag); LocationContextManager &getLocationContextManager(); }; /// It wraps the AnalysisDeclContext to represent both the call stack with /// the help of StackFrameContext and inside the function calls the /// BlockInvocationContext. It is needed for context sensitive analysis to /// model entering, leaving or inlining function calls. class LocationContext : public llvm::FoldingSetNode { public: enum ContextKind { StackFrame, Block }; private: ContextKind Kind; // AnalysisDeclContext can't be const since some methods may modify its // member. AnalysisDeclContext *Ctx; const LocationContext *Parent; int64_t ID; protected: LocationContext(ContextKind k, AnalysisDeclContext *ctx, const LocationContext *parent, int64_t ID) : Kind(k), Ctx(ctx), Parent(parent), ID(ID) { assert(ctx); } public: virtual ~LocationContext(); ContextKind getKind() const { return Kind; } int64_t getID() const { return ID; } LLVM_ATTRIBUTE_RETURNS_NONNULL AnalysisDeclContext *getAnalysisDeclContext() const { return Ctx; } /// It might return null. const LocationContext *getParent() const { return Parent; } bool isParentOf(const LocationContext *LC) const; const Decl *getDecl() const { return Ctx->getDecl(); } CFG *getCFG() const { return Ctx->getCFG(); } template T *getAnalysis() const { return Ctx->getAnalysis(); } const ParentMap &getParentMap() const { return Ctx->getParentMap(); } /// \copydoc AnalysisDeclContext::getSelfDecl() const ImplicitParamDecl *getSelfDecl() const { return Ctx->getSelfDecl(); } const StackFrameContext *getStackFrame() const; /// \returns Whether the current LocationContext has no caller context. virtual bool inTopFrame() const; virtual void Profile(llvm::FoldingSetNodeID &ID) = 0; /// Prints out the call stack. /// /// \param Out The out stream. LLVM_DUMP_METHOD void dumpStack(raw_ostream &Out) const; /// Prints out the call stack in \c json format. /// /// \param Out The out stream. /// \param NL The newline. /// \param Space The space count for indentation. /// \param IsDot Whether the output format is \c dot. /// \param printMoreInfoPerContext /// A callback to print more information for each context, for example: /// \code /// [&](const LocationContext *LC) { LC->dump(); } /// \endcode void printJson( raw_ostream &Out, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false, std::function printMoreInfoPerContext = [](const LocationContext *) {}) const; LLVM_DUMP_METHOD void dump() const; static void ProfileCommon(llvm::FoldingSetNodeID &ID, ContextKind ck, AnalysisDeclContext *ctx, const LocationContext *parent, const void *data); }; /// It represents a stack frame of the call stack (based on CallEvent). class StackFrameContext : public LocationContext { friend class LocationContextManager; // The call site where this stack frame is established. const Stmt *CallSite; // The parent block of the call site. const CFGBlock *Block; // The number of times the 'Block' has been visited. // It allows discriminating between stack frames of the same call that is // called multiple times in a loop. const unsigned BlockCount; // The index of the call site in the CFGBlock. const unsigned Index; StackFrameContext(AnalysisDeclContext *ADC, const LocationContext *ParentLC, const Stmt *S, const CFGBlock *Block, unsigned BlockCount, unsigned Index, int64_t ID) : LocationContext(StackFrame, ADC, ParentLC, ID), CallSite(S), Block(Block), BlockCount(BlockCount), Index(Index) {} public: ~StackFrameContext() override = default; const Stmt *getCallSite() const { return CallSite; } const CFGBlock *getCallSiteBlock() const { return Block; } bool inTopFrame() const override { return getParent() == nullptr; } unsigned getIndex() const { return Index; } CFGElement getCallSiteCFGElement() const { return (*Block)[Index]; } void Profile(llvm::FoldingSetNodeID &ID) override; static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ADC, const LocationContext *ParentLC, const Stmt *S, const CFGBlock *Block, unsigned BlockCount, unsigned Index) { ProfileCommon(ID, StackFrame, ADC, ParentLC, S); ID.AddPointer(Block); ID.AddInteger(BlockCount); ID.AddInteger(Index); } static bool classof(const LocationContext *LC) { return LC->getKind() == StackFrame; } }; /// It represents a block invocation (based on BlockCall). class BlockInvocationContext : public LocationContext { friend class LocationContextManager; const BlockDecl *BD; // FIXME: Come up with a more type-safe way to model context-sensitivity. const void *Data; BlockInvocationContext(AnalysisDeclContext *ADC, const LocationContext *ParentLC, const BlockDecl *BD, const void *Data, int64_t ID) : LocationContext(Block, ADC, ParentLC, ID), BD(BD), Data(Data) {} public: ~BlockInvocationContext() override = default; const BlockDecl *getBlockDecl() const { return BD; } const void *getData() const { return Data; } void Profile(llvm::FoldingSetNodeID &ID) override; static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ADC, const LocationContext *ParentLC, const BlockDecl *BD, const void *Data) { ProfileCommon(ID, Block, ADC, ParentLC, BD); ID.AddPointer(Data); } static bool classof(const LocationContext *LC) { return LC->getKind() == Block; } }; class LocationContextManager { llvm::FoldingSet Contexts; // ID used for generating a new location context. int64_t NewID = 0; public: ~LocationContextManager(); /// Obtain a context of the call stack using its parent context. /// /// \param ADC The AnalysisDeclContext. /// \param ParentLC The parent context of this newly created context. /// \param S The call. /// \param Block The basic block. /// \param BlockCount The current count of entering into \p Blk. /// \param Index The index of \p Blk. /// \returns The context for \p D with parent context \p ParentLC. const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC, const LocationContext *ParentLC, const Stmt *S, const CFGBlock *Block, unsigned BlockCount, unsigned Index); /// Obtain a context of the block invocation using its parent context. /// /// \param ADC The AnalysisDeclContext. /// \param ParentLC The parent context of this newly created context. /// \param BD The BlockDecl. /// \param Data The raw data to store as part of the context. const BlockInvocationContext * getBlockInvocationContext(AnalysisDeclContext *ADC, const LocationContext *ParentLC, const BlockDecl *BD, const void *Data); /// Discard all previously created LocationContext objects. void clear(); }; class AnalysisDeclContextManager { using ContextMap = llvm::DenseMap>; ContextMap Contexts; LocationContextManager LocCtxMgr; CFG::BuildOptions cfgBuildOptions; // Pointer to an interface that can provide function bodies for // declarations from external source. std::unique_ptr Injector; // A factory for creating and caching implementations for common // methods during the analysis. BodyFarm FunctionBodyFarm; // Flag to indicate whether or not bodies should be synthesized // for well-known functions. bool SynthesizeBodies; public: AnalysisDeclContextManager( ASTContext &ASTCtx, bool useUnoptimizedCFG = false, bool addImplicitDtors = false, bool addInitializers = false, bool addTemporaryDtors = false, bool addLifetime = false, bool addLoopExit = false, bool addScopes = false, bool synthesizeBodies = false, bool addStaticInitBranches = false, bool addCXXNewAllocator = true, bool addRichCXXConstructors = true, bool markElidedCXXConstructors = true, bool addVirtualBaseBranches = true, CodeInjector *injector = nullptr); AnalysisDeclContext *getContext(const Decl *D); bool getUseUnoptimizedCFG() const { return !cfgBuildOptions.PruneTriviallyFalseEdges; } CFG::BuildOptions &getCFGBuildOptions() { return cfgBuildOptions; } /// \returns Whether faux bodies should be synthesized for known functions. bool synthesizeBodies() const { return SynthesizeBodies; } /// Obtain the beginning context of the analysis. /// /// \returns The top level stack frame for \p D. const StackFrameContext *getStackFrame(const Decl *D) { return LocCtxMgr.getStackFrame(getContext(D), nullptr, nullptr, nullptr, 0, 0); } /// \copydoc LocationContextManager::getStackFrame() const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC, const LocationContext *Parent, const Stmt *S, const CFGBlock *Block, unsigned BlockCount, unsigned Index) { return LocCtxMgr.getStackFrame(ADC, Parent, S, Block, BlockCount, Index); } BodyFarm &getBodyFarm(); /// Discard all previously created AnalysisDeclContexts. void clear(); private: friend class AnalysisDeclContext; LocationContextManager &getLocationContextManager() { return LocCtxMgr; } }; } // namespace clang #endif // LLVM_CLANG_ANALYSIS_ANALYSISDECLCONTEXT_H