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