//===- CheckerManager.h - Static Analyzer Checker Manager -------*- 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
//
//===----------------------------------------------------------------------===//
//
// Defines the Static Analyzer Checker Manager.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H
#define LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H

#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <vector>

namespace clang {

class AnalyzerOptions;
class CallExpr;
class Decl;
class LocationContext;
class Stmt;
class TranslationUnitDecl;

namespace ento {

class AnalysisManager;
class CXXAllocatorCall;
class BugReporter;
class CallEvent;
class CheckerFrontend;
class CheckerBackend;
class CheckerContext;
class CheckerRegistry;
struct CheckerRegistryData;
class ExplodedGraph;
class ExplodedNode;
class ExplodedNodeSet;
class ExprEngine;
struct EvalCallOptions;
class MemRegion;
class NodeBuilderContext;
class ObjCMethodCall;
class RegionAndSymbolInvalidationTraits;
class SVal;
class SymbolReaper;

template <typename T> class CheckerFn;

template <typename RET, typename... Ps>
class CheckerFn<RET(Ps...)> {
  using Func = RET (*)(void *, Ps...);

  Func Fn;

public:
  CheckerBackend *Checker;

  CheckerFn(CheckerBackend *checker, Func fn) : Fn(fn), Checker(checker) {}

  RET operator()(Ps... ps) const {
    return Fn(Checker, ps...);
  }
};

/// Describes the different reasons a pointer escapes
/// during analysis.
enum PointerEscapeKind {
  /// A pointer escapes due to binding its value to a location
  /// that the analyzer cannot track.
  PSK_EscapeOnBind,

  /// The pointer has been passed to a function call directly.
  PSK_DirectEscapeOnCall,

  /// The pointer has been passed to a function indirectly.
  /// For example, the pointer is accessible through an
  /// argument to a function.
  PSK_IndirectEscapeOnCall,


  /// Escape for a new symbol that was generated into a region
  /// that the analyzer cannot follow during a conservative call.
  PSK_EscapeOutParameters,

  /// The reason for pointer escape is unknown. For example,
  /// a region containing this pointer is invalidated.
  PSK_EscapeOther
};

/// This wrapper is used to ensure that only StringRefs originating from the
/// CheckerRegistry are used as check names. We want to make sure all checker
/// name strings have a lifetime that keeps them alive at least until the path
/// diagnostics have been processed, since they are expected to be constexpr
/// string literals (most likely generated by TblGen).
class CheckerNameRef {
  friend class ::clang::ento::CheckerRegistry;

  StringRef Name;

  explicit CheckerNameRef(StringRef Name) : Name(Name) {}

public:
  CheckerNameRef() = default;

  operator StringRef() const { return Name; }
};

enum class ObjCMessageVisitKind {
  Pre,
  Post,
  MessageNil
};

class CheckerManager {
  ASTContext *Context = nullptr;
  const LangOptions LangOpts;
  const AnalyzerOptions &AOptions;
  const Preprocessor *PP = nullptr;
  CheckerNameRef CurrentCheckerName;
  DiagnosticsEngine &Diags;
  std::unique_ptr<CheckerRegistryData> RegistryData;

public:
  // These constructors are defined in the Frontend library, because
  // CheckerRegistry, a crucial component of the initialization is in there.
  // CheckerRegistry cannot be moved to the Core library, because the checker
  // registration functions are defined in the Checkers library, and the library
  // dependencies look like this: Core -> Checkers -> Frontend.

  CheckerManager(
      ASTContext &Context, AnalyzerOptions &AOptions, const Preprocessor &PP,
      ArrayRef<std::string> plugins,
      ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns);

  /// Constructs a CheckerManager that ignores all non TblGen-generated
  /// checkers. Useful for unit testing, unless the checker infrastructure
  /// itself is tested.
  CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions,
                 const Preprocessor &PP)
      : CheckerManager(Context, AOptions, PP, {}, {}) {}

  /// Constructs a CheckerManager without requiring an AST. No checker
  /// registration will take place. Only useful when one needs to print the
  /// help flags through CheckerRegistryData, and the AST is unavailable.
  CheckerManager(AnalyzerOptions &AOptions, const LangOptions &LangOpts,
                 DiagnosticsEngine &Diags, ArrayRef<std::string> plugins);

