| //===- 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 <map> |
| |
| using namespace clang; |
| using namespace ento; |
| |
| //===----------------------------------------------------------------------===// |
| // Methods of CmdLineOption, PackageInfo and CheckerInfo. |
| //===----------------------------------------------------------------------===// |
| |
| LLVM_DUMP_METHOD void CmdLineOption::dump() const { |
| dumpToStream(llvm::errs()); |
| } |
| |
| 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::dump() const { dumpToStream(llvm::errs()); } |
| |
| 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::dump() const { dumpToStream(llvm::errs()); } |
| |
| 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<size_t>::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 <OPTION1=VALUE,OPTION2=VALUE,...>\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<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); |
| } |
| } |