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/Driver/DriverDiagnostic.h" 13 #include "clang/Frontend/FrontendDiagnostic.h" 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 16 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 17 #include "llvm/ADT/STLExtras.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 namespace checker_registry; 28 using llvm::sys::DynamicLibrary; 29 30 //===----------------------------------------------------------------------===// 31 // Utilities. 32 //===----------------------------------------------------------------------===// 33 34 static bool isCompatibleAPIVersion(const char *VersionString) { 35 // If the version string is null, its not an analyzer plugin. 36 if (!VersionString) 37 return false; 38 39 // For now, none of the static analyzer API is considered stable. 40 // Versions must match exactly. 41 return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; 42 } 43 44 static constexpr char PackageSeparator = '.'; 45 46 //===----------------------------------------------------------------------===// 47 // Methods of CheckerRegistry. 48 //===----------------------------------------------------------------------===// 49 50 CheckerRegistry::CheckerRegistry( 51 CheckerRegistryData &Data, ArrayRef<std::string> Plugins, 52 DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts, 53 ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns) 54 : Data(Data), Diags(Diags), AnOpts(AnOpts) { 55 56 // Register builtin checkers. 57 #define GET_CHECKERS 58 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ 59 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ 60 DOC_URI, IS_HIDDEN); 61 62 #define GET_PACKAGES 63 #define PACKAGE(FULLNAME) addPackage(FULLNAME); 64 65 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 66 #undef CHECKER 67 #undef GET_CHECKERS 68 #undef PACKAGE 69 #undef GET_PACKAGES 70 71 // Register checkers from plugins. 72 for (const std::string &Plugin : Plugins) { 73 // Get access to the plugin. 74 std::string ErrorMsg; 75 DynamicLibrary Lib = 76 DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg); 77 if (!Lib.isValid()) { 78 Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg; 79 continue; 80 } 81 82 // See if its compatible with this build of clang. 83 const char *PluginAPIVersion = static_cast<const char *>( 84 Lib.getAddressOfSymbol("clang_analyzerAPIVersionString")); 85 86 if (!isCompatibleAPIVersion(PluginAPIVersion)) { 87 Diags.Report(diag::warn_incompatible_analyzer_plugin_api) 88 << llvm::sys::path::filename(Plugin); 89 Diags.Report(diag::note_incompatible_analyzer_plugin_api) 90 << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion; 91 continue; 92 } 93 94 using RegisterPluginCheckerFn = void (*)(CheckerRegistry &); 95 // Register its checkers. 96 RegisterPluginCheckerFn RegisterPluginCheckers = 97 reinterpret_cast<RegisterPluginCheckerFn>( 98 Lib.getAddressOfSymbol("clang_registerCheckers")); 99 if (RegisterPluginCheckers) 100 RegisterPluginCheckers(*this); 101 } 102 103 // Register statically linked checkers, that aren't generated from the tblgen 104 // file, but rather passed their registry function as a parameter in 105 // checkerRegistrationFns. 106 107 for (const auto &Fn : CheckerRegistrationFns) 108 Fn(*this); 109 110 // Sort checkers for efficient collection. 111 // FIXME: Alphabetical sort puts 'experimental' in the middle. 112 // Would it be better to name it '~experimental' or something else 113 // that's ASCIIbetically last? 114 llvm::sort(Data.Packages, checker_registry::PackageNameLT{}); 115 llvm::sort(Data.Checkers, checker_registry::CheckerNameLT{}); 116 117 #define GET_CHECKER_DEPENDENCIES 118 119 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ 120 addDependency(FULLNAME, DEPENDENCY); 121 122 #define GET_CHECKER_WEAK_DEPENDENCIES 123 124 #define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY) \ 125 addWeakDependency(FULLNAME, DEPENDENCY); 126 127 #define GET_CHECKER_OPTIONS 128 #define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \ 129 DEVELOPMENT_STATUS, IS_HIDDEN) \ 130 addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \ 131 DEVELOPMENT_STATUS, IS_HIDDEN); 132 133 #define GET_PACKAGE_OPTIONS 134 #define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \ 135 DEVELOPMENT_STATUS, IS_HIDDEN) \ 136 addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \ 137 DEVELOPMENT_STATUS, IS_HIDDEN); 138 139 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 140 #undef CHECKER_DEPENDENCY 141 #undef GET_CHECKER_DEPENDENCIES 142 #undef CHECKER_WEAK_DEPENDENCY 143 #undef GET_CHECKER_WEAK_DEPENDENCIES 144 #undef CHECKER_OPTION 145 #undef GET_CHECKER_OPTIONS 146 #undef PACKAGE_OPTION 147 #undef GET_PACKAGE_OPTIONS 148 149 resolveDependencies<true>(); 150 resolveDependencies<false>(); 151 152 #ifndef NDEBUG 153 for (auto &DepPair : Data.Dependencies) { 154 for (auto &WeakDepPair : Data.WeakDependencies) { 155 // Some assertions to enforce that strong dependencies are relations in 156 // between purely modeling checkers, and weak dependencies are about 157 // diagnostics. 158 assert(WeakDepPair != DepPair && 159 "A checker cannot strong and weak depend on the same checker!"); 160 assert(WeakDepPair.first != DepPair.second && 161 "A strong dependency mustn't have weak dependencies!"); 162 assert(WeakDepPair.second != DepPair.second && 163 "A strong dependency mustn't be a weak dependency as well!"); 164 } 165 } 166 #endif 167 168 resolveCheckerAndPackageOptions(); 169 170 // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the 171 // command line. 172 for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) { 173 CheckerInfoListRange CheckerForCmdLineArg = 174 Data.getMutableCheckersForCmdLineArg(Opt.first); 175 176 if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) { 177 Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first; 178 Diags.Report(diag::note_suggest_disabling_all_checkers); 179 } 180 181 for (CheckerInfo &checker : CheckerForCmdLineArg) { 182 checker.State = Opt.second ? StateFromCmdLine::State_Enabled 183 : StateFromCmdLine::State_Disabled; 184 } 185 } 186 validateCheckerOptions(); 187 } 188 189 //===----------------------------------------------------------------------===// 190 // Dependency resolving. 191 //===----------------------------------------------------------------------===// 192 193 template <typename IsEnabledFn> 194 static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, 195 const CheckerManager &Mgr, 196 CheckerInfoSet &Ret, 197 IsEnabledFn IsEnabled); 198 199 /// Collects weak dependencies in \p enabledData.Checkers. 200 template <typename IsEnabledFn> 201 static void collectWeakDependencies(const ConstCheckerInfoList &Deps, 202 const CheckerManager &Mgr, 203 CheckerInfoSet &Ret, IsEnabledFn IsEnabled); 204 205 void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) { 206 // First, we calculate the list of enabled checkers as specified by the 207 // invocation. Weak dependencies will not enable their unspecified strong 208 // depenencies, but its only after resolving strong dependencies for all 209 // checkers when we know whether they will be enabled. 210 CheckerInfoSet Tmp; 211 auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) { 212 return !Checker->isDisabled(Mgr); 213 }; 214 for (const CheckerInfo &Checker : Data.Checkers) { 215 if (!Checker.isEnabled(Mgr)) 216 continue; 217 218 CheckerInfoSet Deps; 219 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps, 220 IsEnabledFromCmdLine)) { 221 // If we failed to enable any of the dependencies, don't enable this 222 // checker. 223 continue; 224 } 225 226 Tmp.insert(Deps.begin(), Deps.end()); 227 228 // Enable the checker. 229 Tmp.insert(&Checker); 230 } 231 232 // Calculate enabled checkers with the correct registration order. As this is 233 // done recursively, its arguably cheaper, but for sure less error prone to 234 // recalculate from scratch. 235 auto IsEnabled = [&](const CheckerInfo *Checker) { 236 return Tmp.contains(Checker); 237 }; 238 for (const CheckerInfo &Checker : Data.Checkers) { 239 if (!Checker.isEnabled(Mgr)) 240 continue; 241 242 CheckerInfoSet Deps; 243 244 collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled); 245 246 if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps, 247 IsEnabledFromCmdLine)) { 248 // If we failed to enable any of the dependencies, don't enable this 249 // checker. 250 continue; 251 } 252 253 // Note that set_union also preserves the order of insertion. 254 Data.EnabledCheckers.set_union(Deps); 255 Data.EnabledCheckers.insert(&Checker); 256 } 257 } 258 259 template <typename IsEnabledFn> 260 static bool collectStrongDependencies(const ConstCheckerInfoList &Deps, 261 const CheckerManager &Mgr, 262 CheckerInfoSet &Ret, 263 IsEnabledFn IsEnabled) { 264 265 for (const CheckerInfo *Dependency : Deps) { 266 if (!IsEnabled(Dependency)) 267 return false; 268 269 // Collect dependencies recursively. 270 if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret, 271 IsEnabled)) 272 return false; 273 Ret.insert(Dependency); 274 } 275 276 return true; 277 } 278 279 template <typename IsEnabledFn> 280 static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps, 281 const CheckerManager &Mgr, 282 CheckerInfoSet &Ret, 283 IsEnabledFn IsEnabled) { 284 285 for (const CheckerInfo *Dependency : WeakDeps) { 286 // Don't enable this checker if strong dependencies are unsatisfied, but 287 // assume that weak dependencies are transitive. 288 collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled); 289 290 if (IsEnabled(Dependency) && 291 collectStrongDependencies(Dependency->Dependencies, Mgr, Ret, 292 IsEnabled)) 293 Ret.insert(Dependency); 294 } 295 } 296 297 template <bool IsWeak> void CheckerRegistry::resolveDependencies() { 298 for (const std::pair<StringRef, StringRef> &Entry : 299 (IsWeak ? Data.WeakDependencies : Data.Dependencies)) { 300 301 auto CheckerIt = binaryFind(Data.Checkers, Entry.first); 302 assert(CheckerIt != Data.Checkers.end() && 303 CheckerIt->FullName == Entry.first && 304 "Failed to find the checker while attempting to set up its " 305 "dependencies!"); 306 307 auto DependencyIt = binaryFind(Data.Checkers, Entry.second); 308 assert(DependencyIt != Data.Checkers.end() && 309 DependencyIt->FullName == Entry.second && 310 "Failed to find the dependency of a checker!"); 311 312 // We do allow diagnostics from unit test/example dependency checkers. 313 assert((DependencyIt->FullName.starts_with("test") || 314 DependencyIt->FullName.starts_with("example") || IsWeak || 315 DependencyIt->IsHidden) && 316 "Strong dependencies are modeling checkers, and as such " 317 "non-user facing! Mark them hidden in Checkers.td!"); 318 319 if (IsWeak) 320 CheckerIt->WeakDependencies.emplace_back(&*DependencyIt); 321 else 322 CheckerIt->Dependencies.emplace_back(&*DependencyIt); 323 } 324 } 325 326 void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) { 327 Data.Dependencies.emplace_back(FullName, Dependency); 328 } 329 330 void CheckerRegistry::addWeakDependency(StringRef FullName, 331 StringRef Dependency) { 332 Data.WeakDependencies.emplace_back(FullName, Dependency); 333 } 334 335 //===----------------------------------------------------------------------===// 336 // Checker option resolving and validating. 337 //===----------------------------------------------------------------------===// 338 339 /// Insert the checker/package option to AnalyzerOptions' config table, and 340 /// validate it, if the user supplied it on the command line. 341 static void insertAndValidate(StringRef FullName, const CmdLineOption &Option, 342 AnalyzerOptions &AnOpts, 343 DiagnosticsEngine &Diags) { 344 345 std::string FullOption = (FullName + ":" + Option.OptionName).str(); 346 347 auto It = 348 AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)}); 349 350 // Insertation was successful -- CmdLineOption's constructor will validate 351 // whether values received from plugins or TableGen files are correct. 352 if (It.second) 353 return; 354 355 // Insertion failed, the user supplied this package/checker option on the 356 // command line. If the supplied value is invalid, we'll restore the option 357 // to it's default value, and if we're in non-compatibility mode, we'll also 358 // emit an error. 359 360 StringRef SuppliedValue = It.first->getValue(); 361 362 if (Option.OptionType == "bool") { 363 if (SuppliedValue != "true" && SuppliedValue != "false") { 364 if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { 365 Diags.Report(diag::err_analyzer_checker_option_invalid_input) 366 << FullOption << "a boolean value"; 367 } 368 369 It.first->setValue(std::string(Option.DefaultValStr)); 370 } 371 return; 372 } 373 374 if (Option.OptionType == "int") { 375 int Tmp; 376 bool HasFailed = SuppliedValue.getAsInteger(0, Tmp); 377 if (HasFailed) { 378 if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { 379 Diags.Report(diag::err_analyzer_checker_option_invalid_input) 380 << FullOption << "an integer value"; 381 } 382 383 It.first->setValue(std::string(Option.DefaultValStr)); 384 } 385 return; 386 } 387 } 388 389 template <class T> 390 static void insertOptionToCollection(StringRef FullName, T &Collection, 391 const CmdLineOption &Option, 392 AnalyzerOptions &AnOpts, 393 DiagnosticsEngine &Diags) { 394 auto It = binaryFind(Collection, FullName); 395 assert(It != Collection.end() && 396 "Failed to find the checker while attempting to add a command line " 397 "option to it!"); 398 399 insertAndValidate(FullName, Option, AnOpts, Diags); 400 401 It->CmdLineOptions.emplace_back(Option); 402 } 403 404 void CheckerRegistry::resolveCheckerAndPackageOptions() { 405 for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry : 406 Data.CheckerOptions) { 407 insertOptionToCollection(CheckerOptEntry.first, Data.Checkers, 408 CheckerOptEntry.second, AnOpts, Diags); 409 } 410 411 for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry : 412 Data.PackageOptions) { 413 insertOptionToCollection(PackageOptEntry.first, Data.Packages, 414 PackageOptEntry.second, AnOpts, Diags); 415 } 416 } 417 418 void CheckerRegistry::addPackage(StringRef FullName) { 419 Data.Packages.emplace_back(PackageInfo(FullName)); 420 } 421 422 void CheckerRegistry::addPackageOption(StringRef OptionType, 423 StringRef PackageFullName, 424 StringRef OptionName, 425 StringRef DefaultValStr, 426 StringRef Description, 427 StringRef DevelopmentStatus, 428 bool IsHidden) { 429 Data.PackageOptions.emplace_back( 430 PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, 431 Description, DevelopmentStatus, IsHidden}); 432 } 433 434 void CheckerRegistry::addChecker(RegisterCheckerFn Rfn, 435 ShouldRegisterFunction Sfn, StringRef Name, 436 StringRef Desc, StringRef DocsUri, 437 bool IsHidden) { 438 Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden); 439 440 // Record the presence of the checker in its packages. 441 StringRef PackageName, LeafName; 442 std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator); 443 while (!LeafName.empty()) { 444 Data.PackageSizes[PackageName] += 1; 445 std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator); 446 } 447 } 448 449 void CheckerRegistry::addCheckerOption(StringRef OptionType, 450 StringRef CheckerFullName, 451 StringRef OptionName, 452 StringRef DefaultValStr, 453 StringRef Description, 454 StringRef DevelopmentStatus, 455 bool IsHidden) { 456 Data.CheckerOptions.emplace_back( 457 CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, 458 Description, DevelopmentStatus, IsHidden}); 459 } 460 461 void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { 462 // Initialize the CheckerManager with all enabled checkers. 463 for (const auto *Checker : Data.EnabledCheckers) { 464 CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName)); 465 Checker->Initialize(CheckerMgr); 466 } 467 } 468 469 static void isOptionContainedIn(const CmdLineOptionList &OptionList, 470 StringRef SuppliedChecker, 471 StringRef SuppliedOption, 472 const AnalyzerOptions &AnOpts, 473 DiagnosticsEngine &Diags) { 474 475 if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue) 476 return; 477 478 auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) { 479 return Opt.OptionName == SuppliedOption; 480 }; 481 482 if (llvm::none_of(OptionList, SameOptName)) { 483 Diags.Report(diag::err_analyzer_checker_option_unknown) 484 << SuppliedChecker << SuppliedOption; 485 return; 486 } 487 } 488 489 void CheckerRegistry::validateCheckerOptions() const { 490 for (const auto &Config : AnOpts.Config) { 491 492 StringRef SuppliedCheckerOrPackage; 493 StringRef SuppliedOption; 494 std::tie(SuppliedCheckerOrPackage, SuppliedOption) = 495 Config.getKey().split(':'); 496 497 if (SuppliedOption.empty()) 498 continue; 499 500 // AnalyzerOptions' config table contains the user input, so an entry could 501 // look like this: 502 // 503 // cor:NoFalsePositives=true 504 // 505 // Since lower_bound would look for the first element *not less* than "cor", 506 // it would return with an iterator to the first checker in the core, so we 507 // we really have to use find here, which uses operator==. 508 auto CheckerIt = 509 llvm::find(Data.Checkers, CheckerInfo(SuppliedCheckerOrPackage)); 510 if (CheckerIt != Data.Checkers.end()) { 511 isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage, 512 SuppliedOption, AnOpts, Diags); 513 continue; 514 } 515 516 const auto *PackageIt = 517 llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage)); 518 if (PackageIt != Data.Packages.end()) { 519 isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage, 520 SuppliedOption, AnOpts, Diags); 521 continue; 522 } 523 524 Diags.Report(diag::err_unknown_analyzer_checker_or_package) 525 << SuppliedCheckerOrPackage; 526 } 527 } 528