  ~CheckerManager();

  void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; }
  CheckerNameRef getCurrentCheckerName() const { return CurrentCheckerName; }

  bool hasPathSensitiveCheckers() const;

  const LangOptions &getLangOpts() const { return LangOpts; }
  const AnalyzerOptions &getAnalyzerOptions() const { return AOptions; }
  const Preprocessor &getPreprocessor() const {
    assert(PP);
    return *PP;
  }
  const CheckerRegistryData &getCheckerRegistryData() const {
    return *RegistryData;
  }
  DiagnosticsEngine &getDiagnostics() const { return Diags; }
  ASTContext &getASTContext() const {
    assert(Context);
    return *Context;
  }

  /// Emits an error through a DiagnosticsEngine about an invalid user supplied
  /// checker option value.
  void reportInvalidCheckerOptionValue(const CheckerFrontend *Checker,
                                       StringRef OptionName,
                                       StringRef ExpectedValueDesc) const;

  using CheckerTag = const void *;

  //===--------------------------------------------------------------------===//
  // Checker registration.
  //===--------------------------------------------------------------------===//

  /// If the the singleton instance of a checker class is not yet constructed,
  /// then construct it (with the supplied arguments), register it for the
  /// callbacks that are supported by it, and return it. Otherwise, just return
  /// a pointer to the existing instance.
  template <typename CHECKER, typename... AT>
  CHECKER *getChecker(AT &&...Args) {
    CheckerTag Tag = getTag<CHECKER>();

    std::unique_ptr<CheckerBackend> &Ref = CheckerTags[Tag];
    if (!Ref) {
      std::unique_ptr<CHECKER> Checker =
          std::make_unique<CHECKER>(std::forward<AT>(Args)...);
      CHECKER::_register(Checker.get(), *this);
      Ref = std::move(Checker);
    }

    return static_cast<CHECKER *>(Ref.get());
  }

  /// Register a single-part checker (derived from `Checker`): construct its
  /// singleton instance, register it for the supported callbacks and record
  /// its name (with `CheckerFrontend::enable`). Calling this multiple times
  /// triggers an assertion failure.
  template <typename CHECKER, typename... AT>
  CHECKER *registerChecker(AT &&...Args) {
    CHECKER *Chk = getChecker<CHECKER>(std::forward<AT>(Args)...);
    Chk->enable(*this);
    return Chk;
  }

  template <typename CHECKER> bool isRegisteredChecker() {
    return CheckerTags.contains(getTag<CHECKER>());
  }

//===----------------------------------------------------------------------===//
// Functions for running checkers for AST traversing.
//===----------------------------------------------------------------------===//

  /// Run checkers handling Decls.
  void runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
                            BugReporter &BR);

  /// Run checkers handling Decls containing a Stmt body.
  void runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
                            BugReporter &BR);

