blob: 68776d94209f2f4b6d534a1d4b7e1f8db11a5691 [file] [log] [blame]
Eugene Zelenko88f40cf2018-04-03 21:31:50 +00001//===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
Jordy Rose59cce712011-08-16 21:24:21 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Jordy Rose59cce712011-08-16 21:24:21 +00006//
7//===----------------------------------------------------------------------===//
8
Kristof Umann76a21502018-12-15 16:23:51 +00009#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
Gabor Horvathfc4c4d42015-07-09 21:43:45 +000010#include "clang/Basic/Diagnostic.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000011#include "clang/Basic/LLVM.h"
Kristof Umannb0be2ab2018-12-15 18:11:49 +000012#include "clang/Frontend/FrontendDiagnostic.h"
13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000014#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Gabor Horvathfc4c4d42015-07-09 21:43:45 +000015#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000016#include "llvm/ADT/STLExtras.h"
Anna Zaks30373152011-12-15 01:36:04 +000017#include "llvm/ADT/SetVector.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000018#include "llvm/ADT/StringMap.h"
19#include "llvm/ADT/StringRef.h"
Kristof Umannb0be2ab2018-12-15 18:11:49 +000020#include "llvm/Support/DynamicLibrary.h"
21#include "llvm/Support/Path.h"
Benjamin Kramer444a1302012-12-01 17:12:56 +000022#include "llvm/Support/raw_ostream.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000023#include <algorithm>
Jordy Rose59cce712011-08-16 21:24:21 +000024
25using namespace clang;
26using namespace ento;
Kristof Umannb0be2ab2018-12-15 18:11:49 +000027using 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
Kristof Umann3daa2452019-01-26 16:35:33 +000041static 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 assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
69 "In order to efficiently gather checkers, this function expects them "
70 "to be already sorted!");
71
72 // Use a binary search to find the possible start of the package.
73 CheckerRegistry::CheckerInfo
74 packageInfo(nullptr, nullptr, CmdLineArg, "", "");
75 auto it = std::lower_bound(Checkers.begin(), Checkers.end(),
76 packageInfo, checkerNameLT);
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
Kristof Umann98217ad2019-01-26 17:27:40 +000094CheckerRegistry::CheckerRegistry(
95 ArrayRef<std::string> plugins, DiagnosticsEngine &diags,
96 AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
97 ArrayRef<std::function<void(CheckerRegistry &)>>
98 checkerRegistrationFns)
Kristof Umanndd9c86e2019-01-26 15:59:21 +000099 : Diags(diags), AnOpts(AnOpts), LangOpts(LangOpts) {
Kristof Umann058a7a42019-01-26 14:23:08 +0000100
Kristof Umann3daa2452019-01-26 16:35:33 +0000101 // Register builtin checkers.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000102#define GET_CHECKERS
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000103#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
Kristof Umann058a7a42019-01-26 14:23:08 +0000104 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
105 DOC_URI);
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000106#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
107#undef CHECKER
108#undef GET_CHECKERS
109
Kristof Umann3daa2452019-01-26 16:35:33 +0000110 // Register checkers from plugins.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000111 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 }
Jordy Rose59cce712011-08-16 21:24:21 +0000140
Kristof Umann98217ad2019-01-26 17:27:40 +0000141 // 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
Kristof Umann3daa2452019-01-26 16:35:33 +0000148 // 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);
Kristof Umann45beaa02018-11-18 12:47:03 +0000153
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000154#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
Kristof Umann3daa2452019-01-26 16:35:33 +0000163 // 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);
Jordy Rose59cce712011-08-16 21:24:21 +0000168
Kristof Umann3daa2452019-01-26 16:35:33 +0000169 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 }
Jordy Rose59cce712011-08-16 21:24:21 +0000173
Kristof Umann3daa2452019-01-26 16:35:33 +0000174 for (CheckerInfo &checker : checkersForCmdLineArg) {
175 checker.State = opt.second ? StateFromCmdLine::State_Enabled :
176 StateFromCmdLine::State_Disabled;
177 }
178 }
Jordy Rose59cce712011-08-16 21:24:21 +0000179}
180
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000181/// 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
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000221CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
Jordy Rose59cce712011-08-16 21:24:21 +0000222
Kristof Umannf282d272018-12-15 15:44:05 +0000223 CheckerInfoSet enabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000224
Kristof Umann3daa2452019-01-26 16:35:33 +0000225 for (const CheckerInfo &checker : Checkers) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000226 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);
Kristof Umannf282d272018-12-15 15:44:05 +0000244 }
245
246 return enabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000247}
248
Kristof Umann058a7a42019-01-26 14:23:08 +0000249void CheckerRegistry::addChecker(InitializationFunction Rfn,
250 ShouldRegisterFunction Sfn, StringRef Name,
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000251 StringRef Desc, StringRef DocsUri) {
Kristof Umann058a7a42019-01-26 14:23:08 +0000252 Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
Jordy Rose59cce712011-08-16 21:24:21 +0000253
254 // Record the presence of the checker in its packages.
255 StringRef packageName, leafName;
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000256 std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000257 while (!leafName.empty()) {
258 Packages[packageName] += 1;
Benjamin Kramer867ea1d2014-03-02 13:01:17 +0000259 std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000260 }
261}
262
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000263void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000264 // Collect checkers enabled by the options.
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000265 CheckerInfoSet enabledCheckers = getEnabledCheckers();
Jordy Rose59cce712011-08-16 21:24:21 +0000266
267 // Initialize the CheckerManager with all enabled checkers.
Kristof Umannf282d272018-12-15 15:44:05 +0000268 for (const auto *i : enabledCheckers) {
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000269 checkerMgr.setCurrentCheckName(CheckName(i->FullName));
270 i->Initialize(checkerMgr);
Jordy Rose59cce712011-08-16 21:24:21 +0000271 }
272}
273
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000274void CheckerRegistry::validateCheckerOptions() const {
275 for (const auto &config : AnOpts.Config) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000276 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);
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000282 for (const auto &checker : Checkers) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000283 if (checker.FullName.startswith(checkerName) &&
284 (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
285 hasChecker = true;
286 break;
287 }
288 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000289 if (!hasChecker)
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000290 Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000291 }
292}
293
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000294void CheckerRegistry::printHelp(raw_ostream &out,
Jordy Rose59cce712011-08-16 21:24:21 +0000295 size_t maxNameChars) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000296 // FIXME: Print available packages.
297
298 out << "CHECKERS:\n";
299
300 // Find the maximum option length.
301 size_t optionFieldWidth = 0;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000302 for (const auto &i : Checkers) {
Jordy Rose59cce712011-08-16 21:24:21 +0000303 // Limit the amount of padding we are willing to give up for alignment.
304 // Package.Name Description [Hidden]
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000305 size_t nameLength = i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000306 if (nameLength <= maxNameChars)
307 optionFieldWidth = std::max(optionFieldWidth, nameLength);
308 }
309
310 const size_t initialPad = 2;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000311 for (const auto &i : Checkers) {
312 out.indent(initialPad) << i.FullName;
Jordy Rose59cce712011-08-16 21:24:21 +0000313
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000314 int pad = optionFieldWidth - i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000315
316 // Break on long option names.
317 if (pad < 0) {
318 out << '\n';
319 pad = optionFieldWidth + initialPad;
320 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000321 out.indent(pad + 2) << i.Desc;
Jordy Rose59cce712011-08-16 21:24:21 +0000322
323 out << '\n';
324 }
325}
Gabor Horvathc4309902016-08-08 13:41:04 +0000326
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000327void CheckerRegistry::printList(raw_ostream &out) const {
Gabor Horvathc4309902016-08-08 13:41:04 +0000328 // Collect checkers enabled by the options.
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000329 CheckerInfoSet enabledCheckers = getEnabledCheckers();
Gabor Horvathc4309902016-08-08 13:41:04 +0000330
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000331 for (const auto *i : enabledCheckers)
332 out << i->FullName << '\n';
Gabor Horvathc4309902016-08-08 13:41:04 +0000333}