blob: fd40d5087d55ee6124605d2fd1afe4b46f4e463e [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
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000031static bool isCompatibleAPIVersion(const char *VersionString) {
32 // If the version string is null, its not an analyzer plugin.
33 if (!VersionString)
Kristof Umannb0be2ab2018-12-15 18:11:49 +000034 return false;
35
36 // For now, none of the static analyzer API is considered stable.
37 // Versions must match exactly.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000038 return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
Kristof Umannb0be2ab2018-12-15 18:11:49 +000039}
40
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000041namespace {
42template <class T>
43struct FullNameLT {
44 bool operator()(const T &Lhs, const T &Rhs) {
45 return Lhs.FullName < Rhs.FullName;
46 }
47};
48
49using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
50} // end of anonymous namespace
Kristof Umann3daa2452019-01-26 16:35:33 +000051
52static constexpr char PackageSeparator = '.';
53
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000054static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker,
55 StringRef PackageName) {
Kristof Umann3daa2452019-01-26 16:35:33 +000056 // Does the checker's full name have the package as a prefix?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000057 if (!Checker.FullName.startswith(PackageName))
Kristof Umann3daa2452019-01-26 16:35:33 +000058 return false;
59
60 // Is the package actually just the name of a specific checker?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000061 if (Checker.FullName.size() == PackageName.size())
Kristof Umann3daa2452019-01-26 16:35:33 +000062 return true;
63
64 // Is the checker in the package (or a subpackage)?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000065 if (Checker.FullName[PackageName.size()] == PackageSeparator)
Kristof Umann3daa2452019-01-26 16:35:33 +000066 return true;
67
68 return false;
69}
70
71CheckerRegistry::CheckerInfoListRange
72CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
73
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000074 assert(std::is_sorted(Checkers.begin(), Checkers.end(), CheckerNameLT{}) &&
Kristof Umann3daa2452019-01-26 16:35:33 +000075 "In order to efficiently gather checkers, this function expects them "
76 "to be already sorted!");
77
78 // Use a binary search to find the possible start of the package.
79 CheckerRegistry::CheckerInfo
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000080 PackageInfo(nullptr, nullptr, CmdLineArg, "", "");
81 auto It = std::lower_bound(Checkers.begin(), Checkers.end(),
82 PackageInfo, CheckerNameLT{});
Kristof Umann3daa2452019-01-26 16:35:33 +000083
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000084 if (!isInPackage(*It, CmdLineArg))
Kristof Umann3daa2452019-01-26 16:35:33 +000085 return { Checkers.end(), Checkers.end() };
86
87 // See how large the package is.
88 // If the package doesn't exist, assume the option refers to a single
89 // checker.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000090 size_t Size = 1;
91 llvm::StringMap<size_t>::const_iterator PackageSize =
92 PackageSizes.find(CmdLineArg);
Kristof Umann3daa2452019-01-26 16:35:33 +000093
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000094 if (PackageSize != PackageSizes.end())
95 Size = PackageSize->getValue();
Kristof Umann3daa2452019-01-26 16:35:33 +000096
Kristof Umannb9bc7ec2019-04-18 15:19:16 +000097 return { It, It + Size };
Kristof Umann3daa2452019-01-26 16:35:33 +000098}
99
Kristof Umann98217ad2019-01-26 17:27:40 +0000100CheckerRegistry::CheckerRegistry(
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000101 ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags,
Kristof Umann98217ad2019-01-26 17:27:40 +0000102 AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
103 ArrayRef<std::function<void(CheckerRegistry &)>>
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000104 CheckerRegistrationFns)
105 : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) {
Kristof Umann058a7a42019-01-26 14:23:08 +0000106
Kristof Umann3daa2452019-01-26 16:35:33 +0000107 // Register builtin checkers.
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000108#define GET_CHECKERS
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000109#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
Kristof Umann058a7a42019-01-26 14:23:08 +0000110 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \
111 DOC_URI);
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000112
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000113#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
114#undef CHECKER
115#undef GET_CHECKERS
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000116#undef PACKAGE
117#undef GET_PACKAGES
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000118
Kristof Umann3daa2452019-01-26 16:35:33 +0000119 // Register checkers from plugins.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000120 for (const std::string &Plugin : Plugins) {
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000121 // Get access to the plugin.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000122 std::string ErrorMsg;
123 DynamicLibrary Lib =
124 DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
125 if (!Lib.isValid()) {
126 Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000127 continue;
128 }
129
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000130 // See if its compatible with this build of clang.
131 const char *PluginAPIVersion = static_cast<const char *>(
132 Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
133
134 if (!isCompatibleAPIVersion(PluginAPIVersion)) {
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000135 Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000136 << llvm::sys::path::filename(Plugin);
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000137 Diags.Report(diag::note_incompatible_analyzer_plugin_api)
138 << CLANG_ANALYZER_API_VERSION_STRING
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000139 << PluginAPIVersion;
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000140 continue;
141 }
142
143 // Register its checkers.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000144 RegisterCheckersFn RegisterPluginCheckers =
145 reinterpret_cast<RegisterCheckersFn>(Lib.getAddressOfSymbol(
146 "clang_registerCheckers"));
147 if (RegisterPluginCheckers)
148 RegisterPluginCheckers(*this);
Kristof Umannb0be2ab2018-12-15 18:11:49 +0000149 }
Jordy Rose59cce712011-08-16 21:24:21 +0000150
Kristof Umann98217ad2019-01-26 17:27:40 +0000151 // Register statically linked checkers, that aren't generated from the tblgen
152 // file, but rather passed their registry function as a parameter in
153 // checkerRegistrationFns.
154
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000155 for (const auto &Fn : CheckerRegistrationFns)
Kristof Umann98217ad2019-01-26 17:27:40 +0000156 Fn(*this);
157
Kristof Umann3daa2452019-01-26 16:35:33 +0000158 // Sort checkers for efficient collection.
159 // FIXME: Alphabetical sort puts 'experimental' in the middle.
160 // Would it be better to name it '~experimental' or something else
161 // that's ASCIIbetically last?
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000162 llvm::sort(Checkers, CheckerNameLT{});
Kristof Umann45beaa02018-11-18 12:47:03 +0000163
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000164#define GET_CHECKER_DEPENDENCIES
165
166#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
167 addDependency(FULLNAME, DEPENDENCY);
168
169#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
170#undef CHECKER_DEPENDENCY
171#undef GET_CHECKER_DEPENDENCIES
172
Kristof Umann3daa2452019-01-26 16:35:33 +0000173 // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
174 // command line.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000175 for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) {
176 CheckerInfoListRange CheckerForCmdLineArg =
177 getMutableCheckersForCmdLineArg(Opt.first);
Jordy Rose59cce712011-08-16 21:24:21 +0000178
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000179 if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
180 Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first;
Kristof Umann3daa2452019-01-26 16:35:33 +0000181 Diags.Report(diag::note_suggest_disabling_all_checkers);
182 }
Jordy Rose59cce712011-08-16 21:24:21 +0000183
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000184 for (CheckerInfo &checker : CheckerForCmdLineArg) {
185 checker.State = Opt.second ? StateFromCmdLine::State_Enabled :
Kristof Umann3daa2452019-01-26 16:35:33 +0000186 StateFromCmdLine::State_Disabled;
187 }
188 }
Jordy Rose59cce712011-08-16 21:24:21 +0000189}
190
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000191/// Collects dependencies in \p ret, returns false on failure.
192static bool collectDependenciesImpl(
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000193 const CheckerRegistry::ConstCheckerInfoList &Deps,
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000194 const LangOptions &LO,
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000195 CheckerRegistry::CheckerInfoSet &Ret);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000196
197/// Collects dependenies in \p enabledCheckers. Return None on failure.
198LLVM_NODISCARD
199static llvm::Optional<CheckerRegistry::CheckerInfoSet> collectDependencies(
200 const CheckerRegistry::CheckerInfo &checker, const LangOptions &LO) {
201
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000202 CheckerRegistry::CheckerInfoSet Ret;
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000203 // Add dependencies to the enabled checkers only if all of them can be
204 // enabled.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000205 if (!collectDependenciesImpl(checker.Dependencies, LO, Ret))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000206 return None;
207
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000208 return Ret;
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000209}
210
211static bool collectDependenciesImpl(
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000212 const CheckerRegistry::ConstCheckerInfoList &Deps,
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000213 const LangOptions &LO,
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000214 CheckerRegistry::CheckerInfoSet &Ret) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000215
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000216 for (const CheckerRegistry::CheckerInfo *Dependency : Deps) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000217
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000218 if (Dependency->isDisabled(LO))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000219 return false;
220
221 // Collect dependencies recursively.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000222 if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000223 return false;
224
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000225 Ret.insert(Dependency);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000226 }
227
228 return true;
229}
230
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000231CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
Jordy Rose59cce712011-08-16 21:24:21 +0000232
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000233 CheckerInfoSet EnabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000234
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000235 for (const CheckerInfo &Checker : Checkers) {
236 if (!Checker.isEnabled(LangOpts))
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000237 continue;
238
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000239 // Recursively enable its dependencies.
240 llvm::Optional<CheckerInfoSet> Deps =
241 collectDependencies(Checker, LangOpts);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000242
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000243 if (!Deps) {
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000244 // If we failed to enable any of the dependencies, don't enable this
245 // checker.
246 continue;
247 }
248
249 // Note that set_union also preserves the order of insertion.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000250 EnabledCheckers.set_union(*Deps);
Kristof Umann8fd74eb2019-01-26 20:06:54 +0000251
252 // Enable the checker.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000253 EnabledCheckers.insert(&Checker);
Kristof Umannf282d272018-12-15 15:44:05 +0000254 }
255
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000256 return EnabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +0000257}
258
Kristof Umann058a7a42019-01-26 14:23:08 +0000259void CheckerRegistry::addChecker(InitializationFunction Rfn,
260 ShouldRegisterFunction Sfn, StringRef Name,
Aaron Ballman2f234cb2018-12-20 20:20:20 +0000261 StringRef Desc, StringRef DocsUri) {
Kristof Umann058a7a42019-01-26 14:23:08 +0000262 Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
Jordy Rose59cce712011-08-16 21:24:21 +0000263
264 // Record the presence of the checker in its packages.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000265 StringRef PackageName, LeafName;
266 std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
267 while (!LeafName.empty()) {
268 PackageSizes[PackageName] += 1;
269 std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000270 }
271}
272
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000273void CheckerRegistry::addDependency(StringRef FullName, StringRef dependency) {
274 auto CheckerThatNeedsDeps =
275 [&FullName](const CheckerInfo &Chk) { return Chk.FullName == FullName; };
276 auto Dependency =
277 [&dependency](const CheckerInfo &Chk) {
278 return Chk.FullName == dependency;
279 };
280
281 auto CheckerIt = llvm::find_if(Checkers, CheckerThatNeedsDeps);
282 assert(CheckerIt != Checkers.end() &&
283 "Failed to find the checker while attempting to set up its "
284 "dependencies!");
285
286 auto DependencyIt = llvm::find_if(Checkers, Dependency);
287 assert(DependencyIt != Checkers.end() &&
288 "Failed to find the dependency of a checker!");
289
290 CheckerIt->Dependencies.push_back(&*DependencyIt);
291}
292
293void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000294 // Collect checkers enabled by the options.
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000295 CheckerInfoSet enabledCheckers = getEnabledCheckers();
Jordy Rose59cce712011-08-16 21:24:21 +0000296
297 // Initialize the CheckerManager with all enabled checkers.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000298 for (const auto *Checker : enabledCheckers) {
299 CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName));
300 Checker->Initialize(CheckerMgr);
Jordy Rose59cce712011-08-16 21:24:21 +0000301 }
302}
303
Kristof Umanndd9c86e2019-01-26 15:59:21 +0000304void CheckerRegistry::validateCheckerOptions() const {
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000305 for (const auto &Config : AnOpts.Config) {
306 size_t Pos = Config.getKey().find(':');
307 if (Pos == StringRef::npos)
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000308 continue;
309
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000310 bool HasChecker = false;
311 StringRef CheckerName = Config.getKey().substr(0, Pos);
312 for (const auto &Checker : Checkers) {
313 if (Checker.FullName.startswith(CheckerName) &&
314 (Checker.FullName.size() == Pos || Checker.FullName[Pos] == '.')) {
315 HasChecker = true;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000316 break;
317 }
318 }
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000319 if (!HasChecker)
320 Diags.Report(diag::err_unknown_analyzer_checker) << CheckerName;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000321 }
322}
323
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000324void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
325 size_t MaxNameChars) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000326 // FIXME: Print available packages.
327
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000328 Out << "CHECKERS:\n";
Jordy Rose59cce712011-08-16 21:24:21 +0000329
330 // Find the maximum option length.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000331 size_t OptionFieldWidth = 0;
332 for (const auto &Checker : Checkers) {
Jordy Rose59cce712011-08-16 21:24:21 +0000333 // Limit the amount of padding we are willing to give up for alignment.
334 // Package.Name Description [Hidden]
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000335 size_t NameLength = Checker.FullName.size();
336 if (NameLength <= MaxNameChars)
337 OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
Jordy Rose59cce712011-08-16 21:24:21 +0000338 }
339
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000340 const size_t InitialPad = 2;
341 for (const auto &Checker : Checkers) {
342 Out.indent(InitialPad) << Checker.FullName;
Jordy Rose59cce712011-08-16 21:24:21 +0000343
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000344 int Pad = OptionFieldWidth - Checker.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000345
346 // Break on long option names.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000347 if (Pad < 0) {
348 Out << '\n';
349 Pad = OptionFieldWidth + InitialPad;
Jordy Rose59cce712011-08-16 21:24:21 +0000350 }
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000351 Out.indent(Pad + 2) << Checker.Desc;
Jordy Rose59cce712011-08-16 21:24:21 +0000352
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000353 Out << '\n';
Jordy Rose59cce712011-08-16 21:24:21 +0000354 }
355}
Gabor Horvathc4309902016-08-08 13:41:04 +0000356
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000357void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
Gabor Horvathc4309902016-08-08 13:41:04 +0000358 // Collect checkers enabled by the options.
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000359 CheckerInfoSet EnabledCheckers = getEnabledCheckers();
Gabor Horvathc4309902016-08-08 13:41:04 +0000360
Kristof Umannb9bc7ec2019-04-18 15:19:16 +0000361 for (const auto *i : EnabledCheckers)
362 Out << i->FullName << '\n';
Gabor Horvathc4309902016-08-08 13:41:04 +0000363}