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