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