blob: 4d415e2cb5c4df3c688d8550bc7bd03a57f4e66a [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
41CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins,
42 DiagnosticsEngine &diags) : Diags(diags) {
43#define GET_CHECKERS
Aaron Ballman2f234cb2018-12-20 20:20:20 +000044#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
45 addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI);
Kristof Umannb0be2ab2018-12-15 18:11:49 +000046#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
47#undef CHECKER
48#undef GET_CHECKERS
49
50 for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
51 i != e; ++i) {
52 // Get access to the plugin.
53 std::string err;
54 DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err);
55 if (!lib.isValid()) {
56 diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err;
57 continue;
58 }
59
60 // See if it's compatible with this build of clang.
61 const char *pluginAPIVersion =
62 (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
63 if (!isCompatibleAPIVersion(pluginAPIVersion)) {
64 Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
65 << llvm::sys::path::filename(*i);
66 Diags.Report(diag::note_incompatible_analyzer_plugin_api)
67 << CLANG_ANALYZER_API_VERSION_STRING
68 << pluginAPIVersion;
69 continue;
70 }
71
72 // Register its checkers.
73 RegisterCheckersFn registerPluginCheckers =
74 (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
75 "clang_registerCheckers");
76 if (registerPluginCheckers)
77 registerPluginCheckers(*this);
78 }
79}
Jordy Rose59cce712011-08-16 21:24:21 +000080
Kristof Umannf282d272018-12-15 15:44:05 +000081static constexpr char PackageSeparator = '.';
Kristof Umann45beaa02018-11-18 12:47:03 +000082
Jordy Rose59cce712011-08-16 21:24:21 +000083static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
84 const CheckerRegistry::CheckerInfo &b) {
85 return a.FullName < b.FullName;
86}
87
88static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
89 StringRef packageName) {
90 // Does the checker's full name have the package as a prefix?
91 if (!checker.FullName.startswith(packageName))
92 return false;
93
94 // Is the package actually just the name of a specific checker?
95 if (checker.FullName.size() == packageName.size())
96 return true;
97
98 // Is the checker in the package (or a subpackage)?
99 if (checker.FullName[packageName.size()] == PackageSeparator)
100 return true;
101
102 return false;
103}
104
Kristof Umannf282d272018-12-15 15:44:05 +0000105CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers(
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000106 const AnalyzerOptions &Opts) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000107
Kristof Umannf282d272018-12-15 15:44:05 +0000108 assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
109 "In order to efficiently gather checkers, this function expects them "
110 "to be already sorted!");
Jordy Rose59cce712011-08-16 21:24:21 +0000111
Kristof Umannf282d272018-12-15 15:44:05 +0000112 CheckerInfoSet enabledCheckers;
113 const auto end = Checkers.cend();
Jordy Rose59cce712011-08-16 21:24:21 +0000114
Kristof Umannf282d272018-12-15 15:44:05 +0000115 for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) {
116 // Use a binary search to find the possible start of the package.
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000117 CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", "");
Kristof Umannf282d272018-12-15 15:44:05 +0000118 auto firstRelatedChecker =
119 std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +0000120
Kristof Umannf282d272018-12-15 15:44:05 +0000121 if (firstRelatedChecker == end ||
122 !isInPackage(*firstRelatedChecker, opt.first)) {
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000123 Diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
124 Diags.Report(diag::note_suggest_disabling_all_checkers);
Kristof Umannf282d272018-12-15 15:44:05 +0000125 return {};
126 }
Jordy Rose59cce712011-08-16 21:24:21 +0000127
Kristof Umannf282d272018-12-15 15:44:05 +0000128 // See how large the package is.
129 // If the package doesn't exist, assume the option refers to a single
130 // checker.
131 size_t size = 1;
132 llvm::StringMap<size_t>::const_iterator packageSize =
133 Packages.find(opt.first);
134 if (packageSize != Packages.end())
135 size = packageSize->getValue();
136
137 // Step through all the checkers in the package.
138 for (auto lastRelatedChecker = firstRelatedChecker+size;
139 firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker)
140 if (opt.second)
141 enabledCheckers.insert(&*firstRelatedChecker);
142 else
143 enabledCheckers.remove(&*firstRelatedChecker);
144 }
145
146 return enabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000147}
148
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000149void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name,
150 StringRef Desc, StringRef DocsUri) {
151 Checkers.emplace_back(Fn, Name, Desc, DocsUri);
Jordy Rose59cce712011-08-16 21:24:21 +0000152
153 // Record the presence of the checker in its packages.
154 StringRef packageName, leafName;
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000155 std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000156 while (!leafName.empty()) {
157 Packages[packageName] += 1;
Benjamin Kramer867ea1d2014-03-02 13:01:17 +0000158 std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000159 }
160}
161
Ted Kremenek3a0678e2015-09-08 03:50:52 +0000162void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000163 const AnalyzerOptions &Opts) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000164 // Sort checkers for efficient collection.
Fangrui Song55fab262018-09-26 22:16:28 +0000165 llvm::sort(Checkers, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +0000166
167 // Collect checkers enabled by the options.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000168 CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts);
Jordy Rose59cce712011-08-16 21:24:21 +0000169
170 // Initialize the CheckerManager with all enabled checkers.
Kristof Umannf282d272018-12-15 15:44:05 +0000171 for (const auto *i : enabledCheckers) {
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000172 checkerMgr.setCurrentCheckName(CheckName(i->FullName));
173 i->Initialize(checkerMgr);
Jordy Rose59cce712011-08-16 21:24:21 +0000174 }
175}
176
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000177void CheckerRegistry::validateCheckerOptions(
178 const AnalyzerOptions &opts) const {
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000179 for (const auto &config : opts.Config) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000180 size_t pos = config.getKey().find(':');
181 if (pos == StringRef::npos)
182 continue;
183
184 bool hasChecker = false;
185 StringRef checkerName = config.getKey().substr(0, pos);
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000186 for (const auto &checker : Checkers) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000187 if (checker.FullName.startswith(checkerName) &&
188 (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
189 hasChecker = true;
190 break;
191 }
192 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000193 if (!hasChecker)
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000194 Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000195 }
196}
197
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000198void CheckerRegistry::printHelp(raw_ostream &out,
Jordy Rose59cce712011-08-16 21:24:21 +0000199 size_t maxNameChars) const {
200 // FIXME: Alphabetical sort puts 'experimental' in the middle.
201 // Would it be better to name it '~experimental' or something else
202 // that's ASCIIbetically last?
Fangrui Song55fab262018-09-26 22:16:28 +0000203 llvm::sort(Checkers, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +0000204
205 // FIXME: Print available packages.
206
207 out << "CHECKERS:\n";
208
209 // Find the maximum option length.
210 size_t optionFieldWidth = 0;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000211 for (const auto &i : Checkers) {
Jordy Rose59cce712011-08-16 21:24:21 +0000212 // Limit the amount of padding we are willing to give up for alignment.
213 // Package.Name Description [Hidden]
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000214 size_t nameLength = i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000215 if (nameLength <= maxNameChars)
216 optionFieldWidth = std::max(optionFieldWidth, nameLength);
217 }
218
219 const size_t initialPad = 2;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000220 for (const auto &i : Checkers) {
221 out.indent(initialPad) << i.FullName;
Jordy Rose59cce712011-08-16 21:24:21 +0000222
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000223 int pad = optionFieldWidth - i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000224
225 // Break on long option names.
226 if (pad < 0) {
227 out << '\n';
228 pad = optionFieldWidth + initialPad;
229 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000230 out.indent(pad + 2) << i.Desc;
Jordy Rose59cce712011-08-16 21:24:21 +0000231
232 out << '\n';
233 }
234}
Gabor Horvathc4309902016-08-08 13:41:04 +0000235
Kristof Umann45beaa02018-11-18 12:47:03 +0000236void CheckerRegistry::printList(raw_ostream &out,
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000237 const AnalyzerOptions &opts) const {
Kristof Umannf282d272018-12-15 15:44:05 +0000238 // Sort checkers for efficient collection.
Fangrui Song55fab262018-09-26 22:16:28 +0000239 llvm::sort(Checkers, checkerNameLT);
Gabor Horvathc4309902016-08-08 13:41:04 +0000240
241 // Collect checkers enabled by the options.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000242 CheckerInfoSet enabledCheckers = getEnabledCheckers(opts);
Gabor Horvathc4309902016-08-08 13:41:04 +0000243
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000244 for (const auto *i : enabledCheckers)
245 out << i->FullName << '\n';
Gabor Horvathc4309902016-08-08 13:41:04 +0000246}