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
14using namespace clang;
15using namespace ento;
16
17//===----------------------------------------------------------------------===//
18// Methods of CmdLineOption, PackageInfo and CheckerInfo.
19//===----------------------------------------------------------------------===//
20
21LLVM_DUMP_METHOD void CmdLineOption::dump() const {
22  dumpToStream(llvm::errs());
23}
24
25LLVM_DUMP_METHOD void
26CmdLineOption::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
34static 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
46LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
47
48LLVM_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
69LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
70
71LLVM_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
81static constexpr char PackageSeparator = '.';
82
83static 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
99CheckerInfoListRange
100CheckerRegistryData::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
122void 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
173void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
174  for (const auto *i : EnabledCheckers)
175    Out << i->FullName << '\n';
176}
177
178void 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