1*0b57cec5SDimitry Andric //===- CheckerRegistry.cpp - Maintains all available checkers -------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" 10*0b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h" 11*0b57cec5SDimitry Andric #include "clang/Basic/LLVM.h" 12*0b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 13*0b57cec5SDimitry Andric #include "clang/Frontend/FrontendDiagnostic.h" 14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 16*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 17*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 18*0b57cec5SDimitry Andric #include "llvm/ADT/SetVector.h" 19*0b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h" 20*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 21*0b57cec5SDimitry Andric #include "llvm/Support/DynamicLibrary.h" 22*0b57cec5SDimitry Andric #include "llvm/Support/Path.h" 23*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 24*0b57cec5SDimitry Andric #include <algorithm> 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric using namespace clang; 27*0b57cec5SDimitry Andric using namespace ento; 28*0b57cec5SDimitry Andric using llvm::sys::DynamicLibrary; 29*0b57cec5SDimitry Andric 30*0b57cec5SDimitry Andric using RegisterCheckersFn = void (*)(CheckerRegistry &); 31*0b57cec5SDimitry Andric 32*0b57cec5SDimitry Andric static bool isCompatibleAPIVersion(const char *VersionString) { 33*0b57cec5SDimitry Andric // If the version string is null, its not an analyzer plugin. 34*0b57cec5SDimitry Andric if (!VersionString) 35*0b57cec5SDimitry Andric return false; 36*0b57cec5SDimitry Andric 37*0b57cec5SDimitry Andric // For now, none of the static analyzer API is considered stable. 38*0b57cec5SDimitry Andric // Versions must match exactly. 39*0b57cec5SDimitry Andric return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; 40*0b57cec5SDimitry Andric } 41*0b57cec5SDimitry Andric 42*0b57cec5SDimitry Andric namespace { 43*0b57cec5SDimitry Andric template <class T> struct FullNameLT { 44*0b57cec5SDimitry Andric bool operator()(const T &Lhs, const T &Rhs) { 45*0b57cec5SDimitry Andric return Lhs.FullName < Rhs.FullName; 46*0b57cec5SDimitry Andric } 47*0b57cec5SDimitry Andric }; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>; 50*0b57cec5SDimitry Andric using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>; 51*0b57cec5SDimitry Andric } // end of anonymous namespace 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric template <class CheckerOrPackageInfoList> 54*0b57cec5SDimitry Andric static 55*0b57cec5SDimitry Andric typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value, 56*0b57cec5SDimitry Andric typename CheckerOrPackageInfoList::const_iterator, 57*0b57cec5SDimitry Andric typename CheckerOrPackageInfoList::iterator>::type 58*0b57cec5SDimitry Andric binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) { 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type; 61*0b57cec5SDimitry Andric using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>; 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric assert(std::is_sorted(Collection.begin(), Collection.end(), 64*0b57cec5SDimitry Andric CheckerOrPackageFullNameLT{}) && 65*0b57cec5SDimitry Andric "In order to efficiently gather checkers/packages, this function " 66*0b57cec5SDimitry Andric "expects them to be already sorted!"); 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric return llvm::lower_bound(Collection, CheckerOrPackage(FullName), 69*0b57cec5SDimitry Andric CheckerOrPackageFullNameLT{}); 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric static constexpr char PackageSeparator = '.'; 73*0b57cec5SDimitry Andric 74*0b57cec5SDimitry Andric static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker, 75*0b57cec5SDimitry Andric StringRef PackageName) { 76*0b57cec5SDimitry Andric // Does the checker's full name have the package as a prefix? 77*0b57cec5SDimitry Andric if (!Checker.FullName.startswith(PackageName)) 78*0b57cec5SDimitry Andric return false; 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric // Is the package actually just the name of a specific checker? 81*0b57cec5SDimitry Andric if (Checker.FullName.size() == PackageName.size()) 82*0b57cec5SDimitry Andric return true; 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric // Is the checker in the package (or a subpackage)? 85*0b57cec5SDimitry Andric if (Checker.FullName[PackageName.size()] == PackageSeparator) 86*0b57cec5SDimitry Andric return true; 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric return false; 89*0b57cec5SDimitry Andric } 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric CheckerRegistry::CheckerInfoListRange 92*0b57cec5SDimitry Andric CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { 93*0b57cec5SDimitry Andric auto It = binaryFind(Checkers, CmdLineArg); 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric if (!isInPackage(*It, CmdLineArg)) 96*0b57cec5SDimitry Andric return {Checkers.end(), Checkers.end()}; 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric // See how large the package is. 99*0b57cec5SDimitry Andric // If the package doesn't exist, assume the option refers to a single 100*0b57cec5SDimitry Andric // checker. 101*0b57cec5SDimitry Andric size_t Size = 1; 102*0b57cec5SDimitry Andric llvm::StringMap<size_t>::const_iterator PackageSize = 103*0b57cec5SDimitry Andric PackageSizes.find(CmdLineArg); 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric if (PackageSize != PackageSizes.end()) 106*0b57cec5SDimitry Andric Size = PackageSize->getValue(); 107*0b57cec5SDimitry Andric 108*0b57cec5SDimitry Andric return {It, It + Size}; 109*0b57cec5SDimitry Andric } 110*0b57cec5SDimitry Andric 111*0b57cec5SDimitry Andric CheckerRegistry::CheckerRegistry( 112*0b57cec5SDimitry Andric ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags, 113*0b57cec5SDimitry Andric AnalyzerOptions &AnOpts, const LangOptions &LangOpts, 114*0b57cec5SDimitry Andric ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns) 115*0b57cec5SDimitry Andric : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) { 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric // Register builtin checkers. 118*0b57cec5SDimitry Andric #define GET_CHECKERS 119*0b57cec5SDimitry Andric #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ 120*0b57cec5SDimitry Andric addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ 121*0b57cec5SDimitry Andric DOC_URI, IS_HIDDEN); 122*0b57cec5SDimitry Andric 123*0b57cec5SDimitry Andric #define GET_PACKAGES 124*0b57cec5SDimitry Andric #define PACKAGE(FULLNAME) addPackage(FULLNAME); 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 127*0b57cec5SDimitry Andric #undef CHECKER 128*0b57cec5SDimitry Andric #undef GET_CHECKERS 129*0b57cec5SDimitry Andric #undef PACKAGE 130*0b57cec5SDimitry Andric #undef GET_PACKAGES 131*0b57cec5SDimitry Andric 132*0b57cec5SDimitry Andric // Register checkers from plugins. 133*0b57cec5SDimitry Andric for (const std::string &Plugin : Plugins) { 134*0b57cec5SDimitry Andric // Get access to the plugin. 135*0b57cec5SDimitry Andric std::string ErrorMsg; 136*0b57cec5SDimitry Andric DynamicLibrary Lib = 137*0b57cec5SDimitry Andric DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg); 138*0b57cec5SDimitry Andric if (!Lib.isValid()) { 139*0b57cec5SDimitry Andric Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg; 140*0b57cec5SDimitry Andric continue; 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric 143*0b57cec5SDimitry Andric // See if its compatible with this build of clang. 144*0b57cec5SDimitry Andric const char *PluginAPIVersion = static_cast<const char *>( 145*0b57cec5SDimitry Andric Lib.getAddressOfSymbol("clang_analyzerAPIVersionString")); 146*0b57cec5SDimitry Andric 147*0b57cec5SDimitry Andric if (!isCompatibleAPIVersion(PluginAPIVersion)) { 148*0b57cec5SDimitry Andric Diags.Report(diag::warn_incompatible_analyzer_plugin_api) 149*0b57cec5SDimitry Andric << llvm::sys::path::filename(Plugin); 150*0b57cec5SDimitry Andric Diags.Report(diag::note_incompatible_analyzer_plugin_api) 151*0b57cec5SDimitry Andric << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion; 152*0b57cec5SDimitry Andric continue; 153*0b57cec5SDimitry Andric } 154*0b57cec5SDimitry Andric 155*0b57cec5SDimitry Andric // Register its checkers. 156*0b57cec5SDimitry Andric RegisterCheckersFn RegisterPluginCheckers = 157*0b57cec5SDimitry Andric reinterpret_cast<RegisterCheckersFn>( 158*0b57cec5SDimitry Andric Lib.getAddressOfSymbol("clang_registerCheckers")); 159*0b57cec5SDimitry Andric if (RegisterPluginCheckers) 160*0b57cec5SDimitry Andric RegisterPluginCheckers(*this); 161*0b57cec5SDimitry Andric } 162*0b57cec5SDimitry Andric 163*0b57cec5SDimitry Andric // Register statically linked checkers, that aren't generated from the tblgen 164*0b57cec5SDimitry Andric // file, but rather passed their registry function as a parameter in 165*0b57cec5SDimitry Andric // checkerRegistrationFns. 166*0b57cec5SDimitry Andric 167*0b57cec5SDimitry Andric for (const auto &Fn : CheckerRegistrationFns) 168*0b57cec5SDimitry Andric Fn(*this); 169*0b57cec5SDimitry Andric 170*0b57cec5SDimitry Andric // Sort checkers for efficient collection. 171*0b57cec5SDimitry Andric // FIXME: Alphabetical sort puts 'experimental' in the middle. 172*0b57cec5SDimitry Andric // Would it be better to name it '~experimental' or something else 173*0b57cec5SDimitry Andric // that's ASCIIbetically last? 174*0b57cec5SDimitry Andric llvm::sort(Packages, PackageNameLT{}); 175*0b57cec5SDimitry Andric llvm::sort(Checkers, CheckerNameLT{}); 176*0b57cec5SDimitry Andric 177*0b57cec5SDimitry Andric #define GET_CHECKER_DEPENDENCIES 178*0b57cec5SDimitry Andric 179*0b57cec5SDimitry Andric #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ 180*0b57cec5SDimitry Andric addDependency(FULLNAME, DEPENDENCY); 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric #define GET_CHECKER_OPTIONS 183*0b57cec5SDimitry Andric #define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ 184*0b57cec5SDimitry Andric addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); 185*0b57cec5SDimitry Andric 186*0b57cec5SDimitry Andric #define GET_PACKAGE_OPTIONS 187*0b57cec5SDimitry Andric #define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ 188*0b57cec5SDimitry Andric addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); 189*0b57cec5SDimitry Andric 190*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 191*0b57cec5SDimitry Andric #undef CHECKER_DEPENDENCY 192*0b57cec5SDimitry Andric #undef GET_CHECKER_DEPENDENCIES 193*0b57cec5SDimitry Andric #undef CHECKER_OPTION 194*0b57cec5SDimitry Andric #undef GET_CHECKER_OPTIONS 195*0b57cec5SDimitry Andric #undef PACKAGE_OPTION 196*0b57cec5SDimitry Andric #undef GET_PACKAGE_OPTIONS 197*0b57cec5SDimitry Andric 198*0b57cec5SDimitry Andric resolveDependencies(); 199*0b57cec5SDimitry Andric resolveCheckerAndPackageOptions(); 200*0b57cec5SDimitry Andric 201*0b57cec5SDimitry Andric // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the 202*0b57cec5SDimitry Andric // command line. 203*0b57cec5SDimitry Andric for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) { 204*0b57cec5SDimitry Andric CheckerInfoListRange CheckerForCmdLineArg = 205*0b57cec5SDimitry Andric getMutableCheckersForCmdLineArg(Opt.first); 206*0b57cec5SDimitry Andric 207*0b57cec5SDimitry Andric if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) { 208*0b57cec5SDimitry Andric Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first; 209*0b57cec5SDimitry Andric Diags.Report(diag::note_suggest_disabling_all_checkers); 210*0b57cec5SDimitry Andric } 211*0b57cec5SDimitry Andric 212*0b57cec5SDimitry Andric for (CheckerInfo &checker : CheckerForCmdLineArg) { 213*0b57cec5SDimitry Andric checker.State = Opt.second ? StateFromCmdLine::State_Enabled 214*0b57cec5SDimitry Andric : StateFromCmdLine::State_Disabled; 215*0b57cec5SDimitry Andric } 216*0b57cec5SDimitry Andric } 217*0b57cec5SDimitry Andric } 218*0b57cec5SDimitry Andric 219*0b57cec5SDimitry Andric /// Collects dependencies in \p ret, returns false on failure. 220*0b57cec5SDimitry Andric static bool 221*0b57cec5SDimitry Andric collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, 222*0b57cec5SDimitry Andric const LangOptions &LO, 223*0b57cec5SDimitry Andric CheckerRegistry::CheckerInfoSet &Ret); 224*0b57cec5SDimitry Andric 225*0b57cec5SDimitry Andric /// Collects dependenies in \p enabledCheckers. Return None on failure. 226*0b57cec5SDimitry Andric LLVM_NODISCARD 227*0b57cec5SDimitry Andric static llvm::Optional<CheckerRegistry::CheckerInfoSet> 228*0b57cec5SDimitry Andric collectDependencies(const CheckerRegistry::CheckerInfo &checker, 229*0b57cec5SDimitry Andric const LangOptions &LO) { 230*0b57cec5SDimitry Andric 231*0b57cec5SDimitry Andric CheckerRegistry::CheckerInfoSet Ret; 232*0b57cec5SDimitry Andric // Add dependencies to the enabled checkers only if all of them can be 233*0b57cec5SDimitry Andric // enabled. 234*0b57cec5SDimitry Andric if (!collectDependenciesImpl(checker.Dependencies, LO, Ret)) 235*0b57cec5SDimitry Andric return None; 236*0b57cec5SDimitry Andric 237*0b57cec5SDimitry Andric return Ret; 238*0b57cec5SDimitry Andric } 239*0b57cec5SDimitry Andric 240*0b57cec5SDimitry Andric static bool 241*0b57cec5SDimitry Andric collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps, 242*0b57cec5SDimitry Andric const LangOptions &LO, 243*0b57cec5SDimitry Andric CheckerRegistry::CheckerInfoSet &Ret) { 244*0b57cec5SDimitry Andric 245*0b57cec5SDimitry Andric for (const CheckerRegistry::CheckerInfo *Dependency : Deps) { 246*0b57cec5SDimitry Andric 247*0b57cec5SDimitry Andric if (Dependency->isDisabled(LO)) 248*0b57cec5SDimitry Andric return false; 249*0b57cec5SDimitry Andric 250*0b57cec5SDimitry Andric // Collect dependencies recursively. 251*0b57cec5SDimitry Andric if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret)) 252*0b57cec5SDimitry Andric return false; 253*0b57cec5SDimitry Andric 254*0b57cec5SDimitry Andric Ret.insert(Dependency); 255*0b57cec5SDimitry Andric } 256*0b57cec5SDimitry Andric 257*0b57cec5SDimitry Andric return true; 258*0b57cec5SDimitry Andric } 259*0b57cec5SDimitry Andric 260*0b57cec5SDimitry Andric CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { 261*0b57cec5SDimitry Andric 262*0b57cec5SDimitry Andric CheckerInfoSet EnabledCheckers; 263*0b57cec5SDimitry Andric 264*0b57cec5SDimitry Andric for (const CheckerInfo &Checker : Checkers) { 265*0b57cec5SDimitry Andric if (!Checker.isEnabled(LangOpts)) 266*0b57cec5SDimitry Andric continue; 267*0b57cec5SDimitry Andric 268*0b57cec5SDimitry Andric // Recursively enable its dependencies. 269*0b57cec5SDimitry Andric llvm::Optional<CheckerInfoSet> Deps = 270*0b57cec5SDimitry Andric collectDependencies(Checker, LangOpts); 271*0b57cec5SDimitry Andric 272*0b57cec5SDimitry Andric if (!Deps) { 273*0b57cec5SDimitry Andric // If we failed to enable any of the dependencies, don't enable this 274*0b57cec5SDimitry Andric // checker. 275*0b57cec5SDimitry Andric continue; 276*0b57cec5SDimitry Andric } 277*0b57cec5SDimitry Andric 278*0b57cec5SDimitry Andric // Note that set_union also preserves the order of insertion. 279*0b57cec5SDimitry Andric EnabledCheckers.set_union(*Deps); 280*0b57cec5SDimitry Andric 281*0b57cec5SDimitry Andric // Enable the checker. 282*0b57cec5SDimitry Andric EnabledCheckers.insert(&Checker); 283*0b57cec5SDimitry Andric } 284*0b57cec5SDimitry Andric 285*0b57cec5SDimitry Andric return EnabledCheckers; 286*0b57cec5SDimitry Andric } 287*0b57cec5SDimitry Andric 288*0b57cec5SDimitry Andric void CheckerRegistry::resolveDependencies() { 289*0b57cec5SDimitry Andric for (const std::pair<StringRef, StringRef> &Entry : Dependencies) { 290*0b57cec5SDimitry Andric auto CheckerIt = binaryFind(Checkers, Entry.first); 291*0b57cec5SDimitry Andric assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first && 292*0b57cec5SDimitry Andric "Failed to find the checker while attempting to set up its " 293*0b57cec5SDimitry Andric "dependencies!"); 294*0b57cec5SDimitry Andric 295*0b57cec5SDimitry Andric auto DependencyIt = binaryFind(Checkers, Entry.second); 296*0b57cec5SDimitry Andric assert(DependencyIt != Checkers.end() && 297*0b57cec5SDimitry Andric DependencyIt->FullName == Entry.second && 298*0b57cec5SDimitry Andric "Failed to find the dependency of a checker!"); 299*0b57cec5SDimitry Andric 300*0b57cec5SDimitry Andric CheckerIt->Dependencies.emplace_back(&*DependencyIt); 301*0b57cec5SDimitry Andric } 302*0b57cec5SDimitry Andric 303*0b57cec5SDimitry Andric Dependencies.clear(); 304*0b57cec5SDimitry Andric } 305*0b57cec5SDimitry Andric 306*0b57cec5SDimitry Andric void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) { 307*0b57cec5SDimitry Andric Dependencies.emplace_back(FullName, Dependency); 308*0b57cec5SDimitry Andric } 309*0b57cec5SDimitry Andric 310*0b57cec5SDimitry Andric /// Insert the checker/package option to AnalyzerOptions' config table, and 311*0b57cec5SDimitry Andric /// validate it, if the user supplied it on the command line. 312*0b57cec5SDimitry Andric static void insertAndValidate(StringRef FullName, 313*0b57cec5SDimitry Andric const CheckerRegistry::CmdLineOption &Option, 314*0b57cec5SDimitry Andric AnalyzerOptions &AnOpts, 315*0b57cec5SDimitry Andric DiagnosticsEngine &Diags) { 316*0b57cec5SDimitry Andric 317*0b57cec5SDimitry Andric std::string FullOption = (FullName + ":" + Option.OptionName).str(); 318*0b57cec5SDimitry Andric 319*0b57cec5SDimitry Andric auto It = AnOpts.Config.insert({FullOption, Option.DefaultValStr}); 320*0b57cec5SDimitry Andric 321*0b57cec5SDimitry Andric // Insertation was successful -- CmdLineOption's constructor will validate 322*0b57cec5SDimitry Andric // whether values received from plugins or TableGen files are correct. 323*0b57cec5SDimitry Andric if (It.second) 324*0b57cec5SDimitry Andric return; 325*0b57cec5SDimitry Andric 326*0b57cec5SDimitry Andric // Insertion failed, the user supplied this package/checker option on the 327*0b57cec5SDimitry Andric // command line. If the supplied value is invalid, we'll restore the option 328*0b57cec5SDimitry Andric // to it's default value, and if we're in non-compatibility mode, we'll also 329*0b57cec5SDimitry Andric // emit an error. 330*0b57cec5SDimitry Andric 331*0b57cec5SDimitry Andric StringRef SuppliedValue = It.first->getValue(); 332*0b57cec5SDimitry Andric 333*0b57cec5SDimitry Andric if (Option.OptionType == "bool") { 334*0b57cec5SDimitry Andric if (SuppliedValue != "true" && SuppliedValue != "false") { 335*0b57cec5SDimitry Andric if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { 336*0b57cec5SDimitry Andric Diags.Report(diag::err_analyzer_checker_option_invalid_input) 337*0b57cec5SDimitry Andric << FullOption << "a boolean value"; 338*0b57cec5SDimitry Andric } 339*0b57cec5SDimitry Andric 340*0b57cec5SDimitry Andric It.first->setValue(Option.DefaultValStr); 341*0b57cec5SDimitry Andric } 342*0b57cec5SDimitry Andric return; 343*0b57cec5SDimitry Andric } 344*0b57cec5SDimitry Andric 345*0b57cec5SDimitry Andric if (Option.OptionType == "int") { 346*0b57cec5SDimitry Andric int Tmp; 347*0b57cec5SDimitry Andric bool HasFailed = SuppliedValue.getAsInteger(0, Tmp); 348*0b57cec5SDimitry Andric if (HasFailed) { 349*0b57cec5SDimitry Andric if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) { 350*0b57cec5SDimitry Andric Diags.Report(diag::err_analyzer_checker_option_invalid_input) 351*0b57cec5SDimitry Andric << FullOption << "an integer value"; 352*0b57cec5SDimitry Andric } 353*0b57cec5SDimitry Andric 354*0b57cec5SDimitry Andric It.first->setValue(Option.DefaultValStr); 355*0b57cec5SDimitry Andric } 356*0b57cec5SDimitry Andric return; 357*0b57cec5SDimitry Andric } 358*0b57cec5SDimitry Andric } 359*0b57cec5SDimitry Andric 360*0b57cec5SDimitry Andric template <class T> 361*0b57cec5SDimitry Andric static void 362*0b57cec5SDimitry Andric insertOptionToCollection(StringRef FullName, T &Collection, 363*0b57cec5SDimitry Andric const CheckerRegistry::CmdLineOption &Option, 364*0b57cec5SDimitry Andric AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { 365*0b57cec5SDimitry Andric auto It = binaryFind(Collection, FullName); 366*0b57cec5SDimitry Andric assert(It != Collection.end() && 367*0b57cec5SDimitry Andric "Failed to find the checker while attempting to add a command line " 368*0b57cec5SDimitry Andric "option to it!"); 369*0b57cec5SDimitry Andric 370*0b57cec5SDimitry Andric insertAndValidate(FullName, Option, AnOpts, Diags); 371*0b57cec5SDimitry Andric 372*0b57cec5SDimitry Andric It->CmdLineOptions.emplace_back(Option); 373*0b57cec5SDimitry Andric } 374*0b57cec5SDimitry Andric 375*0b57cec5SDimitry Andric void CheckerRegistry::resolveCheckerAndPackageOptions() { 376*0b57cec5SDimitry Andric for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry : 377*0b57cec5SDimitry Andric CheckerOptions) { 378*0b57cec5SDimitry Andric insertOptionToCollection(CheckerOptEntry.first, Checkers, 379*0b57cec5SDimitry Andric CheckerOptEntry.second, AnOpts, Diags); 380*0b57cec5SDimitry Andric } 381*0b57cec5SDimitry Andric CheckerOptions.clear(); 382*0b57cec5SDimitry Andric 383*0b57cec5SDimitry Andric for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry : 384*0b57cec5SDimitry Andric PackageOptions) { 385*0b57cec5SDimitry Andric insertOptionToCollection(PackageOptEntry.first, Packages, 386*0b57cec5SDimitry Andric PackageOptEntry.second, AnOpts, Diags); 387*0b57cec5SDimitry Andric } 388*0b57cec5SDimitry Andric PackageOptions.clear(); 389*0b57cec5SDimitry Andric } 390*0b57cec5SDimitry Andric 391*0b57cec5SDimitry Andric void CheckerRegistry::addPackage(StringRef FullName) { 392*0b57cec5SDimitry Andric Packages.emplace_back(PackageInfo(FullName)); 393*0b57cec5SDimitry Andric } 394*0b57cec5SDimitry Andric 395*0b57cec5SDimitry Andric void CheckerRegistry::addPackageOption(StringRef OptionType, 396*0b57cec5SDimitry Andric StringRef PackageFullName, 397*0b57cec5SDimitry Andric StringRef OptionName, 398*0b57cec5SDimitry Andric StringRef DefaultValStr, 399*0b57cec5SDimitry Andric StringRef Description, 400*0b57cec5SDimitry Andric StringRef DevelopmentStatus, 401*0b57cec5SDimitry Andric bool IsHidden) { 402*0b57cec5SDimitry Andric PackageOptions.emplace_back( 403*0b57cec5SDimitry Andric PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, 404*0b57cec5SDimitry Andric Description, DevelopmentStatus, IsHidden}); 405*0b57cec5SDimitry Andric } 406*0b57cec5SDimitry Andric 407*0b57cec5SDimitry Andric void CheckerRegistry::addChecker(InitializationFunction Rfn, 408*0b57cec5SDimitry Andric ShouldRegisterFunction Sfn, StringRef Name, 409*0b57cec5SDimitry Andric StringRef Desc, StringRef DocsUri, 410*0b57cec5SDimitry Andric bool IsHidden) { 411*0b57cec5SDimitry Andric Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden); 412*0b57cec5SDimitry Andric 413*0b57cec5SDimitry Andric // Record the presence of the checker in its packages. 414*0b57cec5SDimitry Andric StringRef PackageName, LeafName; 415*0b57cec5SDimitry Andric std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator); 416*0b57cec5SDimitry Andric while (!LeafName.empty()) { 417*0b57cec5SDimitry Andric PackageSizes[PackageName] += 1; 418*0b57cec5SDimitry Andric std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator); 419*0b57cec5SDimitry Andric } 420*0b57cec5SDimitry Andric } 421*0b57cec5SDimitry Andric 422*0b57cec5SDimitry Andric void CheckerRegistry::addCheckerOption(StringRef OptionType, 423*0b57cec5SDimitry Andric StringRef CheckerFullName, 424*0b57cec5SDimitry Andric StringRef OptionName, 425*0b57cec5SDimitry Andric StringRef DefaultValStr, 426*0b57cec5SDimitry Andric StringRef Description, 427*0b57cec5SDimitry Andric StringRef DevelopmentStatus, 428*0b57cec5SDimitry Andric bool IsHidden) { 429*0b57cec5SDimitry Andric CheckerOptions.emplace_back( 430*0b57cec5SDimitry Andric CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, 431*0b57cec5SDimitry Andric Description, DevelopmentStatus, IsHidden}); 432*0b57cec5SDimitry Andric } 433*0b57cec5SDimitry Andric 434*0b57cec5SDimitry Andric void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { 435*0b57cec5SDimitry Andric // Collect checkers enabled by the options. 436*0b57cec5SDimitry Andric CheckerInfoSet enabledCheckers = getEnabledCheckers(); 437*0b57cec5SDimitry Andric 438*0b57cec5SDimitry Andric // Initialize the CheckerManager with all enabled checkers. 439*0b57cec5SDimitry Andric for (const auto *Checker : enabledCheckers) { 440*0b57cec5SDimitry Andric CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName)); 441*0b57cec5SDimitry Andric Checker->Initialize(CheckerMgr); 442*0b57cec5SDimitry Andric } 443*0b57cec5SDimitry Andric } 444*0b57cec5SDimitry Andric 445*0b57cec5SDimitry Andric static void 446*0b57cec5SDimitry Andric isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList, 447*0b57cec5SDimitry Andric StringRef SuppliedChecker, StringRef SuppliedOption, 448*0b57cec5SDimitry Andric const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) { 449*0b57cec5SDimitry Andric 450*0b57cec5SDimitry Andric if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue) 451*0b57cec5SDimitry Andric return; 452*0b57cec5SDimitry Andric 453*0b57cec5SDimitry Andric using CmdLineOption = CheckerRegistry::CmdLineOption; 454*0b57cec5SDimitry Andric 455*0b57cec5SDimitry Andric auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) { 456*0b57cec5SDimitry Andric return Opt.OptionName == SuppliedOption; 457*0b57cec5SDimitry Andric }; 458*0b57cec5SDimitry Andric 459*0b57cec5SDimitry Andric auto OptionIt = llvm::find_if(OptionList, SameOptName); 460*0b57cec5SDimitry Andric 461*0b57cec5SDimitry Andric if (OptionIt == OptionList.end()) { 462*0b57cec5SDimitry Andric Diags.Report(diag::err_analyzer_checker_option_unknown) 463*0b57cec5SDimitry Andric << SuppliedChecker << SuppliedOption; 464*0b57cec5SDimitry Andric return; 465*0b57cec5SDimitry Andric } 466*0b57cec5SDimitry Andric } 467*0b57cec5SDimitry Andric 468*0b57cec5SDimitry Andric void CheckerRegistry::validateCheckerOptions() const { 469*0b57cec5SDimitry Andric for (const auto &Config : AnOpts.Config) { 470*0b57cec5SDimitry Andric 471*0b57cec5SDimitry Andric StringRef SuppliedChecker; 472*0b57cec5SDimitry Andric StringRef SuppliedOption; 473*0b57cec5SDimitry Andric std::tie(SuppliedChecker, SuppliedOption) = Config.getKey().split(':'); 474*0b57cec5SDimitry Andric 475*0b57cec5SDimitry Andric if (SuppliedOption.empty()) 476*0b57cec5SDimitry Andric continue; 477*0b57cec5SDimitry Andric 478*0b57cec5SDimitry Andric // AnalyzerOptions' config table contains the user input, so an entry could 479*0b57cec5SDimitry Andric // look like this: 480*0b57cec5SDimitry Andric // 481*0b57cec5SDimitry Andric // cor:NoFalsePositives=true 482*0b57cec5SDimitry Andric // 483*0b57cec5SDimitry Andric // Since lower_bound would look for the first element *not less* than "cor", 484*0b57cec5SDimitry Andric // it would return with an iterator to the first checker in the core, so we 485*0b57cec5SDimitry Andric // we really have to use find here, which uses operator==. 486*0b57cec5SDimitry Andric auto CheckerIt = llvm::find(Checkers, CheckerInfo(SuppliedChecker)); 487*0b57cec5SDimitry Andric if (CheckerIt != Checkers.end()) { 488*0b57cec5SDimitry Andric isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedChecker, 489*0b57cec5SDimitry Andric SuppliedOption, AnOpts, Diags); 490*0b57cec5SDimitry Andric continue; 491*0b57cec5SDimitry Andric } 492*0b57cec5SDimitry Andric 493*0b57cec5SDimitry Andric auto PackageIt = llvm::find(Packages, PackageInfo(SuppliedChecker)); 494*0b57cec5SDimitry Andric if (PackageIt != Packages.end()) { 495*0b57cec5SDimitry Andric isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedChecker, 496*0b57cec5SDimitry Andric SuppliedOption, AnOpts, Diags); 497*0b57cec5SDimitry Andric continue; 498*0b57cec5SDimitry Andric } 499*0b57cec5SDimitry Andric 500*0b57cec5SDimitry Andric Diags.Report(diag::err_unknown_analyzer_checker) << SuppliedChecker; 501*0b57cec5SDimitry Andric } 502*0b57cec5SDimitry Andric } 503*0b57cec5SDimitry Andric 504*0b57cec5SDimitry Andric void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out, 505*0b57cec5SDimitry Andric size_t MaxNameChars) const { 506*0b57cec5SDimitry Andric // FIXME: Print available packages. 507*0b57cec5SDimitry Andric 508*0b57cec5SDimitry Andric Out << "CHECKERS:\n"; 509*0b57cec5SDimitry Andric 510*0b57cec5SDimitry Andric // Find the maximum option length. 511*0b57cec5SDimitry Andric size_t OptionFieldWidth = 0; 512*0b57cec5SDimitry Andric for (const auto &Checker : Checkers) { 513*0b57cec5SDimitry Andric // Limit the amount of padding we are willing to give up for alignment. 514*0b57cec5SDimitry Andric // Package.Name Description [Hidden] 515*0b57cec5SDimitry Andric size_t NameLength = Checker.FullName.size(); 516*0b57cec5SDimitry Andric if (NameLength <= MaxNameChars) 517*0b57cec5SDimitry Andric OptionFieldWidth = std::max(OptionFieldWidth, NameLength); 518*0b57cec5SDimitry Andric } 519*0b57cec5SDimitry Andric 520*0b57cec5SDimitry Andric const size_t InitialPad = 2; 521*0b57cec5SDimitry Andric 522*0b57cec5SDimitry Andric auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker, 523*0b57cec5SDimitry Andric StringRef Description) { 524*0b57cec5SDimitry Andric AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description}, 525*0b57cec5SDimitry Andric InitialPad, OptionFieldWidth); 526*0b57cec5SDimitry Andric Out << '\n'; 527*0b57cec5SDimitry Andric }; 528*0b57cec5SDimitry Andric 529*0b57cec5SDimitry Andric for (const auto &Checker : Checkers) { 530*0b57cec5SDimitry Andric // The order of this if branches is significant, we wouldn't like to display 531*0b57cec5SDimitry Andric // developer checkers even in the alpha output. For example, 532*0b57cec5SDimitry Andric // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden 533*0b57cec5SDimitry Andric // by default, and users (even when the user is a developer of an alpha 534*0b57cec5SDimitry Andric // checker) shouldn't normally tinker with whether they should be enabled. 535*0b57cec5SDimitry Andric 536*0b57cec5SDimitry Andric if (Checker.IsHidden) { 537*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerHelpDeveloper) 538*0b57cec5SDimitry Andric Print(Out, Checker, Checker.Desc); 539*0b57cec5SDimitry Andric continue; 540*0b57cec5SDimitry Andric } 541*0b57cec5SDimitry Andric 542*0b57cec5SDimitry Andric if (Checker.FullName.startswith("alpha")) { 543*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerHelpAlpha) 544*0b57cec5SDimitry Andric Print(Out, Checker, 545*0b57cec5SDimitry Andric ("(Enable only for development!) " + Checker.Desc).str()); 546*0b57cec5SDimitry Andric continue; 547*0b57cec5SDimitry Andric } 548*0b57cec5SDimitry Andric 549*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerHelp) 550*0b57cec5SDimitry Andric Print(Out, Checker, Checker.Desc); 551*0b57cec5SDimitry Andric } 552*0b57cec5SDimitry Andric } 553*0b57cec5SDimitry Andric 554*0b57cec5SDimitry Andric void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const { 555*0b57cec5SDimitry Andric // Collect checkers enabled by the options. 556*0b57cec5SDimitry Andric CheckerInfoSet EnabledCheckers = getEnabledCheckers(); 557*0b57cec5SDimitry Andric 558*0b57cec5SDimitry Andric for (const auto *i : EnabledCheckers) 559*0b57cec5SDimitry Andric Out << i->FullName << '\n'; 560*0b57cec5SDimitry Andric } 561*0b57cec5SDimitry Andric 562*0b57cec5SDimitry Andric void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const { 563*0b57cec5SDimitry Andric Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n"; 564*0b57cec5SDimitry Andric Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; 565*0b57cec5SDimitry Andric Out << " -analyzer-config OPTION1=VALUE, -analyzer-config " 566*0b57cec5SDimitry Andric "OPTION2=VALUE, ...\n\n"; 567*0b57cec5SDimitry Andric Out << "OPTIONS:\n\n"; 568*0b57cec5SDimitry Andric 569*0b57cec5SDimitry Andric std::multimap<StringRef, const CmdLineOption &> OptionMap; 570*0b57cec5SDimitry Andric 571*0b57cec5SDimitry Andric for (const CheckerInfo &Checker : Checkers) { 572*0b57cec5SDimitry Andric for (const CmdLineOption &Option : Checker.CmdLineOptions) { 573*0b57cec5SDimitry Andric OptionMap.insert({Checker.FullName, Option}); 574*0b57cec5SDimitry Andric } 575*0b57cec5SDimitry Andric } 576*0b57cec5SDimitry Andric 577*0b57cec5SDimitry Andric for (const PackageInfo &Package : Packages) { 578*0b57cec5SDimitry Andric for (const CmdLineOption &Option : Package.CmdLineOptions) { 579*0b57cec5SDimitry Andric OptionMap.insert({Package.FullName, Option}); 580*0b57cec5SDimitry Andric } 581*0b57cec5SDimitry Andric } 582*0b57cec5SDimitry Andric 583*0b57cec5SDimitry Andric auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) { 584*0b57cec5SDimitry Andric AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc}, 585*0b57cec5SDimitry Andric /*InitialPad*/ 2, 586*0b57cec5SDimitry Andric /*EntryWidth*/ 50, 587*0b57cec5SDimitry Andric /*MinLineWidth*/ 90); 588*0b57cec5SDimitry Andric Out << "\n\n"; 589*0b57cec5SDimitry Andric }; 590*0b57cec5SDimitry Andric for (const std::pair<StringRef, const CmdLineOption &> &Entry : OptionMap) { 591*0b57cec5SDimitry Andric const CmdLineOption &Option = Entry.second; 592*0b57cec5SDimitry Andric std::string FullOption = (Entry.first + ":" + Option.OptionName).str(); 593*0b57cec5SDimitry Andric 594*0b57cec5SDimitry Andric std::string Desc = 595*0b57cec5SDimitry Andric ("(" + Option.OptionType + ") " + Option.Description + " (default: " + 596*0b57cec5SDimitry Andric (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")") 597*0b57cec5SDimitry Andric .str(); 598*0b57cec5SDimitry Andric 599*0b57cec5SDimitry Andric // The list of these if branches is significant, we wouldn't like to 600*0b57cec5SDimitry Andric // display hidden alpha checker options for 601*0b57cec5SDimitry Andric // -analyzer-checker-option-help-alpha. 602*0b57cec5SDimitry Andric 603*0b57cec5SDimitry Andric if (Option.IsHidden) { 604*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerOptionDeveloperList) 605*0b57cec5SDimitry Andric Print(Out, FullOption, Desc); 606*0b57cec5SDimitry Andric continue; 607*0b57cec5SDimitry Andric } 608*0b57cec5SDimitry Andric 609*0b57cec5SDimitry Andric if (Option.DevelopmentStatus == "alpha" || 610*0b57cec5SDimitry Andric Entry.first.startswith("alpha")) { 611*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerOptionAlphaList) 612*0b57cec5SDimitry Andric Print(Out, FullOption, 613*0b57cec5SDimitry Andric llvm::Twine("(Enable only for development!) " + Desc).str()); 614*0b57cec5SDimitry Andric continue; 615*0b57cec5SDimitry Andric } 616*0b57cec5SDimitry Andric 617*0b57cec5SDimitry Andric if (AnOpts.ShowCheckerOptionList) 618*0b57cec5SDimitry Andric Print(Out, FullOption, Desc); 619*0b57cec5SDimitry Andric } 620*0b57cec5SDimitry Andric } 621