forked from OSchip/llvm-project
197 lines
6.6 KiB
C++
197 lines
6.6 KiB
C++
//===--- 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/Basic/Diagnostic.h"
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
|
#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
|
|
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.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(nullptr, opt.getName(), "");
|
|
auto end = checkers.cend();
|
|
CheckerRegistry::CheckerInfoList::const_iterator i =
|
|
std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT);
|
|
|
|
// If we didn't even find a possible package, give up.
|
|
if (i == end)
|
|
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 (auto checkEnd = i+size; i != checkEnd; ++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;
|
|
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,
|
|
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) {
|
|
checkerMgr.setCurrentCheckName(CheckName((*i)->FullName));
|
|
(*i)->Initialize(checkerMgr);
|
|
}
|
|
}
|
|
|
|
void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts,
|
|
DiagnosticsEngine &diags) const {
|
|
for (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 (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?
|
|
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';
|
|
}
|
|
}
|
|
|
|
void CheckerRegistry::printList(
|
|
raw_ostream &out, SmallVectorImpl<CheckerOptInfo> &opts) const {
|
|
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);
|
|
}
|
|
|
|
for (CheckerInfoSet::const_iterator i = enabledCheckers.begin(),
|
|
e = enabledCheckers.end();
|
|
i != e; ++i) {
|
|
out << (*i)->FullName << '\n';
|
|
}
|
|
}
|