blob: d1931d3e6fd84d06174245e613e2dc19654283cd [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//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Kristof Umann76a21502018-12-15 16:23:51 +000010#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
Gabor Horvathfc4c4d42015-07-09 21:43:45 +000011#include "clang/Basic/Diagnostic.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000012#include "clang/Basic/LLVM.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000013#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Gabor Horvathfc4c4d42015-07-09 21:43:45 +000014#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000015#include "llvm/ADT/STLExtras.h"
Anna Zaks30373152011-12-15 01:36:04 +000016#include "llvm/ADT/SetVector.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000017#include "llvm/ADT/StringMap.h"
18#include "llvm/ADT/StringRef.h"
Benjamin Kramer444a1302012-12-01 17:12:56 +000019#include "llvm/Support/raw_ostream.h"
Eugene Zelenko88f40cf2018-04-03 21:31:50 +000020#include <algorithm>
Jordy Rose59cce712011-08-16 21:24:21 +000021
22using namespace clang;
23using namespace ento;
24
Kristof Umannf282d272018-12-15 15:44:05 +000025static constexpr char PackageSeparator = '.';
Kristof Umann45beaa02018-11-18 12:47:03 +000026
Jordy Rose59cce712011-08-16 21:24:21 +000027static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
28 const CheckerRegistry::CheckerInfo &b) {
29 return a.FullName < b.FullName;
30}
31
32static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
33 StringRef packageName) {
34 // Does the checker's full name have the package as a prefix?
35 if (!checker.FullName.startswith(packageName))
36 return false;
37
38 // Is the package actually just the name of a specific checker?
39 if (checker.FullName.size() == packageName.size())
40 return true;
41
42 // Is the checker in the package (or a subpackage)?
43 if (checker.FullName[packageName.size()] == PackageSeparator)
44 return true;
45
46 return false;
47}
48
Kristof Umannf282d272018-12-15 15:44:05 +000049CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers(
50 const AnalyzerOptions &Opts,
51 DiagnosticsEngine &diags) const {
Jordy Rose59cce712011-08-16 21:24:21 +000052
Kristof Umannf282d272018-12-15 15:44:05 +000053 assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
54 "In order to efficiently gather checkers, this function expects them "
55 "to be already sorted!");
Jordy Rose59cce712011-08-16 21:24:21 +000056
Kristof Umannf282d272018-12-15 15:44:05 +000057 CheckerInfoSet enabledCheckers;
58 const auto end = Checkers.cend();
Jordy Rose59cce712011-08-16 21:24:21 +000059
Kristof Umannf282d272018-12-15 15:44:05 +000060 for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) {
61 // Use a binary search to find the possible start of the package.
62 CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "");
63 auto firstRelatedChecker =
64 std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +000065
Kristof Umannf282d272018-12-15 15:44:05 +000066 if (firstRelatedChecker == end ||
67 !isInPackage(*firstRelatedChecker, opt.first)) {
68 diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
69 diags.Report(diag::note_suggest_disabling_all_checkers);
70 return {};
71 }
Jordy Rose59cce712011-08-16 21:24:21 +000072
Kristof Umannf282d272018-12-15 15:44:05 +000073 // See how large the package is.
74 // If the package doesn't exist, assume the option refers to a single
75 // checker.
76 size_t size = 1;
77 llvm::StringMap<size_t>::const_iterator packageSize =
78 Packages.find(opt.first);
79 if (packageSize != Packages.end())
80 size = packageSize->getValue();
81
82 // Step through all the checkers in the package.
83 for (auto lastRelatedChecker = firstRelatedChecker+size;
84 firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker)
85 if (opt.second)
86 enabledCheckers.insert(&*firstRelatedChecker);
87 else
88 enabledCheckers.remove(&*firstRelatedChecker);
89 }
90
91 return enabledCheckers;
Jordy Rose59cce712011-08-16 21:24:21 +000092}
93
94void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
95 StringRef desc) {
96 Checkers.push_back(CheckerInfo(fn, name, desc));
97
98 // Record the presence of the checker in its packages.
99 StringRef packageName, leafName;
Benjamin Kramer867ea1d2014-03-02 13:01:17 +0000100 std::tie(packageName, leafName) = name.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000101 while (!leafName.empty()) {
102 Packages[packageName] += 1;
Benjamin Kramer867ea1d2014-03-02 13:01:17 +0000103 std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
Jordy Rose59cce712011-08-16 21:24:21 +0000104 }
105}
106
Ted Kremenek3a0678e2015-09-08 03:50:52 +0000107void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
Kristof Umann45beaa02018-11-18 12:47:03 +0000108 const AnalyzerOptions &Opts,
109 DiagnosticsEngine &diags) const {
Jordy Rose59cce712011-08-16 21:24:21 +0000110 // Sort checkers for efficient collection.
Fangrui Song55fab262018-09-26 22:16:28 +0000111 llvm::sort(Checkers, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +0000112
113 // Collect checkers enabled by the options.
Kristof Umannf282d272018-12-15 15:44:05 +0000114 CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts, diags);
Jordy Rose59cce712011-08-16 21:24:21 +0000115
116 // Initialize the CheckerManager with all enabled checkers.
Kristof Umannf282d272018-12-15 15:44:05 +0000117 for (const auto *i : enabledCheckers) {
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000118 checkerMgr.setCurrentCheckName(CheckName(i->FullName));
119 i->Initialize(checkerMgr);
Jordy Rose59cce712011-08-16 21:24:21 +0000120 }
121}
122
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000123void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts,
124 DiagnosticsEngine &diags) const {
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000125 for (const auto &config : opts.Config) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000126 size_t pos = config.getKey().find(':');
127 if (pos == StringRef::npos)
128 continue;
129
130 bool hasChecker = false;
131 StringRef checkerName = config.getKey().substr(0, pos);
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000132 for (const auto &checker : Checkers) {
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000133 if (checker.FullName.startswith(checkerName) &&
134 (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
135 hasChecker = true;
136 break;
137 }
138 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000139 if (!hasChecker)
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000140 diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
Gabor Horvathfc4c4d42015-07-09 21:43:45 +0000141 }
142}
143
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000144void CheckerRegistry::printHelp(raw_ostream &out,
Jordy Rose59cce712011-08-16 21:24:21 +0000145 size_t maxNameChars) const {
146 // FIXME: Alphabetical sort puts 'experimental' in the middle.
147 // Would it be better to name it '~experimental' or something else
148 // that's ASCIIbetically last?
Fangrui Song55fab262018-09-26 22:16:28 +0000149 llvm::sort(Checkers, checkerNameLT);
Jordy Rose59cce712011-08-16 21:24:21 +0000150
151 // FIXME: Print available packages.
152
153 out << "CHECKERS:\n";
154
155 // Find the maximum option length.
156 size_t optionFieldWidth = 0;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000157 for (const auto &i : Checkers) {
Jordy Rose59cce712011-08-16 21:24:21 +0000158 // Limit the amount of padding we are willing to give up for alignment.
159 // Package.Name Description [Hidden]
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000160 size_t nameLength = i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000161 if (nameLength <= maxNameChars)
162 optionFieldWidth = std::max(optionFieldWidth, nameLength);
163 }
164
165 const size_t initialPad = 2;
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000166 for (const auto &i : Checkers) {
167 out.indent(initialPad) << i.FullName;
Jordy Rose59cce712011-08-16 21:24:21 +0000168
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000169 int pad = optionFieldWidth - i.FullName.size();
Jordy Rose59cce712011-08-16 21:24:21 +0000170
171 // Break on long option names.
172 if (pad < 0) {
173 out << '\n';
174 pad = optionFieldWidth + initialPad;
175 }
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000176 out.indent(pad + 2) << i.Desc;
Jordy Rose59cce712011-08-16 21:24:21 +0000177
178 out << '\n';
179 }
180}
Gabor Horvathc4309902016-08-08 13:41:04 +0000181
Kristof Umann45beaa02018-11-18 12:47:03 +0000182void CheckerRegistry::printList(raw_ostream &out,
Kristof Umannf282d272018-12-15 15:44:05 +0000183 const AnalyzerOptions &opts,
184 DiagnosticsEngine &diags) const {
185 // Sort checkers for efficient collection.
Fangrui Song55fab262018-09-26 22:16:28 +0000186 llvm::sort(Checkers, checkerNameLT);
Gabor Horvathc4309902016-08-08 13:41:04 +0000187
188 // Collect checkers enabled by the options.
Kristof Umannf282d272018-12-15 15:44:05 +0000189 CheckerInfoSet enabledCheckers = getEnabledCheckers(opts, diags);
Gabor Horvathc4309902016-08-08 13:41:04 +0000190
Eugene Zelenko88f40cf2018-04-03 21:31:50 +0000191 for (const auto *i : enabledCheckers)
192 out << i->FullName << '\n';
Gabor Horvathc4309902016-08-08 13:41:04 +0000193}