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