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