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
isCompatibleAPIVersion(const char * VersionString)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
CheckerRegistry(CheckerRegistryData & Data,ArrayRef<std::string> Plugins,DiagnosticsEngine & Diags,AnalyzerOptions & AnOpts,ArrayRef<std::function<void (CheckerRegistry &)>> CheckerRegistrationFns)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
initializeRegistry(const CheckerManager & Mgr)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>
collectStrongDependencies(const ConstCheckerInfoList & Deps,const CheckerManager & Mgr,CheckerInfoSet & Ret,IsEnabledFn IsEnabled)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>
collectWeakDependencies(const ConstCheckerInfoList & WeakDeps,const CheckerManager & Mgr,CheckerInfoSet & Ret,IsEnabledFn IsEnabled)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
resolveDependencies()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
addDependency(StringRef FullName,StringRef Dependency)3260b57cec5SDimitry Andric void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
3275ffd83dbSDimitry Andric Data.Dependencies.emplace_back(FullName, Dependency);
3280b57cec5SDimitry Andric }
3290b57cec5SDimitry Andric
addWeakDependency(StringRef FullName,StringRef Dependency)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.
insertAndValidate(StringRef FullName,const CmdLineOption & Option,AnalyzerOptions & AnOpts,DiagnosticsEngine & Diags)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>
insertOptionToCollection(StringRef FullName,T & Collection,const CmdLineOption & Option,AnalyzerOptions & AnOpts,DiagnosticsEngine & Diags)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
resolveCheckerAndPackageOptions()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
addPackage(StringRef FullName)4180b57cec5SDimitry Andric void CheckerRegistry::addPackage(StringRef FullName) {
4195ffd83dbSDimitry Andric Data.Packages.emplace_back(PackageInfo(FullName));
4200b57cec5SDimitry Andric }
4210b57cec5SDimitry Andric
addPackageOption(StringRef OptionType,StringRef PackageFullName,StringRef OptionName,StringRef DefaultValStr,StringRef Description,StringRef DevelopmentStatus,bool IsHidden)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
addChecker(RegisterCheckerFn Rfn,ShouldRegisterFunction Sfn,StringRef Name,StringRef Desc,StringRef DocsUri,bool IsHidden)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
addCheckerOption(StringRef OptionType,StringRef CheckerFullName,StringRef OptionName,StringRef DefaultValStr,StringRef Description,StringRef DevelopmentStatus,bool IsHidden)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
initializeManager(CheckerManager & CheckerMgr) const4610b57cec5SDimitry 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
isOptionContainedIn(const CmdLineOptionList & OptionList,StringRef SuppliedChecker,StringRef SuppliedOption,const AnalyzerOptions & AnOpts,DiagnosticsEngine & Diags)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
validateCheckerOptions() const4890b57cec5SDimitry 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