xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
100b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h"
110b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
120b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
130b57cec5SDimitry Andric #include "clang/Frontend/FrontendDiagnostic.h"
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
170b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
180b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
190b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
200b57cec5SDimitry Andric #include "llvm/Support/DynamicLibrary.h"
210b57cec5SDimitry Andric #include "llvm/Support/Path.h"
220b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
230b57cec5SDimitry Andric #include <algorithm>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
275ffd83dbSDimitry Andric using namespace checker_registry;
280b57cec5SDimitry Andric using llvm::sys::DynamicLibrary;
290b57cec5SDimitry Andric 
305ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
315ffd83dbSDimitry Andric // Utilities.
325ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric static bool isCompatibleAPIVersion(const char *VersionString) {
350b57cec5SDimitry Andric   // If the version string is null, its not an analyzer plugin.
360b57cec5SDimitry Andric   if (!VersionString)
370b57cec5SDimitry Andric     return false;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric   // For now, none of the static analyzer API is considered stable.
400b57cec5SDimitry Andric   // Versions must match exactly.
410b57cec5SDimitry Andric   return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
420b57cec5SDimitry Andric }
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric static constexpr char PackageSeparator = '.';
450b57cec5SDimitry Andric 
465ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
475ffd83dbSDimitry Andric // Methods of CheckerRegistry.
485ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric CheckerRegistry::CheckerRegistry(
515ffd83dbSDimitry Andric     CheckerRegistryData &Data, ArrayRef<std::string> Plugins,
525ffd83dbSDimitry Andric     DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
530b57cec5SDimitry Andric     ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
545ffd83dbSDimitry Andric     : Data(Data), Diags(Diags), AnOpts(AnOpts) {
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   // Register builtin checkers.
570b57cec5SDimitry Andric #define GET_CHECKERS
580b57cec5SDimitry Andric #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN)                 \
590b57cec5SDimitry Andric   addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT,       \
600b57cec5SDimitry Andric              DOC_URI, IS_HIDDEN);
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric #define GET_PACKAGES
630b57cec5SDimitry Andric #define PACKAGE(FULLNAME) addPackage(FULLNAME);
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
660b57cec5SDimitry Andric #undef CHECKER
670b57cec5SDimitry Andric #undef GET_CHECKERS
680b57cec5SDimitry Andric #undef PACKAGE
690b57cec5SDimitry Andric #undef GET_PACKAGES
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   // Register checkers from plugins.
720b57cec5SDimitry Andric   for (const std::string &Plugin : Plugins) {
730b57cec5SDimitry Andric     // Get access to the plugin.
740b57cec5SDimitry Andric     std::string ErrorMsg;
750b57cec5SDimitry Andric     DynamicLibrary Lib =
760b57cec5SDimitry Andric         DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
770b57cec5SDimitry Andric     if (!Lib.isValid()) {
780b57cec5SDimitry Andric       Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
790b57cec5SDimitry Andric       continue;
800b57cec5SDimitry Andric     }
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric     // See if its compatible with this build of clang.
830b57cec5SDimitry Andric     const char *PluginAPIVersion = static_cast<const char *>(
840b57cec5SDimitry Andric         Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric     if (!isCompatibleAPIVersion(PluginAPIVersion)) {
870b57cec5SDimitry Andric       Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
880b57cec5SDimitry Andric           << llvm::sys::path::filename(Plugin);
890b57cec5SDimitry Andric       Diags.Report(diag::note_incompatible_analyzer_plugin_api)
900b57cec5SDimitry Andric           << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion;
910b57cec5SDimitry Andric       continue;
920b57cec5SDimitry Andric     }
930b57cec5SDimitry Andric 
945ffd83dbSDimitry Andric     using RegisterPluginCheckerFn = void (*)(CheckerRegistry &);
950b57cec5SDimitry Andric     // Register its checkers.
965ffd83dbSDimitry Andric     RegisterPluginCheckerFn RegisterPluginCheckers =
975ffd83dbSDimitry Andric         reinterpret_cast<RegisterPluginCheckerFn>(
980b57cec5SDimitry Andric             Lib.getAddressOfSymbol("clang_registerCheckers"));
990b57cec5SDimitry Andric     if (RegisterPluginCheckers)
1000b57cec5SDimitry Andric       RegisterPluginCheckers(*this);
1010b57cec5SDimitry Andric   }
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric   // Register statically linked checkers, that aren't generated from the tblgen
1040b57cec5SDimitry Andric   // file, but rather passed their registry function as a parameter in
1050b57cec5SDimitry Andric   // checkerRegistrationFns.
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   for (const auto &Fn : CheckerRegistrationFns)
1080b57cec5SDimitry Andric     Fn(*this);
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric   // Sort checkers for efficient collection.
1110b57cec5SDimitry Andric   // FIXME: Alphabetical sort puts 'experimental' in the middle.
1120b57cec5SDimitry Andric   // Would it be better to name it '~experimental' or something else
1130b57cec5SDimitry Andric   // that's ASCIIbetically last?
1145ffd83dbSDimitry Andric   llvm::sort(Data.Packages, checker_registry::PackageNameLT{});
1155ffd83dbSDimitry Andric   llvm::sort(Data.Checkers, checker_registry::CheckerNameLT{});
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric #define GET_CHECKER_DEPENDENCIES
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \
1200b57cec5SDimitry Andric   addDependency(FULLNAME, DEPENDENCY);
1210b57cec5SDimitry Andric 
1225ffd83dbSDimitry Andric #define GET_CHECKER_WEAK_DEPENDENCIES
1235ffd83dbSDimitry Andric 
1245ffd83dbSDimitry Andric #define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY)                          \
1255ffd83dbSDimitry Andric   addWeakDependency(FULLNAME, DEPENDENCY);
1265ffd83dbSDimitry Andric 
1270b57cec5SDimitry Andric #define GET_CHECKER_OPTIONS
1285ffd83dbSDimitry Andric #define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,             \
1295ffd83dbSDimitry Andric                        DEVELOPMENT_STATUS, IS_HIDDEN)                          \
1305ffd83dbSDimitry Andric   addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC,                 \
1315ffd83dbSDimitry Andric                    DEVELOPMENT_STATUS, IS_HIDDEN);
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric #define GET_PACKAGE_OPTIONS
1345ffd83dbSDimitry Andric #define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL,             \
1355ffd83dbSDimitry Andric                        DEVELOPMENT_STATUS, IS_HIDDEN)                          \
1365ffd83dbSDimitry Andric   addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC,                 \
1375ffd83dbSDimitry Andric                    DEVELOPMENT_STATUS, IS_HIDDEN);
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
1400b57cec5SDimitry Andric #undef CHECKER_DEPENDENCY
1410b57cec5SDimitry Andric #undef GET_CHECKER_DEPENDENCIES
1425ffd83dbSDimitry Andric #undef CHECKER_WEAK_DEPENDENCY
1435ffd83dbSDimitry Andric #undef GET_CHECKER_WEAK_DEPENDENCIES
1440b57cec5SDimitry Andric #undef CHECKER_OPTION
1450b57cec5SDimitry Andric #undef GET_CHECKER_OPTIONS
1460b57cec5SDimitry Andric #undef PACKAGE_OPTION
1470b57cec5SDimitry Andric #undef GET_PACKAGE_OPTIONS
1480b57cec5SDimitry Andric 
1495ffd83dbSDimitry Andric   resolveDependencies<true>();
1505ffd83dbSDimitry Andric   resolveDependencies<false>();
1515ffd83dbSDimitry Andric 
1525ffd83dbSDimitry Andric #ifndef NDEBUG
1535ffd83dbSDimitry Andric   for (auto &DepPair : Data.Dependencies) {
1545ffd83dbSDimitry Andric     for (auto &WeakDepPair : Data.WeakDependencies) {
1555ffd83dbSDimitry Andric       // Some assertions to enforce that strong dependencies are relations in
1565ffd83dbSDimitry Andric       // between purely modeling checkers, and weak dependencies are about
1575ffd83dbSDimitry Andric       // diagnostics.
1585ffd83dbSDimitry Andric       assert(WeakDepPair != DepPair &&
1595ffd83dbSDimitry Andric              "A checker cannot strong and weak depend on the same checker!");
1605ffd83dbSDimitry Andric       assert(WeakDepPair.first != DepPair.second &&
1615ffd83dbSDimitry Andric              "A strong dependency mustn't have weak dependencies!");
1625ffd83dbSDimitry Andric       assert(WeakDepPair.second != DepPair.second &&
1635ffd83dbSDimitry Andric              "A strong dependency mustn't be a weak dependency as well!");
1645ffd83dbSDimitry Andric     }
1655ffd83dbSDimitry Andric   }
1665ffd83dbSDimitry Andric #endif
1675ffd83dbSDimitry Andric 
1680b57cec5SDimitry Andric   resolveCheckerAndPackageOptions();
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
1710b57cec5SDimitry Andric   // command line.
172a7dea167SDimitry Andric   for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
1730b57cec5SDimitry Andric     CheckerInfoListRange CheckerForCmdLineArg =
1745ffd83dbSDimitry Andric         Data.getMutableCheckersForCmdLineArg(Opt.first);
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric     if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
177a7dea167SDimitry Andric       Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
1780b57cec5SDimitry Andric       Diags.Report(diag::note_suggest_disabling_all_checkers);
1790b57cec5SDimitry Andric     }
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric     for (CheckerInfo &checker : CheckerForCmdLineArg) {
1820b57cec5SDimitry Andric       checker.State = Opt.second ? StateFromCmdLine::State_Enabled
1830b57cec5SDimitry Andric                                  : StateFromCmdLine::State_Disabled;
1840b57cec5SDimitry Andric     }
1850b57cec5SDimitry Andric   }
1865ffd83dbSDimitry Andric   validateCheckerOptions();
1870b57cec5SDimitry Andric }
1880b57cec5SDimitry Andric 
1895ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
1905ffd83dbSDimitry Andric // Dependency resolving.
1915ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
1920b57cec5SDimitry Andric 
1935ffd83dbSDimitry Andric template <typename IsEnabledFn>
1945ffd83dbSDimitry Andric static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
1955ffd83dbSDimitry Andric                                       const CheckerManager &Mgr,
1965ffd83dbSDimitry Andric                                       CheckerInfoSet &Ret,
1975ffd83dbSDimitry Andric                                       IsEnabledFn IsEnabled);
1980b57cec5SDimitry Andric 
1995ffd83dbSDimitry Andric /// Collects weak dependencies in \p enabledData.Checkers.
2005ffd83dbSDimitry Andric template <typename IsEnabledFn>
2015ffd83dbSDimitry Andric static void collectWeakDependencies(const ConstCheckerInfoList &Deps,
2025ffd83dbSDimitry Andric                                     const CheckerManager &Mgr,
2035ffd83dbSDimitry Andric                                     CheckerInfoSet &Ret, IsEnabledFn IsEnabled);
2040b57cec5SDimitry Andric 
2055ffd83dbSDimitry Andric void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) {
2065ffd83dbSDimitry Andric   // First, we calculate the list of enabled checkers as specified by the
2075ffd83dbSDimitry Andric   // invocation. Weak dependencies will not enable their unspecified strong
2085ffd83dbSDimitry Andric   // depenencies, but its only after resolving strong dependencies for all
2095ffd83dbSDimitry Andric   // checkers when we know whether they will be enabled.
2105ffd83dbSDimitry Andric   CheckerInfoSet Tmp;
2115ffd83dbSDimitry Andric   auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) {
2125ffd83dbSDimitry Andric     return !Checker->isDisabled(Mgr);
2135ffd83dbSDimitry Andric   };
2145ffd83dbSDimitry Andric   for (const CheckerInfo &Checker : Data.Checkers) {
2155ffd83dbSDimitry Andric     if (!Checker.isEnabled(Mgr))
2160b57cec5SDimitry Andric       continue;
2170b57cec5SDimitry Andric 
2185ffd83dbSDimitry Andric     CheckerInfoSet Deps;
2195ffd83dbSDimitry Andric     if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
2205ffd83dbSDimitry Andric                                    IsEnabledFromCmdLine)) {
2215ffd83dbSDimitry Andric       // If we failed to enable any of the dependencies, don't enable this
2225ffd83dbSDimitry Andric       // checker.
2235ffd83dbSDimitry Andric       continue;
2245ffd83dbSDimitry Andric     }
2250b57cec5SDimitry Andric 
2265ffd83dbSDimitry Andric     Tmp.insert(Deps.begin(), Deps.end());
2275ffd83dbSDimitry Andric 
2285ffd83dbSDimitry Andric     // Enable the checker.
2295ffd83dbSDimitry Andric     Tmp.insert(&Checker);
2305ffd83dbSDimitry Andric   }
2315ffd83dbSDimitry Andric 
2325ffd83dbSDimitry Andric   // Calculate enabled checkers with the correct registration order. As this is
2335ffd83dbSDimitry Andric   // done recursively, its arguably cheaper, but for sure less error prone to
2345ffd83dbSDimitry Andric   // recalculate from scratch.
2355ffd83dbSDimitry Andric   auto IsEnabled = [&](const CheckerInfo *Checker) {
23606c3fb27SDimitry Andric     return Tmp.contains(Checker);
2375ffd83dbSDimitry Andric   };
2385ffd83dbSDimitry Andric   for (const CheckerInfo &Checker : Data.Checkers) {
2395ffd83dbSDimitry Andric     if (!Checker.isEnabled(Mgr))
2405ffd83dbSDimitry Andric       continue;
2415ffd83dbSDimitry Andric 
2425ffd83dbSDimitry Andric     CheckerInfoSet Deps;
2435ffd83dbSDimitry Andric 
2445ffd83dbSDimitry Andric     collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled);
2455ffd83dbSDimitry Andric 
2465ffd83dbSDimitry Andric     if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
2475ffd83dbSDimitry Andric                                    IsEnabledFromCmdLine)) {
2480b57cec5SDimitry Andric       // If we failed to enable any of the dependencies, don't enable this
2490b57cec5SDimitry Andric       // checker.
2500b57cec5SDimitry Andric       continue;
2510b57cec5SDimitry Andric     }
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric     // Note that set_union also preserves the order of insertion.
2545ffd83dbSDimitry Andric     Data.EnabledCheckers.set_union(Deps);
2555ffd83dbSDimitry Andric     Data.EnabledCheckers.insert(&Checker);
2565ffd83dbSDimitry Andric   }
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric 
2595ffd83dbSDimitry Andric template <typename IsEnabledFn>
2605ffd83dbSDimitry Andric static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
2615ffd83dbSDimitry Andric                                       const CheckerManager &Mgr,
2625ffd83dbSDimitry Andric                                       CheckerInfoSet &Ret,
2635ffd83dbSDimitry Andric                                       IsEnabledFn IsEnabled) {
2645ffd83dbSDimitry Andric 
2655ffd83dbSDimitry Andric   for (const CheckerInfo *Dependency : Deps) {
2665ffd83dbSDimitry Andric     if (!IsEnabled(Dependency))
2675ffd83dbSDimitry Andric       return false;
2685ffd83dbSDimitry Andric 
2695ffd83dbSDimitry Andric     // Collect dependencies recursively.
2705ffd83dbSDimitry Andric     if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
2715ffd83dbSDimitry Andric                                    IsEnabled))
2725ffd83dbSDimitry Andric       return false;
2735ffd83dbSDimitry Andric     Ret.insert(Dependency);
2740b57cec5SDimitry Andric   }
2750b57cec5SDimitry Andric 
2765ffd83dbSDimitry Andric   return true;
2775ffd83dbSDimitry Andric }
2785ffd83dbSDimitry Andric 
2795ffd83dbSDimitry Andric template <typename IsEnabledFn>
2805ffd83dbSDimitry Andric static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps,
2815ffd83dbSDimitry Andric                                     const CheckerManager &Mgr,
2825ffd83dbSDimitry Andric                                     CheckerInfoSet &Ret,
2835ffd83dbSDimitry Andric                                     IsEnabledFn IsEnabled) {
2845ffd83dbSDimitry Andric 
2855ffd83dbSDimitry Andric   for (const CheckerInfo *Dependency : WeakDeps) {
2865ffd83dbSDimitry Andric     // Don't enable this checker if strong dependencies are unsatisfied, but
2875ffd83dbSDimitry Andric     // assume that weak dependencies are transitive.
2885ffd83dbSDimitry Andric     collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled);
2895ffd83dbSDimitry Andric 
2905ffd83dbSDimitry Andric     if (IsEnabled(Dependency) &&
2915ffd83dbSDimitry Andric         collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
2925ffd83dbSDimitry Andric                                   IsEnabled))
2935ffd83dbSDimitry Andric       Ret.insert(Dependency);
2945ffd83dbSDimitry Andric   }
2955ffd83dbSDimitry Andric }
2965ffd83dbSDimitry Andric 
2975ffd83dbSDimitry Andric template <bool IsWeak> void CheckerRegistry::resolveDependencies() {
2985ffd83dbSDimitry Andric   for (const std::pair<StringRef, StringRef> &Entry :
2995ffd83dbSDimitry Andric        (IsWeak ? Data.WeakDependencies : Data.Dependencies)) {
3005ffd83dbSDimitry Andric 
3015ffd83dbSDimitry Andric     auto CheckerIt = binaryFind(Data.Checkers, Entry.first);
3025ffd83dbSDimitry Andric     assert(CheckerIt != Data.Checkers.end() &&
3035ffd83dbSDimitry Andric            CheckerIt->FullName == Entry.first &&
3040b57cec5SDimitry Andric            "Failed to find the checker while attempting to set up its "
3050b57cec5SDimitry Andric            "dependencies!");
3060b57cec5SDimitry Andric 
3075ffd83dbSDimitry Andric     auto DependencyIt = binaryFind(Data.Checkers, Entry.second);
3085ffd83dbSDimitry Andric     assert(DependencyIt != Data.Checkers.end() &&
3090b57cec5SDimitry Andric            DependencyIt->FullName == Entry.second &&
3100b57cec5SDimitry Andric            "Failed to find the dependency of a checker!");
3110b57cec5SDimitry Andric 
3125ffd83dbSDimitry Andric     // We do allow diagnostics from unit test/example dependency checkers.
313*5f757f3fSDimitry Andric     assert((DependencyIt->FullName.starts_with("test") ||
314*5f757f3fSDimitry Andric             DependencyIt->FullName.starts_with("example") || IsWeak ||
3155ffd83dbSDimitry Andric             DependencyIt->IsHidden) &&
3165ffd83dbSDimitry Andric            "Strong dependencies are modeling checkers, and as such "
3175ffd83dbSDimitry Andric            "non-user facing! Mark them hidden in Checkers.td!");
3185ffd83dbSDimitry Andric 
3195ffd83dbSDimitry Andric     if (IsWeak)
3205ffd83dbSDimitry Andric       CheckerIt->WeakDependencies.emplace_back(&*DependencyIt);
3215ffd83dbSDimitry Andric     else
3220b57cec5SDimitry Andric       CheckerIt->Dependencies.emplace_back(&*DependencyIt);
3230b57cec5SDimitry Andric   }
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
3275ffd83dbSDimitry Andric   Data.Dependencies.emplace_back(FullName, Dependency);
3280b57cec5SDimitry Andric }
3290b57cec5SDimitry Andric 
3305ffd83dbSDimitry Andric void CheckerRegistry::addWeakDependency(StringRef FullName,
3315ffd83dbSDimitry Andric                                         StringRef Dependency) {
3325ffd83dbSDimitry Andric   Data.WeakDependencies.emplace_back(FullName, Dependency);
3335ffd83dbSDimitry Andric }
3345ffd83dbSDimitry Andric 
3355ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
3365ffd83dbSDimitry Andric // Checker option resolving and validating.
3375ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
3385ffd83dbSDimitry Andric 
3390b57cec5SDimitry Andric /// Insert the checker/package option to AnalyzerOptions' config table, and
3400b57cec5SDimitry Andric /// validate it, if the user supplied it on the command line.
3415ffd83dbSDimitry Andric static void insertAndValidate(StringRef FullName, const CmdLineOption &Option,
3420b57cec5SDimitry Andric                               AnalyzerOptions &AnOpts,
3430b57cec5SDimitry Andric                               DiagnosticsEngine &Diags) {
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   std::string FullOption = (FullName + ":" + Option.OptionName).str();
3460b57cec5SDimitry Andric 
3475ffd83dbSDimitry Andric   auto It =
3485ffd83dbSDimitry Andric       AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)});
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   // Insertation was successful -- CmdLineOption's constructor will validate
3510b57cec5SDimitry Andric   // whether values received from plugins or TableGen files are correct.
3520b57cec5SDimitry Andric   if (It.second)
3530b57cec5SDimitry Andric     return;
3540b57cec5SDimitry Andric 
3550b57cec5SDimitry Andric   // Insertion failed, the user supplied this package/checker option on the
3560b57cec5SDimitry Andric   // command line. If the supplied value is invalid, we'll restore the option
3570b57cec5SDimitry Andric   // to it's default value, and if we're in non-compatibility mode, we'll also
3580b57cec5SDimitry Andric   // emit an error.
3590b57cec5SDimitry Andric 
3600b57cec5SDimitry Andric   StringRef SuppliedValue = It.first->getValue();
3610b57cec5SDimitry Andric 
3620b57cec5SDimitry Andric   if (Option.OptionType == "bool") {
3630b57cec5SDimitry Andric     if (SuppliedValue != "true" && SuppliedValue != "false") {
3640b57cec5SDimitry Andric       if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
3650b57cec5SDimitry Andric         Diags.Report(diag::err_analyzer_checker_option_invalid_input)
3660b57cec5SDimitry Andric             << FullOption << "a boolean value";
3670b57cec5SDimitry Andric       }
3680b57cec5SDimitry Andric 
3695ffd83dbSDimitry Andric       It.first->setValue(std::string(Option.DefaultValStr));
3700b57cec5SDimitry Andric     }
3710b57cec5SDimitry Andric     return;
3720b57cec5SDimitry Andric   }
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric   if (Option.OptionType == "int") {
3750b57cec5SDimitry Andric     int Tmp;
3760b57cec5SDimitry Andric     bool HasFailed = SuppliedValue.getAsInteger(0, Tmp);
3770b57cec5SDimitry Andric     if (HasFailed) {
3780b57cec5SDimitry Andric       if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
3790b57cec5SDimitry Andric         Diags.Report(diag::err_analyzer_checker_option_invalid_input)
3800b57cec5SDimitry Andric             << FullOption << "an integer value";
3810b57cec5SDimitry Andric       }
3820b57cec5SDimitry Andric 
3835ffd83dbSDimitry Andric       It.first->setValue(std::string(Option.DefaultValStr));
3840b57cec5SDimitry Andric     }
3850b57cec5SDimitry Andric     return;
3860b57cec5SDimitry Andric   }
3870b57cec5SDimitry Andric }
3880b57cec5SDimitry Andric 
3890b57cec5SDimitry Andric template <class T>
3905ffd83dbSDimitry Andric static void insertOptionToCollection(StringRef FullName, T &Collection,
3915ffd83dbSDimitry Andric                                      const CmdLineOption &Option,
3925ffd83dbSDimitry Andric                                      AnalyzerOptions &AnOpts,
3935ffd83dbSDimitry Andric                                      DiagnosticsEngine &Diags) {
3940b57cec5SDimitry Andric   auto It = binaryFind(Collection, FullName);
3950b57cec5SDimitry Andric   assert(It != Collection.end() &&
3960b57cec5SDimitry Andric          "Failed to find the checker while attempting to add a command line "
3970b57cec5SDimitry Andric          "option to it!");
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric   insertAndValidate(FullName, Option, AnOpts, Diags);
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric   It->CmdLineOptions.emplace_back(Option);
4020b57cec5SDimitry Andric }
4030b57cec5SDimitry Andric 
4040b57cec5SDimitry Andric void CheckerRegistry::resolveCheckerAndPackageOptions() {
4050b57cec5SDimitry Andric   for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
4065ffd83dbSDimitry Andric        Data.CheckerOptions) {
4075ffd83dbSDimitry Andric     insertOptionToCollection(CheckerOptEntry.first, Data.Checkers,
4080b57cec5SDimitry Andric                              CheckerOptEntry.second, AnOpts, Diags);
4090b57cec5SDimitry Andric   }
4100b57cec5SDimitry Andric 
4110b57cec5SDimitry Andric   for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
4125ffd83dbSDimitry Andric        Data.PackageOptions) {
4135ffd83dbSDimitry Andric     insertOptionToCollection(PackageOptEntry.first, Data.Packages,
4140b57cec5SDimitry Andric                              PackageOptEntry.second, AnOpts, Diags);
4150b57cec5SDimitry Andric   }
4160b57cec5SDimitry Andric }
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric void CheckerRegistry::addPackage(StringRef FullName) {
4195ffd83dbSDimitry Andric   Data.Packages.emplace_back(PackageInfo(FullName));
4200b57cec5SDimitry Andric }
4210b57cec5SDimitry Andric 
4220b57cec5SDimitry Andric void CheckerRegistry::addPackageOption(StringRef OptionType,
4230b57cec5SDimitry Andric                                        StringRef PackageFullName,
4240b57cec5SDimitry Andric                                        StringRef OptionName,
4250b57cec5SDimitry Andric                                        StringRef DefaultValStr,
4260b57cec5SDimitry Andric                                        StringRef Description,
4270b57cec5SDimitry Andric                                        StringRef DevelopmentStatus,
4280b57cec5SDimitry Andric                                        bool IsHidden) {
4295ffd83dbSDimitry Andric   Data.PackageOptions.emplace_back(
4300b57cec5SDimitry Andric       PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
4310b57cec5SDimitry Andric                                      Description, DevelopmentStatus, IsHidden});
4320b57cec5SDimitry Andric }
4330b57cec5SDimitry Andric 
4345ffd83dbSDimitry Andric void CheckerRegistry::addChecker(RegisterCheckerFn Rfn,
4350b57cec5SDimitry Andric                                  ShouldRegisterFunction Sfn, StringRef Name,
4360b57cec5SDimitry Andric                                  StringRef Desc, StringRef DocsUri,
4370b57cec5SDimitry Andric                                  bool IsHidden) {
4385ffd83dbSDimitry Andric   Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
4390b57cec5SDimitry Andric 
4400b57cec5SDimitry Andric   // Record the presence of the checker in its packages.
4410b57cec5SDimitry Andric   StringRef PackageName, LeafName;
4420b57cec5SDimitry Andric   std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
4430b57cec5SDimitry Andric   while (!LeafName.empty()) {
4445ffd83dbSDimitry Andric     Data.PackageSizes[PackageName] += 1;
4450b57cec5SDimitry Andric     std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
4460b57cec5SDimitry Andric   }
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric 
4490b57cec5SDimitry Andric void CheckerRegistry::addCheckerOption(StringRef OptionType,
4500b57cec5SDimitry Andric                                        StringRef CheckerFullName,
4510b57cec5SDimitry Andric                                        StringRef OptionName,
4520b57cec5SDimitry Andric                                        StringRef DefaultValStr,
4530b57cec5SDimitry Andric                                        StringRef Description,
4540b57cec5SDimitry Andric                                        StringRef DevelopmentStatus,
4550b57cec5SDimitry Andric                                        bool IsHidden) {
4565ffd83dbSDimitry Andric   Data.CheckerOptions.emplace_back(
4570b57cec5SDimitry Andric       CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
4580b57cec5SDimitry Andric                                      Description, DevelopmentStatus, IsHidden});
4590b57cec5SDimitry Andric }
4600b57cec5SDimitry Andric 
4610b57cec5SDimitry Andric void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
4620b57cec5SDimitry Andric   // Initialize the CheckerManager with all enabled checkers.
4635ffd83dbSDimitry Andric   for (const auto *Checker : Data.EnabledCheckers) {
464a7dea167SDimitry Andric     CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
4650b57cec5SDimitry Andric     Checker->Initialize(CheckerMgr);
4660b57cec5SDimitry Andric   }
4670b57cec5SDimitry Andric }
4680b57cec5SDimitry Andric 
4695ffd83dbSDimitry Andric static void isOptionContainedIn(const CmdLineOptionList &OptionList,
4705ffd83dbSDimitry Andric                                 StringRef SuppliedChecker,
4715ffd83dbSDimitry Andric                                 StringRef SuppliedOption,
4725ffd83dbSDimitry Andric                                 const AnalyzerOptions &AnOpts,
4735ffd83dbSDimitry Andric                                 DiagnosticsEngine &Diags) {
4740b57cec5SDimitry Andric 
4750b57cec5SDimitry Andric   if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue)
4760b57cec5SDimitry Andric     return;
4770b57cec5SDimitry Andric 
4780b57cec5SDimitry Andric   auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {
4790b57cec5SDimitry Andric     return Opt.OptionName == SuppliedOption;
4800b57cec5SDimitry Andric   };
4810b57cec5SDimitry Andric 
48281ad6265SDimitry Andric   if (llvm::none_of(OptionList, SameOptName)) {
4830b57cec5SDimitry Andric     Diags.Report(diag::err_analyzer_checker_option_unknown)
4840b57cec5SDimitry Andric         << SuppliedChecker << SuppliedOption;
4850b57cec5SDimitry Andric     return;
4860b57cec5SDimitry Andric   }
4870b57cec5SDimitry Andric }
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric void CheckerRegistry::validateCheckerOptions() const {
4900b57cec5SDimitry Andric   for (const auto &Config : AnOpts.Config) {
4910b57cec5SDimitry Andric 
492a7dea167SDimitry Andric     StringRef SuppliedCheckerOrPackage;
4930b57cec5SDimitry Andric     StringRef SuppliedOption;
494a7dea167SDimitry Andric     std::tie(SuppliedCheckerOrPackage, SuppliedOption) =
495a7dea167SDimitry Andric         Config.getKey().split(':');
4960b57cec5SDimitry Andric 
4970b57cec5SDimitry Andric     if (SuppliedOption.empty())
4980b57cec5SDimitry Andric       continue;
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric     // AnalyzerOptions' config table contains the user input, so an entry could
5010b57cec5SDimitry Andric     // look like this:
5020b57cec5SDimitry Andric     //
5030b57cec5SDimitry Andric     //   cor:NoFalsePositives=true
5040b57cec5SDimitry Andric     //
5050b57cec5SDimitry Andric     // Since lower_bound would look for the first element *not less* than "cor",
5060b57cec5SDimitry Andric     // it would return with an iterator to the first checker in the core, so we
5070b57cec5SDimitry Andric     // we really have to use find here, which uses operator==.
508a7dea167SDimitry Andric     auto CheckerIt =
5095ffd83dbSDimitry Andric         llvm::find(Data.Checkers, CheckerInfo(SuppliedCheckerOrPackage));
5105ffd83dbSDimitry Andric     if (CheckerIt != Data.Checkers.end()) {
511a7dea167SDimitry Andric       isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
5120b57cec5SDimitry Andric                           SuppliedOption, AnOpts, Diags);
5130b57cec5SDimitry Andric       continue;
5140b57cec5SDimitry Andric     }
5150b57cec5SDimitry Andric 
5165ffd83dbSDimitry Andric     const auto *PackageIt =
5175ffd83dbSDimitry Andric         llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage));
5185ffd83dbSDimitry Andric     if (PackageIt != Data.Packages.end()) {
519a7dea167SDimitry Andric       isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
5200b57cec5SDimitry Andric                           SuppliedOption, AnOpts, Diags);
5210b57cec5SDimitry Andric       continue;
5220b57cec5SDimitry Andric     }
5230b57cec5SDimitry Andric 
524a7dea167SDimitry Andric     Diags.Report(diag::err_unknown_analyzer_checker_or_package)
525a7dea167SDimitry Andric         << SuppliedCheckerOrPackage;
5260b57cec5SDimitry Andric   }
5270b57cec5SDimitry Andric }
528