Clang Project

clang_source_code/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
1//===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
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/Frontend/CheckerRegistry.h"
10#include "clang/Basic/Diagnostic.h"
11#include "clang/Basic/LLVM.h"
12#include "clang/Frontend/FrontendDiagnostic.h"
13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14#include "clang/StaticAnalyzer/Core/CheckerManager.h"
15#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/SetVector.h"
18#include "llvm/ADT/StringMap.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/DynamicLibrary.h"
21#include "llvm/Support/Path.h"
22#include "llvm/Support/raw_ostream.h"
23#include <algorithm>
24
25using namespace clang;
26using namespace ento;
27using llvm::sys::DynamicLibrary;
28
29using RegisterCheckersFn = void (*)(CheckerRegistry &);
30
31static bool isCompatibleAPIVersion(const char *versionString) {
32  // If the version string is null, it's not an analyzer plugin.
33  if (!versionString)
34    return false;
35
36  // For now, none of the static analyzer API is considered stable.
37  // Versions must match exactly.
38  return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
39}
40
41static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
42                          const CheckerRegistry::CheckerInfo &b) {
43  return a.FullName < b.FullName;
44}
45
46static constexpr char PackageSeparator = '.';
47
48static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
49                        StringRef packageName) {
50  // Does the checker's full name have the package as a prefix?
51  if (!checker.FullName.startswith(packageName))
52    return false;
53
54  // Is the package actually just the name of a specific checker?
55  if (checker.FullName.size() == packageName.size())
56    return true;
57
58  // Is the checker in the package (or a subpackage)?
59  if (checker.FullName[packageName.size()] == PackageSeparator)
60    return true;
61
62  return false;
63}
64
65CheckerRegistry::CheckerInfoListRange
66CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
67
68   (0) . __assert_fail ("std..is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && \"In order to efficiently gather checkers, this function expects them \" \"to be already sorted!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp", 70, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
69 (0) . __assert_fail ("std..is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && \"In order to efficiently gather checkers, this function expects them \" \"to be already sorted!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp", 70, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">         "In order to efficiently gather checkers, this function expects them "
70 (0) . __assert_fail ("std..is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && \"In order to efficiently gather checkers, this function expects them \" \"to be already sorted!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp", 70, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">         "to be already sorted!");
71
72  // Use a binary search to find the possible start of the package.
73  CheckerRegistry::CheckerInfo
74      packageInfo(nullptrnullptr, CmdLineArg, """");
75  auto it = std::lower_bound(Checkers.begin(), Checkers.end(),
76                             packageInfocheckerNameLT);
77
78  if (!isInPackage(*it, CmdLineArg))
79    return { Checkers.end(), Checkers.end() };
80
81  // See how large the package is.
82  // If the package doesn't exist, assume the option refers to a single
83  // checker.
84  size_t size = 1;
85  llvm::StringMap<size_t>::const_iterator packageSize =
86      Packages.find(CmdLineArg);
87
88  if (packageSize != Packages.end())
89    size = packageSize->getValue();
90
91  return { it, it + size };
92}
93
94CheckerRegistry::CheckerRegistry(
95     ArrayRef<std::stringpluginsDiagnosticsEngine &diags,
96     AnalyzerOptions &AnOptsconst LangOptions &LangOpts,
97     ArrayRef<std::function<void(CheckerRegistry &)>>
98         checkerRegistrationFns)
99  : Diags(diags), AnOpts(AnOpts), LangOpts(LangOpts) {
100
101  // Register builtin checkers.
102#define GET_CHECKERS
103#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI)                            \
104  addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT,       \
105             DOC_URI);
106#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
107#undef CHECKER
108#undef GET_CHECKERS
109
110  // Register checkers from plugins.
111  for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
112       i != e; ++i) {
113    // Get access to the plugin.
114    std::string err;
115    DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err);
116    if (!lib.isValid()) {
117      diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err;
118      continue;
119    }
120
121    // See if it's compatible with this build of clang.
122    const char *pluginAPIVersion =
123      (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
124    if (!isCompatibleAPIVersion(pluginAPIVersion)) {
125      Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
126          << llvm::sys::path::filename(*i);
127      Diags.Report(diag::note_incompatible_analyzer_plugin_api)
128          << CLANG_ANALYZER_API_VERSION_STRING
129          << pluginAPIVersion;
130      continue;
131    }
132
133    // Register its checkers.
134    RegisterCheckersFn registerPluginCheckers =
135      (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
136                                                      "clang_registerCheckers");
137    if (registerPluginCheckers)
138      registerPluginCheckers(*this);
139  }
140
141  // Register statically linked checkers, that aren't generated from the tblgen
142  // file, but rather passed their registry function as a parameter in 
143  // checkerRegistrationFns. 
144
145  for (const auto &Fn : checkerRegistrationFns)
146    Fn(*this);
147
148  // Sort checkers for efficient collection.
149  // FIXME: Alphabetical sort puts 'experimental' in the middle.
150  // Would it be better to name it '~experimental' or something else
151  // that's ASCIIbetically last?
152  llvm::sort(Checkers, checkerNameLT);
153
154#define GET_CHECKER_DEPENDENCIES
155
156#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \
157  addDependency(FULLNAME, DEPENDENCY);
158
159#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
160#undef CHECKER_DEPENDENCY
161#undef GET_CHECKER_DEPENDENCIES
162
163  // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
164  // command line.
165  for (const std::pair<std::string, bool> &opt : AnOpts.CheckersControlList) {
166    CheckerInfoListRange checkersForCmdLineArg =
167                                     getMutableCheckersForCmdLineArg(opt.first);
168
169    if (checkersForCmdLineArg.begin() == checkersForCmdLineArg.end()) {
170      Diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
171      Diags.Report(diag::note_suggest_disabling_all_checkers);
172    }
173
174    for (CheckerInfo &checker : checkersForCmdLineArg) {
175      checker.State = opt.second ? StateFromCmdLine::State_Enabled :
176                                   StateFromCmdLine::State_Disabled;
177    }
178  }
179}
180
181/// Collects dependencies in \p ret, returns false on failure.
182static bool collectDependenciesImpl(
183                              const CheckerRegistry::ConstCheckerInfoList &deps,
184                              const LangOptions &LO,
185                              CheckerRegistry::CheckerInfoSet &ret);
186
187/// Collects dependenies in \p enabledCheckers. Return None on failure.
188LLVM_NODISCARD
189static llvm::Optional<CheckerRegistry::CheckerInfoSet> collectDependencies(
190     const CheckerRegistry::CheckerInfo &checker, const LangOptions &LO) {
191
192  CheckerRegistry::CheckerInfoSet ret;
193  // Add dependencies to the enabled checkers only if all of them can be
194  // enabled.
195  if (!collectDependenciesImpl(checker.Dependencies, LO, ret))
196    return None;
197
198  return ret;
199}
200
201static bool collectDependenciesImpl(
202                              const CheckerRegistry::ConstCheckerInfoList &deps,
203                              const LangOptions &LO,
204                              CheckerRegistry::CheckerInfoSet &ret) {
205
206  for (const CheckerRegistry::CheckerInfo *dependency : deps) {
207
208    if (dependency->isDisabled(LO))
209      return false;
210
211    // Collect dependencies recursively.
212    if (!collectDependenciesImpl(dependency->Dependencies, LO, ret))
213      return false;
214
215    ret.insert(dependency);
216  }
217
218  return true;
219}
220
221CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
222
223  CheckerInfoSet enabledCheckers;
224
225  for (const CheckerInfo &checker : Checkers) {
226    if (!checker.isEnabled(LangOpts))
227      continue;
228
229    // Recursively enable it's dependencies.
230    llvm::Optional<CheckerInfoSet> deps =
231        collectDependencies(checker, LangOpts);
232
233    if (!deps) {
234      // If we failed to enable any of the dependencies, don't enable this
235      // checker.
236      continue;
237    }
238
239    // Note that set_union also preserves the order of insertion.
240    enabledCheckers.set_union(*deps);
241
242    // Enable the checker.
243    enabledCheckers.insert(&checker);
244  }
245
246  return enabledCheckers;
247}
248
249void CheckerRegistry::addChecker(InitializationFunction Rfn,
250                                 ShouldRegisterFunction SfnStringRef Name,
251                                 StringRef DescStringRef DocsUri) {
252  Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
253
254  // Record the presence of the checker in its packages.
255  StringRef packageNameleafName;
256  std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
257  while (!leafName.empty()) {
258    Packages[packageName] += 1;
259    std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
260  }
261}
262
263void CheckerRegistry::initializeManager(CheckerManager &checkerMgrconst {
264  // Collect checkers enabled by the options.
265  CheckerInfoSet enabledCheckers = getEnabledCheckers();
266
267  // Initialize the CheckerManager with all enabled checkers.
268  for (const auto *i : enabledCheckers) {
269    checkerMgr.setCurrentCheckName(CheckName(i->FullName));
270    i->Initialize(checkerMgr);
271  }
272}
273
274void CheckerRegistry::validateCheckerOptions() const {
275  for (const auto &config : AnOpts.Config) {
276    size_t pos = config.getKey().find(':');
277    if (pos == StringRef::npos)
278      continue;
279
280    bool hasChecker = false;
281    StringRef checkerName = config.getKey().substr(0, pos);
282    for (const auto &checker : Checkers) {
283      if (checker.FullName.startswith(checkerName) &&
284          (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
285        hasChecker = true;
286        break;
287      }
288    }
289    if (!hasChecker)
290      Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
291  }
292}
293
294void CheckerRegistry::printHelp(raw_ostream &out,
295                                size_t maxNameCharsconst {
296  // FIXME: Print available packages.
297
298  out << "CHECKERS:\n";
299
300  // Find the maximum option length.
301  size_t optionFieldWidth = 0;
302  for (const auto &i : Checkers) {
303    // Limit the amount of padding we are willing to give up for alignment.
304    //   Package.Name     Description  [Hidden]
305    size_t nameLength = i.FullName.size();
306    if (nameLength <= maxNameChars)
307      optionFieldWidth = std::max(optionFieldWidth, nameLength);
308  }
309
310  const size_t initialPad = 2;
311  for (const auto &i : Checkers) {
312    out.indent(initialPad) << i.FullName;
313
314    int pad = optionFieldWidth - i.FullName.size();
315
316    // Break on long option names.
317    if (pad < 0) {
318      out << '\n';
319      pad = optionFieldWidth + initialPad;
320    }
321    out.indent(pad + 2) << i.Desc;
322
323    out << '\n';
324  }
325}
326
327void CheckerRegistry::printList(raw_ostream &outconst {
328  // Collect checkers enabled by the options.
329  CheckerInfoSet enabledCheckers = getEnabledCheckers();
330
331  for (const auto *i : enabledCheckers)
332    out << i->FullName << '\n';
333}
334
clang::ento::CheckerRegistry::getMutableCheckersForCmdLineArg
clang::ento::CheckerRegistry::getEnabledCheckers
clang::ento::CheckerRegistry::addChecker
clang::ento::CheckerRegistry::initializeManager
clang::ento::CheckerRegistry::validateCheckerOptions
clang::ento::CheckerRegistry::printHelp
clang::ento::CheckerRegistry::printList