Index: cfe/trunk/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ cfe/trunk/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -90,11 +90,25 @@ using ShouldRegisterFunction = bool (*)(const LangOptions &); struct CheckerInfo { + enum class StateFromCmdLine { + // This checker wasn't explicitly enabled or disabled. + State_Unspecified, + // This checker was explicitly disabled. + State_Disabled, + // This checker was explicitly enabled. + State_Enabled + }; + InitializationFunction Initialize; ShouldRegisterFunction ShouldRegister; StringRef FullName; StringRef Desc; StringRef DocumentationUri; + StateFromCmdLine State = StateFromCmdLine::State_Unspecified; + + bool isEnabled(const LangOptions &LO) const { + return State == StateFromCmdLine::State_Enabled && ShouldRegister(LO); + } CheckerInfo(InitializationFunction Fn, ShouldRegisterFunction sfn, StringRef Name, StringRef Desc, StringRef DocsUri) @@ -102,7 +116,9 @@ DocumentationUri(DocsUri) {} }; + using StateFromCmdLine = CheckerInfo::StateFromCmdLine; using CheckerInfoList = std::vector; + using CheckerInfoListRange = llvm::iterator_range; using CheckerInfoSet = llvm::SetVector; private: @@ -133,6 +149,7 @@ &CheckerRegistry::returnTrue, FullName, Desc, DocsUri); } + // FIXME: This *really* should be added to the frontend flag descriptions. /// Initializes a CheckerManager by calling the initialization functions for /// all checkers specified by the given CheckerOptInfo list. The order of this /// list is significant; later options can be used to reverse earlier ones. @@ -150,8 +167,13 @@ private: CheckerInfoSet getEnabledCheckers() const; - mutable CheckerInfoList Checkers; - mutable llvm::StringMap Packages; + /// Return an iterator range of mutable CheckerInfos \p CmdLineArg applies to. + /// For example, it'll return the checkers for the core package, if + /// \p CmdLineArg is "core". + CheckerInfoListRange getMutableCheckersForCmdLineArg(StringRef CmdLineArg); + + CheckerInfoList Checkers; + llvm::StringMap Packages; DiagnosticsEngine &Diags; AnalyzerOptions &AnOpts; Index: cfe/trunk/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ cfe/trunk/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -38,12 +38,66 @@ return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; } +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + +static constexpr char PackageSeparator = '.'; + +static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, + StringRef packageName) { + // Does the checker's full name have the package as a prefix? + if (!checker.FullName.startswith(packageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (checker.FullName.size() == packageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (checker.FullName[packageName.size()] == PackageSeparator) + return true; + + return false; +} + +CheckerRegistry::CheckerInfoListRange +CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { + + assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && + "In order to efficiently gather checkers, this function expects them " + "to be already sorted!"); + + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo + packageInfo(nullptr, nullptr, CmdLineArg, "", ""); + auto it = std::lower_bound(Checkers.begin(), Checkers.end(), + packageInfo, checkerNameLT); + + if (!isInPackage(*it, CmdLineArg)) + return { Checkers.end(), Checkers.end() }; + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single + // checker. + size_t size = 1; + llvm::StringMap::const_iterator packageSize = + Packages.find(CmdLineArg); + + if (packageSize != Packages.end()) + size = packageSize->getValue(); + + return { it, it + size }; +} + CheckerRegistry::CheckerRegistry(ArrayRef plugins, DiagnosticsEngine &diags, AnalyzerOptions &AnOpts, const LangOptions &LangOpts) : Diags(diags), AnOpts(AnOpts), LangOpts(LangOpts) { + // Register builtin checkers. #define GET_CHECKERS #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ @@ -52,6 +106,7 @@ #undef CHECKER #undef GET_CHECKERS + // Register checkers from plugins. for (ArrayRef::iterator i = plugins.begin(), e = plugins.end(); i != e; ++i) { // Get access to the plugin. @@ -81,73 +136,38 @@ if (registerPluginCheckers) registerPluginCheckers(*this); } -} - -static constexpr char PackageSeparator = '.'; - -static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, - const CheckerRegistry::CheckerInfo &b) { - return a.FullName < b.FullName; -} -static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, - StringRef packageName) { - // Does the checker's full name have the package as a prefix? - if (!checker.FullName.startswith(packageName)) - return false; + // Sort checkers for efficient collection. + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + llvm::sort(Checkers, checkerNameLT); - // Is the package actually just the name of a specific checker? - if (checker.FullName.size() == packageName.size()) - return true; + // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the + // command line. + for (const std::pair &opt : AnOpts.CheckersControlList) { + CheckerInfoListRange checkersForCmdLineArg = + getMutableCheckersForCmdLineArg(opt.first); - // Is the checker in the package (or a subpackage)? - if (checker.FullName[packageName.size()] == PackageSeparator) - return true; + if (checkersForCmdLineArg.begin() == checkersForCmdLineArg.end()) { + Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; + Diags.Report(diag::note_suggest_disabling_all_checkers); + } - return false; + for (CheckerInfo &checker : checkersForCmdLineArg) { + checker.State = opt.second ? StateFromCmdLine::State_Enabled : + StateFromCmdLine::State_Disabled; + } + } } CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { - assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && - "In order to efficiently gather checkers, this function expects them " - "to be already sorted!"); - CheckerInfoSet enabledCheckers; - const auto end = Checkers.cend(); - - for (const std::pair &opt : AnOpts.CheckersControlList) { - // Use a binary search to find the possible start of the package. - CheckerRegistry::CheckerInfo - packageInfo(nullptr, nullptr, opt.first, "", ""); - auto firstRelatedChecker = - std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); - if (firstRelatedChecker == end || - !isInPackage(*firstRelatedChecker, opt.first)) { - Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; - Diags.Report(diag::note_suggest_disabling_all_checkers); - return {}; - } - - // See how large the package is. - // If the package doesn't exist, assume the option refers to a single - // checker. - size_t size = 1; - llvm::StringMap::const_iterator packageSize = - Packages.find(opt.first); - if (packageSize != Packages.end()) - size = packageSize->getValue(); - - // Step through all the checkers in the package. - for (auto lastRelatedChecker = firstRelatedChecker+size; - firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) - if (opt.second) { - if (firstRelatedChecker->ShouldRegister(LangOpts)) - enabledCheckers.insert(&*firstRelatedChecker); - } else { - enabledCheckers.remove(&*firstRelatedChecker); - } + for (const CheckerInfo &checker : Checkers) { + if (checker.isEnabled(LangOpts)) + enabledCheckers.insert(&checker); } return enabledCheckers; @@ -168,8 +188,6 @@ } void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(); @@ -203,11 +221,6 @@ void CheckerRegistry::printHelp(raw_ostream &out, size_t maxNameChars) const { - // FIXME: Alphabetical sort puts 'experimental' in the middle. - // Would it be better to name it '~experimental' or something else - // that's ASCIIbetically last? - llvm::sort(Checkers, checkerNameLT); - // FIXME: Print available packages. out << "CHECKERS:\n"; @@ -240,9 +253,6 @@ } void CheckerRegistry::printList(raw_ostream &out) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers();