1 //===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
10 #include "clang/Basic/Diagnostic.h"
11 #include "clang/Basic/LLVM.h"
12 #include "clang/Frontend/FrontendDiagnostic.h"
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
15 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SetVector.h"
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/DynamicLibrary.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include <algorithm>
24 
25 using namespace clang;
26 using namespace ento;
27 using llvm::sys::DynamicLibrary;
28 
29 using RegisterCheckersFn = void (*)(CheckerRegistry &);
30 
31 static 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 
41 static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
42                           const CheckerRegistry::CheckerInfo &b) {
43   return a.FullName < b.FullName;
44 }
45 
46 static constexpr char PackageSeparator = '.';
47 
48 static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
49                         StringRef packageName) {
50   // Does the checker's full name have the package as a prefix?
51   if (!checker.FullName.startswith(packageName))
52     return false;
53 
54   // Is the package actually just the name of a specific checker?
55   if (checker.FullName.size() == packageName.size())
56     return true;
57 
58   // Is the checker in the package (or a subpackage)?
59   if (checker.FullName[packageName.size()] == PackageSeparator)
60     return true;
61 
62   return false;
63 }
64 
65 CheckerRegistry::CheckerInfoListRange
66 CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
67 
68   assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
69          "In order to efficiently gather checkers, this function expects them "
70          "to be already sorted!");
71 
72   // Use a binary search to find the possible start of the package.
73   CheckerRegistry::CheckerInfo
74       packageInfo(nullptr, nullptr, CmdLineArg, "", "");
75   auto it = std::lower_bound(Checkers.begin(), Checkers.end(),
76                              packageInfo, checkerNameLT);
77 
78   if (!isInPackage(*it, CmdLineArg))
79     return { Checkers.end(), Checkers.end() };
80 
81   // See how large the package is.
82   // If the package doesn't exist, assume the option refers to a single
83   // checker.
84   size_t size = 1;
85   llvm::StringMap<size_t>::const_iterator packageSize =
86       Packages.find(CmdLineArg);
87 
88   if (packageSize != Packages.end())
89     size = packageSize->getValue();
90 
91   return { it, it + size };
92 }
93 
94 CheckerRegistry::CheckerRegistry(
95      ArrayRef<std::string> plugins, DiagnosticsEngine &diags,
96      AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
97      ArrayRef<std::function<void(CheckerRegistry &)>>
98          checkerRegistrationFns)
99   : Diags(diags), AnOpts(AnOpts), LangOpts(LangOpts) {
100 
101   // Register builtin checkers.
102 #define GET_CHECKERS
103 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI)                            \
104   addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT,       \
105              DOC_URI);
106 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
107 #undef CHECKER
108 #undef GET_CHECKERS
109 
110   // Register checkers from plugins.
111   for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
112        i != e; ++i) {
113     // Get access to the plugin.
114     std::string err;
115     DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err);
116     if (!lib.isValid()) {
117       diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err;
118       continue;
119     }
120 
121     // See if it's compatible with this build of clang.
122     const char *pluginAPIVersion =
123       (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
124     if (!isCompatibleAPIVersion(pluginAPIVersion)) {
125       Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
126           << llvm::sys::path::filename(*i);
127       Diags.Report(diag::note_incompatible_analyzer_plugin_api)
128           << CLANG_ANALYZER_API_VERSION_STRING
129           << pluginAPIVersion;
130       continue;
131     }
132 
133     // Register its checkers.
134     RegisterCheckersFn registerPluginCheckers =
135       (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
136                                                       "clang_registerCheckers");
137     if (registerPluginCheckers)
138       registerPluginCheckers(*this);
139   }
140 
141   // Register statically linked checkers, that aren't generated from the tblgen
142   // file, but rather passed their registry function as a parameter in
143   // checkerRegistrationFns.
144 
145   for (const auto &Fn : checkerRegistrationFns)
146     Fn(*this);
147 
148   // Sort checkers for efficient collection.
149   // FIXME: Alphabetical sort puts 'experimental' in the middle.
150   // Would it be better to name it '~experimental' or something else
151   // that's ASCIIbetically last?
152   llvm::sort(Checkers, checkerNameLT);
153 
154 #define GET_CHECKER_DEPENDENCIES
155 
156 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \
157   addDependency(FULLNAME, DEPENDENCY);
158 
159 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
160 #undef CHECKER_DEPENDENCY
161 #undef GET_CHECKER_DEPENDENCIES
162 
163   // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
164   // command line.
165   for (const std::pair<std::string, bool> &opt : AnOpts.CheckersControlList) {
166     CheckerInfoListRange checkersForCmdLineArg =
167                                      getMutableCheckersForCmdLineArg(opt.first);
168 
169     if (checkersForCmdLineArg.begin() == checkersForCmdLineArg.end()) {
170       Diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
171       Diags.Report(diag::note_suggest_disabling_all_checkers);
172     }
173 
174     for (CheckerInfo &checker : checkersForCmdLineArg) {
175       checker.State = opt.second ? StateFromCmdLine::State_Enabled :
176                                    StateFromCmdLine::State_Disabled;
177     }
178   }
179 }
180 
181 /// Collects dependencies in \p ret, returns false on failure.
182 static bool collectDependenciesImpl(
183                               const CheckerRegistry::ConstCheckerInfoList &deps,
184                               const LangOptions &LO,
185                               CheckerRegistry::CheckerInfoSet &ret);
186 
187 /// Collects dependenies in \p enabledCheckers. Return None on failure.
188 LLVM_NODISCARD
189 static llvm::Optional<CheckerRegistry::CheckerInfoSet> collectDependencies(
190      const CheckerRegistry::CheckerInfo &checker, const LangOptions &LO) {
191 
192   CheckerRegistry::CheckerInfoSet ret;
193   // Add dependencies to the enabled checkers only if all of them can be
194   // enabled.
195   if (!collectDependenciesImpl(checker.Dependencies, LO, ret))
196     return None;
197 
198   return ret;
199 }
200 
201 static bool collectDependenciesImpl(
202                               const CheckerRegistry::ConstCheckerInfoList &deps,
203                               const LangOptions &LO,
204                               CheckerRegistry::CheckerInfoSet &ret) {
205 
206   for (const CheckerRegistry::CheckerInfo *dependency : deps) {
207 
208     if (dependency->isDisabled(LO))
209       return false;
210 
211     // Collect dependencies recursively.
212     if (!collectDependenciesImpl(dependency->Dependencies, LO, ret))
213       return false;
214 
215     ret.insert(dependency);
216   }
217 
218   return true;
219 }
220 
221 CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
222 
223   CheckerInfoSet enabledCheckers;
224 
225   for (const CheckerInfo &checker : Checkers) {
226     if (!checker.isEnabled(LangOpts))
227       continue;
228 
229     // Recursively enable it's dependencies.
230     llvm::Optional<CheckerInfoSet> deps =
231         collectDependencies(checker, LangOpts);
232 
233     if (!deps) {
234       // If we failed to enable any of the dependencies, don't enable this
235       // checker.
236       continue;
237     }
238 
239     // Note that set_union also preserves the order of insertion.
240     enabledCheckers.set_union(*deps);
241 
242     // Enable the checker.
243     enabledCheckers.insert(&checker);
244   }
245 
246   return enabledCheckers;
247 }
248 
249 void CheckerRegistry::addChecker(InitializationFunction Rfn,
250                                  ShouldRegisterFunction Sfn, StringRef Name,
251                                  StringRef Desc, StringRef DocsUri) {
252   Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
253 
254   // Record the presence of the checker in its packages.
255   StringRef packageName, leafName;
256   std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
257   while (!leafName.empty()) {
258     Packages[packageName] += 1;
259     std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
260   }
261 }
262 
263 void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const {
264   // Collect checkers enabled by the options.
265   CheckerInfoSet enabledCheckers = getEnabledCheckers();
266 
267   // Initialize the CheckerManager with all enabled checkers.
268   for (const auto *i : enabledCheckers) {
269     checkerMgr.setCurrentCheckName(CheckName(i->FullName));
270     i->Initialize(checkerMgr);
271   }
272 }
273 
274 void CheckerRegistry::validateCheckerOptions() const {
275   for (const auto &config : AnOpts.Config) {
276     size_t pos = config.getKey().find(':');
277     if (pos == StringRef::npos)
278       continue;
279 
280     bool hasChecker = false;
281     StringRef checkerName = config.getKey().substr(0, pos);
282     for (const auto &checker : Checkers) {
283       if (checker.FullName.startswith(checkerName) &&
284           (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
285         hasChecker = true;
286         break;
287       }
288     }
289     if (!hasChecker)
290       Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
291   }
292 }
293 
294 void CheckerRegistry::printHelp(raw_ostream &out,
295                                 size_t maxNameChars) const {
296   // FIXME: Print available packages.
297 
298   out << "CHECKERS:\n";
299 
300   // Find the maximum option length.
301   size_t optionFieldWidth = 0;
302   for (const auto &i : Checkers) {
303     // Limit the amount of padding we are willing to give up for alignment.
304     //   Package.Name     Description  [Hidden]
305     size_t nameLength = i.FullName.size();
306     if (nameLength <= maxNameChars)
307       optionFieldWidth = std::max(optionFieldWidth, nameLength);
308   }
309 
310   const size_t initialPad = 2;
311   for (const auto &i : Checkers) {
312     out.indent(initialPad) << i.FullName;
313 
314     int pad = optionFieldWidth - i.FullName.size();
315 
316     // Break on long option names.
317     if (pad < 0) {
318       out << '\n';
319       pad = optionFieldWidth + initialPad;
320     }
321     out.indent(pad + 2) << i.Desc;
322 
323     out << '\n';
324   }
325 }
326 
327 void CheckerRegistry::printList(raw_ostream &out) const {
328   // Collect checkers enabled by the options.
329   CheckerInfoSet enabledCheckers = getEnabledCheckers();
330 
331   for (const auto *i : enabledCheckers)
332     out << i->FullName << '\n';
333 }
334