diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 | 
| commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
| tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /clang/lib/StaticAnalyzer/Frontend | |
| parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) | |
Notes
Diffstat (limited to 'clang/lib/StaticAnalyzer/Frontend')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 175 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp (renamed from clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp) | 62 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp | 494 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp | 50 | 
4 files changed, 302 insertions, 479 deletions
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fea8100c3b3bc..392049e21c6e5 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -12,7 +12,6 @@  #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"  #include "ModelInjector.h" -#include "clang/Analysis/PathDiagnostic.h"  #include "clang/AST/Decl.h"  #include "clang/AST/DeclCXX.h"  #include "clang/AST/DeclObjC.h" @@ -21,10 +20,12 @@  #include "clang/Analysis/CFG.h"  #include "clang/Analysis/CallGraph.h"  #include "clang/Analysis/CodeInjector.h" +#include "clang/Analysis/PathDiagnostic.h"  #include "clang/Basic/SourceManager.h"  #include "clang/CrossTU/CrossTranslationUnit.h"  #include "clang/Frontend/CompilerInstance.h"  #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h"  #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"  #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"  #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" @@ -32,7 +33,6 @@  #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"  #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"  #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"  #include "llvm/ADT/PostOrderIterator.h"  #include "llvm/ADT/Statistic.h"  #include "llvm/Support/FileSystem.h" @@ -61,114 +61,6 @@ STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");  STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");  //===----------------------------------------------------------------------===// -// Special PathDiagnosticConsumers. -//===----------------------------------------------------------------------===// - -void ento::createPlistHTMLDiagnosticConsumer( -    AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, -    const std::string &prefix, const Preprocessor &PP, -    const cross_tu::CrossTranslationUnitContext &CTU) { -  createHTMLDiagnosticConsumer(AnalyzerOpts, C, -                               llvm::sys::path::parent_path(prefix), PP, CTU); -  createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU); -} - -void ento::createTextPathDiagnosticConsumer( -    AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, -    const std::string &Prefix, const clang::Preprocessor &PP, -    const cross_tu::CrossTranslationUnitContext &CTU) { -  llvm_unreachable("'text' consumer should be enabled on ClangDiags"); -} - -namespace { -class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { -  DiagnosticsEngine &Diag; -  bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false; - -public: -  ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) -      : Diag(Diag) {} -  ~ClangDiagPathDiagConsumer() override {} -  StringRef getName() const override { return "ClangDiags"; } - -  bool supportsLogicalOpControlFlow() const override { return true; } -  bool supportsCrossFileDiagnostics() const override { return true; } - -  PathGenerationScheme getGenerationScheme() const override { -    return IncludePath ? Minimal : None; -  } - -  void enablePaths() { IncludePath = true; } -  void enableWerror() { ShouldEmitAsError = true; } -  void enableFixitsAsRemarks() { FixitsAsRemarks = true; } - -  void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, -                            FilesMade *filesMade) override { -    unsigned WarnID = -        ShouldEmitAsError -            ? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0") -            : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); -    unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0"); -    unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0"); - -    auto reportPiece = -        [&](unsigned ID, SourceLocation Loc, StringRef String, -            ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) { -          if (!FixitsAsRemarks) { -            Diag.Report(Loc, ID) << String << Ranges << Fixits; -          } else { -            Diag.Report(Loc, ID) << String << Ranges; -            for (const FixItHint &Hint : Fixits) { -              SourceManager &SM = Diag.getSourceManager(); -              llvm::SmallString<128> Str; -              llvm::raw_svector_ostream OS(Str); -              // FIXME: Add support for InsertFromRange and -              // BeforePreviousInsertion. -              assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!"); -              assert(!Hint.BeforePreviousInsertions && "Not implemented yet!"); -              OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin()) -                 << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd()) -                 << ": '" << Hint.CodeToInsert << "'"; -              Diag.Report(Loc, RemarkID) << OS.str(); -            } -          } -        }; - -    for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), -         E = Diags.end(); -         I != E; ++I) { -      const PathDiagnostic *PD = *I; -      reportPiece(WarnID, PD->getLocation().asLocation(), -                  PD->getShortDescription(), PD->path.back()->getRanges(), -                  PD->path.back()->getFixits()); - -      // First, add extra notes, even if paths should not be included. -      for (const auto &Piece : PD->path) { -        if (!isa<PathDiagnosticNotePiece>(Piece.get())) -          continue; - -        reportPiece(NoteID, Piece->getLocation().asLocation(), -                    Piece->getString(), Piece->getRanges(), Piece->getFixits()); -      } - -      if (!IncludePath) -        continue; - -      // Then, add the path notes if necessary. -      PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); -      for (const auto &Piece : FlatPath) { -        if (isa<PathDiagnosticNotePiece>(Piece.get())) -          continue; - -        reportPiece(NoteID, Piece->getLocation().asLocation(), -                    Piece->getString(), Piece->getRanges(), Piece->getFixits()); -      } -    } -  } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===//  // AnalysisConsumer declaration.  //===----------------------------------------------------------------------===// @@ -192,7 +84,7 @@ class AnalysisConsumer : public AnalysisASTConsumer,  public:    ASTContext *Ctx; -  const Preprocessor &PP; +  Preprocessor &PP;    const std::string OutDir;    AnalyzerOptionsRef Opts;    ArrayRef<std::string> Plugins; @@ -253,31 +145,16 @@ public:    }    void DigestAnalyzerOptions() { -    if (Opts->AnalysisDiagOpt != PD_NONE) { -      // Create the PathDiagnosticConsumer. -      ClangDiagPathDiagConsumer *clangDiags = -          new ClangDiagPathDiagConsumer(PP.getDiagnostics()); -      PathConsumers.push_back(clangDiags); - -      if (Opts->AnalyzerWerror) -        clangDiags->enableWerror(); - -      if (Opts->ShouldEmitFixItHintsAsRemarks) -        clangDiags->enableFixitsAsRemarks(); - -      if (Opts->AnalysisDiagOpt == PD_TEXT) { -        clangDiags->enablePaths(); - -      } else if (!OutDir.empty()) { -        switch (Opts->AnalysisDiagOpt) { -        default: +    switch (Opts->AnalysisDiagOpt) { +    case PD_NONE: +      break;  #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)                    \    case PD_##NAME:                                                              \      CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU);                     \      break;  #include "clang/StaticAnalyzer/Core/Analyses.def" -        } -      } +    default: +      llvm_unreachable("Unknown analyzer output type!");      }      // Create the analyzer component creators. @@ -313,30 +190,29 @@ public:        else if (Mode == AM_Path) {          llvm::errs() << " (Path, ";          switch (IMode) { -          case ExprEngine::Inline_Minimal: -            llvm::errs() << " Inline_Minimal"; -            break; -          case ExprEngine::Inline_Regular: -            llvm::errs() << " Inline_Regular"; -            break; +        case ExprEngine::Inline_Minimal: +          llvm::errs() << " Inline_Minimal"; +          break; +        case ExprEngine::Inline_Regular: +          llvm::errs() << " Inline_Regular"; +          break;          }          llvm::errs() << ")"; -      } -      else +      } else          assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!"); -      llvm::errs() << ": " << Loc.getFilename() << ' ' -                           << getFunctionName(D) << '\n'; +      llvm::errs() << ": " << Loc.getFilename() << ' ' << getFunctionName(D) +                   << '\n';      }    }    void Initialize(ASTContext &Context) override {      Ctx = &Context; -    checkerMgr = createCheckerManager( -        *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics()); +    checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins, +                                                  CheckerRegistrationFns); -    Mgr = std::make_unique<AnalysisManager>(*Ctx, PathConsumers, CreateStoreMgr, -                                            CreateConstraintMgr, +    Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers, +                                            CreateStoreMgr, CreateConstraintMgr,                                              checkerMgr.get(), *Opts, Injector);    } @@ -469,7 +345,7 @@ private:    /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.    void reportAnalyzerProgress(StringRef S); -}; +}; // namespace  } // end anonymous namespace @@ -503,6 +379,13 @@ static bool shouldSkipFunction(const Decl *D,    if (VisitedAsTopLevel.count(D))      return true; +  // Skip analysis of inheriting constructors as top-level functions. These +  // constructors don't even have a body written down in the code, so even if +  // we find a bug, we won't be able to display it. +  if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) +    if (CD->isInheritingConstructor()) +      return true; +    // We want to re-analyse the functions as top level in the following cases:    // - The 'init' methods should be reanalyzed because    //   ObjCNonNilReturnValueChecker assumes that '[super init]' never returns diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp index f4f06e32cd1d7..eb6014a0629df 100644 --- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp @@ -10,8 +10,9 @@  //  //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" +#include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h"  #include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h"  #include "clang/Frontend/FrontendDiagnostic.h"  #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"  #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -24,53 +25,36 @@  using namespace clang;  using namespace ento; -std::unique_ptr<CheckerManager> ento::createCheckerManager( -    ASTContext &context, -    AnalyzerOptions &opts, -    ArrayRef<std::string> plugins, -    ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, -    DiagnosticsEngine &diags) { -  auto checkerMgr = std::make_unique<CheckerManager>(context, opts); - -  CheckerRegistry allCheckers(plugins, diags, opts, context.getLangOpts(), -                              checkerRegistrationFns); - -  allCheckers.initializeManager(*checkerMgr); -  allCheckers.validateCheckerOptions(); -  checkerMgr->finishedCheckerRegistration(); - -  return checkerMgr; -} - -void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins, -                            AnalyzerOptions &anopts, -                            DiagnosticsEngine &diags, -                            const LangOptions &langOpts) { +void ento::printCheckerHelp(raw_ostream &out, CompilerInstance &CI) {    out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n";    out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n"; -  CheckerRegistry(plugins, diags, anopts, langOpts) -      .printCheckerWithDescList(out); +  auto CheckerMgr = std::make_unique<CheckerManager>( +      *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), +      CI.getFrontendOpts().Plugins); + +  CheckerMgr->getCheckerRegistryData().printCheckerWithDescList( +      *CI.getAnalyzerOpts(), out);  } -void ento::printEnabledCheckerList(raw_ostream &out, -                                   ArrayRef<std::string> plugins, -                                   AnalyzerOptions &anopts, -                                   DiagnosticsEngine &diags, -                                   const LangOptions &langOpts) { +void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) {    out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; -  CheckerRegistry(plugins, diags, anopts, langOpts) -      .printEnabledCheckerList(out); +  auto CheckerMgr = std::make_unique<CheckerManager>( +      *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), +      CI.getFrontendOpts().Plugins); + +  CheckerMgr->getCheckerRegistryData().printEnabledCheckerList(out);  } -void ento::printCheckerConfigList(raw_ostream &OS, -                                  ArrayRef<std::string> plugins, -                                  AnalyzerOptions &opts, -                                  DiagnosticsEngine &diags, -                                  const LangOptions &LangOpts) { -  CheckerRegistry(plugins, diags, opts, LangOpts) -      .printCheckerOptionList(OS); +void ento::printCheckerConfigList(raw_ostream &out, CompilerInstance &CI) { + +  auto CheckerMgr = std::make_unique<CheckerManager>( +      *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), +      CI.getFrontendOpts().Plugins); + +  CheckerMgr->getCheckerRegistryData().printCheckerOptionList( +      *CI.getAnalyzerOpts(), out);  }  void ento::printAnalyzerConfigList(raw_ostream &out) { diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp index f5c05281adab2..528284ca89858 100644 --- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -25,9 +25,12 @@  using namespace clang;  using namespace ento; +using namespace checker_registry;  using llvm::sys::DynamicLibrary; -using RegisterCheckersFn = void (*)(CheckerRegistry &); +//===----------------------------------------------------------------------===// +// Utilities. +//===----------------------------------------------------------------------===//  static bool isCompatibleAPIVersion(const char *VersionString) {    // If the version string is null, its not an analyzer plugin. @@ -39,80 +42,17 @@ static bool isCompatibleAPIVersion(const char *VersionString) {    return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;  } -namespace { -template <class T> struct FullNameLT { -  bool operator()(const T &Lhs, const T &Rhs) { -    return Lhs.FullName < Rhs.FullName; -  } -}; - -using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>; -using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>; -} // end of anonymous namespace - -template <class CheckerOrPackageInfoList> -static -    typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value, -                              typename CheckerOrPackageInfoList::const_iterator, -                              typename CheckerOrPackageInfoList::iterator>::type -    binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) { - -  using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type; -  using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>; - -  assert(std::is_sorted(Collection.begin(), Collection.end(), -                        CheckerOrPackageFullNameLT{}) && -         "In order to efficiently gather checkers/packages, this function " -         "expects them to be already sorted!"); - -  return llvm::lower_bound(Collection, CheckerOrPackage(FullName), -                           CheckerOrPackageFullNameLT{}); -} -  static constexpr char PackageSeparator = '.'; -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; -} - -CheckerRegistry::CheckerInfoListRange -CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { -  auto It = binaryFind(Checkers, CmdLineArg); - -  if (!isInPackage(*It, CmdLineArg)) -    return {Checkers.end(), Checkers.end()}; - -  // 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(CmdLineArg); - -  if (PackageSize != PackageSizes.end()) -    Size = PackageSize->getValue(); - -  return {It, It + Size}; -} +//===----------------------------------------------------------------------===// +// Methods of CheckerRegistry. +//===----------------------------------------------------------------------===//  CheckerRegistry::CheckerRegistry( -    ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags, -    AnalyzerOptions &AnOpts, const LangOptions &LangOpts, +    CheckerRegistryData &Data, ArrayRef<std::string> Plugins, +    DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,      ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns) -    : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) { +    : Data(Data), Diags(Diags), AnOpts(AnOpts) {    // Register builtin checkers.  #define GET_CHECKERS @@ -152,9 +92,10 @@ CheckerRegistry::CheckerRegistry(        continue;      } +    using RegisterPluginCheckerFn = void (*)(CheckerRegistry &);      // Register its checkers. -    RegisterCheckersFn RegisterPluginCheckers = -        reinterpret_cast<RegisterCheckersFn>( +    RegisterPluginCheckerFn RegisterPluginCheckers = +        reinterpret_cast<RegisterPluginCheckerFn>(              Lib.getAddressOfSymbol("clang_registerCheckers"));      if (RegisterPluginCheckers)        RegisterPluginCheckers(*this); @@ -171,38 +112,67 @@ CheckerRegistry::CheckerRegistry(    // FIXME: Alphabetical sort puts 'experimental' in the middle.    // Would it be better to name it '~experimental' or something else    // that's ASCIIbetically last? -  llvm::sort(Packages, PackageNameLT{}); -  llvm::sort(Checkers, CheckerNameLT{}); +  llvm::sort(Data.Packages, checker_registry::PackageNameLT{}); +  llvm::sort(Data.Checkers, checker_registry::CheckerNameLT{});  #define GET_CHECKER_DEPENDENCIES  #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \    addDependency(FULLNAME, DEPENDENCY); +#define GET_CHECKER_WEAK_DEPENDENCIES + +#define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY)                          \ +  addWeakDependency(FULLNAME, DEPENDENCY); +  #define GET_CHECKER_OPTIONS -#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN)  \ -  addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); +#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,             \ +                       DEVELOPMENT_STATUS, IS_HIDDEN)                          \ +  addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC,                 \ +                   DEVELOPMENT_STATUS, IS_HIDDEN);  #define GET_PACKAGE_OPTIONS -#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN)  \ -  addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); +#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,             \ +                       DEVELOPMENT_STATUS, IS_HIDDEN)                          \ +  addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC,                 \ +                   DEVELOPMENT_STATUS, IS_HIDDEN);  #include "clang/StaticAnalyzer/Checkers/Checkers.inc"  #undef CHECKER_DEPENDENCY  #undef GET_CHECKER_DEPENDENCIES +#undef CHECKER_WEAK_DEPENDENCY +#undef GET_CHECKER_WEAK_DEPENDENCIES  #undef CHECKER_OPTION  #undef GET_CHECKER_OPTIONS  #undef PACKAGE_OPTION  #undef GET_PACKAGE_OPTIONS -  resolveDependencies(); +  resolveDependencies<true>(); +  resolveDependencies<false>(); + +#ifndef NDEBUG +  for (auto &DepPair : Data.Dependencies) { +    for (auto &WeakDepPair : Data.WeakDependencies) { +      // Some assertions to enforce that strong dependencies are relations in +      // between purely modeling checkers, and weak dependencies are about +      // diagnostics. +      assert(WeakDepPair != DepPair && +             "A checker cannot strong and weak depend on the same checker!"); +      assert(WeakDepPair.first != DepPair.second && +             "A strong dependency mustn't have weak dependencies!"); +      assert(WeakDepPair.second != DepPair.second && +             "A strong dependency mustn't be a weak dependency as well!"); +    } +  } +#endif +    resolveCheckerAndPackageOptions();    // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the    // command line.    for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {      CheckerInfoListRange CheckerForCmdLineArg = -        getMutableCheckersForCmdLineArg(Opt.first); +        Data.getMutableCheckersForCmdLineArg(Opt.first);      if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {        Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first; @@ -214,109 +184,169 @@ CheckerRegistry::CheckerRegistry(                                   : StateFromCmdLine::State_Disabled;      }    } +  validateCheckerOptions();  } -/// Collects dependencies in \p ret, returns false on failure. -static bool -collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, -                        const LangOptions &LO, -                        CheckerRegistry::CheckerInfoSet &Ret); - -/// Collects dependenies in \p enabledCheckers. Return None on failure. -LLVM_NODISCARD -static llvm::Optional<CheckerRegistry::CheckerInfoSet> -collectDependencies(const CheckerRegistry::CheckerInfo &checker, -                    const LangOptions &LO) { - -  CheckerRegistry::CheckerInfoSet Ret; -  // Add dependencies to the enabled checkers only if all of them can be -  // enabled. -  if (!collectDependenciesImpl(checker.Dependencies, LO, Ret)) -    return None; - -  return Ret; -} - -static bool -collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, -                        const LangOptions &LO, -                        CheckerRegistry::CheckerInfoSet &Ret) { +//===----------------------------------------------------------------------===// +// Dependency resolving. +//===----------------------------------------------------------------------===// -  for (const CheckerRegistry::CheckerInfo *Dependency : Deps) { +template <typename IsEnabledFn> +static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, +                                      const CheckerManager &Mgr, +                                      CheckerInfoSet &Ret, +                                      IsEnabledFn IsEnabled); + +/// Collects weak dependencies in \p enabledData.Checkers. +template <typename IsEnabledFn> +static void collectWeakDependencies(const ConstCheckerInfoList &Deps, +                                    const CheckerManager &Mgr, +                                    CheckerInfoSet &Ret, IsEnabledFn IsEnabled); + +void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) { +  // First, we calculate the list of enabled checkers as specified by the +  // invocation. Weak dependencies will not enable their unspecified strong +  // depenencies, but its only after resolving strong dependencies for all +  // checkers when we know whether they will be enabled. +  CheckerInfoSet Tmp; +  auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) { +    return !Checker->isDisabled(Mgr); +  }; +  for (const CheckerInfo &Checker : Data.Checkers) { +    if (!Checker.isEnabled(Mgr)) +      continue; -    if (Dependency->isDisabled(LO)) -      return false; +    CheckerInfoSet Deps; +    if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps, +                                   IsEnabledFromCmdLine)) { +      // If we failed to enable any of the dependencies, don't enable this +      // checker. +      continue; +    } -    // Collect dependencies recursively. -    if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret)) -      return false; +    Tmp.insert(Deps.begin(), Deps.end()); -    Ret.insert(Dependency); +    // Enable the checker. +    Tmp.insert(&Checker);    } -  return true; -} - -CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { - -  CheckerInfoSet EnabledCheckers; - -  for (const CheckerInfo &Checker : Checkers) { -    if (!Checker.isEnabled(LangOpts)) +  // Calculate enabled checkers with the correct registration order. As this is +  // done recursively, its arguably cheaper, but for sure less error prone to +  // recalculate from scratch. +  auto IsEnabled = [&](const CheckerInfo *Checker) { +    return llvm::is_contained(Tmp, Checker); +  }; +  for (const CheckerInfo &Checker : Data.Checkers) { +    if (!Checker.isEnabled(Mgr))        continue; -    // Recursively enable its dependencies. -    llvm::Optional<CheckerInfoSet> Deps = -        collectDependencies(Checker, LangOpts); +    CheckerInfoSet Deps; -    if (!Deps) { +    collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled); + +    if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps, +                                   IsEnabledFromCmdLine)) {        // If we failed to enable any of the dependencies, don't enable this        // checker.        continue;      }      // Note that set_union also preserves the order of insertion. -    EnabledCheckers.set_union(*Deps); +    Data.EnabledCheckers.set_union(Deps); +    Data.EnabledCheckers.insert(&Checker); +  } +} -    // Enable the checker. -    EnabledCheckers.insert(&Checker); +template <typename IsEnabledFn> +static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, +                                      const CheckerManager &Mgr, +                                      CheckerInfoSet &Ret, +                                      IsEnabledFn IsEnabled) { + +  for (const CheckerInfo *Dependency : Deps) { +    if (!IsEnabled(Dependency)) +      return false; + +    // Collect dependencies recursively. +    if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret, +                                   IsEnabled)) +      return false; +    Ret.insert(Dependency);    } -  return EnabledCheckers; +  return true; +} + +template <typename IsEnabledFn> +static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps, +                                    const CheckerManager &Mgr, +                                    CheckerInfoSet &Ret, +                                    IsEnabledFn IsEnabled) { + +  for (const CheckerInfo *Dependency : WeakDeps) { +    // Don't enable this checker if strong dependencies are unsatisfied, but +    // assume that weak dependencies are transitive. +    collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled); + +    if (IsEnabled(Dependency) && +        collectStrongDependencies(Dependency->Dependencies, Mgr, Ret, +                                  IsEnabled)) +      Ret.insert(Dependency); +  }  } -void CheckerRegistry::resolveDependencies() { -  for (const std::pair<StringRef, StringRef> &Entry : Dependencies) { -    auto CheckerIt = binaryFind(Checkers, Entry.first); -    assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first && +template <bool IsWeak> void CheckerRegistry::resolveDependencies() { +  for (const std::pair<StringRef, StringRef> &Entry : +       (IsWeak ? Data.WeakDependencies : Data.Dependencies)) { + +    auto CheckerIt = binaryFind(Data.Checkers, Entry.first); +    assert(CheckerIt != Data.Checkers.end() && +           CheckerIt->FullName == Entry.first &&             "Failed to find the checker while attempting to set up its "             "dependencies!"); -    auto DependencyIt = binaryFind(Checkers, Entry.second); -    assert(DependencyIt != Checkers.end() && +    auto DependencyIt = binaryFind(Data.Checkers, Entry.second); +    assert(DependencyIt != Data.Checkers.end() &&             DependencyIt->FullName == Entry.second &&             "Failed to find the dependency of a checker!"); -    CheckerIt->Dependencies.emplace_back(&*DependencyIt); +    // We do allow diagnostics from unit test/example dependency checkers. +    assert((DependencyIt->FullName.startswith("test") || +            DependencyIt->FullName.startswith("example") || IsWeak || +            DependencyIt->IsHidden) && +           "Strong dependencies are modeling checkers, and as such " +           "non-user facing! Mark them hidden in Checkers.td!"); + +    if (IsWeak) +      CheckerIt->WeakDependencies.emplace_back(&*DependencyIt); +    else +      CheckerIt->Dependencies.emplace_back(&*DependencyIt);    } - -  Dependencies.clear();  }  void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) { -  Dependencies.emplace_back(FullName, Dependency); +  Data.Dependencies.emplace_back(FullName, Dependency);  } +void CheckerRegistry::addWeakDependency(StringRef FullName, +                                        StringRef Dependency) { +  Data.WeakDependencies.emplace_back(FullName, Dependency); +} + +//===----------------------------------------------------------------------===// +// Checker option resolving and validating. +//===----------------------------------------------------------------------===// +  /// Insert the checker/package option to AnalyzerOptions' config table, and  /// validate it, if the user supplied it on the command line. -static void insertAndValidate(StringRef FullName, -                              const CheckerRegistry::CmdLineOption &Option, +static void insertAndValidate(StringRef FullName, const CmdLineOption &Option,                                AnalyzerOptions &AnOpts,                                DiagnosticsEngine &Diags) {    std::string FullOption = (FullName + ":" + Option.OptionName).str(); -  auto It = AnOpts.Config.insert({FullOption, Option.DefaultValStr}); +  auto It = +      AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)});    // Insertation was successful -- CmdLineOption's constructor will validate    // whether values received from plugins or TableGen files are correct. @@ -337,7 +367,7 @@ static void insertAndValidate(StringRef FullName,              << FullOption << "a boolean value";        } -      It.first->setValue(Option.DefaultValStr); +      It.first->setValue(std::string(Option.DefaultValStr));      }      return;    } @@ -351,17 +381,17 @@ static void insertAndValidate(StringRef FullName,              << FullOption << "an integer value";        } -      It.first->setValue(Option.DefaultValStr); +      It.first->setValue(std::string(Option.DefaultValStr));      }      return;    }  }  template <class T> -static void -insertOptionToCollection(StringRef FullName, T &Collection, -                         const CheckerRegistry::CmdLineOption &Option, -                         AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { +static void insertOptionToCollection(StringRef FullName, T &Collection, +                                     const CmdLineOption &Option, +                                     AnalyzerOptions &AnOpts, +                                     DiagnosticsEngine &Diags) {    auto It = binaryFind(Collection, FullName);    assert(It != Collection.end() &&           "Failed to find the checker while attempting to add a command line " @@ -374,22 +404,20 @@ insertOptionToCollection(StringRef FullName, T &Collection,  void CheckerRegistry::resolveCheckerAndPackageOptions() {    for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry : -       CheckerOptions) { -    insertOptionToCollection(CheckerOptEntry.first, Checkers, +       Data.CheckerOptions) { +    insertOptionToCollection(CheckerOptEntry.first, Data.Checkers,                               CheckerOptEntry.second, AnOpts, Diags);    } -  CheckerOptions.clear();    for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry : -       PackageOptions) { -    insertOptionToCollection(PackageOptEntry.first, Packages, +       Data.PackageOptions) { +    insertOptionToCollection(PackageOptEntry.first, Data.Packages,                               PackageOptEntry.second, AnOpts, Diags);    } -  PackageOptions.clear();  }  void CheckerRegistry::addPackage(StringRef FullName) { -  Packages.emplace_back(PackageInfo(FullName)); +  Data.Packages.emplace_back(PackageInfo(FullName));  }  void CheckerRegistry::addPackageOption(StringRef OptionType, @@ -399,22 +427,22 @@ void CheckerRegistry::addPackageOption(StringRef OptionType,                                         StringRef Description,                                         StringRef DevelopmentStatus,                                         bool IsHidden) { -  PackageOptions.emplace_back( +  Data.PackageOptions.emplace_back(        PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,                                       Description, DevelopmentStatus, IsHidden});  } -void CheckerRegistry::addChecker(InitializationFunction Rfn, +void CheckerRegistry::addChecker(RegisterCheckerFn Rfn,                                   ShouldRegisterFunction Sfn, StringRef Name,                                   StringRef Desc, StringRef DocsUri,                                   bool IsHidden) { -  Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden); +  Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);    // Record the presence of the checker in its packages.    StringRef PackageName, LeafName;    std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);    while (!LeafName.empty()) { -    PackageSizes[PackageName] += 1; +    Data.PackageSizes[PackageName] += 1;      std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);    }  } @@ -426,37 +454,33 @@ void CheckerRegistry::addCheckerOption(StringRef OptionType,                                         StringRef Description,                                         StringRef DevelopmentStatus,                                         bool IsHidden) { -  CheckerOptions.emplace_back( +  Data.CheckerOptions.emplace_back(        CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,                                       Description, DevelopmentStatus, IsHidden});  }  void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { -  // Collect checkers enabled by the options. -  CheckerInfoSet enabledCheckers = getEnabledCheckers(); -    // Initialize the CheckerManager with all enabled checkers. -  for (const auto *Checker : enabledCheckers) { +  for (const auto *Checker : Data.EnabledCheckers) {      CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));      Checker->Initialize(CheckerMgr);    }  } -static void -isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList, -                    StringRef SuppliedChecker, StringRef SuppliedOption, -                    const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { +static void isOptionContainedIn(const CmdLineOptionList &OptionList, +                                StringRef SuppliedChecker, +                                StringRef SuppliedOption, +                                const AnalyzerOptions &AnOpts, +                                DiagnosticsEngine &Diags) {    if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue)      return; -  using CmdLineOption = CheckerRegistry::CmdLineOption; -    auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {      return Opt.OptionName == SuppliedOption;    }; -  auto OptionIt = llvm::find_if(OptionList, SameOptName); +  const auto *OptionIt = llvm::find_if(OptionList, SameOptName);    if (OptionIt == OptionList.end()) {      Diags.Report(diag::err_analyzer_checker_option_unknown) @@ -485,16 +509,16 @@ void CheckerRegistry::validateCheckerOptions() const {      // it would return with an iterator to the first checker in the core, so we      // we really have to use find here, which uses operator==.      auto CheckerIt = -        llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage)); -    if (CheckerIt != Checkers.end()) { +        llvm::find(Data.Checkers, CheckerInfo(SuppliedCheckerOrPackage)); +    if (CheckerIt != Data.Checkers.end()) {        isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,                            SuppliedOption, AnOpts, Diags);        continue;      } -    auto PackageIt = -        llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage)); -    if (PackageIt != Packages.end()) { +    const auto *PackageIt = +        llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage)); +    if (PackageIt != Data.Packages.end()) {        isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,                            SuppliedOption, AnOpts, Diags);        continue; @@ -505,121 +529,3 @@ void CheckerRegistry::validateCheckerOptions() const {    }  } -void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out, -                                               size_t MaxNameChars) const { -  // FIXME: Print available packages. - -  Out << "CHECKERS:\n"; - -  // Find the maximum option length. -  size_t OptionFieldWidth = 0; -  for (const auto &Checker : Checkers) { -    // Limit the amount of padding we are willing to give up for alignment. -    //   Package.Name     Description  [Hidden] -    size_t NameLength = Checker.FullName.size(); -    if (NameLength <= MaxNameChars) -      OptionFieldWidth = std::max(OptionFieldWidth, NameLength); -  } - -  const size_t InitialPad = 2; - -  auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker, -                   StringRef Description) { -    AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description}, -                                         InitialPad, OptionFieldWidth); -    Out << '\n'; -  }; - -  for (const auto &Checker : Checkers) { -    // The order of this if branches is significant, we wouldn't like to display -    // developer checkers even in the alpha output. For example, -    // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden -    // by default, and users (even when the user is a developer of an alpha -    // checker) shouldn't normally tinker with whether they should be enabled. - -    if (Checker.IsHidden) { -      if (AnOpts.ShowCheckerHelpDeveloper) -        Print(Out, Checker, Checker.Desc); -      continue; -    } - -    if (Checker.FullName.startswith("alpha")) { -      if (AnOpts.ShowCheckerHelpAlpha) -        Print(Out, Checker, -              ("(Enable only for development!) " + Checker.Desc).str()); -      continue; -    } - -    if (AnOpts.ShowCheckerHelp) -        Print(Out, Checker, Checker.Desc); -  } -} - -void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const { -  // Collect checkers enabled by the options. -  CheckerInfoSet EnabledCheckers = getEnabledCheckers(); - -  for (const auto *i : EnabledCheckers) -    Out << i->FullName << '\n'; -} - -void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const { -  Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n"; -  Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; -  Out << "       -analyzer-config OPTION1=VALUE, -analyzer-config " -         "OPTION2=VALUE, ...\n\n"; -  Out << "OPTIONS:\n\n"; - -  std::multimap<StringRef, const CmdLineOption &> OptionMap; - -  for (const CheckerInfo &Checker : Checkers) { -    for (const CmdLineOption &Option : Checker.CmdLineOptions) { -      OptionMap.insert({Checker.FullName, Option}); -    } -  } - -  for (const PackageInfo &Package : Packages) { -    for (const CmdLineOption &Option : Package.CmdLineOptions) { -      OptionMap.insert({Package.FullName, Option}); -    } -  } - -  auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) { -    AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc}, -                                         /*InitialPad*/ 2, -                                         /*EntryWidth*/ 50, -                                         /*MinLineWidth*/ 90); -    Out << "\n\n"; -  }; -  for (const std::pair<const StringRef, const CmdLineOption &> &Entry : -       OptionMap) { -    const CmdLineOption &Option = Entry.second; -    std::string FullOption = (Entry.first + ":" + Option.OptionName).str(); - -    std::string Desc = -        ("(" + Option.OptionType + ") " + Option.Description + " (default: " + -         (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")") -            .str(); - -    // The list of these if branches is significant, we wouldn't like to -    // display hidden alpha checker options for -    // -analyzer-checker-option-help-alpha. - -    if (Option.IsHidden) { -      if (AnOpts.ShowCheckerOptionDeveloperList) -        Print(Out, FullOption, Desc); -      continue; -    } - -    if (Option.DevelopmentStatus == "alpha" || -        Entry.first.startswith("alpha")) { -      if (AnOpts.ShowCheckerOptionAlphaList) -        Print(Out, FullOption, -              llvm::Twine("(Enable only for development!) " + Desc).str()); -      continue; -    } - -    if (AnOpts.ShowCheckerOptionList) -      Print(Out, FullOption, Desc); -  } -} diff --git a/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp b/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp new file mode 100644 index 0000000000000..21a60785eb525 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp @@ -0,0 +1,50 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include <memory> + +namespace clang { +namespace ento { + +CheckerManager::CheckerManager( +    ASTContext &Context, AnalyzerOptions &AOptions, const Preprocessor &PP, +    ArrayRef<std::string> plugins, +    ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns) +    : Context(&Context), LangOpts(Context.getLangOpts()), AOptions(AOptions), +      PP(&PP), Diags(Context.getDiagnostics()), +      RegistryData(std::make_unique<CheckerRegistryData>()) { +  CheckerRegistry Registry(*RegistryData, plugins, Context.getDiagnostics(), +                           AOptions, checkerRegistrationFns); +  Registry.initializeRegistry(*this); +  Registry.initializeManager(*this); +  finishedCheckerRegistration(); +} + +CheckerManager::CheckerManager(AnalyzerOptions &AOptions, +                               const LangOptions &LangOpts, +                               DiagnosticsEngine &Diags, +                               ArrayRef<std::string> plugins) +    : LangOpts(LangOpts), AOptions(AOptions), Diags(Diags), +      RegistryData(std::make_unique<CheckerRegistryData>()) { +  CheckerRegistry Registry(*RegistryData, plugins, Diags, AOptions, {}); +  Registry.initializeRegistry(*this); +} + +CheckerManager::~CheckerManager() { +  for (const auto &CheckerDtor : CheckerDtors) +    CheckerDtor(); +} + +} // namespace ento +} // namespace clang  | 
