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/AnalyzerOptions.h"
15 #include "clang/StaticAnalyzer/Core/CheckerManager.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, its 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 namespace {
42 template <class T> struct FullNameLT {
43   bool operator()(const T &Lhs, const T &Rhs) {
44     return Lhs.FullName < Rhs.FullName;
45   }
46 };
47 
48 using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>;
49 using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
50 } // end of anonymous namespace
51 
52 template <class CheckerOrPackageInfoList>
53 static
54     typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value,
55                               typename CheckerOrPackageInfoList::const_iterator,
56                               typename CheckerOrPackageInfoList::iterator>::type
57     binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) {
58 
59   using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type;
60   using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>;
61 
62   assert(std::is_sorted(Collection.begin(), Collection.end(),
63                         CheckerOrPackageFullNameLT{}) &&
64          "In order to efficiently gather checkers/packages, this function "
65          "expects them to be already sorted!");
66 
67   return llvm::lower_bound(Collection, CheckerOrPackage(FullName),
68                            CheckerOrPackageFullNameLT{});
69 }
70 
71 static constexpr char PackageSeparator = '.';
72 
73 static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker,
74                         StringRef PackageName) {
75   // Does the checker's full name have the package as a prefix?
76   if (!Checker.FullName.startswith(PackageName))
77     return false;
78 
79   // Is the package actually just the name of a specific checker?
80   if (Checker.FullName.size() == PackageName.size())
81     return true;
82 
83   // Is the checker in the package (or a subpackage)?
84   if (Checker.FullName[PackageName.size()] == PackageSeparator)
85     return true;
86 
87   return false;
88 }
89 
90 CheckerRegistry::CheckerInfoListRange
91 CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
92   auto It = binaryFind(Checkers, CmdLineArg);
93 
94   if (!isInPackage(*It, CmdLineArg))
95     return {Checkers.end(), Checkers.end()};
96 
97   // See how large the package is.
98   // If the package doesn't exist, assume the option refers to a single
99   // checker.
100   size_t Size = 1;
101   llvm::StringMap<size_t>::const_iterator PackageSize =
102       PackageSizes.find(CmdLineArg);
103 
104   if (PackageSize != PackageSizes.end())
105     Size = PackageSize->getValue();
106 
107   return {It, It + Size};
108 }
109 
110 CheckerRegistry::CheckerRegistry(
111     ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags,
112     AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
113     ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
114     : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) {
115 
116   // Register builtin checkers.
117 #define GET_CHECKERS
118 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI)                            \
119   addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT,       \
120              DOC_URI);
121 
122 #define GET_PACKAGES
123 #define PACKAGE(FULLNAME) addPackage(FULLNAME);
124 
125 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
126 #undef CHECKER
127 #undef GET_CHECKERS
128 #undef PACKAGE
129 #undef GET_PACKAGES
130 
131   // Register checkers from plugins.
132   for (const std::string &Plugin : Plugins) {
133     // Get access to the plugin.
134     std::string ErrorMsg;
135     DynamicLibrary Lib =
136         DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
137     if (!Lib.isValid()) {
138       Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
139       continue;
140     }
141 
142     // See if its compatible with this build of clang.
143     const char *PluginAPIVersion = static_cast<const char *>(
144         Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
145 
146     if (!isCompatibleAPIVersion(PluginAPIVersion)) {
147       Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
148           << llvm::sys::path::filename(Plugin);
149       Diags.Report(diag::note_incompatible_analyzer_plugin_api)
150           << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion;
151       continue;
152     }
153 
154     // Register its checkers.
155     RegisterCheckersFn RegisterPluginCheckers =
156         reinterpret_cast<RegisterCheckersFn>(
157             Lib.getAddressOfSymbol("clang_registerCheckers"));
158     if (RegisterPluginCheckers)
159       RegisterPluginCheckers(*this);
160   }
161 
162   // Register statically linked checkers, that aren't generated from the tblgen
163   // file, but rather passed their registry function as a parameter in
164   // checkerRegistrationFns.
165 
166   for (const auto &Fn : CheckerRegistrationFns)
167     Fn(*this);
168 
169   // Sort checkers for efficient collection.
170   // FIXME: Alphabetical sort puts 'experimental' in the middle.
171   // Would it be better to name it '~experimental' or something else
172   // that's ASCIIbetically last?
173   llvm::sort(Packages, PackageNameLT{});
174   llvm::sort(Checkers, CheckerNameLT{});
175 
176 #define GET_CHECKER_DEPENDENCIES
177 
178 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \
179   addDependency(FULLNAME, DEPENDENCY);
180 
181 #define GET_CHECKER_OPTIONS
182 #define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL)             \
183   addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC);
184 
185 #define GET_PACKAGE_OPTIONS
186 #define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL)             \
187   addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC);
188 
189 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
190 #undef CHECKER_DEPENDENCY
191 #undef GET_CHECKER_DEPENDENCIES
192 #undef CHECKER_OPTION
193 #undef GET_CHECKER_OPTIONS
194 #undef PACKAGE_OPTION
195 #undef GET_PACKAGE_OPTIONS
196 
197   resolveDependencies();
198   resolveCheckerAndPackageOptions();
199 
200   // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
201   // command line.
202   for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) {
203     CheckerInfoListRange CheckerForCmdLineArg =
204         getMutableCheckersForCmdLineArg(Opt.first);
205 
206     if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
207       Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first;
208       Diags.Report(diag::note_suggest_disabling_all_checkers);
209     }
210 
211     for (CheckerInfo &checker : CheckerForCmdLineArg) {
212       checker.State = Opt.second ? StateFromCmdLine::State_Enabled
213                                  : StateFromCmdLine::State_Disabled;
214     }
215   }
216 }
217 
218 /// Collects dependencies in \p ret, returns false on failure.
219 static bool
220 collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
221                         const LangOptions &LO,
222                         CheckerRegistry::CheckerInfoSet &Ret);
223 
224 /// Collects dependenies in \p enabledCheckers. Return None on failure.
225 LLVM_NODISCARD
226 static llvm::Optional<CheckerRegistry::CheckerInfoSet>
227 collectDependencies(const CheckerRegistry::CheckerInfo &checker,
228                     const LangOptions &LO) {
229 
230   CheckerRegistry::CheckerInfoSet Ret;
231   // Add dependencies to the enabled checkers only if all of them can be
232   // enabled.
233   if (!collectDependenciesImpl(checker.Dependencies, LO, Ret))
234     return None;
235 
236   return Ret;
237 }
238 
239 static bool
240 collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
241                         const LangOptions &LO,
242                         CheckerRegistry::CheckerInfoSet &Ret) {
243 
244   for (const CheckerRegistry::CheckerInfo *Dependency : Deps) {
245 
246     if (Dependency->isDisabled(LO))
247       return false;
248 
249     // Collect dependencies recursively.
250     if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret))
251       return false;
252 
253     Ret.insert(Dependency);
254   }
255 
256   return true;
257 }
258 
259 CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
260 
261   CheckerInfoSet EnabledCheckers;
262 
263   for (const CheckerInfo &Checker : Checkers) {
264     if (!Checker.isEnabled(LangOpts))
265       continue;
266 
267     // Recursively enable its dependencies.
268     llvm::Optional<CheckerInfoSet> Deps =
269         collectDependencies(Checker, LangOpts);
270 
271     if (!Deps) {
272       // If we failed to enable any of the dependencies, don't enable this
273       // checker.
274       continue;
275     }
276 
277     // Note that set_union also preserves the order of insertion.
278     EnabledCheckers.set_union(*Deps);
279 
280     // Enable the checker.
281     EnabledCheckers.insert(&Checker);
282   }
283 
284   return EnabledCheckers;
285 }
286 
287 void CheckerRegistry::resolveDependencies() {
288   for (const std::pair<StringRef, StringRef> &Entry : Dependencies) {
289     auto CheckerIt = binaryFind(Checkers, Entry.first);
290     assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first &&
291            "Failed to find the checker while attempting to set up its "
292            "dependencies!");
293 
294     auto DependencyIt = binaryFind(Checkers, Entry.second);
295     assert(DependencyIt != Checkers.end() &&
296            DependencyIt->FullName == Entry.second &&
297            "Failed to find the dependency of a checker!");
298 
299     CheckerIt->Dependencies.emplace_back(&*DependencyIt);
300   }
301 
302   Dependencies.clear();
303 }
304 
305 void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
306   Dependencies.emplace_back(FullName, Dependency);
307 }
308 
309 template <class T>
310 static void
311 insertOptionToCollection(StringRef FullName, T &Collection,
312                          const CheckerRegistry::CmdLineOption &&Option) {
313   auto It = binaryFind(Collection, FullName);
314   assert(It != Collection.end() &&
315          "Failed to find the checker while attempting to add a command line "
316          "option to it!");
317 
318   It->CmdLineOptions.emplace_back(std::move(Option));
319 }
320 
321 void CheckerRegistry::resolveCheckerAndPackageOptions() {
322   for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
323        CheckerOptions) {
324     insertOptionToCollection(CheckerOptEntry.first, Checkers,
325                              std::move(CheckerOptEntry.second));
326   }
327   CheckerOptions.clear();
328 
329   for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
330        PackageOptions) {
331     insertOptionToCollection(PackageOptEntry.first, Checkers,
332                              std::move(PackageOptEntry.second));
333   }
334   PackageOptions.clear();
335 }
336 
337 void CheckerRegistry::addPackage(StringRef FullName) {
338   Packages.emplace_back(PackageInfo(FullName));
339 }
340 
341 void CheckerRegistry::addPackageOption(StringRef OptionType,
342                                        StringRef PackageFullName,
343                                        StringRef OptionName,
344                                        StringRef DefaultValStr,
345                                        StringRef Description) {
346   PackageOptions.emplace_back(
347       PackageFullName,
348       CmdLineOption{OptionType, OptionName, DefaultValStr, Description});
349 }
350 
351 void CheckerRegistry::addChecker(InitializationFunction Rfn,
352                                  ShouldRegisterFunction Sfn, StringRef Name,
353                                  StringRef Desc, StringRef DocsUri) {
354   Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri);
355 
356   // Record the presence of the checker in its packages.
357   StringRef PackageName, LeafName;
358   std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
359   while (!LeafName.empty()) {
360     PackageSizes[PackageName] += 1;
361     std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
362   }
363 }
364 
365 void CheckerRegistry::addCheckerOption(StringRef OptionType,
366                                        StringRef CheckerFullName,
367                                        StringRef OptionName,
368                                        StringRef DefaultValStr,
369                                        StringRef Description) {
370   CheckerOptions.emplace_back(
371       CheckerFullName,
372       CmdLineOption{OptionType, OptionName, DefaultValStr, Description});
373 }
374 
375 void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
376   // Collect checkers enabled by the options.
377   CheckerInfoSet enabledCheckers = getEnabledCheckers();
378 
379   // Initialize the CheckerManager with all enabled checkers.
380   for (const auto *Checker : enabledCheckers) {
381     CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName));
382     Checker->Initialize(CheckerMgr);
383   }
384 }
385 
386 void CheckerRegistry::validateCheckerOptions() const {
387   for (const auto &Config : AnOpts.Config) {
388     size_t Pos = Config.getKey().find(':');
389     if (Pos == StringRef::npos)
390       continue;
391 
392     bool HasChecker = false;
393     StringRef CheckerName = Config.getKey().substr(0, Pos);
394     for (const auto &Checker : Checkers) {
395       if (Checker.FullName.startswith(CheckerName) &&
396           (Checker.FullName.size() == Pos || Checker.FullName[Pos] == '.')) {
397         HasChecker = true;
398         break;
399       }
400     }
401     if (!HasChecker)
402       Diags.Report(diag::err_unknown_analyzer_checker) << CheckerName;
403   }
404 }
405 
406 void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
407                                                size_t MaxNameChars) const {
408   // FIXME: Print available packages.
409 
410   Out << "CHECKERS:\n";
411 
412   // Find the maximum option length.
413   size_t OptionFieldWidth = 0;
414   for (const auto &Checker : Checkers) {
415     // Limit the amount of padding we are willing to give up for alignment.
416     //   Package.Name     Description  [Hidden]
417     size_t NameLength = Checker.FullName.size();
418     if (NameLength <= MaxNameChars)
419       OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
420   }
421 
422   const size_t InitialPad = 2;
423   for (const auto &Checker : Checkers) {
424     Out.indent(InitialPad) << Checker.FullName;
425 
426     int Pad = OptionFieldWidth - Checker.FullName.size();
427 
428     // Break on long option names.
429     if (Pad < 0) {
430       Out << '\n';
431       Pad = OptionFieldWidth + InitialPad;
432     }
433     Out.indent(Pad + 2) << Checker.Desc;
434 
435     Out << '\n';
436   }
437 }
438 
439 void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
440   // Collect checkers enabled by the options.
441   CheckerInfoSet EnabledCheckers = getEnabledCheckers();
442 
443   for (const auto *i : EnabledCheckers)
444     Out << i->FullName << '\n';
445 }
446