xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp (revision ec727ea710c91afd8ce4f788c5aaa8482b7b69b2)
1*ec727ea7Spatrick //===- CheckerRegistry.h - Maintains all available checkers -----*- C++ -*-===//
2*ec727ea7Spatrick //
3*ec727ea7Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*ec727ea7Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*ec727ea7Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*ec727ea7Spatrick //
7*ec727ea7Spatrick //===----------------------------------------------------------------------===//
8*ec727ea7Spatrick 
9*ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
10*ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
11*ec727ea7Spatrick #include "llvm/ADT/Twine.h"
12*ec727ea7Spatrick #include <map>
13*ec727ea7Spatrick 
14*ec727ea7Spatrick using namespace clang;
15*ec727ea7Spatrick using namespace ento;
16*ec727ea7Spatrick 
17*ec727ea7Spatrick //===----------------------------------------------------------------------===//
18*ec727ea7Spatrick // Methods of CmdLineOption, PackageInfo and CheckerInfo.
19*ec727ea7Spatrick //===----------------------------------------------------------------------===//
20*ec727ea7Spatrick 
dump() const21*ec727ea7Spatrick LLVM_DUMP_METHOD void CmdLineOption::dump() const {
22*ec727ea7Spatrick   dumpToStream(llvm::errs());
23*ec727ea7Spatrick }
24*ec727ea7Spatrick 
25*ec727ea7Spatrick LLVM_DUMP_METHOD void
dumpToStream(llvm::raw_ostream & Out) const26*ec727ea7Spatrick CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const {
27*ec727ea7Spatrick   // The description can be just checked in Checkers.inc, the point here is to
28*ec727ea7Spatrick   // debug whether we succeeded in parsing it.
29*ec727ea7Spatrick   Out << OptionName << " (" << OptionType << ", "
30*ec727ea7Spatrick       << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \""
31*ec727ea7Spatrick       << DefaultValStr;
32*ec727ea7Spatrick }
33*ec727ea7Spatrick 
toString(StateFromCmdLine Kind)34*ec727ea7Spatrick static StringRef toString(StateFromCmdLine Kind) {
35*ec727ea7Spatrick   switch (Kind) {
36*ec727ea7Spatrick   case StateFromCmdLine::State_Disabled:
37*ec727ea7Spatrick     return "Disabled";
38*ec727ea7Spatrick   case StateFromCmdLine::State_Enabled:
39*ec727ea7Spatrick     return "Enabled";
40*ec727ea7Spatrick   case StateFromCmdLine::State_Unspecified:
41*ec727ea7Spatrick     return "Unspecified";
42*ec727ea7Spatrick   }
43*ec727ea7Spatrick   llvm_unreachable("Unhandled StateFromCmdLine enum");
44*ec727ea7Spatrick }
45*ec727ea7Spatrick 
dump() const46*ec727ea7Spatrick LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
47*ec727ea7Spatrick 
dumpToStream(llvm::raw_ostream & Out) const48*ec727ea7Spatrick LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const {
49*ec727ea7Spatrick   // The description can be just checked in Checkers.inc, the point here is to
50*ec727ea7Spatrick   // debug whether we succeeded in parsing it. Same with documentation uri.
51*ec727ea7Spatrick   Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "")
52*ec727ea7Spatrick       << ")\n";
53*ec727ea7Spatrick   Out << "  Options:\n";
54*ec727ea7Spatrick   for (const CmdLineOption &Option : CmdLineOptions) {
55*ec727ea7Spatrick     Out << "    ";
56*ec727ea7Spatrick     Option.dumpToStream(Out);
57*ec727ea7Spatrick     Out << '\n';
58*ec727ea7Spatrick   }
59*ec727ea7Spatrick   Out << "  Dependencies:\n";
60*ec727ea7Spatrick   for (const CheckerInfo *Dependency : Dependencies) {
61*ec727ea7Spatrick     Out << "  " << Dependency->FullName << '\n';
62*ec727ea7Spatrick   }
63*ec727ea7Spatrick   Out << "  Weak dependencies:\n";
64*ec727ea7Spatrick   for (const CheckerInfo *Dependency : WeakDependencies) {
65*ec727ea7Spatrick     Out << "    " << Dependency->FullName << '\n';
66*ec727ea7Spatrick   }
67*ec727ea7Spatrick }
68*ec727ea7Spatrick 
dump() const69*ec727ea7Spatrick LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
70*ec727ea7Spatrick 
dumpToStream(llvm::raw_ostream & Out) const71*ec727ea7Spatrick LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const {
72*ec727ea7Spatrick   Out << FullName << "\n";
73*ec727ea7Spatrick   Out << "  Options:\n";
74*ec727ea7Spatrick   for (const CmdLineOption &Option : CmdLineOptions) {
75*ec727ea7Spatrick     Out << "    ";
76*ec727ea7Spatrick     Option.dumpToStream(Out);
77*ec727ea7Spatrick     Out << '\n';
78*ec727ea7Spatrick   }
79*ec727ea7Spatrick }
80*ec727ea7Spatrick 
81*ec727ea7Spatrick static constexpr char PackageSeparator = '.';
82*ec727ea7Spatrick 
isInPackage(const CheckerInfo & Checker,StringRef PackageName)83*ec727ea7Spatrick static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) {
84*ec727ea7Spatrick   // Does the checker's full name have the package as a prefix?
85*ec727ea7Spatrick   if (!Checker.FullName.startswith(PackageName))
86*ec727ea7Spatrick     return false;
87*ec727ea7Spatrick 
88*ec727ea7Spatrick   // Is the package actually just the name of a specific checker?
89*ec727ea7Spatrick   if (Checker.FullName.size() == PackageName.size())
90*ec727ea7Spatrick     return true;
91*ec727ea7Spatrick 
92*ec727ea7Spatrick   // Is the checker in the package (or a subpackage)?
93*ec727ea7Spatrick   if (Checker.FullName[PackageName.size()] == PackageSeparator)
94*ec727ea7Spatrick     return true;
95*ec727ea7Spatrick 
96*ec727ea7Spatrick   return false;
97*ec727ea7Spatrick }
98*ec727ea7Spatrick 
99*ec727ea7Spatrick CheckerInfoListRange
getMutableCheckersForCmdLineArg(StringRef CmdLineArg)100*ec727ea7Spatrick CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
101*ec727ea7Spatrick   auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
102*ec727ea7Spatrick 
103*ec727ea7Spatrick   if (!isInPackage(*It, CmdLineArg))
104*ec727ea7Spatrick     return {Checkers.end(), Checkers.end()};
105*ec727ea7Spatrick 
106*ec727ea7Spatrick   // See how large the package is.
107*ec727ea7Spatrick   // If the package doesn't exist, assume the option refers to a single
108*ec727ea7Spatrick   // checker.
109*ec727ea7Spatrick   size_t Size = 1;
110*ec727ea7Spatrick   llvm::StringMap<size_t>::const_iterator PackageSize =
111*ec727ea7Spatrick       PackageSizes.find(CmdLineArg);
112*ec727ea7Spatrick 
113*ec727ea7Spatrick   if (PackageSize != PackageSizes.end())
114*ec727ea7Spatrick     Size = PackageSize->getValue();
115*ec727ea7Spatrick 
116*ec727ea7Spatrick   return {It, It + Size};
117*ec727ea7Spatrick }
118*ec727ea7Spatrick //===----------------------------------------------------------------------===//
119*ec727ea7Spatrick // Printing functions.
120*ec727ea7Spatrick //===----------------------------------------------------------------------===//
121*ec727ea7Spatrick 
printCheckerWithDescList(const AnalyzerOptions & AnOpts,raw_ostream & Out,size_t MaxNameChars) const122*ec727ea7Spatrick void CheckerRegistryData::printCheckerWithDescList(
123*ec727ea7Spatrick     const AnalyzerOptions &AnOpts, raw_ostream &Out,
124*ec727ea7Spatrick     size_t MaxNameChars) const {
125*ec727ea7Spatrick   // FIXME: Print available packages.
126*ec727ea7Spatrick 
127*ec727ea7Spatrick   Out << "CHECKERS:\n";
128*ec727ea7Spatrick 
129*ec727ea7Spatrick   // Find the maximum option length.
130*ec727ea7Spatrick   size_t OptionFieldWidth = 0;
131*ec727ea7Spatrick   for (const auto &Checker : Checkers) {
132*ec727ea7Spatrick     // Limit the amount of padding we are willing to give up for alignment.
133*ec727ea7Spatrick     //   Package.Name     Description  [Hidden]
134*ec727ea7Spatrick     size_t NameLength = Checker.FullName.size();
135*ec727ea7Spatrick     if (NameLength <= MaxNameChars)
136*ec727ea7Spatrick       OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
137*ec727ea7Spatrick   }
138*ec727ea7Spatrick 
139*ec727ea7Spatrick   const size_t InitialPad = 2;
140*ec727ea7Spatrick 
141*ec727ea7Spatrick   auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
142*ec727ea7Spatrick                    StringRef Description) {
143*ec727ea7Spatrick     AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
144*ec727ea7Spatrick                                          InitialPad, OptionFieldWidth);
145*ec727ea7Spatrick     Out << '\n';
146*ec727ea7Spatrick   };
147*ec727ea7Spatrick 
148*ec727ea7Spatrick   for (const auto &Checker : Checkers) {
149*ec727ea7Spatrick     // The order of this if branches is significant, we wouldn't like to display
150*ec727ea7Spatrick     // developer checkers even in the alpha output. For example,
151*ec727ea7Spatrick     // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
152*ec727ea7Spatrick     // by default, and users (even when the user is a developer of an alpha
153*ec727ea7Spatrick     // checker) shouldn't normally tinker with whether they should be enabled.
154*ec727ea7Spatrick 
155*ec727ea7Spatrick     if (Checker.IsHidden) {
156*ec727ea7Spatrick       if (AnOpts.ShowCheckerHelpDeveloper)
157*ec727ea7Spatrick         Print(Out, Checker, Checker.Desc);
158*ec727ea7Spatrick       continue;
159*ec727ea7Spatrick     }
160*ec727ea7Spatrick 
161*ec727ea7Spatrick     if (Checker.FullName.startswith("alpha")) {
162*ec727ea7Spatrick       if (AnOpts.ShowCheckerHelpAlpha)
163*ec727ea7Spatrick         Print(Out, Checker,
164*ec727ea7Spatrick               ("(Enable only for development!) " + Checker.Desc).str());
165*ec727ea7Spatrick       continue;
166*ec727ea7Spatrick     }
167*ec727ea7Spatrick 
168*ec727ea7Spatrick     if (AnOpts.ShowCheckerHelp)
169*ec727ea7Spatrick       Print(Out, Checker, Checker.Desc);
170*ec727ea7Spatrick   }
171*ec727ea7Spatrick }
172*ec727ea7Spatrick 
printEnabledCheckerList(raw_ostream & Out) const173*ec727ea7Spatrick void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
174*ec727ea7Spatrick   for (const auto *i : EnabledCheckers)
175*ec727ea7Spatrick     Out << i->FullName << '\n';
176*ec727ea7Spatrick }
177*ec727ea7Spatrick 
printCheckerOptionList(const AnalyzerOptions & AnOpts,raw_ostream & Out) const178*ec727ea7Spatrick void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
179*ec727ea7Spatrick                                                  raw_ostream &Out) const {
180*ec727ea7Spatrick   Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
181*ec727ea7Spatrick   Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
182*ec727ea7Spatrick   Out << "       -analyzer-config OPTION1=VALUE, -analyzer-config "
183*ec727ea7Spatrick          "OPTION2=VALUE, ...\n\n";
184*ec727ea7Spatrick   Out << "OPTIONS:\n\n";
185*ec727ea7Spatrick 
186*ec727ea7Spatrick   // It's usually ill-advised to use multimap, but clang will terminate after
187*ec727ea7Spatrick   // this function.
188*ec727ea7Spatrick   std::multimap<StringRef, const CmdLineOption &> OptionMap;
189*ec727ea7Spatrick 
190*ec727ea7Spatrick   for (const CheckerInfo &Checker : Checkers) {
191*ec727ea7Spatrick     for (const CmdLineOption &Option : Checker.CmdLineOptions) {
192*ec727ea7Spatrick       OptionMap.insert({Checker.FullName, Option});
193*ec727ea7Spatrick     }
194*ec727ea7Spatrick   }
195*ec727ea7Spatrick 
196*ec727ea7Spatrick   for (const PackageInfo &Package : Packages) {
197*ec727ea7Spatrick     for (const CmdLineOption &Option : Package.CmdLineOptions) {
198*ec727ea7Spatrick       OptionMap.insert({Package.FullName, Option});
199*ec727ea7Spatrick     }
200*ec727ea7Spatrick   }
201*ec727ea7Spatrick 
202*ec727ea7Spatrick   auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
203*ec727ea7Spatrick                   StringRef Desc) {
204*ec727ea7Spatrick     AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
205*ec727ea7Spatrick                                          /*InitialPad*/ 2,
206*ec727ea7Spatrick                                          /*EntryWidth*/ 50,
207*ec727ea7Spatrick                                          /*MinLineWidth*/ 90);
208*ec727ea7Spatrick     Out << "\n\n";
209*ec727ea7Spatrick   };
210*ec727ea7Spatrick   for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
211*ec727ea7Spatrick        OptionMap) {
212*ec727ea7Spatrick     const CmdLineOption &Option = Entry.second;
213*ec727ea7Spatrick     std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
214*ec727ea7Spatrick 
215*ec727ea7Spatrick     std::string Desc =
216*ec727ea7Spatrick         ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
217*ec727ea7Spatrick          (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
218*ec727ea7Spatrick             .str();
219*ec727ea7Spatrick 
220*ec727ea7Spatrick     // The list of these if branches is significant, we wouldn't like to
221*ec727ea7Spatrick     // display hidden alpha checker options for
222*ec727ea7Spatrick     // -analyzer-checker-option-help-alpha.
223*ec727ea7Spatrick 
224*ec727ea7Spatrick     if (Option.IsHidden) {
225*ec727ea7Spatrick       if (AnOpts.ShowCheckerOptionDeveloperList)
226*ec727ea7Spatrick         Print(Out, FullOption, Desc);
227*ec727ea7Spatrick       continue;
228*ec727ea7Spatrick     }
229*ec727ea7Spatrick 
230*ec727ea7Spatrick     if (Option.DevelopmentStatus == "alpha" ||
231*ec727ea7Spatrick         Entry.first.startswith("alpha")) {
232*ec727ea7Spatrick       if (AnOpts.ShowCheckerOptionAlphaList)
233*ec727ea7Spatrick         Print(Out, FullOption,
234*ec727ea7Spatrick               llvm::Twine("(Enable only for development!) " + Desc).str());
235*ec727ea7Spatrick       continue;
236*ec727ea7Spatrick     }
237*ec727ea7Spatrick 
238*ec727ea7Spatrick     if (AnOpts.ShowCheckerOptionList)
239*ec727ea7Spatrick       Print(Out, FullOption, Desc);
240*ec727ea7Spatrick   }
241*ec727ea7Spatrick }
242