//===----------------------------------------------------------------------===//
// Functions for running checkers for path-sensitive checking.
//===----------------------------------------------------------------------===//

  /// Run checkers for pre-visiting Stmts.
  ///
  /// The notification is performed for every explored CFGElement, which does
  /// not include the control flow statements such as IfStmt.
  ///
  /// \sa runCheckersForBranchCondition, runCheckersForPostStmt
  void runCheckersForPreStmt(ExplodedNodeSet &Dst,
                             const ExplodedNodeSet &Src,
                             const Stmt *S,
                             ExprEngine &Eng) {
    runCheckersForStmt(/*isPreVisit=*/true, Dst, Src, S, Eng);
  }

  /// Run checkers for post-visiting Stmts.
  ///
  /// The notification is performed for every explored CFGElement, which does
  /// not include the control flow statements such as IfStmt.
  ///
  /// \sa runCheckersForBranchCondition, runCheckersForPreStmt
  void runCheckersForPostStmt(ExplodedNodeSet &Dst,
                              const ExplodedNodeSet &Src,
                              const Stmt *S,
                              ExprEngine &Eng,
                              bool wasInlined = false) {
    runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng, wasInlined);
  }

  /// Run checkers for visiting Stmts.
  void runCheckersForStmt(bool isPreVisit,
                          ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
                          const Stmt *S, ExprEngine &Eng,
                          bool wasInlined = false);

  /// Run checkers for pre-visiting obj-c messages.
  void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst,
                                    const ExplodedNodeSet &Src,
                                    const ObjCMethodCall &msg,
                                    ExprEngine &Eng) {
    runCheckersForObjCMessage(ObjCMessageVisitKind::Pre, Dst, Src, msg, Eng);
  }

  /// Run checkers for post-visiting obj-c messages.
  void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst,
                                     const ExplodedNodeSet &Src,
                                     const ObjCMethodCall &msg,
                                     ExprEngine &Eng,
                                     bool wasInlined = false) {
    runCheckersForObjCMessage(ObjCMessageVisitKind::Post, Dst, Src, msg, Eng,
                              wasInlined);
  }

  /// Run checkers for visiting an obj-c message to nil.
  void runCheckersForObjCMessageNil(ExplodedNodeSet &Dst,
                                    const ExplodedNodeSet &Src,
                                    const ObjCMethodCall &msg,
                                    ExprEngine &Eng) {
    runCheckersForObjCMessage(ObjCMessageVisitKind::MessageNil, Dst, Src, msg,
                              Eng);
  }

  /// Run checkers for visiting obj-c messages.
  void runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
                                 ExplodedNodeSet &Dst,
                                 const ExplodedNodeSet &Src,
                                 const ObjCMethodCall &msg, ExprEngine &Eng,
                                 bool wasInlined = false);

  /// Run checkers for pre-visiting obj-c messages.
  void runCheckersForPreCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
                             const CallEvent &Call, ExprEngine &Eng) {
    runCheckersForCallEvent(/*isPreVisit=*/true, Dst, Src, Call, Eng);
  }

  /// Run checkers for post-visiting obj-c messages.
  void runCheckersForPostCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
                              const CallEvent &Call, ExprEngine &Eng,
                              bool wasInlined = false) {
    runCheckersForCallEvent(/*isPreVisit=*/false, Dst, Src, Call, Eng,
                            wasInlined);
  }

  /// Run checkers for visiting obj-c messages.
  void runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst,
                               const ExplodedNodeSet &Src,
                               const CallEvent &Call, ExprEngine &Eng,
                               bool wasInlined = false);

  /// Run checkers for load/store of a location.
  void runCheckersForLocation(ExplodedNodeSet &Dst,
                              const ExplodedNodeSet &Src,
                              SVal location,
                              bool isLoad,
                              const Stmt *NodeEx,
                              const Stmt *BoundEx,
                              ExprEngine &Eng);

  /// Run checkers for binding of a value to a location.
  void runCheckersForBind(ExplodedNodeSet &Dst,
                          const ExplodedNodeSet &Src,
                          SVal location, SVal val,
                          const Stmt *S, ExprEngine &Eng,
                          const ProgramPoint &PP);

  /// Run checkers after taking a control flow edge.
  void runCheckersForBlockEntrance(ExplodedNodeSet &Dst,
                                   const ExplodedNodeSet &Src,
                                   const BlockEntrance &Entrance,
                                   ExprEngine &Eng) const;

  /// Run checkers for end of analysis.
  void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
                                 ExprEngine &Eng);

  /// Run checkers on beginning of function.
  void runCheckersForBeginFunction(ExplodedNodeSet &Dst,
                                   const BlockEdge &L,
                                   ExplodedNode *Pred,
                                   ExprEngine &Eng);

  /// Run checkers on end of function.
  void runCheckersForEndFunction(NodeBuilderContext &BC,
                                 ExplodedNodeSet &Dst,
                                 ExplodedNode *Pred,
                                 ExprEngine &Eng,
                                 const ReturnStmt *RS);

  /// Run checkers for branch condition.
  void runCheckersForBranchCondition(const Stmt *condition,
                                     ExplodedNodeSet &Dst, ExplodedNode *Pred,
                                     ExprEngine &Eng);

  /// Run checkers between C++ operator new and constructor calls.
  void runCheckersForNewAllocator(const CXXAllocatorCall &Call,
                                  ExplodedNodeSet &Dst, ExplodedNode *Pred,
                                  ExprEngine &Eng, bool wasInlined = false);

  /// Run checkers for live symbols.
  ///
  /// Allows modifying SymbolReaper object. For example, checkers can explicitly
  /// register symbols of interest as live. These symbols will not be marked
  /// dead and removed.
  void runCheckersForLiveSymbols(ProgramStateRef state,
                                 SymbolReaper &SymReaper);

  /// Run checkers for dead symbols.
  ///
  /// Notifies checkers when symbols become dead. For example, this allows
  /// checkers to aggressively clean up/reduce the checker state and produce
  /// precise diagnostics.
  void runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
                                 const ExplodedNodeSet &Src,
                                 SymbolReaper &SymReaper, const Stmt *S,
                                 ExprEngine &Eng,
                                 ProgramPoint::Kind K);

  /// Run checkers for region changes.
  ///
  /// This corresponds to the check::RegionChanges callback.
  /// \param state The current program state.
  /// \param invalidated A set of all symbols potentially touched by the change.
  /// \param ExplicitRegions The regions explicitly requested for invalidation.
  ///   For example, in the case of a function call, these would be arguments.
  /// \param Regions The transitive closure of accessible regions,
  ///   i.e. all regions that may have been touched by this change.
  /// \param Call The call expression wrapper if the regions are invalidated
  ///   by a call.
  ProgramStateRef
  runCheckersForRegionChanges(ProgramStateRef state,
                              const InvalidatedSymbols *invalidated,
                              ArrayRef<const MemRegion *> ExplicitRegions,
                              ArrayRef<const MemRegion *> Regions,
                              const LocationContext *LCtx,
                              const CallEvent *Call);

  /// Run checkers when pointers escape.
  ///
  /// This notifies the checkers about pointer escape, which occurs whenever
  /// the analyzer cannot track the symbol any more. For example, as a
  /// result of assigning a pointer into a global or when it's passed to a
  /// function call the analyzer cannot model.
  ///
  /// \param State The state at the point of escape.
  /// \param Escaped The list of escaped symbols.
  /// \param Call The corresponding CallEvent, if the symbols escape as
  ///        parameters to the given call.
  /// \param Kind The reason of pointer escape.
  /// \param ITraits Information about invalidation for a particular
  ///        region/symbol.
  /// \returns Checkers can modify the state by returning a new one.
  ProgramStateRef
  runCheckersForPointerEscape(ProgramStateRef State,
                              const InvalidatedSymbols &Escaped,
                              const CallEvent *Call,
                              PointerEscapeKind Kind,
                              RegionAndSymbolInvalidationTraits *ITraits);

  /// Run checkers for handling assumptions on symbolic values.
  ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state,
                                           SVal Cond, bool Assumption);

  /// Run checkers for evaluating a call.
  ///
  /// Warning: Currently, the CallEvent MUST come from a CallExpr!
  void runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
                              const CallEvent &CE, ExprEngine &Eng,
                              const EvalCallOptions &CallOpts);

  /// Run checkers for the entire Translation Unit.
  void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU,
                                         AnalysisManager &mgr,
                                         BugReporter &BR);

  /// Run checkers for debug-printing a ProgramState.
  ///
  /// Unlike most other callbacks, any checker can simply implement the virtual
  /// method CheckerBackend::printState if it has custom data to print.
  ///
  /// \param Out   The output stream
  /// \param State The state being printed
  /// \param NL    The preferred representation of a newline.
  /// \param Space The preferred space between the left side and the message.
  /// \param IsDot Whether the message will be printed in 'dot' format.
  void runCheckersForPrintStateJson(raw_ostream &Out, ProgramStateRef State,
                                    const char *NL = "\n",
                                    unsigned int Space = 0,
                                    bool IsDot = false) const;

  //===--------------------------------------------------------------------===//
  // Internal registration functions for AST traversing.
  //===--------------------------------------------------------------------===//

  // Functions used by the registration mechanism, checkers should not touch
  // these directly.

  using CheckDeclFunc =
      CheckerFn<void (const Decl *, AnalysisManager&, BugReporter &)>;

  using HandlesDeclFunc = bool (*)(const Decl *D);

  void _registerForDecl(CheckDeclFunc checkfn, HandlesDeclFunc isForDeclFn);

  void _registerForBody(CheckDeclFunc checkfn);

  //===--------------------------------------------------------------------===//
  // Internal registration functions for path-sensitive checking.
  //===--------------------------------------------------------------------===//

  using CheckStmtFunc = CheckerFn<void (const Stmt *, CheckerContext &)>;

  using CheckObjCMessageFunc =
      CheckerFn<void (const ObjCMethodCall &, CheckerContext &)>;

  using CheckCallFunc =
      CheckerFn<void (const CallEvent &, CheckerContext &)>;

  using CheckLocationFunc = CheckerFn<void(SVal location, bool isLoad,
                                           const Stmt *S, CheckerContext &)>;

  using CheckBindFunc =
      CheckerFn<void(SVal location, SVal val, const Stmt *S, CheckerContext &)>;

  using CheckBlockEntranceFunc =
      CheckerFn<void(const BlockEntrance &, CheckerContext &)>;

  using CheckEndAnalysisFunc =
      CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>;

  using CheckBeginFunctionFunc = CheckerFn<void (CheckerContext &)>;

  using CheckEndFunctionFunc =
      CheckerFn<void (const ReturnStmt *, CheckerContext &)>;

  using CheckBranchConditionFunc =
      CheckerFn<void (const Stmt *, CheckerContext &)>;

  using CheckNewAllocatorFunc =
      CheckerFn<void(const CXXAllocatorCall &Call, CheckerContext &)>;

  using CheckDeadSymbolsFunc =
      CheckerFn<void (SymbolReaper &, CheckerContext &)>;

  using CheckLiveSymbolsFunc = CheckerFn<void (ProgramStateRef,SymbolReaper &)>;

  using CheckRegionChangesFunc =
      CheckerFn<ProgramStateRef (ProgramStateRef,
                                 const InvalidatedSymbols *symbols,
                                 ArrayRef<const MemRegion *> ExplicitRegions,
                                 ArrayRef<const MemRegion *> Regions,
                                 const LocationContext *LCtx,
                                 const CallEvent *Call)>;

  using CheckPointerEscapeFunc =
      CheckerFn<ProgramStateRef (ProgramStateRef,
                                 const InvalidatedSymbols &Escaped,
                                 const CallEvent *Call, PointerEscapeKind Kind,
                                 RegionAndSymbolInvalidationTraits *ITraits)>;

  using EvalAssumeFunc =
      CheckerFn<ProgramStateRef(ProgramStateRef, SVal cond, bool assumption)>;

  using EvalCallFunc = CheckerFn<bool (const CallEvent &, CheckerContext &)>;

  using CheckEndOfTranslationUnit =
      CheckerFn<void (const TranslationUnitDecl *, AnalysisManager &,
                      BugReporter &)>;

  using HandlesStmtFunc = bool (*)(const Stmt *D);

  void _registerForPreStmt(CheckStmtFunc checkfn,
                           HandlesStmtFunc isForStmtFn);
  void _registerForPostStmt(CheckStmtFunc checkfn,
                            HandlesStmtFunc isForStmtFn);

  void _registerForPreObjCMessage(CheckObjCMessageFunc checkfn);
  void _registerForPostObjCMessage(CheckObjCMessageFunc checkfn);

  void _registerForObjCMessageNil(CheckObjCMessageFunc checkfn);

  void _registerForPreCall(CheckCallFunc checkfn);
  void _registerForPostCall(CheckCallFunc checkfn);

  void _registerForLocation(CheckLocationFunc checkfn);

  void _registerForBind(CheckBindFunc checkfn);

  void _registerForBlockEntrance(CheckBlockEntranceFunc checkfn);

  void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);

  void _registerForBeginFunction(CheckBeginFunctionFunc checkfn);
  void _registerForEndFunction(CheckEndFunctionFunc checkfn);

  void _registerForBranchCondition(CheckBranchConditionFunc checkfn);

  void _registerForNewAllocator(CheckNewAllocatorFunc checkfn);

  void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn);

  void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn);

  void _registerForRegionChanges(CheckRegionChangesFunc checkfn);

  void _registerForPointerEscape(CheckPointerEscapeFunc checkfn);

  void _registerForConstPointerEscape(CheckPointerEscapeFunc checkfn);

  void _registerForEvalAssume(EvalAssumeFunc checkfn);

  void _registerForEvalCall(EvalCallFunc checkfn);

  void _registerForEndOfTranslationUnit(CheckEndOfTranslationUnit checkfn);

  //===--------------------------------------------------------------------===//
  // Internal registration functions for events.
  //===--------------------------------------------------------------------===//

  using EventTag = void *;
  using CheckEventFunc = CheckerFn<void (const void *event)>;

  template <typename EVENT>
  void _registerListenerForEvent(CheckEventFunc checkfn) {
    EventInfo &info = Events[&EVENT::Tag];
    info.Checkers.push_back(checkfn);
  }

  template <typename EVENT>
  void _registerDispatcherForEvent() {
    EventInfo &info = Events[&EVENT::Tag];
    info.HasDispatcher = true;
  }

  template <typename EVENT>
  void _dispatchEvent(const EVENT &event) const {
    EventsTy::const_iterator I = Events.find(&EVENT::Tag);
    if (I == Events.end())
      return;
    const EventInfo &info = I->second;
    for (const auto &Checker : info.Checkers)
      Checker(&event);
  }

  //===--------------------------------------------------------------------===//
  // Implementation details.
  //===--------------------------------------------------------------------===//

