diff options
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 |