xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
15ffd83dbSDimitry Andric //===- CheckerRegistry.h - Maintains all available checkers -----*- C++ -*-===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
105ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
115ffd83dbSDimitry Andric #include "llvm/ADT/Twine.h"
125ffd83dbSDimitry Andric #include <map>
135ffd83dbSDimitry Andric 
145ffd83dbSDimitry Andric using namespace clang;
155ffd83dbSDimitry Andric using namespace ento;
165ffd83dbSDimitry Andric 
175ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
185ffd83dbSDimitry Andric // Methods of CmdLineOption, PackageInfo and CheckerInfo.
195ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
205ffd83dbSDimitry Andric 
dump() const215ffd83dbSDimitry Andric LLVM_DUMP_METHOD void CmdLineOption::dump() const {
225ffd83dbSDimitry Andric   dumpToStream(llvm::errs());
235ffd83dbSDimitry Andric }
245ffd83dbSDimitry Andric 
255ffd83dbSDimitry Andric LLVM_DUMP_METHOD void
dumpToStream(llvm::raw_ostream & Out) const265ffd83dbSDimitry Andric CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const {
275ffd83dbSDimitry Andric   // The description can be just checked in Checkers.inc, the point here is to
285ffd83dbSDimitry Andric   // debug whether we succeeded in parsing it.
295ffd83dbSDimitry Andric   Out << OptionName << " (" << OptionType << ", "
305ffd83dbSDimitry Andric       << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \""
315ffd83dbSDimitry Andric       << DefaultValStr;
325ffd83dbSDimitry Andric }
335ffd83dbSDimitry Andric 
toString(StateFromCmdLine Kind)345ffd83dbSDimitry Andric static StringRef toString(StateFromCmdLine Kind) {
355ffd83dbSDimitry Andric   switch (Kind) {
365ffd83dbSDimitry Andric   case StateFromCmdLine::State_Disabled:
375ffd83dbSDimitry Andric     return "Disabled";
385ffd83dbSDimitry Andric   case StateFromCmdLine::State_Enabled:
395ffd83dbSDimitry Andric     return "Enabled";
405ffd83dbSDimitry Andric   case StateFromCmdLine::State_Unspecified:
415ffd83dbSDimitry Andric     return "Unspecified";
425ffd83dbSDimitry Andric   }
435ffd83dbSDimitry Andric   llvm_unreachable("Unhandled StateFromCmdLine enum");
445ffd83dbSDimitry Andric }
455ffd83dbSDimitry Andric 
dump() const465ffd83dbSDimitry Andric LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
475ffd83dbSDimitry Andric 
dumpToStream(llvm::raw_ostream & Out) const485ffd83dbSDimitry Andric LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const {
495ffd83dbSDimitry Andric   // The description can be just checked in Checkers.inc, the point here is to
505ffd83dbSDimitry Andric   // debug whether we succeeded in parsing it. Same with documentation uri.
515ffd83dbSDimitry Andric   Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "")
525ffd83dbSDimitry Andric       << ")\n";
535ffd83dbSDimitry Andric   Out << "  Options:\n";
545ffd83dbSDimitry Andric   for (const CmdLineOption &Option : CmdLineOptions) {
555ffd83dbSDimitry Andric     Out << "    ";
565ffd83dbSDimitry Andric     Option.dumpToStream(Out);
575ffd83dbSDimitry Andric     Out << '\n';
585ffd83dbSDimitry Andric   }
595ffd83dbSDimitry Andric   Out << "  Dependencies:\n";
605ffd83dbSDimitry Andric   for (const CheckerInfo *Dependency : Dependencies) {
615ffd83dbSDimitry Andric     Out << "  " << Dependency->FullName << '\n';
625ffd83dbSDimitry Andric   }
635ffd83dbSDimitry Andric   Out << "  Weak dependencies:\n";
645ffd83dbSDimitry Andric   for (const CheckerInfo *Dependency : WeakDependencies) {
655ffd83dbSDimitry Andric     Out << "    " << Dependency->FullName << '\n';
665ffd83dbSDimitry Andric   }
675ffd83dbSDimitry Andric }
685ffd83dbSDimitry Andric 
dump() const695ffd83dbSDimitry Andric LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
705ffd83dbSDimitry Andric 
dumpToStream(llvm::raw_ostream & Out) const715ffd83dbSDimitry Andric LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const {
725ffd83dbSDimitry Andric   Out << FullName << "\n";
735ffd83dbSDimitry Andric   Out << "  Options:\n";
745ffd83dbSDimitry Andric   for (const CmdLineOption &Option : CmdLineOptions) {
755ffd83dbSDimitry Andric     Out << "    ";
765ffd83dbSDimitry Andric     Option.dumpToStream(Out);
775ffd83dbSDimitry Andric     Out << '\n';
785ffd83dbSDimitry Andric   }
795ffd83dbSDimitry Andric }
805ffd83dbSDimitry Andric 
815ffd83dbSDimitry Andric static constexpr char PackageSeparator = '.';
825ffd83dbSDimitry Andric 
isInPackage(const CheckerInfo & Checker,StringRef PackageName)835ffd83dbSDimitry Andric static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) {
845ffd83dbSDimitry Andric   // Does the checker's full name have the package as a prefix?
85*5f757f3fSDimitry Andric   if (!Checker.FullName.starts_with(PackageName))
865ffd83dbSDimitry Andric     return false;
875ffd83dbSDimitry Andric 
885ffd83dbSDimitry Andric   // Is the package actually just the name of a specific checker?
895ffd83dbSDimitry Andric   if (Checker.FullName.size() == PackageName.size())
905ffd83dbSDimitry Andric     return true;
915ffd83dbSDimitry Andric 
925ffd83dbSDimitry Andric   // Is the checker in the package (or a subpackage)?
935ffd83dbSDimitry Andric   if (Checker.FullName[PackageName.size()] == PackageSeparator)
945ffd83dbSDimitry Andric     return true;
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   return false;
975ffd83dbSDimitry Andric }
985ffd83dbSDimitry Andric 
995ffd83dbSDimitry Andric CheckerInfoListRange
getMutableCheckersForCmdLineArg(StringRef CmdLineArg)1005ffd83dbSDimitry Andric CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
1015ffd83dbSDimitry Andric   auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
1025ffd83dbSDimitry Andric 
1035ffd83dbSDimitry Andric   if (!isInPackage(*It, CmdLineArg))
1045ffd83dbSDimitry Andric     return {Checkers.end(), Checkers.end()};
1055ffd83dbSDimitry Andric 
1065ffd83dbSDimitry Andric   // See how large the package is.
1075ffd83dbSDimitry Andric   // If the package doesn't exist, assume the option refers to a single
1085ffd83dbSDimitry Andric   // checker.
1095ffd83dbSDimitry Andric   size_t Size = 1;
1105ffd83dbSDimitry Andric   llvm::StringMap<size_t>::const_iterator PackageSize =
1115ffd83dbSDimitry Andric       PackageSizes.find(CmdLineArg);
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric   if (PackageSize != PackageSizes.end())
1145ffd83dbSDimitry Andric     Size = PackageSize->getValue();
1155ffd83dbSDimitry Andric 
1165ffd83dbSDimitry Andric   return {It, It + Size};
1175ffd83dbSDimitry Andric }
1185ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
1195ffd83dbSDimitry Andric // Printing functions.
1205ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
1215ffd83dbSDimitry Andric 
printCheckerWithDescList(const AnalyzerOptions & AnOpts,raw_ostream & Out,size_t MaxNameChars) const1225ffd83dbSDimitry Andric void CheckerRegistryData::printCheckerWithDescList(
1235ffd83dbSDimitry Andric     const AnalyzerOptions &AnOpts, raw_ostream &Out,
1245ffd83dbSDimitry Andric     size_t MaxNameChars) const {
1255ffd83dbSDimitry Andric   // FIXME: Print available packages.
1265ffd83dbSDimitry Andric 
1275ffd83dbSDimitry Andric   Out << "CHECKERS:\n";
1285ffd83dbSDimitry Andric 
1295ffd83dbSDimitry Andric   // Find the maximum option length.
1305ffd83dbSDimitry Andric   size_t OptionFieldWidth = 0;
1315ffd83dbSDimitry Andric   for (const auto &Checker : Checkers) {
1325ffd83dbSDimitry Andric     // Limit the amount of padding we are willing to give up for alignment.
1335ffd83dbSDimitry Andric     //   Package.Name     Description  [Hidden]
1345ffd83dbSDimitry Andric     size_t NameLength = Checker.FullName.size();
1355ffd83dbSDimitry Andric     if (NameLength <= MaxNameChars)
1365ffd83dbSDimitry Andric       OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
1375ffd83dbSDimitry Andric   }
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric   const size_t InitialPad = 2;
1405ffd83dbSDimitry Andric 
1415ffd83dbSDimitry Andric   auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
1425ffd83dbSDimitry Andric                    StringRef Description) {
1435ffd83dbSDimitry Andric     AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
1445ffd83dbSDimitry Andric                                          InitialPad, OptionFieldWidth);
1455ffd83dbSDimitry Andric     Out << '\n';
1465ffd83dbSDimitry Andric   };
1475ffd83dbSDimitry Andric 
1485ffd83dbSDimitry Andric   for (const auto &Checker : Checkers) {
1495ffd83dbSDimitry Andric     // The order of this if branches is significant, we wouldn't like to display
1505ffd83dbSDimitry Andric     // developer checkers even in the alpha output. For example,
1515ffd83dbSDimitry Andric     // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
1525ffd83dbSDimitry Andric     // by default, and users (even when the user is a developer of an alpha
1535ffd83dbSDimitry Andric     // checker) shouldn't normally tinker with whether they should be enabled.
1545ffd83dbSDimitry Andric 
1555ffd83dbSDimitry Andric     if (Checker.IsHidden) {
1565ffd83dbSDimitry Andric       if (AnOpts.ShowCheckerHelpDeveloper)
1575ffd83dbSDimitry Andric         Print(Out, Checker, Checker.Desc);
1585ffd83dbSDimitry Andric       continue;
1595ffd83dbSDimitry Andric     }
1605ffd83dbSDimitry Andric 
161*5f757f3fSDimitry Andric     if (Checker.FullName.starts_with("alpha")) {
1625ffd83dbSDimitry Andric       if (AnOpts.ShowCheckerHelpAlpha)
1635ffd83dbSDimitry Andric         Print(Out, Checker,
1645ffd83dbSDimitry Andric               ("(Enable only for development!) " + Checker.Desc).str());
1655ffd83dbSDimitry Andric       continue;
1665ffd83dbSDimitry Andric     }
1675ffd83dbSDimitry Andric 
1685ffd83dbSDimitry Andric     if (AnOpts.ShowCheckerHelp)
1695ffd83dbSDimitry Andric       Print(Out, Checker, Checker.Desc);
1705ffd83dbSDimitry Andric   }
1715ffd83dbSDimitry Andric }
1725ffd83dbSDimitry Andric 
printEnabledCheckerList(raw_ostream & Out) const1735ffd83dbSDimitry Andric void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
1745ffd83dbSDimitry Andric   for (const auto *i : EnabledCheckers)
1755ffd83dbSDimitry Andric     Out << i->FullName << '\n';
1765ffd83dbSDimitry Andric }
1775ffd83dbSDimitry Andric 
printCheckerOptionList(const AnalyzerOptions & AnOpts,raw_ostream & Out) const1785ffd83dbSDimitry Andric void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
1795ffd83dbSDimitry Andric                                                  raw_ostream &Out) const {
1805ffd83dbSDimitry Andric   Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
1815ffd83dbSDimitry Andric   Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
1825ffd83dbSDimitry Andric   Out << "       -analyzer-config OPTION1=VALUE, -analyzer-config "
1835ffd83dbSDimitry Andric          "OPTION2=VALUE, ...\n\n";
1845ffd83dbSDimitry Andric   Out << "OPTIONS:\n\n";
1855ffd83dbSDimitry Andric 
1865ffd83dbSDimitry Andric   // It's usually ill-advised to use multimap, but clang will terminate after
1875ffd83dbSDimitry Andric   // this function.
1885ffd83dbSDimitry Andric   std::multimap<StringRef, const CmdLineOption &> OptionMap;
1895ffd83dbSDimitry Andric 
1905ffd83dbSDimitry Andric   for (const CheckerInfo &Checker : Checkers) {
1915ffd83dbSDimitry Andric     for (const CmdLineOption &Option : Checker.CmdLineOptions) {
1925ffd83dbSDimitry Andric       OptionMap.insert({Checker.FullName, Option});
1935ffd83dbSDimitry Andric     }
1945ffd83dbSDimitry Andric   }
1955ffd83dbSDimitry Andric 
1965ffd83dbSDimitry Andric   for (const PackageInfo &Package : Packages) {
1975ffd83dbSDimitry Andric     for (const CmdLineOption &Option : Package.CmdLineOptions) {
1985ffd83dbSDimitry Andric       OptionMap.insert({Package.FullName, Option});
1995ffd83dbSDimitry Andric     }
2005ffd83dbSDimitry Andric   }
2015ffd83dbSDimitry Andric 
2025ffd83dbSDimitry Andric   auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
2035ffd83dbSDimitry Andric                   StringRef Desc) {
2045ffd83dbSDimitry Andric     AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
2055ffd83dbSDimitry Andric                                          /*InitialPad*/ 2,
2065ffd83dbSDimitry Andric                                          /*EntryWidth*/ 50,
2075ffd83dbSDimitry Andric                                          /*MinLineWidth*/ 90);
2085ffd83dbSDimitry Andric     Out << "\n\n";
2095ffd83dbSDimitry Andric   };
2105ffd83dbSDimitry Andric   for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
2115ffd83dbSDimitry Andric        OptionMap) {
2125ffd83dbSDimitry Andric     const CmdLineOption &Option = Entry.second;
2135ffd83dbSDimitry Andric     std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
2145ffd83dbSDimitry Andric 
2155ffd83dbSDimitry Andric     std::string Desc =
2165ffd83dbSDimitry Andric         ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
2175ffd83dbSDimitry Andric          (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
2185ffd83dbSDimitry Andric             .str();
2195ffd83dbSDimitry Andric 
2205ffd83dbSDimitry Andric     // The list of these if branches is significant, we wouldn't like to
2215ffd83dbSDimitry Andric     // display hidden alpha checker options for
2225ffd83dbSDimitry Andric     // -analyzer-checker-option-help-alpha.
2235ffd83dbSDimitry Andric 
2245ffd83dbSDimitry Andric     if (Option.IsHidden) {
2255ffd83dbSDimitry Andric       if (AnOpts.ShowCheckerOptionDeveloperList)
2265ffd83dbSDimitry Andric         Print(Out, FullOption, Desc);
2275ffd83dbSDimitry Andric       continue;
2285ffd83dbSDimitry Andric     }
2295ffd83dbSDimitry Andric 
2305ffd83dbSDimitry Andric     if (Option.DevelopmentStatus == "alpha" ||
231*5f757f3fSDimitry Andric         Entry.first.starts_with("alpha")) {
2325ffd83dbSDimitry Andric       if (AnOpts.ShowCheckerOptionAlphaList)
2335ffd83dbSDimitry Andric         Print(Out, FullOption,
2345ffd83dbSDimitry Andric               llvm::Twine("(Enable only for development!) " + Desc).str());
2355ffd83dbSDimitry Andric       continue;
2365ffd83dbSDimitry Andric     }
2375ffd83dbSDimitry Andric 
2385ffd83dbSDimitry Andric     if (AnOpts.ShowCheckerOptionList)
2395ffd83dbSDimitry Andric       Print(Out, FullOption, Desc);
2405ffd83dbSDimitry Andric   }
2415ffd83dbSDimitry Andric }
242