private:
  template <typename T>
  static void *getTag() { static int tag; return &tag; }

  llvm::DenseMap<CheckerTag, std::unique_ptr<CheckerBackend>> CheckerTags;

  struct DeclCheckerInfo {
    CheckDeclFunc CheckFn;
    HandlesDeclFunc IsForDeclFn;
  };
  std::vector<DeclCheckerInfo> DeclCheckers;

  std::vector<CheckDeclFunc> BodyCheckers;

  using CachedDeclCheckers = SmallVector<CheckDeclFunc, 4>;
  using CachedDeclCheckersMapTy = llvm::DenseMap<unsigned, CachedDeclCheckers>;
  CachedDeclCheckersMapTy CachedDeclCheckersMap;

  struct StmtCheckerInfo {
    CheckStmtFunc CheckFn;
    HandlesStmtFunc IsForStmtFn;
    bool IsPreVisit;
  };
  std::vector<StmtCheckerInfo> StmtCheckers;

  using CachedStmtCheckers = SmallVector<CheckStmtFunc, 4>;
  using CachedStmtCheckersMapTy = llvm::DenseMap<unsigned, CachedStmtCheckers>;
  CachedStmtCheckersMapTy CachedStmtCheckersMap;

  const CachedStmtCheckers &getCachedStmtCheckersFor(const Stmt *S,
                                                     bool isPreVisit);

  /// Returns the checkers that have registered for callbacks of the
  /// given \p Kind.
  const std::vector<CheckObjCMessageFunc> &
  getObjCMessageCheckers(ObjCMessageVisitKind Kind) const;

  std::vector<CheckObjCMessageFunc> PreObjCMessageCheckers;
  std::vector<CheckObjCMessageFunc> PostObjCMessageCheckers;
  std::vector<CheckObjCMessageFunc> ObjCMessageNilCheckers;

  std::vector<CheckCallFunc> PreCallCheckers;
  std::vector<CheckCallFunc> PostCallCheckers;

  std::vector<CheckLocationFunc> LocationCheckers;

  std::vector<CheckBindFunc> BindCheckers;

  std::vector<CheckBlockEntranceFunc> BlockEntranceCheckers;

  std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;

  std::vector<CheckBeginFunctionFunc> BeginFunctionCheckers;
  std::vector<CheckEndFunctionFunc> EndFunctionCheckers;

  std::vector<CheckBranchConditionFunc> BranchConditionCheckers;

  std::vector<CheckNewAllocatorFunc> NewAllocatorCheckers;

  std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers;

  std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers;

  std::vector<CheckRegionChangesFunc> RegionChangesCheckers;

  std::vector<CheckPointerEscapeFunc> PointerEscapeCheckers;

  std::vector<EvalAssumeFunc> EvalAssumeCheckers;

  std::vector<EvalCallFunc> EvalCallCheckers;

  std::vector<CheckEndOfTranslationUnit> EndOfTranslationUnitCheckers;

  struct EventInfo {
    SmallVector<CheckEventFunc, 4> Checkers;
    bool HasDispatcher = false;

    EventInfo() = default;
  };

  using EventsTy = llvm::DenseMap<EventTag, EventInfo>;
  EventsTy Events;
};

} // namespace ento

} // namespace clang

#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERMANAGER_H
