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