diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -43,6 +43,7 @@ class CheckerBase; class CheckerContext; class CheckerRegistry; +struct CheckerRegistryData; class ExplodedGraph; class ExplodedNode; class ExplodedNodeSet; @@ -130,7 +131,7 @@ const Preprocessor *PP = nullptr; CheckerNameRef CurrentCheckerName; DiagnosticsEngine &Diags; - std::unique_ptr Registry; + std::unique_ptr RegistryData; public: // These constructors are defined in the Frontend library, because @@ -152,8 +153,8 @@ : CheckerManager(Context, AOptions, PP, {}, {}) {} /// Constructs a CheckerManager without requiring an AST. No checker - /// registration will take place. Only useful for retrieving the - /// CheckerRegistry and print for help flags where the AST is unavalaible. + /// registration will take place. Only useful when one needs to print the + /// help flags through CheckerRegistryData, and the AST is unavalaible. CheckerManager(AnalyzerOptions &AOptions, const LangOptions &LangOpts, DiagnosticsEngine &Diags, ArrayRef plugins); @@ -172,7 +173,9 @@ assert(PP); return *PP; } - const CheckerRegistry &getCheckerRegistry() const { return *Registry; } + const CheckerRegistryData &getCheckerRegistryData() const { + return *RegistryData; + } DiagnosticsEngine &getDiagnostics() const { return Diags; } ASTContext &getASTContext() const { assert(Context); diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerRegistryData.h b/clang/include/clang/StaticAnalyzer/Core/CheckerRegistryData.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerRegistryData.h @@ -0,0 +1,226 @@ +//===- CheckerRegistryData.h ------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains the data structures to which the TableGen file Checkers.td +// maps to, as well as what was parsed from the the specific invocation (whether +// a checker/package is enabled, their options values, etc). +// +// The parsing of the invocation is done by CheckerRegistry, which is found in +// the Frontend library. This allows the Core and Checkers libraries to utilize +// this information, such as enforcing rules on checker dependency bug emission, +// ensuring all checker options were queried, etc. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRYDATA_H +#define LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRYDATA_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +class AnalyzerOptions; + +namespace ento { + +class CheckerManager; + +/// Initialization functions perform any necessary setup for a checker. +/// They should include a call to CheckerManager::registerChecker. +using RegisterCheckerFn = void (*)(CheckerManager &); +using ShouldRegisterFunction = bool (*)(const CheckerManager &); + +/// Specifies a command line option. It may either belong to a checker or a +/// package. +struct CmdLineOption { + StringRef OptionType; + StringRef OptionName; + StringRef DefaultValStr; + StringRef Description; + StringRef DevelopmentStatus; + bool IsHidden; + + CmdLineOption(StringRef OptionType, StringRef OptionName, + StringRef DefaultValStr, StringRef Description, + StringRef DevelopmentStatus, bool IsHidden) + : OptionType(OptionType), OptionName(OptionName), + DefaultValStr(DefaultValStr), Description(Description), + DevelopmentStatus(DevelopmentStatus), IsHidden(IsHidden) { + + assert((OptionType == "bool" || OptionType == "string" || + OptionType == "int") && + "Unknown command line option type!"); + + assert((OptionType != "bool" || + (DefaultValStr == "true" || DefaultValStr == "false")) && + "Invalid value for boolean command line option! Maybe incorrect " + "parameters to the addCheckerOption or addPackageOption method?"); + + int Tmp; + assert((OptionType != "int" || !DefaultValStr.getAsInteger(0, Tmp)) && + "Invalid value for integer command line option! Maybe incorrect " + "parameters to the addCheckerOption or addPackageOption method?"); + (void)Tmp; + + assert((DevelopmentStatus == "alpha" || DevelopmentStatus == "beta" || + DevelopmentStatus == "released") && + "Invalid development status!"); + } + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; +}; + +using CmdLineOptionList = llvm::SmallVector; + +struct CheckerInfo; + +using CheckerInfoList = std::vector; +using CheckerInfoListRange = llvm::iterator_range; +using ConstCheckerInfoList = llvm::SmallVector; +using CheckerInfoSet = llvm::SetVector; + +/// Specifies a checker. Note that this isn't what we call a checker object, +/// it merely contains everything required to create one. +struct CheckerInfo { + enum class StateFromCmdLine { + // This checker wasn't explicitly enabled or disabled. + State_Unspecified, + // This checker was explicitly disabled. + State_Disabled, + // This checker was explicitly enabled. + State_Enabled + }; + + RegisterCheckerFn Initialize = nullptr; + ShouldRegisterFunction ShouldRegister = nullptr; + StringRef FullName; + StringRef Desc; + StringRef DocumentationUri; + CmdLineOptionList CmdLineOptions; + bool IsHidden = false; + StateFromCmdLine State = StateFromCmdLine::State_Unspecified; + + ConstCheckerInfoList Dependencies; + ConstCheckerInfoList WeakDependencies; + + bool isEnabled(const CheckerManager &mgr) const { + return State == StateFromCmdLine::State_Enabled && ShouldRegister(mgr); + } + + bool isDisabled(const CheckerManager &mgr) const { + return State == StateFromCmdLine::State_Disabled || !ShouldRegister(mgr); + } + + // Since each checker must have a different full name, we can identify + // CheckerInfo objects by them. + bool operator==(const CheckerInfo &Rhs) const { + return FullName == Rhs.FullName; + } + + CheckerInfo(RegisterCheckerFn Fn, ShouldRegisterFunction sfn, StringRef Name, + StringRef Desc, StringRef DocsUri, bool IsHidden) + : Initialize(Fn), ShouldRegister(sfn), FullName(Name), Desc(Desc), + DocumentationUri(DocsUri), IsHidden(IsHidden) {} + + // Used for lower_bound. + explicit CheckerInfo(StringRef FullName) : FullName(FullName) {} + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; +}; + +using StateFromCmdLine = CheckerInfo::StateFromCmdLine; + +/// Specifies a package. Each package option is implicitly an option for all +/// checkers within the package. +struct PackageInfo { + StringRef FullName; + CmdLineOptionList CmdLineOptions; + + // Since each package must have a different full name, we can identify + // CheckerInfo objects by them. + bool operator==(const PackageInfo &Rhs) const { + return FullName == Rhs.FullName; + } + + explicit PackageInfo(StringRef FullName) : FullName(FullName) {} + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; +}; + +using PackageInfoList = llvm::SmallVector; + +namespace checker_registry { + +template struct FullNameLT { + bool operator()(const T &Lhs, const T &Rhs) { + return Lhs.FullName < Rhs.FullName; + } +}; + +using PackageNameLT = FullNameLT; +using CheckerNameLT = FullNameLT; + +template +std::conditional_t::value, + typename CheckerOrPackageInfoList::const_iterator, + typename CheckerOrPackageInfoList::iterator> +binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) { + + using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type; + using CheckerOrPackageFullNameLT = FullNameLT; + + assert(llvm::is_sorted(Collection, CheckerOrPackageFullNameLT{}) && + "In order to efficiently gather checkers/packages, this function " + "expects them to be already sorted!"); + + return llvm::lower_bound(Collection, CheckerOrPackage(FullName), + CheckerOrPackageFullNameLT{}); +} +} // namespace checker_registry + +struct CheckerRegistryData { +public: + CheckerInfoSet EnabledCheckers; + + CheckerInfoList Checkers; + PackageInfoList Packages; + /// Used for counting how many checkers belong to a certain package in the + /// \c Checkers field. For convenience purposes. + llvm::StringMap PackageSizes; + + /// Contains all (FullName, CmdLineOption) pairs. Similarly to dependencies, + /// we only modify the actual CheckerInfo and PackageInfo objects once all + /// of them have been added. + llvm::SmallVector, 0> PackageOptions; + llvm::SmallVector, 0> CheckerOptions; + + llvm::SmallVector, 0> Dependencies; + llvm::SmallVector, 0> WeakDependencies; + + CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); + + /// Prints the name and description of all checkers in this registry. + /// This output is not intended to be machine-parseable. + void printCheckerWithDescList(const AnalyzerOptions &AnOpts, raw_ostream &Out, + size_t MaxNameChars = 30) const; + void printEnabledCheckerList(raw_ostream &Out) const; + void printCheckerOptionList(const AnalyzerOptions &AnOpts, + raw_ostream &Out) const; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRYDATA_H diff --git a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h --- a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -5,17 +5,22 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// Contains the logic for parsing the TableGen file Checkers.td, and parsing the +// specific invocation of the analyzer (which checker/package is enabled, values +// of their options, etc). This is in the frontend library because checker +// registry functions are called from here but are defined in the dependent +// library libStaticAnalyzerCheckers, but the actual data structure that holds +// the parsed information is in the Core library. +// +//===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H -#define LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H +#ifndef LLVM_CLANG_STATICANALYZER_FRONTEND_CHECKERREGISTRY_H +#define LLVM_CLANG_STATICANALYZER_FRONTEND_CHECKERREGISTRY_H #include "clang/Basic/LLVM.h" -#include "llvm/ADT/SetVector.h" -#include "llvm/ADT/StringMap.h" +#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/raw_ostream.h" -#include -#include // FIXME: move this information to an HTML file in docs/. // At the very least, a checker plugin is a dynamic library that exports @@ -83,143 +88,16 @@ /// "core.builtin", or the full name "core.builtin.NoReturnFunctionChecker". class CheckerRegistry { public: - CheckerRegistry(ArrayRef plugins, DiagnosticsEngine &diags, - AnalyzerOptions &AnOpts, + CheckerRegistry(CheckerRegistryData &Data, ArrayRef Plugins, + DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts, ArrayRef> - checkerRegistrationFns = {}); + CheckerRegistrationFns = {}); /// Collects all enabled checkers in the field EnabledCheckers. It preserves /// the order of insertion, as dependencies have to be enabled before the /// checkers that depend on them. void initializeRegistry(const CheckerManager &Mgr); - /// Initialization functions perform any necessary setup for a checker. - /// They should include a call to CheckerManager::registerChecker. - using InitializationFunction = void (*)(CheckerManager &); - using ShouldRegisterFunction = bool (*)(const CheckerManager &); - - /// Specifies a command line option. It may either belong to a checker or a - /// package. - struct CmdLineOption { - StringRef OptionType; - StringRef OptionName; - StringRef DefaultValStr; - StringRef Description; - StringRef DevelopmentStatus; - bool IsHidden; - - CmdLineOption(StringRef OptionType, StringRef OptionName, - StringRef DefaultValStr, StringRef Description, - StringRef DevelopmentStatus, bool IsHidden) - : OptionType(OptionType), OptionName(OptionName), - DefaultValStr(DefaultValStr), Description(Description), - DevelopmentStatus(DevelopmentStatus), IsHidden(IsHidden) { - - assert((OptionType == "bool" || OptionType == "string" || - OptionType == "int") && - "Unknown command line option type!"); - - assert((OptionType != "bool" || - (DefaultValStr == "true" || DefaultValStr == "false")) && - "Invalid value for boolean command line option! Maybe incorrect " - "parameters to the addCheckerOption or addPackageOption method?"); - - int Tmp; - assert((OptionType != "int" || !DefaultValStr.getAsInteger(0, Tmp)) && - "Invalid value for integer command line option! Maybe incorrect " - "parameters to the addCheckerOption or addPackageOption method?"); - (void)Tmp; - - assert((DevelopmentStatus == "alpha" || DevelopmentStatus == "beta" || - DevelopmentStatus == "released") && - "Invalid development status!"); - } - - LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } - LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; - }; - - using CmdLineOptionList = llvm::SmallVector; - - struct CheckerInfo; - - using CheckerInfoList = std::vector; - using CheckerInfoListRange = llvm::iterator_range; - using ConstCheckerInfoList = llvm::SmallVector; - using CheckerInfoSet = llvm::SetVector; - - /// Specifies a checker. Note that this isn't what we call a checker object, - /// it merely contains everything required to create one. - struct CheckerInfo { - enum class StateFromCmdLine { - // This checker wasn't explicitly enabled or disabled. - State_Unspecified, - // This checker was explicitly disabled. - State_Disabled, - // This checker was explicitly enabled. - State_Enabled - }; - - InitializationFunction Initialize = nullptr; - ShouldRegisterFunction ShouldRegister = nullptr; - StringRef FullName; - StringRef Desc; - StringRef DocumentationUri; - CmdLineOptionList CmdLineOptions; - bool IsHidden = false; - StateFromCmdLine State = StateFromCmdLine::State_Unspecified; - - ConstCheckerInfoList Dependencies; - ConstCheckerInfoList WeakDependencies; - - bool isEnabled(const CheckerManager &mgr) const { - return State == StateFromCmdLine::State_Enabled && ShouldRegister(mgr); - } - - bool isDisabled(const CheckerManager &mgr) const { - return State == StateFromCmdLine::State_Disabled || !ShouldRegister(mgr); - } - - // Since each checker must have a different full name, we can identify - // CheckerInfo objects by them. - bool operator==(const CheckerInfo &Rhs) const { - return FullName == Rhs.FullName; - } - - CheckerInfo(InitializationFunction Fn, ShouldRegisterFunction sfn, - StringRef Name, StringRef Desc, StringRef DocsUri, - bool IsHidden) - : Initialize(Fn), ShouldRegister(sfn), FullName(Name), Desc(Desc), - DocumentationUri(DocsUri), IsHidden(IsHidden) {} - - // Used for lower_bound. - explicit CheckerInfo(StringRef FullName) : FullName(FullName) {} - - LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } - LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; - }; - - using StateFromCmdLine = CheckerInfo::StateFromCmdLine; - - /// Specifies a package. Each package option is implicitly an option for all - /// checkers within the package. - struct PackageInfo { - StringRef FullName; - CmdLineOptionList CmdLineOptions; - - // Since each package must have a different full name, we can identify - // CheckerInfo objects by them. - bool operator==(const PackageInfo &Rhs) const { - return FullName == Rhs.FullName; - } - - explicit PackageInfo(StringRef FullName) : FullName(FullName) {} - - LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } - LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; - }; - - using PackageInfoList = llvm::SmallVector; private: /// Default initialization function for checkers -- since CheckerManager @@ -237,7 +115,7 @@ public: /// Adds a checker to the registry. Use this non-templated overload when your /// checker requires custom initialization. - void addChecker(InitializationFunction Fn, ShouldRegisterFunction sfn, + void addChecker(RegisterCheckerFn Fn, ShouldRegisterFunction sfn, StringRef FullName, StringRef Desc, StringRef DocsUri, bool IsHidden); @@ -305,46 +183,17 @@ /// Check if every option corresponds to a specific checker or package. void validateCheckerOptions() const; - /// Prints the name and description of all checkers in this registry. - /// This output is not intended to be machine-parseable. - void printCheckerWithDescList(raw_ostream &Out, - size_t MaxNameChars = 30) const; - void printEnabledCheckerList(raw_ostream &Out) const; - void printCheckerOptionList(raw_ostream &Out) const; - private: - /// Return an iterator range of mutable CheckerInfos \p CmdLineArg applies to. - /// For example, it'll return the checkers for the core package, if - /// \p CmdLineArg is "core". - CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); - - CheckerInfoList Checkers; - PackageInfoList Packages; - /// Used for couting how many checkers belong to a certain package in the - /// \c Checkers field. For convenience purposes. - llvm::StringMap PackageSizes; - - /// Contains all (Dependendent checker, Dependency) pairs. We need this, as - /// we'll resolve dependencies after all checkers were added first. - llvm::SmallVector, 0> Dependencies; - llvm::SmallVector, 0> WeakDependencies; - template void resolveDependencies(); - - /// Contains all (FullName, CmdLineOption) pairs. Similarly to dependencies, - /// we only modify the actual CheckerInfo and PackageInfo objects once all - /// of them have been added. - llvm::SmallVector, 0> PackageOptions; - llvm::SmallVector, 0> CheckerOptions; - void resolveCheckerAndPackageOptions(); + CheckerRegistryData &Data; + DiagnosticsEngine &Diags; AnalyzerOptions &AnOpts; - CheckerInfoSet EnabledCheckers; }; } // namespace ento } // namespace clang -#endif // LLVM_CLANG_STATICANALYZER_CORE_CHECKERREGISTRY_H +#endif // LLVM_CLANG_STATICANALYZER_FRONTEND_CHECKERREGISTRY_H diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -16,6 +16,7 @@ CheckerContext.cpp CheckerHelpers.cpp CheckerManager.cpp + CheckerRegistryData.cpp CommonBugCategories.cpp ConstraintManager.cpp CoreEngine.cpp diff --git a/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp b/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp @@ -0,0 +1,233 @@ +//===- CheckerRegistry.h - Maintains all available checkers -----*- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/Twine.h" +#include + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Methods of CmdLineOption, PackageInfo and CheckerInfo. +//===----------------------------------------------------------------------===// + +LLVM_DUMP_METHOD void +CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const { + // The description can be just checked in Checkers.inc, the point here is to + // debug whether we succeeded in parsing it. + Out << OptionName << " (" << OptionType << ", " + << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \"" + << DefaultValStr; +} + +static StringRef toString(StateFromCmdLine Kind) { + switch (Kind) { + case StateFromCmdLine::State_Disabled: + return "Disabled"; + case StateFromCmdLine::State_Enabled: + return "Enabled"; + case StateFromCmdLine::State_Unspecified: + return "Unspecified"; + } + llvm_unreachable("Unhandled StateFromCmdLine enum"); +} + +LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const { + // The description can be just checked in Checkers.inc, the point here is to + // debug whether we succeeded in parsing it. Same with documentation uri. + Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "") + << ")\n"; + Out << " Options:\n"; + for (const CmdLineOption &Option : CmdLineOptions) { + Out << " "; + Option.dumpToStream(Out); + Out << '\n'; + } + Out << " Dependencies:\n"; + for (const CheckerInfo *Dependency : Dependencies) { + Out << " " << Dependency->FullName << '\n'; + } + Out << " Weak dependencies:\n"; + for (const CheckerInfo *Dependency : WeakDependencies) { + Out << " " << Dependency->FullName << '\n'; + } +} + +LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const { + Out << FullName << "\n"; + Out << " Options:\n"; + for (const CmdLineOption &Option : CmdLineOptions) { + Out << " "; + Option.dumpToStream(Out); + Out << '\n'; + } +} + +static constexpr char PackageSeparator = '.'; + +static bool isInPackage(const 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; +} + +CheckerInfoListRange +CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { + auto It = checker_registry::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::const_iterator PackageSize = + PackageSizes.find(CmdLineArg); + + if (PackageSize != PackageSizes.end()) + Size = PackageSize->getValue(); + + return {It, It + Size}; +} +//===----------------------------------------------------------------------===// +// Printing functions. +//===----------------------------------------------------------------------===// + +void CheckerRegistryData::printCheckerWithDescList( + const AnalyzerOptions &AnOpts, 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 CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const { + for (const auto *i : EnabledCheckers) + Out << i->FullName << '\n'; +} + +void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts, + raw_ostream &Out) const { + Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n"; + Out << "USAGE: -analyzer-config \n\n"; + Out << " -analyzer-config OPTION1=VALUE, -analyzer-config " + "OPTION2=VALUE, ...\n\n"; + Out << "OPTIONS:\n\n"; + + // It's usually ill-advised to use multimap, but clang will terminate after + // this function. + std::multimap 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 &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/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp --- a/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -183,12 +183,18 @@ F.getTreeFactory()); // Iterate over the block-expr bindings. - for (Environment::iterator I = Env.begin(), E = Env.end(); - I != E; ++I) { + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { const EnvironmentEntry &BlkExpr = I.getKey(); const SVal &X = I.getData(); - if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { + const bool IsBlkExprLive = + SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext()); + + assert((isa(BlkExpr.getStmt()) || !IsBlkExprLive) && + "Only Exprs can be live, LivenessAnalysis argues about the liveness " + "of *values*!"); + + if (IsBlkExprLive) { // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp --- a/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp @@ -33,7 +33,8 @@ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); - CheckerMgr->getCheckerRegistry().printCheckerWithDescList(out); + CheckerMgr->getCheckerRegistryData().printCheckerWithDescList( + *CI.getAnalyzerOpts(), out); } void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) { @@ -43,7 +44,7 @@ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); - CheckerMgr->getCheckerRegistry().printEnabledCheckerList(out); + CheckerMgr->getCheckerRegistryData().printEnabledCheckerList(out); } void ento::printCheckerConfigList(raw_ostream &out, CompilerInstance &CI) { @@ -52,7 +53,8 @@ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); - CheckerMgr->getCheckerRegistry().printCheckerOptionList(out); + 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 --- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -25,14 +25,13 @@ using namespace clang; using namespace ento; +using namespace checker_registry; using llvm::sys::DynamicLibrary; //===----------------------------------------------------------------------===// // Utilities. //===----------------------------------------------------------------------===// -using RegisterCheckersFn = void (*)(CheckerRegistry &); - static bool isCompatibleAPIVersion(const char *VersionString) { // If the version string is null, its not an analyzer plugin. if (!VersionString) @@ -43,140 +42,17 @@ return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; } -namespace { -template struct FullNameLT { - bool operator()(const T &Lhs, const T &Rhs) { - return Lhs.FullName < Rhs.FullName; - } -}; - -using PackageNameLT = FullNameLT; -using CheckerNameLT = FullNameLT; -} // end of anonymous namespace - -template -static std::conditional_t::value, - typename CheckerOrPackageInfoList::const_iterator, - typename CheckerOrPackageInfoList::iterator> -binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) { - - using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type; - using CheckerOrPackageFullNameLT = FullNameLT; - - assert(llvm::is_sorted(Collection, 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; -} - -//===----------------------------------------------------------------------===// -// Methods of CmdLineOption, PackageInfo and CheckerInfo. -//===----------------------------------------------------------------------===// - -LLVM_DUMP_METHOD void -CheckerRegistry::CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const { - // The description can be just checked in Checkers.inc, the point here is to - // debug whether we succeeded in parsing it. - Out << OptionName << " (" << OptionType << ", " - << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \"" - << DefaultValStr; -} - -static StringRef toString(CheckerRegistry::StateFromCmdLine Kind) { - switch (Kind) { - case CheckerRegistry::StateFromCmdLine::State_Disabled: - return "Disabled"; - case CheckerRegistry::StateFromCmdLine::State_Enabled: - return "Enabled"; - case CheckerRegistry::StateFromCmdLine::State_Unspecified: - return "Unspecified"; - } - llvm_unreachable("Unhandled CheckerRegistry::StateFromCmdLine enum"); -} - -LLVM_DUMP_METHOD void -CheckerRegistry::CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const { - // The description can be just checked in Checkers.inc, the point here is to - // debug whether we succeeded in parsing it. Same with documentation uri. - Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "") - << ")\n"; - Out << " Options:\n"; - for (const CmdLineOption &Option : CmdLineOptions) { - Out << " "; - Option.dumpToStream(Out); - Out << '\n'; - } - Out << " Dependencies:\n"; - for (const CheckerInfo *Dependency : Dependencies) { - Out << " " << Dependency->FullName << '\n'; - } - Out << " Weak dependencies:\n"; - for (const CheckerInfo *Dependency : WeakDependencies) { - Out << " " << Dependency->FullName << '\n'; - } -} - -LLVM_DUMP_METHOD void -CheckerRegistry::PackageInfo::dumpToStream(llvm::raw_ostream &Out) const { - Out << FullName << "\n"; - Out << " Options:\n"; - for (const CmdLineOption &Option : CmdLineOptions) { - Out << " "; - Option.dumpToStream(Out); - Out << '\n'; - } -} - //===----------------------------------------------------------------------===// // Methods of CheckerRegistry. //===----------------------------------------------------------------------===// -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::const_iterator PackageSize = - PackageSizes.find(CmdLineArg); - - if (PackageSize != PackageSizes.end()) - Size = PackageSize->getValue(); - - return {It, It + Size}; -} - CheckerRegistry::CheckerRegistry( - ArrayRef Plugins, DiagnosticsEngine &Diags, - AnalyzerOptions &AnOpts, + CheckerRegistryData &Data, ArrayRef Plugins, + DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts, ArrayRef> CheckerRegistrationFns) - : Diags(Diags), AnOpts(AnOpts) { + : Data(Data), Diags(Diags), AnOpts(AnOpts) { // Register builtin checkers. #define GET_CHECKERS @@ -216,9 +92,10 @@ continue; } + using RegisterPluginCheckerFn = void (*)(CheckerRegistry &); // Register its checkers. - RegisterCheckersFn RegisterPluginCheckers = - reinterpret_cast( + RegisterPluginCheckerFn RegisterPluginCheckers = + reinterpret_cast( Lib.getAddressOfSymbol("clang_registerCheckers")); if (RegisterPluginCheckers) RegisterPluginCheckers(*this); @@ -235,8 +112,8 @@ // 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 @@ -274,8 +151,8 @@ resolveDependencies(); #ifndef NDEBUG - for (auto &DepPair : Dependencies) { - for (auto &WeakDepPair : WeakDependencies) { + 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. @@ -295,7 +172,7 @@ // command line. for (const std::pair &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; @@ -315,19 +192,16 @@ //===----------------------------------------------------------------------===// template -static bool -collectStrongDependencies(const CheckerRegistry::ConstCheckerInfoList &Deps, - const CheckerManager &Mgr, - CheckerRegistry::CheckerInfoSet &Ret, - IsEnabledFn IsEnabled); +static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, + const CheckerManager &Mgr, + CheckerInfoSet &Ret, + IsEnabledFn IsEnabled); -/// Collects weak dependencies in \p enabledCheckers. +/// Collects weak dependencies in \p enabledData.Checkers. template -static void -collectWeakDependencies(const CheckerRegistry::ConstCheckerInfoList &Deps, - const CheckerManager &Mgr, - CheckerRegistry::CheckerInfoSet &Ret, - IsEnabledFn IsEnabled); +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 @@ -338,7 +212,7 @@ auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) { return !Checker->isDisabled(Mgr); }; - for (const CheckerInfo &Checker : Checkers) { + for (const CheckerInfo &Checker : Data.Checkers) { if (!Checker.isEnabled(Mgr)) continue; @@ -362,7 +236,7 @@ auto IsEnabled = [&](const CheckerInfo *Checker) { return llvm::is_contained(Tmp, Checker); }; - for (const CheckerInfo &Checker : Checkers) { + for (const CheckerInfo &Checker : Data.Checkers) { if (!Checker.isEnabled(Mgr)) continue; @@ -378,19 +252,18 @@ } // Note that set_union also preserves the order of insertion. - EnabledCheckers.set_union(Deps); - EnabledCheckers.insert(&Checker); + Data.EnabledCheckers.set_union(Deps); + Data.EnabledCheckers.insert(&Checker); } } template -static bool -collectStrongDependencies(const CheckerRegistry::ConstCheckerInfoList &Deps, - const CheckerManager &Mgr, - CheckerRegistry::CheckerInfoSet &Ret, - IsEnabledFn IsEnabled) { +static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, + const CheckerManager &Mgr, + CheckerInfoSet &Ret, + IsEnabledFn IsEnabled) { - for (const CheckerRegistry::CheckerInfo *Dependency : Deps) { + for (const CheckerInfo *Dependency : Deps) { if (!IsEnabled(Dependency)) return false; @@ -405,13 +278,12 @@ } template -static void -collectWeakDependencies(const CheckerRegistry::ConstCheckerInfoList &WeakDeps, - const CheckerManager &Mgr, - CheckerRegistry::CheckerInfoSet &Ret, - IsEnabledFn IsEnabled) { +static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps, + const CheckerManager &Mgr, + CheckerInfoSet &Ret, + IsEnabledFn IsEnabled) { - for (const CheckerRegistry::CheckerInfo *Dependency : WeakDeps) { + 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); @@ -425,15 +297,16 @@ template void CheckerRegistry::resolveDependencies() { for (const std::pair &Entry : - (IsWeak ? WeakDependencies : Dependencies)) { + (IsWeak ? Data.WeakDependencies : Data.Dependencies)) { - auto CheckerIt = binaryFind(Checkers, Entry.first); - assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first && + 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!"); @@ -445,12 +318,12 @@ } 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) { - WeakDependencies.emplace_back(FullName, Dependency); + Data.WeakDependencies.emplace_back(FullName, Dependency); } //===----------------------------------------------------------------------===// @@ -459,8 +332,7 @@ /// 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) { @@ -509,10 +381,10 @@ } template -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 " @@ -525,20 +397,20 @@ void CheckerRegistry::resolveCheckerAndPackageOptions() { for (const std::pair &CheckerOptEntry : - CheckerOptions) { - insertOptionToCollection(CheckerOptEntry.first, Checkers, + Data.CheckerOptions) { + insertOptionToCollection(CheckerOptEntry.first, Data.Checkers, CheckerOptEntry.second, AnOpts, Diags); } for (const std::pair &PackageOptEntry : - PackageOptions) { - insertOptionToCollection(PackageOptEntry.first, Packages, + Data.PackageOptions) { + insertOptionToCollection(PackageOptEntry.first, Data.Packages, PackageOptEntry.second, AnOpts, Diags); } } void CheckerRegistry::addPackage(StringRef FullName) { - Packages.emplace_back(PackageInfo(FullName)); + Data.Packages.emplace_back(PackageInfo(FullName)); } void CheckerRegistry::addPackageOption(StringRef OptionType, @@ -548,22 +420,22 @@ 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); } } @@ -575,29 +447,28 @@ 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 { // 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; }; @@ -631,16 +502,16 @@ // 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; } const auto *PackageIt = - llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage)); - if (PackageIt != Packages.end()) { + llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage)); + if (PackageIt != Data.Packages.end()) { isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage, SuppliedOption, AnOpts, Diags); continue; @@ -651,122 +522,3 @@ } } -//===----------------------------------------------------------------------===// -// Printing functions. -//===----------------------------------------------------------------------===// - -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 { - 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 \n\n"; - Out << " -analyzer-config OPTION1=VALUE, -analyzer-config " - "OPTION2=VALUE, ...\n\n"; - Out << "OPTIONS:\n\n"; - - std::multimap 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 &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 --- a/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp @@ -23,11 +23,11 @@ ArrayRef> checkerRegistrationFns) : Context(&Context), LangOpts(Context.getLangOpts()), AOptions(AOptions), PP(&PP), Diags(Context.getDiagnostics()), - Registry( - std::make_unique(plugins, Context.getDiagnostics(), - AOptions, checkerRegistrationFns)) { - Registry->initializeRegistry(*this); - Registry->initializeManager(*this); + RegistryData(std::make_unique()) { + CheckerRegistry Registry(*RegistryData, plugins, Context.getDiagnostics(), + AOptions, checkerRegistrationFns); + Registry.initializeRegistry(*this); + Registry.initializeManager(*this); finishedCheckerRegistration(); } @@ -36,8 +36,9 @@ DiagnosticsEngine &Diags, ArrayRef plugins) : LangOpts(LangOpts), AOptions(AOptions), Diags(Diags), - Registry(std::make_unique(plugins, Diags, AOptions)) { - Registry->initializeRegistry(*this); + RegistryData(std::make_unique()) { + CheckerRegistry Registry(*RegistryData, plugins, Diags, AOptions, {}); + Registry.initializeRegistry(*this); } CheckerManager::~CheckerManager() { diff --git a/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp --- a/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp +++ b/clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -11,6 +11,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" @@ -100,7 +101,7 @@ llvm::raw_svector_ostream OS(Buf); C.getAnalysisManager() .getCheckerManager() - ->getCheckerRegistry() + ->getCheckerRegistryData() .printEnabledCheckerList(OS); // Strip a newline off. auto R =