diff options
author | Kristof Umann <dkszelethus@gmail.com> | 2018-12-15 16:23:51 +0000 |
---|---|---|
committer | Kristof Umann <dkszelethus@gmail.com> | 2018-12-15 16:23:51 +0000 |
commit | 76a21502fdc12366cdda4fe92b2dbdfe7661064a (patch) | |
tree | 41a46bc6d7b02047057c94a0cd9b27e13369fcc2 /clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp | |
parent | f282d272154edd6a4d86d9d770ceea0822800062 (diff) | |
download | bcm5719-llvm-76a21502fdc12366cdda4fe92b2dbdfe7661064a.tar.gz bcm5719-llvm-76a21502fdc12366cdda4fe92b2dbdfe7661064a.zip |
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
Diffstat (limited to 'clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp new file mode 100644 index 00000000000..d1931d3e6fd --- /dev/null +++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -0,0 +1,193 @@ +//===- CheckerRegistry.cpp - Maintains all available checkers -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace clang; +using namespace ento; + +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; + + // 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::CheckerInfoSet CheckerRegistry::getEnabledCheckers( + const AnalyzerOptions &Opts, + DiagnosticsEngine &diags) 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<std::string, bool> &opt : Opts.CheckersControlList) { + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo packageInfo(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<size_t>::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) + enabledCheckers.insert(&*firstRelatedChecker); + else + enabledCheckers.remove(&*firstRelatedChecker); + } + + return enabledCheckers; +} + +void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, + StringRef desc) { + Checkers.push_back(CheckerInfo(fn, name, desc)); + + // Record the presence of the checker in its packages. + StringRef packageName, leafName; + std::tie(packageName, leafName) = name.rsplit(PackageSeparator); + while (!leafName.empty()) { + Packages[packageName] += 1; + std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, + const AnalyzerOptions &Opts, + DiagnosticsEngine &diags) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts, diags); + + // Initialize the CheckerManager with all enabled checkers. + for (const auto *i : enabledCheckers) { + checkerMgr.setCurrentCheckName(CheckName(i->FullName)); + i->Initialize(checkerMgr); + } +} + +void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts, + DiagnosticsEngine &diags) const { + for (const auto &config : opts.Config) { + size_t pos = config.getKey().find(':'); + if (pos == StringRef::npos) + continue; + + bool hasChecker = false; + StringRef checkerName = config.getKey().substr(0, pos); + for (const auto &checker : Checkers) { + if (checker.FullName.startswith(checkerName) && + (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { + hasChecker = true; + break; + } + } + if (!hasChecker) + diags.Report(diag::err_unknown_analyzer_checker) << checkerName; + } +} + +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"; + + // Find the maximum option length. + size_t optionFieldWidth = 0; + for (const auto &i : Checkers) { + // Limit the amount of padding we are willing to give up for alignment. + // Package.Name Description [Hidden] + size_t nameLength = i.FullName.size(); + if (nameLength <= maxNameChars) + optionFieldWidth = std::max(optionFieldWidth, nameLength); + } + + const size_t initialPad = 2; + for (const auto &i : Checkers) { + out.indent(initialPad) << i.FullName; + + int pad = optionFieldWidth - i.FullName.size(); + + // Break on long option names. + if (pad < 0) { + out << '\n'; + pad = optionFieldWidth + initialPad; + } + out.indent(pad + 2) << i.Desc; + + out << '\n'; + } +} + +void CheckerRegistry::printList(raw_ostream &out, + const AnalyzerOptions &opts, + DiagnosticsEngine &diags) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(opts, diags); + + for (const auto *i : enabledCheckers) + out << i->FullName << '\n'; +} |