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 
dump() const21 LLVM_DUMP_METHOD void CmdLineOption::dump() const {
22   dumpToStream(llvm::errs());
23 }
24 
25 LLVM_DUMP_METHOD void
dumpToStream(llvm::raw_ostream & Out) const26 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 
toString(StateFromCmdLine Kind)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 
dump() const46 LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
47 
dumpToStream(llvm::raw_ostream & Out) const48 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(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 
dump() const69 LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
70 
dumpToStream(llvm::raw_ostream & Out) const71 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 
isInPackage(const CheckerInfo & Checker,StringRef PackageName)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.startswith(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
getMutableCheckersForCmdLineArg(StringRef CmdLineArg)100 CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
101   auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
102 
103   if (!isInPackage(*It, 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(CmdLineArg);
112 
113   if (PackageSize != PackageSizes.end())
114     Size = PackageSize->getValue();
115 
116   return {It, It + Size};
117 }
118 //===----------------------------------------------------------------------===//
119 // Printing functions.
120 //===----------------------------------------------------------------------===//
121 
printCheckerWithDescList(const AnalyzerOptions & AnOpts,raw_ostream & Out,size_t MaxNameChars) const122 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(OptionFieldWidth, 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, {Checker.FullName, Description},
144                                          InitialPad, 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.startswith("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 
printEnabledCheckerList(raw_ostream & Out) const173 void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
174   for (const auto *i : EnabledCheckers)
175     Out << i->FullName << '\n';
176 }
177 
printCheckerOptionList(const AnalyzerOptions & AnOpts,raw_ostream & Out) const178 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({Checker.FullName, Option});
193     }
194   }
195 
196   for (const PackageInfo &Package : Packages) {
197     for (const CmdLineOption &Option : Package.CmdLineOptions) {
198       OptionMap.insert({Package.FullName, Option});
199     }
200   }
201 
202   auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
203                   StringRef Desc) {
204     AnalyzerOptions::printFormattedEntry(Out, {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.startswith("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