blob: e00fd976f6b80b854141b747d6d2608bcaa5af50 [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 Umann85cf76e2019-05-17 09:51:59 +000012#include "clang/Driver/DriverDiagnostic.h"
Kristof Umannb0be2ab2018-12-15 18:11:49 +000013#include "clang/Frontend/FrontendDiagnostic.h"
14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Gabor Horvathfc4c4d42015-07-09 21:43:45 +000015#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
Kristof Umanna57d4ea2019-04-18 17:32:51 +000016#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000017#include "llvm/ADT/STLExtras.h"
Anna Zaks30373152011-12-15 01:36:04 +000018#include "llvm/ADT/SetVector.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000019#include "llvm/ADT/StringMap.h"
20#include "llvm/ADT/StringRef.h"
Kristof Umannb0be2ab2018-12-15 18:11:49 +000021#include "llvm/Support/DynamicLibrary.h"
22#include "llvm/Support/Path.h"
Benjamin Kramer444a1302012-12-01 17:12:56 +000023#include "llvm/Support/raw_ostream.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000024#include <algorithm>
Jordy Rose59cce712011-08-16 21:24:21 +000025
26using namespace clang;
27using namespace ento;
Kristof Umannb0be2ab2018-12-15 18:11:49 +000028using llvm::sys::DynamicLibrary;
29
30using RegisterCheckersFn = void (*)(CheckerRegistry &);
31
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000032static bool isCompatibleAPIVersion(const char *VersionString) {
33 // If the version string is null, its not an analyzer plugin.
34 if (!VersionString)
Kristof Umannb0be2ab2018-12-15 18:11:49 +000035 return false;
36
37 // For now, none of the static analyzer API is considered stable.
38 // Versions must match exactly.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000039 return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
Kristof Umannb0be2ab2018-12-15 18:11:49 +000040}
41
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000042namespace {
Kristof Umanna57d4ea2019-04-18 17:32:51 +000043template <class T> struct FullNameLT {
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000044 bool operator()(const T &Lhs, const T &Rhs) {
45 return Lhs.FullName < Rhs.FullName;
46 }
47};
48
Kristof Umannb4788b22019-04-19 12:32:10 +000049using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>;
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000050using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
51} // end of anonymous namespace
Kristof Umann3daa2452019-01-26 16:35:33 +000052
Kristof Umann640f7b52019-04-18 17:34:45 +000053template <class CheckerOrPackageInfoList>
54static
55 typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value,
56 typename CheckerOrPackageInfoList::const_iterator,
57 typename CheckerOrPackageInfoList::iterator>::type
58 binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) {
59
60 using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type;
61 using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>;
62
63 assert(std::is_sorted(Collection.begin(), Collection.end(),
64 CheckerOrPackageFullNameLT{}) &&
65 "In order to efficiently gather checkers/packages, this function "
66 "expects them to be already sorted!");
67
Fangrui Song92063352019-04-19 01:54:36 +000068 return llvm::lower_bound(Collection, CheckerOrPackage(FullName),
69 CheckerOrPackageFullNameLT{});
Kristof Umann640f7b52019-04-18 17:34:45 +000070}
71
Kristof Umann3daa2452019-01-26 16:35:33 +000072static constexpr char PackageSeparator = '.';
73
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000074static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker,
75 StringRef PackageName) {
Kristof Umann3daa2452019-01-26 16:35:33 +000076 // Does the checker's full name have the package as a prefix?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000077 if (!Checker.FullName.startswith(PackageName))
Kristof Umann3daa2452019-01-26 16:35:33 +000078 return false;
79
80 // Is the package actually just the name of a specific checker?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000081 if (Checker.FullName.size() == PackageName.size())
Kristof Umann3daa2452019-01-26 16:35:33 +000082 return true;
83
84 // Is the checker in the package (or a subpackage)?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000085 if (Checker.FullName[PackageName.size()] == PackageSeparator)
Kristof Umann3daa2452019-01-26 16:35:33 +000086 return true;
87
88 return false;
89}
90
91CheckerRegistry::CheckerInfoListRange
92CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
Kristof Umann640f7b52019-04-18 17:34:45 +000093 auto It = binaryFind(Checkers, CmdLineArg);
Kristof Umann3daa2452019-01-26 16:35:33 +000094
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000095 if (!isInPackage(*It, CmdLineArg))
Kristof Umanna57d4ea2019-04-18 17:32:51 +000096 return {Checkers.end(), Checkers.end()};
Kristof Umann3daa2452019-01-26 16:35:33 +000097
98 // See how large the package is.
99 // If the package doesn't exist, assume the option refers to a single
100 // checker.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000101 size_t Size = 1;
102 llvm::StringMap<size_t>::const_iterator PackageSize =
103 PackageSizes.find(CmdLineArg);
Kristof Umann3daa2452019-01-26 16:35:33 +0000104
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000105 if (PackageSize != PackageSizes.end())
106 Size = PackageSize->getValue();
Kristof Umann3daa2452019-01-26 16:35:33 +0000107
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000108 return {It, It + Size};
Kristof Umann3daa2452019-01-26 16:35:33 +0000109}
110
Kristof Umann98217ad2019-01-26 17:27:40 +0000111CheckerRegistry::CheckerRegistry(
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000112 ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags,
113 AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
114 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
115 : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) {
Kristof Umann058a7a42019-01-26 14:23:08 +0000116
Kristof Umann3daa2452019-01-26 16:35:33 +0000117 // Register builtin checkers.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000118#define GET_CHECKERS
Kristof Umann9f7fc982019-05-01 19:56:47 +0000119#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
Kristof Umann058a7a42019-01-26 14:23:08 +0000120 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
Kristof Umann9f7fc982019-05-01 19:56:47 +0000121 DOC_URI, IS_HIDDEN);
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000122
Kristof Umannb4788b22019-04-19 12:32:10 +0000123#define GET_PACKAGES
124#define PACKAGE(FULLNAME) addPackage(FULLNAME);
125
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000126#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
127#undef CHECKER
128#undef GET_CHECKERS
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000129#undef PACKAGE
130#undef GET_PACKAGES
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000131
Kristof Umann3daa2452019-01-26 16:35:33 +0000132 // Register checkers from plugins.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000133 for (const std::string &Plugin : Plugins) {
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000134 // Get access to the plugin.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000135 std::string ErrorMsg;
136 DynamicLibrary Lib =
137 DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
138 if (!Lib.isValid()) {
139 Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000140 continue;
141 }
142
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000143 // See if its compatible with this build of clang.
144 const char *PluginAPIVersion = static_cast<const char *>(
145 Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
146
147 if (!isCompatibleAPIVersion(PluginAPIVersion)) {
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000148 Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000149 << llvm::sys::path::filename(Plugin);
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000150 Diags.Report(diag::note_incompatible_analyzer_plugin_api)
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000151 << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion;
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000152 continue;
153 }
154
155 // Register its checkers.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000156 RegisterCheckersFn RegisterPluginCheckers =
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000157 reinterpret_cast<RegisterCheckersFn>(
158 Lib.getAddressOfSymbol("clang_registerCheckers"));
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000159 if (RegisterPluginCheckers)
160 RegisterPluginCheckers(*this);
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000161 }
Jordy Rose59cce712011-08-16 21:24:21 +0000162
Kristof Umann98217ad2019-01-26 17:27:40 +0000163 // Register statically linked checkers, that aren't generated from the tblgen
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000164 // file, but rather passed their registry function as a parameter in
165 // checkerRegistrationFns.
Kristof Umann98217ad2019-01-26 17:27:40 +0000166
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000167 for (const auto &Fn : CheckerRegistrationFns)
Kristof Umann98217ad2019-01-26 17:27:40 +0000168 Fn(*this);
169
Kristof Umann3daa2452019-01-26 16:35:33 +0000170 // Sort checkers for efficient collection.
171 // FIXME: Alphabetical sort puts 'experimental' in the middle.
172 // Would it be better to name it '~experimental' or something else
173 // that's ASCIIbetically last?
Kristof Umannb4788b22019-04-19 12:32:10 +0000174 llvm::sort(Packages, PackageNameLT{});
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000175 llvm::sort(Checkers, CheckerNameLT{});
Kristof Umann45beaa02018-11-18 12:47:03 +0000176
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000177#define GET_CHECKER_DEPENDENCIES
178
179#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
180 addDependency(FULLNAME, DEPENDENCY);
181
Kristof Umannb4788b22019-04-19 12:32:10 +0000182#define GET_CHECKER_OPTIONS
Kristof Umannac95c862019-05-23 22:52:09 +0000183#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \
184 addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
Kristof Umannb4788b22019-04-19 12:32:10 +0000185
186#define GET_PACKAGE_OPTIONS
Kristof Umannac95c862019-05-23 22:52:09 +0000187#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \
188 addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
Kristof Umannb4788b22019-04-19 12:32:10 +0000189
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000190#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
191#undef CHECKER_DEPENDENCY
192#undef GET_CHECKER_DEPENDENCIES
Kristof Umannb4788b22019-04-19 12:32:10 +0000193#undef CHECKER_OPTION
194#undef GET_CHECKER_OPTIONS
195#undef PACKAGE_OPTION
196#undef GET_PACKAGE_OPTIONS
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000197
Kristof Umanncd3f1472019-04-19 11:01:35 +0000198 resolveDependencies();
Kristof Umannb4788b22019-04-19 12:32:10 +0000199 resolveCheckerAndPackageOptions();
Kristof Umanncd3f1472019-04-19 11:01:35 +0000200
Kristof Umann3daa2452019-01-26 16:35:33 +0000201 // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
202 // command line.
Csaba Dabisa079a422019-08-16 01:53:14 +0000203 for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000204 CheckerInfoListRange CheckerForCmdLineArg =
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000205 getMutableCheckersForCmdLineArg(Opt.first);
Jordy Rose59cce712011-08-16 21:24:21 +0000206
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000207 if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
Csaba Dabisa079a422019-08-16 01:53:14 +0000208 Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
Kristof Umann3daa2452019-01-26 16:35:33 +0000209 Diags.Report(diag::note_suggest_disabling_all_checkers);
210 }
Jordy Rose59cce712011-08-16 21:24:21 +0000211
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000212 for (CheckerInfo &checker : CheckerForCmdLineArg) {
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000213 checker.State = Opt.second ? StateFromCmdLine::State_Enabled
214 : StateFromCmdLine::State_Disabled;
Kristof Umann3daa2452019-01-26 16:35:33 +0000215 }
216 }
Jordy Rose59cce712011-08-16 21:24:21 +0000217}
218
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000219/// Collects dependencies in \p ret, returns false on failure.
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000220static bool
221collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
222 const LangOptions &LO,
223 CheckerRegistry::CheckerInfoSet &Ret);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000224
225/// Collects dependenies in \p enabledCheckers. Return None on failure.
226LLVM_NODISCARD
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000227static llvm::Optional<CheckerRegistry::CheckerInfoSet>
228collectDependencies(const CheckerRegistry::CheckerInfo &checker,
229 const LangOptions &LO) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000230
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000231 CheckerRegistry::CheckerInfoSet Ret;
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000232 // Add dependencies to the enabled checkers only if all of them can be
233 // enabled.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000234 if (!collectDependenciesImpl(checker.Dependencies, LO, Ret))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000235 return None;
236
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000237 return Ret;
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000238}
239
Kristof Umanna57d4ea2019-04-18 17:32:51 +0000240static bool
241collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
242 const LangOptions &LO,
243 CheckerRegistry::CheckerInfoSet &Ret) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000244
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000245 for (const CheckerRegistry::CheckerInfo *Dependency : Deps) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000246
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000247 if (Dependency->isDisabled(LO))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000248 return false;
249
250 // Collect dependencies recursively.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000251 if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000252 return false;
253
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000254 Ret.insert(Dependency);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000255 }
256
257 return true;
258}
259
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000260CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
Jordy Rose59cce712011-08-16 21:24:21 +0000261
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000262 CheckerInfoSet EnabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000263
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000264 for (const CheckerInfo &Checker : Checkers) {
265 if (!Checker.isEnabled(LangOpts))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000266 continue;
267
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000268 // Recursively enable its dependencies.
269 llvm::Optional<CheckerInfoSet> Deps =
270 collectDependencies(Checker, LangOpts);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000271
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000272 if (!Deps) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000273 // If we failed to enable any of the dependencies, don't enable this
274 // checker.
275 continue;
276 }
277
278 // Note that set_union also preserves the order of insertion.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000279 EnabledCheckers.set_union(*Deps);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000280
281 // Enable the checker.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000282 EnabledCheckers.insert(&Checker);
Kristof Umannf282d272018-12-15 15:44:05 +0000283 }
284
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000285 return EnabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000286}
287
Kristof Umanncd3f1472019-04-19 11:01:35 +0000288void CheckerRegistry::resolveDependencies() {
289 for (const std::pair<StringRef, StringRef> &Entry : Dependencies) {
290 auto CheckerIt = binaryFind(Checkers, Entry.first);
291 assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first &&
292 "Failed to find the checker while attempting to set up its "
293 "dependencies!");
294
295 auto DependencyIt = binaryFind(Checkers, Entry.second);
296 assert(DependencyIt != Checkers.end() &&
297 DependencyIt->FullName == Entry.second &&
298 "Failed to find the dependency of a checker!");
299
300 CheckerIt->Dependencies.emplace_back(&*DependencyIt);
301 }
302
303 Dependencies.clear();
304}
305
Kristof Umann640f7b52019-04-18 17:34:45 +0000306void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
Kristof Umanncd3f1472019-04-19 11:01:35 +0000307 Dependencies.emplace_back(FullName, Dependency);
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000308}
309
Kristof Umann85cf76e2019-05-17 09:51:59 +0000310/// Insert the checker/package option to AnalyzerOptions' config table, and
311/// validate it, if the user supplied it on the command line.
312static void insertAndValidate(StringRef FullName,
313 const CheckerRegistry::CmdLineOption &Option,
314 AnalyzerOptions &AnOpts,
315 DiagnosticsEngine &Diags) {
316
317 std::string FullOption = (FullName + ":" + Option.OptionName).str();
318
319 auto It = AnOpts.Config.insert({FullOption, Option.DefaultValStr});
320
321 // Insertation was successful -- CmdLineOption's constructor will validate
322 // whether values received from plugins or TableGen files are correct.
323 if (It.second)
324 return;
325
326 // Insertion failed, the user supplied this package/checker option on the
Kristof Umann83cc1b32019-05-17 15:52:13 +0000327 // command line. If the supplied value is invalid, we'll restore the option
328 // to it's default value, and if we're in non-compatibility mode, we'll also
329 // emit an error.
Kristof Umann85cf76e2019-05-17 09:51:59 +0000330
331 StringRef SuppliedValue = It.first->getValue();
332
333 if (Option.OptionType == "bool") {
334 if (SuppliedValue != "true" && SuppliedValue != "false") {
335 if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
336 Diags.Report(diag::err_analyzer_checker_option_invalid_input)
337 << FullOption << "a boolean value";
338 }
Kristof Umann83cc1b32019-05-17 15:52:13 +0000339
340 It.first->setValue(Option.DefaultValStr);
Kristof Umann85cf76e2019-05-17 09:51:59 +0000341 }
342 return;
343 }
344
345 if (Option.OptionType == "int") {
346 int Tmp;
347 bool HasFailed = SuppliedValue.getAsInteger(0, Tmp);
348 if (HasFailed) {
349 if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
350 Diags.Report(diag::err_analyzer_checker_option_invalid_input)
351 << FullOption << "an integer value";
352 }
Kristof Umann83cc1b32019-05-17 15:52:13 +0000353
354 It.first->setValue(Option.DefaultValStr);
Kristof Umann85cf76e2019-05-17 09:51:59 +0000355 }
356 return;
357 }
358}
359
Kristof Umannb4788b22019-04-19 12:32:10 +0000360template <class T>
361static void
362insertOptionToCollection(StringRef FullName, T &Collection,
Kristof Umann30b23072019-05-17 09:29:44 +0000363 const CheckerRegistry::CmdLineOption &Option,
Kristof Umann85cf76e2019-05-17 09:51:59 +0000364 AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
Kristof Umannb4788b22019-04-19 12:32:10 +0000365 auto It = binaryFind(Collection, FullName);
366 assert(It != Collection.end() &&
367 "Failed to find the checker while attempting to add a command line "
368 "option to it!");
369
Kristof Umann85cf76e2019-05-17 09:51:59 +0000370 insertAndValidate(FullName, Option, AnOpts, Diags);
Kristof Umann30b23072019-05-17 09:29:44 +0000371
372 It->CmdLineOptions.emplace_back(Option);
Kristof Umannb4788b22019-04-19 12:32:10 +0000373}
374
375void CheckerRegistry::resolveCheckerAndPackageOptions() {
376 for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
377 CheckerOptions) {
378 insertOptionToCollection(CheckerOptEntry.first, Checkers,
Kristof Umann85cf76e2019-05-17 09:51:59 +0000379 CheckerOptEntry.second, AnOpts, Diags);
Kristof Umannb4788b22019-04-19 12:32:10 +0000380 }
381 CheckerOptions.clear();
382
383 for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
384 PackageOptions) {
Kristof Umann85cf76e2019-05-17 09:51:59 +0000385 insertOptionToCollection(PackageOptEntry.first, Packages,
386 PackageOptEntry.second, AnOpts, Diags);
Kristof Umannb4788b22019-04-19 12:32:10 +0000387 }
388 PackageOptions.clear();
389}
390
391void CheckerRegistry::addPackage(StringRef FullName) {
392 Packages.emplace_back(PackageInfo(FullName));
393}
394
395void CheckerRegistry::addPackageOption(StringRef OptionType,
396 StringRef PackageFullName,
397 StringRef OptionName,
398 StringRef DefaultValStr,
Kristof Umannac95c862019-05-23 22:52:09 +0000399 StringRef Description,
400 StringRef DevelopmentStatus,
401 bool IsHidden) {
Kristof Umannb4788b22019-04-19 12:32:10 +0000402 PackageOptions.emplace_back(
Kristof Umann7e55ed82019-05-23 22:07:16 +0000403 PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
Kristof Umannac95c862019-05-23 22:52:09 +0000404 Description, DevelopmentStatus, IsHidden});
Kristof Umannb4788b22019-04-19 12:32:10 +0000405}
406
407void CheckerRegistry::addChecker(InitializationFunction Rfn,
408 ShouldRegisterFunction Sfn, StringRef Name,
Kristof Umann9f7fc982019-05-01 19:56:47 +0000409 StringRef Desc, StringRef DocsUri,
410 bool IsHidden) {
411 Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
Kristof Umannb4788b22019-04-19 12:32:10 +0000412
413 // Record the presence of the checker in its packages.
414 StringRef PackageName, LeafName;
415 std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
416 while (!LeafName.empty()) {
417 PackageSizes[PackageName] += 1;
418 std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
419 }
420}
421
422void CheckerRegistry::addCheckerOption(StringRef OptionType,
423 StringRef CheckerFullName,
424 StringRef OptionName,
425 StringRef DefaultValStr,
Kristof Umannac95c862019-05-23 22:52:09 +0000426 StringRef Description,
427 StringRef DevelopmentStatus,
428 bool IsHidden) {
Kristof Umannb4788b22019-04-19 12:32:10 +0000429 CheckerOptions.emplace_back(
Kristof Umann7e55ed82019-05-23 22:07:16 +0000430 CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
Kristof Umannac95c862019-05-23 22:52:09 +0000431 Description, DevelopmentStatus, IsHidden});
Kristof Umannb4788b22019-04-19 12:32:10 +0000432}
433
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000434void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000435 // Collect checkers enabled by the options.
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000436 CheckerInfoSet enabledCheckers = getEnabledCheckers();
Jordy Rose59cce712011-08-16 21:24:21 +0000437
438 // Initialize the CheckerManager with all enabled checkers.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000439 for (const auto *Checker : enabledCheckers) {
Kristof Umann72649422019-09-12 19:09:24 +0000440 CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000441 Checker->Initialize(CheckerMgr);
Jordy Rose59cce712011-08-16 21:24:21 +0000442 }
443}
444
Kristof Umann85cf76e2019-05-17 09:51:59 +0000445static void
446isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList,
447 StringRef SuppliedChecker, StringRef SuppliedOption,
448 const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
449
450 if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue)
451 return;
452
453 using CmdLineOption = CheckerRegistry::CmdLineOption;
454
455 auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {
456 return Opt.OptionName == SuppliedOption;
457 };
458
459 auto OptionIt = llvm::find_if(OptionList, SameOptName);
460
461 if (OptionIt == OptionList.end()) {
462 Diags.Report(diag::err_analyzer_checker_option_unknown)
463 << SuppliedChecker << SuppliedOption;
464 return;
465 }
466}
467
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000468void CheckerRegistry::validateCheckerOptions() const {
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000469 for (const auto &Config : AnOpts.Config) {
Kristof Umann85cf76e2019-05-17 09:51:59 +0000470
Csaba Dabisa079a422019-08-16 01:53:14 +0000471 StringRef SuppliedCheckerOrPackage;
Kristof Umann85cf76e2019-05-17 09:51:59 +0000472 StringRef SuppliedOption;
Csaba Dabisa079a422019-08-16 01:53:14 +0000473 std::tie(SuppliedCheckerOrPackage, SuppliedOption) =
474 Config.getKey().split(':');
Kristof Umann85cf76e2019-05-17 09:51:59 +0000475
476 if (SuppliedOption.empty())
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000477 continue;
478
Kristof Umann85cf76e2019-05-17 09:51:59 +0000479 // AnalyzerOptions' config table contains the user input, so an entry could
480 // look like this:
481 //
482 // cor:NoFalsePositives=true
483 //
484 // Since lower_bound would look for the first element *not less* than "cor",
485 // it would return with an iterator to the first checker in the core, so we
486 // we really have to use find here, which uses operator==.
Csaba Dabisa079a422019-08-16 01:53:14 +0000487 auto CheckerIt =
488 llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage));
Kristof Umann85cf76e2019-05-17 09:51:59 +0000489 if (CheckerIt != Checkers.end()) {
Csaba Dabisa079a422019-08-16 01:53:14 +0000490 isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
Kristof Umann85cf76e2019-05-17 09:51:59 +0000491 SuppliedOption, AnOpts, Diags);
492 continue;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000493 }
Kristof Umann85cf76e2019-05-17 09:51:59 +0000494
Csaba Dabisa079a422019-08-16 01:53:14 +0000495 auto PackageIt =
496 llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage));
Kristof Umann85cf76e2019-05-17 09:51:59 +0000497 if (PackageIt != Packages.end()) {
Csaba Dabisa079a422019-08-16 01:53:14 +0000498 isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
Kristof Umann85cf76e2019-05-17 09:51:59 +0000499 SuppliedOption, AnOpts, Diags);
500 continue;
501 }
502
Csaba Dabisa079a422019-08-16 01:53:14 +0000503 Diags.Report(diag::err_unknown_analyzer_checker_or_package)
504 << SuppliedCheckerOrPackage;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000505 }
506}
507
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000508void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
509 size_t MaxNameChars) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000510 // FIXME: Print available packages.
511
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000512 Out << "CHECKERS:\n";
Jordy Rose59cce712011-08-16 21:24:21 +0000513
514 // Find the maximum option length.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000515 size_t OptionFieldWidth = 0;
516 for (const auto &Checker : Checkers) {
Jordy Rose59cce712011-08-16 21:24:21 +0000517 // Limit the amount of padding we are willing to give up for alignment.
518 // Package.Name Description [Hidden]
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000519 size_t NameLength = Checker.FullName.size();
520 if (NameLength <= MaxNameChars)
521 OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
Jordy Rose59cce712011-08-16 21:24:21 +0000522 }
523
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000524 const size_t InitialPad = 2;
Kristof Umann9f7fc982019-05-01 19:56:47 +0000525
Kristof Umann5bc40d92019-05-23 21:46:51 +0000526 auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
527 StringRef Description) {
528 AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
Kristof Umanne8df27d2019-05-23 20:47:28 +0000529 InitialPad, OptionFieldWidth);
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000530 Out << '\n';
Kristof Umann5bc40d92019-05-23 21:46:51 +0000531 };
532
533 for (const auto &Checker : Checkers) {
534 // The order of this if branches is significant, we wouldn't like to display
535 // developer checkers even in the alpha output. For example,
536 // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
537 // by default, and users (even when the user is a developer of an alpha
538 // checker) shouldn't normally tinker with whether they should be enabled.
539
540 if (Checker.IsHidden) {
541 if (AnOpts.ShowCheckerHelpDeveloper)
542 Print(Out, Checker, Checker.Desc);
543 continue;
544 }
545
546 if (Checker.FullName.startswith("alpha")) {
547 if (AnOpts.ShowCheckerHelpAlpha)
548 Print(Out, Checker,
549 ("(Enable only for development!) " + Checker.Desc).str());
550 continue;
551 }
552
553 if (AnOpts.ShowCheckerHelp)
554 Print(Out, Checker, Checker.Desc);
Jordy Rose59cce712011-08-16 21:24:21 +0000555 }
556}
Gabor Horvathc4309902016-08-08 13:41:04 +0000557
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000558void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
Gabor Horvathc4309902016-08-08 13:41:04 +0000559 // Collect checkers enabled by the options.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000560 CheckerInfoSet EnabledCheckers = getEnabledCheckers();
Gabor Horvathc4309902016-08-08 13:41:04 +0000561
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000562 for (const auto *i : EnabledCheckers)
563 Out << i->FullName << '\n';
Gabor Horvathc4309902016-08-08 13:41:04 +0000564}
Kristof Umanne8df27d2019-05-23 20:47:28 +0000565
566void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const {
567 Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
568 Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
569 Out << " -analyzer-config OPTION1=VALUE, -analyzer-config "
570 "OPTION2=VALUE, ...\n\n";
571 Out << "OPTIONS:\n\n";
572
573 std::multimap<StringRef, const CmdLineOption &> OptionMap;
574
575 for (const CheckerInfo &Checker : Checkers) {
576 for (const CmdLineOption &Option : Checker.CmdLineOptions) {
577 OptionMap.insert({Checker.FullName, Option});
578 }
579 }
580
581 for (const PackageInfo &Package : Packages) {
582 for (const CmdLineOption &Option : Package.CmdLineOptions) {
583 OptionMap.insert({Package.FullName, Option});
584 }
585 }
586
Kristof Umannac95c862019-05-23 22:52:09 +0000587 auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) {
588 AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
589 /*InitialPad*/ 2,
590 /*EntryWidth*/ 50,
591 /*MinLineWidth*/ 90);
592 Out << "\n\n";
593 };
Kristof Umanne8df27d2019-05-23 20:47:28 +0000594 for (const std::pair<StringRef, const CmdLineOption &> &Entry : OptionMap) {
595 const CmdLineOption &Option = Entry.second;
596 std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
597
598 std::string Desc =
599 ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
600 (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
601 .str();
602
Kristof Umannac95c862019-05-23 22:52:09 +0000603 // The list of these if branches is significant, we wouldn't like to
604 // display hidden alpha checker options for
605 // -analyzer-checker-option-help-alpha.
606
607 if (Option.IsHidden) {
608 if (AnOpts.ShowCheckerOptionDeveloperList)
609 Print(Out, FullOption, Desc);
610 continue;
611 }
612
613 if (Option.DevelopmentStatus == "alpha" ||
614 Entry.first.startswith("alpha")) {
615 if (AnOpts.ShowCheckerOptionAlphaList)
616 Print(Out, FullOption,
617 llvm::Twine("(Enable only for development!) " + Desc).str());
618 continue;
619 }
620
621 if (AnOpts.ShowCheckerOptionList)
622 Print(Out, FullOption, Desc);
Kristof Umanne8df27d2019-05-23 20:47:28 +0000623 }
624}