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