1 //===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
2 //
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
10 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
11 #include "clang/Basic/Diagnostic.h"
12 #include "clang/Basic/LLVM.h"
13 #include "clang/Frontend/FrontendDiagnostic.h"
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
16 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SetVector.h"
19 #include "llvm/ADT/StringMap.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/DynamicLibrary.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <algorithm>
25
26 using namespace clang;
27 using namespace ento;
28 using llvm::sys::DynamicLibrary;
29
30 using RegisterCheckersFn = void (*)(CheckerRegistry &);
31
isCompatibleAPIVersion(const char * versionString)32 static bool isCompatibleAPIVersion(const char *versionString) {
33 // If the version string is null, it's not an analyzer plugin.
34 if (!versionString)
35 return false;
36
37 // For now, none of the static analyzer API is considered stable.
38 // Versions must match exactly.
39 return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
40 }
41
CheckerRegistry(ArrayRef<std::string> plugins,DiagnosticsEngine & diags)42 CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins,
43 DiagnosticsEngine &diags) : Diags(diags) {
44 #define GET_CHECKERS
45 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
46 addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI);
47 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
48 #undef CHECKER
49 #undef GET_CHECKERS
50
51 for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
52 i != e; ++i) {
53 // Get access to the plugin.
54 std::string err;
55 DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err);
56 if (!lib.isValid()) {
57 diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err;
58 continue;
59 }
60
61 // See if it's compatible with this build of clang.
62 const char *pluginAPIVersion =
63 (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
64 if (!isCompatibleAPIVersion(pluginAPIVersion)) {
65 Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
66 << llvm::sys::path::filename(*i);
67 Diags.Report(diag::note_incompatible_analyzer_plugin_api)
68 << CLANG_ANALYZER_API_VERSION_STRING
69 << pluginAPIVersion;
70 continue;
71 }
72
73 // Register its checkers.
74 RegisterCheckersFn registerPluginCheckers =
75 (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
76 "clang_registerCheckers");
77 if (registerPluginCheckers)
78 registerPluginCheckers(*this);
79 }
80 }
81
82 static constexpr char PackageSeparator = '.';
83
checkerNameLT(const CheckerRegistry::CheckerInfo & a,const CheckerRegistry::CheckerInfo & b)84 static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
85 const CheckerRegistry::CheckerInfo &b) {
86 return a.FullName < b.FullName;
87 }
88
isInPackage(const CheckerRegistry::CheckerInfo & checker,StringRef packageName)89 static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
90 StringRef packageName) {
91 // Does the checker's full name have the package as a prefix?
92 if (!checker.FullName.startswith(packageName))
93 return false;
94
95 // Is the package actually just the name of a specific checker?
96 if (checker.FullName.size() == packageName.size())
97 return true;
98
99 // Is the checker in the package (or a subpackage)?
100 if (checker.FullName[packageName.size()] == PackageSeparator)
101 return true;
102
103 return false;
104 }
105
getEnabledCheckers(const AnalyzerOptions & Opts) const106 CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers(
107 const AnalyzerOptions &Opts) const {
108
109 assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
110 "In order to efficiently gather checkers, this function expects them "
111 "to be already sorted!");
112
113 CheckerInfoSet enabledCheckers;
114 const auto end = Checkers.cend();
115
116 for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) {
117 // Use a binary search to find the possible start of the package.
118 CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", "");
119 auto firstRelatedChecker =
120 std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT);
121
122 if (firstRelatedChecker == end ||
123 !isInPackage(*firstRelatedChecker, opt.first)) {
124 Diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
125 Diags.Report(diag::note_suggest_disabling_all_checkers);
126 return {};
127 }
128
129 // See how large the package is.
130 // If the package doesn't exist, assume the option refers to a single
131 // checker.
132 size_t size = 1;
133 llvm::StringMap<size_t>::const_iterator packageSize =
134 Packages.find(opt.first);
135 if (packageSize != Packages.end())
136 size = packageSize->getValue();
137
138 // Step through all the checkers in the package.
139 for (auto lastRelatedChecker = firstRelatedChecker+size;
140 firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker)
141 if (opt.second)
142 enabledCheckers.insert(&*firstRelatedChecker);
143 else
144 enabledCheckers.remove(&*firstRelatedChecker);
145 }
146
147 return enabledCheckers;
148 }
149
addChecker(InitializationFunction Fn,StringRef Name,StringRef Desc,StringRef DocsUri)150 void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name,
151 StringRef Desc, StringRef DocsUri) {
152 Checkers.emplace_back(Fn, Name, Desc, DocsUri);
153
154 // Record the presence of the checker in its packages.
155 StringRef packageName, leafName;
156 std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
157 while (!leafName.empty()) {
158 Packages[packageName] += 1;
159 std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
160 }
161 }
162
initializeManager(CheckerManager & checkerMgr,const AnalyzerOptions & Opts) const163 void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
164 const AnalyzerOptions &Opts) const {
165 // Sort checkers for efficient collection.
166 llvm::sort(Checkers, checkerNameLT);
167
168 // Collect checkers enabled by the options.
169 CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts);
170
171 // Initialize the CheckerManager with all enabled checkers.
172 for (const auto *i : enabledCheckers) {
173 checkerMgr.setCurrentCheckName(CheckName(i->FullName));
174 i->Initialize(checkerMgr);
175 }
176 }
177
validateCheckerOptions(const AnalyzerOptions & opts) const178 void CheckerRegistry::validateCheckerOptions(
179 const AnalyzerOptions &opts) const {
180 for (const auto &config : opts.Config) {
181 size_t pos = config.getKey().find(':');
182 if (pos == StringRef::npos)
183 continue;
184
185 bool hasChecker = false;
186 StringRef checkerName = config.getKey().substr(0, pos);
187 for (const auto &checker : Checkers) {
188 if (checker.FullName.startswith(checkerName) &&
189 (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
190 hasChecker = true;
191 break;
192 }
193 }
194 if (!hasChecker)
195 Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
196 }
197 }
198
printHelp(raw_ostream & out,size_t maxNameChars) const199 void CheckerRegistry::printHelp(raw_ostream &out,
200 size_t maxNameChars) const {
201 // FIXME: Alphabetical sort puts 'experimental' in the middle.
202 // Would it be better to name it '~experimental' or something else
203 // that's ASCIIbetically last?
204 llvm::sort(Checkers, checkerNameLT);
205
206 // FIXME: Print available packages.
207
208 out << "CHECKERS:\n";
209
210 // Find the maximum option length.
211 size_t optionFieldWidth = 0;
212 for (const auto &i : Checkers) {
213 // Limit the amount of padding we are willing to give up for alignment.
214 // Package.Name Description [Hidden]
215 size_t nameLength = i.FullName.size();
216 if (nameLength <= maxNameChars)
217 optionFieldWidth = std::max(optionFieldWidth, nameLength);
218 }
219
220 const size_t initialPad = 2;
221 for (const auto &i : Checkers) {
222 out.indent(initialPad) << i.FullName;
223
224 int pad = optionFieldWidth - i.FullName.size();
225
226 // Break on long option names.
227 if (pad < 0) {
228 out << '\n';
229 pad = optionFieldWidth + initialPad;
230 }
231 out.indent(pad + 2) << i.Desc;
232
233 out << '\n';
234 }
235 }
236
printList(raw_ostream & out,const AnalyzerOptions & opts) const237 void CheckerRegistry::printList(raw_ostream &out,
238 const AnalyzerOptions &opts) const {
239 // Sort checkers for efficient collection.
240 llvm::sort(Checkers, checkerNameLT);
241
242 // Collect checkers enabled by the options.
243 CheckerInfoSet enabledCheckers = getEnabledCheckers(opts);
244
245 for (const auto *i : enabledCheckers)
246 out << i->FullName << '\n';
247 }
248