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