clang 20.0.0git
CheckerRegistry.cpp
Go to the documentation of this file.
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
11#include "clang/Basic/LLVM.h"
17#include "llvm/ADT/STLExtras.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 namespace checker_registry;
28using llvm::sys::DynamicLibrary;
29
30//===----------------------------------------------------------------------===//
31// Utilities.
32//===----------------------------------------------------------------------===//
33
34static bool isCompatibleAPIVersion(const char *VersionString) {
35 // If the version string is null, its not an analyzer plugin.
36 if (!VersionString)
37 return false;
38
39 // For now, none of the static analyzer API is considered stable.
40 // Versions must match exactly.
41 return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
42}
43
44static constexpr char PackageSeparator = '.';
45
46//===----------------------------------------------------------------------===//
47// Methods of CheckerRegistry.
48//===----------------------------------------------------------------------===//
49
52 DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
53 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
54 : Data(Data), Diags(Diags), AnOpts(AnOpts) {
55
56 // Register builtin checkers.
57#define GET_CHECKERS
58#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
59 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
60 DOC_URI, IS_HIDDEN);
61
62#define GET_PACKAGES
63#define PACKAGE(FULLNAME) addPackage(FULLNAME);
64
65#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
66#undef CHECKER
67#undef GET_CHECKERS
68#undef PACKAGE
69#undef GET_PACKAGES
70
71 // Register checkers from plugins.
72 for (const std::string &Plugin : Plugins) {
73 // Get access to the plugin.
74 std::string ErrorMsg;
75 DynamicLibrary Lib =
76 DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
77 if (!Lib.isValid()) {
78 Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
79 continue;
80 }
81
82 // See if its compatible with this build of clang.
83 const char *PluginAPIVersion = static_cast<const char *>(
84 Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
85
86 if (!isCompatibleAPIVersion(PluginAPIVersion)) {
87 Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
88 << llvm::sys::path::filename(Plugin);
89 Diags.Report(diag::note_incompatible_analyzer_plugin_api)
90 << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion;
91 continue;
92 }
93
94 using RegisterPluginCheckerFn = void (*)(CheckerRegistry &);
95 // Register its checkers.
96 RegisterPluginCheckerFn RegisterPluginCheckers =
97 reinterpret_cast<RegisterPluginCheckerFn>(
98 Lib.getAddressOfSymbol("clang_registerCheckers"));
99 if (RegisterPluginCheckers)
100 RegisterPluginCheckers(*this);
101 }
102
103 // Register statically linked checkers, that aren't generated from the tblgen
104 // file, but rather passed their registry function as a parameter in
105 // checkerRegistrationFns.
106
107 for (const auto &Fn : CheckerRegistrationFns)
108 Fn(*this);
109
110 // Sort checkers for efficient collection.
111 // FIXME: Alphabetical sort puts 'experimental' in the middle.
112 // Would it be better to name it '~experimental' or something else
113 // that's ASCIIbetically last?
114 llvm::sort(Data.Packages, checker_registry::PackageNameLT{});
115 llvm::sort(Data.Checkers, checker_registry::CheckerNameLT{});
116
117#define GET_CHECKER_DEPENDENCIES
118
119#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
120 addDependency(FULLNAME, DEPENDENCY);
121
122#define GET_CHECKER_WEAK_DEPENDENCIES
123
124#define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY) \
125 addWeakDependency(FULLNAME, DEPENDENCY);
126
127#define GET_CHECKER_OPTIONS
128#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \
129 DEVELOPMENT_STATUS, IS_HIDDEN) \
130 addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \
131 DEVELOPMENT_STATUS, IS_HIDDEN);
132
133#define GET_PACKAGE_OPTIONS
134#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \
135 DEVELOPMENT_STATUS, IS_HIDDEN) \
136 addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \
137 DEVELOPMENT_STATUS, IS_HIDDEN);
138
139#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
140#undef CHECKER_DEPENDENCY
141#undef GET_CHECKER_DEPENDENCIES
142#undef CHECKER_WEAK_DEPENDENCY
143#undef GET_CHECKER_WEAK_DEPENDENCIES
144#undef CHECKER_OPTION
145#undef GET_CHECKER_OPTIONS
146#undef PACKAGE_OPTION
147#undef GET_PACKAGE_OPTIONS
148
149 resolveDependencies<true>();
150 resolveDependencies<false>();
151
152#ifndef NDEBUG
153 for (auto &DepPair : Data.Dependencies) {
154 for (auto &WeakDepPair : Data.WeakDependencies) {
155 // Some assertions to enforce that strong dependencies are relations in
156 // between purely modeling checkers, and weak dependencies are about
157 // diagnostics.
158 assert(WeakDepPair != DepPair &&
159 "A checker cannot strong and weak depend on the same checker!");
160 assert(WeakDepPair.first != DepPair.second &&
161 "A strong dependency mustn't have weak dependencies!");
162 assert(WeakDepPair.second != DepPair.second &&
163 "A strong dependency mustn't be a weak dependency as well!");
164 }
165 }
166#endif
167
168 resolveCheckerAndPackageOptions();
169
170 // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
171 // command line.
172 for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
173 CheckerInfoListRange CheckerForCmdLineArg =
174 Data.getMutableCheckersForCmdLineArg(Opt.first);
175
176 if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
177 Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
178 Diags.Report(diag::note_suggest_disabling_all_checkers);
179 }
180
181 for (CheckerInfo &checker : CheckerForCmdLineArg) {
182 checker.State = Opt.second ? StateFromCmdLine::State_Enabled
183 : StateFromCmdLine::State_Disabled;
184 }
185 }
187}
188
189//===----------------------------------------------------------------------===//
190// Dependency resolving.
191//===----------------------------------------------------------------------===//
192
193template <typename IsEnabledFn>
194static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
195 const CheckerManager &Mgr,
196 CheckerInfoSet &Ret,
197 IsEnabledFn IsEnabled);
198
199/// Collects weak dependencies in \p enabledData.Checkers.
200template <typename IsEnabledFn>
201static void collectWeakDependencies(const ConstCheckerInfoList &Deps,
202 const CheckerManager &Mgr,
203 CheckerInfoSet &Ret, IsEnabledFn IsEnabled);
204
206 // First, we calculate the list of enabled checkers as specified by the
207 // invocation. Weak dependencies will not enable their unspecified strong
208 // depenencies, but its only after resolving strong dependencies for all
209 // checkers when we know whether they will be enabled.
210 CheckerInfoSet Tmp;
211 auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) {
212 return !Checker->isDisabled(Mgr);
213 };
214 for (const CheckerInfo &Checker : Data.Checkers) {
215 if (!Checker.isEnabled(Mgr))
216 continue;
217
218 CheckerInfoSet Deps;
219 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
220 IsEnabledFromCmdLine)) {
221 // If we failed to enable any of the dependencies, don't enable this
222 // checker.
223 continue;
224 }
225
226 Tmp.insert(Deps.begin(), Deps.end());
227
228 // Enable the checker.
229 Tmp.insert(&Checker);
230 }
231
232 // Calculate enabled checkers with the correct registration order. As this is
233 // done recursively, its arguably cheaper, but for sure less error prone to
234 // recalculate from scratch.
235 auto IsEnabled = [&](const CheckerInfo *Checker) {
236 return Tmp.contains(Checker);
237 };
238 for (const CheckerInfo &Checker : Data.Checkers) {
239 if (!Checker.isEnabled(Mgr))
240 continue;
241
242 CheckerInfoSet Deps;
243
244 collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled);
245
246 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
247 IsEnabledFromCmdLine)) {
248 // If we failed to enable any of the dependencies, don't enable this
249 // checker.
250 continue;
251 }
252
253 // Note that set_union also preserves the order of insertion.
254 Data.EnabledCheckers.set_union(Deps);
255 Data.EnabledCheckers.insert(&Checker);
256 }
257}
258
259template <typename IsEnabledFn>
261 const CheckerManager &Mgr,
262 CheckerInfoSet &Ret,
263 IsEnabledFn IsEnabled) {
264
265 for (const CheckerInfo *Dependency : Deps) {
266 if (!IsEnabled(Dependency))
267 return false;
268
269 // Collect dependencies recursively.
270 if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
271 IsEnabled))
272 return false;
273 Ret.insert(Dependency);
274 }
275
276 return true;
277}
278
279template <typename IsEnabledFn>
281 const CheckerManager &Mgr,
282 CheckerInfoSet &Ret,
283 IsEnabledFn IsEnabled) {
284
285 for (const CheckerInfo *Dependency : WeakDeps) {
286 // Don't enable this checker if strong dependencies are unsatisfied, but
287 // assume that weak dependencies are transitive.
288 collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled);
289
290 if (IsEnabled(Dependency) &&
291 collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
292 IsEnabled))
293 Ret.insert(Dependency);
294 }
295}
296
297template <bool IsWeak> void CheckerRegistry::resolveDependencies() {
298 for (const std::pair<StringRef, StringRef> &Entry :
299 (IsWeak ? Data.WeakDependencies : Data.Dependencies)) {
300
301 auto CheckerIt = binaryFind(Data.Checkers, Entry.first);
302 assert(CheckerIt != Data.Checkers.end() &&
303 CheckerIt->FullName == Entry.first &&
304 "Failed to find the checker while attempting to set up its "
305 "dependencies!");
306
307 auto DependencyIt = binaryFind(Data.Checkers, Entry.second);
308 assert(DependencyIt != Data.Checkers.end() &&
309 DependencyIt->FullName == Entry.second &&
310 "Failed to find the dependency of a checker!");
311
312 // We do allow diagnostics from unit test/example dependency checkers.
313 assert((DependencyIt->FullName.starts_with("test") ||
314 DependencyIt->FullName.starts_with("example") || IsWeak ||
315 DependencyIt->IsHidden) &&
316 "Strong dependencies are modeling checkers, and as such "
317 "non-user facing! Mark them hidden in Checkers.td!");
318
319 if (IsWeak)
320 CheckerIt->WeakDependencies.emplace_back(&*DependencyIt);
321 else
322 CheckerIt->Dependencies.emplace_back(&*DependencyIt);
323 }
324}
325
326void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
327 Data.Dependencies.emplace_back(FullName, Dependency);
328}
329
331 StringRef Dependency) {
332 Data.WeakDependencies.emplace_back(FullName, Dependency);
333}
334
335//===----------------------------------------------------------------------===//
336// Checker option resolving and validating.
337//===----------------------------------------------------------------------===//
338
339/// Insert the checker/package option to AnalyzerOptions' config table, and
340/// validate it, if the user supplied it on the command line.
341static void insertAndValidate(StringRef FullName, const CmdLineOption &Option,
342 AnalyzerOptions &AnOpts,
343 DiagnosticsEngine &Diags) {
344
345 std::string FullOption = (FullName + ":" + Option.OptionName).str();
346
347 auto It =
348 AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)});
349
350 // Insertation was successful -- CmdLineOption's constructor will validate
351 // whether values received from plugins or TableGen files are correct.
352 if (It.second)
353 return;
354
355 // Insertion failed, the user supplied this package/checker option on the
356 // command line. If the supplied value is invalid, we'll restore the option
357 // to it's default value, and if we're in non-compatibility mode, we'll also
358 // emit an error.
359
360 StringRef SuppliedValue = It.first->getValue();
361
362 if (Option.OptionType == "bool") {
363 if (SuppliedValue != "true" && SuppliedValue != "false") {
365 Diags.Report(diag::err_analyzer_checker_option_invalid_input)
366 << FullOption << "a boolean value";
367 }
368
369 It.first->setValue(std::string(Option.DefaultValStr));
370 }
371 return;
372 }
373
374 if (Option.OptionType == "int") {
375 int Tmp;
376 bool HasFailed = SuppliedValue.getAsInteger(0, Tmp);
377 if (HasFailed) {
379 Diags.Report(diag::err_analyzer_checker_option_invalid_input)
380 << FullOption << "an integer value";
381 }
382
383 It.first->setValue(std::string(Option.DefaultValStr));
384 }
385 return;
386 }
387}
388
389template <class T>
390static void insertOptionToCollection(StringRef FullName, T &Collection,
391 const CmdLineOption &Option,
392 AnalyzerOptions &AnOpts,
393 DiagnosticsEngine &Diags) {
394 auto It = binaryFind(Collection, FullName);
395 assert(It != Collection.end() &&
396 "Failed to find the checker while attempting to add a command line "
397 "option to it!");
398
399 insertAndValidate(FullName, Option, AnOpts, Diags);
400
401 It->CmdLineOptions.emplace_back(Option);
402}
403
404void CheckerRegistry::resolveCheckerAndPackageOptions() {
405 for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
406 Data.CheckerOptions) {
407 insertOptionToCollection(CheckerOptEntry.first, Data.Checkers,
408 CheckerOptEntry.second, AnOpts, Diags);
409 }
410
411 for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
412 Data.PackageOptions) {
413 insertOptionToCollection(PackageOptEntry.first, Data.Packages,
414 PackageOptEntry.second, AnOpts, Diags);
415 }
416}
417
419 Data.Packages.emplace_back(PackageInfo(FullName));
420}
421
422void CheckerRegistry::addPackageOption(StringRef OptionType,
423 StringRef PackageFullName,
424 StringRef OptionName,
425 StringRef DefaultValStr,
426 StringRef Description,
427 StringRef DevelopmentStatus,
428 bool IsHidden) {
429 Data.PackageOptions.emplace_back(
430 PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
431 Description, DevelopmentStatus, IsHidden});
432}
433
435 ShouldRegisterFunction Sfn, StringRef Name,
436 StringRef Desc, StringRef DocsUri,
437 bool IsHidden) {
438 Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
439
440 // Record the presence of the checker in its packages.
441 StringRef PackageName, LeafName;
442 std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
443 while (!LeafName.empty()) {
444 Data.PackageSizes[PackageName] += 1;
445 std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
446 }
447}
448
449void CheckerRegistry::addCheckerOption(StringRef OptionType,
450 StringRef CheckerFullName,
451 StringRef OptionName,
452 StringRef DefaultValStr,
453 StringRef Description,
454 StringRef DevelopmentStatus,
455 bool IsHidden) {
456 Data.CheckerOptions.emplace_back(
457 CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
458 Description, DevelopmentStatus, IsHidden});
459}
460
461void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
462 // Initialize the CheckerManager with all enabled checkers.
463 for (const auto *Checker : Data.EnabledCheckers) {
464 CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
465 Checker->Initialize(CheckerMgr);
466 }
467}
468
469static void isOptionContainedIn(const CmdLineOptionList &OptionList,
470 StringRef SuppliedChecker,
471 StringRef SuppliedOption,
472 const AnalyzerOptions &AnOpts,
473 DiagnosticsEngine &Diags) {
474
476 return;
477
478 auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {
479 return Opt.OptionName == SuppliedOption;
480 };
481
482 if (llvm::none_of(OptionList, SameOptName)) {
483 Diags.Report(diag::err_analyzer_checker_option_unknown)
484 << SuppliedChecker << SuppliedOption;
485 return;
486 }
487}
488
490 for (const auto &Config : AnOpts.Config) {
491
492 StringRef SuppliedCheckerOrPackage;
493 StringRef SuppliedOption;
494 std::tie(SuppliedCheckerOrPackage, SuppliedOption) =
495 Config.getKey().split(':');
496
497 if (SuppliedOption.empty())
498 continue;
499
500 // AnalyzerOptions' config table contains the user input, so an entry could
501 // look like this:
502 //
503 // cor:NoFalsePositives=true
504 //
505 // Since lower_bound would look for the first element *not less* than "cor",
506 // it would return with an iterator to the first checker in the core, so we
507 // we really have to use find here, which uses operator==.
508 auto CheckerIt =
509 llvm::find(Data.Checkers, CheckerInfo(SuppliedCheckerOrPackage));
510 if (CheckerIt != Data.Checkers.end()) {
511 isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
512 SuppliedOption, AnOpts, Diags);
513 continue;
514 }
515
516 const auto *PackageIt =
517 llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage));
518 if (PackageIt != Data.Packages.end()) {
519 isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
520 SuppliedOption, AnOpts, Diags);
521 continue;
522 }
523
524 Diags.Report(diag::err_unknown_analyzer_checker_or_package)
525 << SuppliedCheckerOrPackage;
526 }
527}
Defines the Diagnostic-related interfaces.
static constexpr char PackageSeparator
static bool isCompatibleAPIVersion(const char *VersionString)
static void collectWeakDependencies(const ConstCheckerInfoList &Deps, const CheckerManager &Mgr, CheckerInfoSet &Ret, IsEnabledFn IsEnabled)
Collects weak dependencies in enabledData.Checkers.
static constexpr char PackageSeparator
static void insertAndValidate(StringRef FullName, const CmdLineOption &Option, AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags)
Insert the checker/package option to AnalyzerOptions' config table, and validate it,...
static void isOptionContainedIn(const CmdLineOptionList &OptionList, StringRef SuppliedChecker, StringRef SuppliedOption, const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags)
static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, const CheckerManager &Mgr, CheckerInfoSet &Ret, IsEnabledFn IsEnabled)
static void insertOptionToCollection(StringRef FullName, T &Collection, const CmdLineOption &Option, AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags)
#define CLANG_ANALYZER_API_VERSION_STRING
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
int32_t FullName
Definition: SemaARM.cpp:1135
const char * Data
Stores options for the analyzer from the command line.
std::vector< std::pair< std::string, bool > > CheckersAndPackages
Pairs of checker/package name and enable/disable.
ConfigTable Config
A key-value table of use-specified configuration values.
unsigned ShouldEmitErrorsOnInvalidConfigValue
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:231
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1493
void setCurrentCheckerName(CheckerNameRef name)
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
Manages a set of available checkers for running a static analysis.
void addCheckerOption(StringRef OptionType, StringRef CheckerFullName, StringRef OptionName, StringRef DefaultValStr, StringRef Description, StringRef DevelopmentStatus, bool IsHidden=false)
Registers an option to a given checker.
void addWeakDependency(StringRef FullName, StringRef Dependency)
Makes the checker with the full name fullName weak depend on the checker called dependency.
void addPackageOption(StringRef OptionType, StringRef PackageFullName, StringRef OptionName, StringRef DefaultValStr, StringRef Description, StringRef DevelopmentStatus, bool IsHidden=false)
Registers an option to a given package.
void initializeRegistry(const CheckerManager &Mgr)
Collects all enabled checkers in the field EnabledCheckers.
void addChecker(RegisterCheckerFn Fn, ShouldRegisterFunction sfn, StringRef FullName, StringRef Desc, StringRef DocsUri, bool IsHidden)
Adds a checker to the registry.
void addPackage(StringRef FullName)
Adds a package to the registry.
void validateCheckerOptions() const
Check if every option corresponds to a specific checker or package.
void addDependency(StringRef FullName, StringRef Dependency)
Makes the checker with the full name fullName depend on the checker called dependency.
CheckerRegistry(CheckerRegistryData &Data, ArrayRef< std::string > Plugins, DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts, ArrayRef< std::function< void(CheckerRegistry &)> > CheckerRegistrationFns={})
std::conditional_t< std::is_const< CheckerOrPackageInfoList >::value, typename CheckerOrPackageInfoList::const_iterator, typename CheckerOrPackageInfoList::iterator > binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName)
llvm::iterator_range< CheckerInfoList::iterator > CheckerInfoListRange
bool(*)(const CheckerManager &) ShouldRegisterFunction
void(*)(CheckerManager &) RegisterCheckerFn
Initialization functions perform any necessary setup for a checker.
llvm::SetVector< const CheckerInfo * > CheckerInfoSet
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Specifies a checker.
Specifies a command line option.