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