| //===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" |
| #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace clang; |
| using namespace ento; |
| |
| static const char PackageSeparator = '.'; |
| typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; |
| |
| |
| 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; |
| } |
| |
| static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, |
| const llvm::StringMap<size_t> &packageSizes, |
| CheckerOptInfo &opt, CheckerInfoSet &collected) { |
| // Use a binary search to find the possible start of the package. |
| CheckerRegistry::CheckerInfo packageInfo(NULL, opt.getName(), ""); |
| CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end(); |
| CheckerRegistry::CheckerInfoList::const_iterator i = |
| std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT); |
| |
| // If we didn't even find a possible package, give up. |
| if (i == e) |
| return; |
| |
| // If what we found doesn't actually start the package, give up. |
| if (!isInPackage(*i, opt.getName())) |
| return; |
| |
| // There is at least one checker in the package; claim the option. |
| opt.claim(); |
| |
| // 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 = |
| packageSizes.find(opt.getName()); |
| if (packageSize != packageSizes.end()) |
| size = packageSize->getValue(); |
| |
| // Step through all the checkers in the package. |
| for (e = i+size; i != e; ++i) { |
| if (opt.isEnabled()) |
| collected.insert(&*i); |
| else |
| collected.remove(&*i); |
| } |
| } |
| |
| 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; |
| llvm::tie(packageName, leafName) = name.rsplit(PackageSeparator); |
| while (!leafName.empty()) { |
| Packages[packageName] += 1; |
| llvm::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); |
| } |
| } |
| |
| void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, |
| SmallVectorImpl<CheckerOptInfo> &opts) const { |
| // Sort checkers for efficient collection. |
| std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); |
| |
| // Collect checkers enabled by the options. |
| CheckerInfoSet enabledCheckers; |
| for (SmallVectorImpl<CheckerOptInfo>::iterator |
| i = opts.begin(), e = opts.end(); i != e; ++i) { |
| collectCheckers(Checkers, Packages, *i, enabledCheckers); |
| } |
| |
| // Initialize the CheckerManager with all enabled checkers. |
| for (CheckerInfoSet::iterator |
| i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) { |
| (*i)->Initialize(checkerMgr); |
| } |
| } |
| |
| 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? |
| std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); |
| |
| // FIXME: Print available packages. |
| |
| out << "CHECKERS:\n"; |
| |
| // Find the maximum option length. |
| size_t optionFieldWidth = 0; |
| for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); |
| i != e; ++i) { |
| // 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 (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); |
| i != e; ++i) { |
| 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'; |
| } |
| } |