Clang-tidy: added --disable-checks, --list-checks options.

Summary:
Allow disabling checks by regex. By default, disable alpha.* checks,
that are not particularly good tested (e.g. IdempotentOperationChecker, see
http://llvm-reviews.chandlerc.com/D2427).

Fixed a bug, that would disable all analyzer checks, when using a regex more
strict, than 'clang-analyzer-', for example --checks='clang-analyzer-deadcode-'.

Added --list-checks to list all enabled checks. This is useful to test specific
values in --checks/--disable-checks.

Reviewers: djasper, klimek

Reviewed By: klimek

CC: cfe-commits, klimek

Differential Revision: http://llvm-reviews.chandlerc.com/D2444

llvm-svn: 197717
This commit is contained in:
Alexander Kornienko 2013-12-19 19:57:05 +00:00
parent aa47d24a47
commit fb9e92b167
5 changed files with 154 additions and 59 deletions

View File

@ -35,6 +35,7 @@
#include "clang/Tooling/Refactoring.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include <algorithm>
#include <vector>
using namespace clang::ast_matchers;
@ -78,6 +79,17 @@ private:
llvm::OwningPtr<ASTConsumer> Consumer2;
};
static StringRef StaticAnalyzerCheckers[] = {
#define GET_CHECKERS
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
FULLNAME,
#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
#undef GET_CHECKERS
};
static const char *AnalyzerCheckerNamePrefix = "clang-analyzer-";
/// \brief Action that runs clang-tidy and static analyzer checks.
///
/// FIXME: Note that this inherits from \c AnalysisAction as this is the only
@ -86,37 +98,45 @@ private:
/// checkers in clang-tidy, but that needs some preparation work first.
class ClangTidyAction : public ento::AnalysisAction {
public:
ClangTidyAction(StringRef CheckRegexString,
ClangTidyAction(ChecksFilter &Filter,
SmallVectorImpl<ClangTidyCheck *> &Checks,
ClangTidyContext &Context, MatchFinder &Finder)
: CheckRegexString(CheckRegexString), Checks(Checks), Context(Context),
Finder(Finder) {}
: Filter(Filter), Checks(Checks), Context(Context), Finder(Finder) {}
typedef std::vector<std::pair<std::string, bool> > CheckersList;
void fillCheckersControlList(CheckersList &List) {
ArrayRef<StringRef> Checkers(StaticAnalyzerCheckers);
bool AnalyzerChecksEnabled = false;
for (unsigned i = 0; i < Checkers.size(); ++i) {
std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
AnalyzerChecksEnabled |=
Filter.IsCheckEnabled(Checker) && !Checkers[i].startswith("debug");
}
if (!AnalyzerChecksEnabled)
return;
// Run our regex against all possible static analyzer checkers.
// Note that debug checkers print values / run programs to visualize the CFG
// and are thus not applicable to clang-tidy in general.
// Always add all core checkers if any other static analyzer checks are
// enabled. This is currently necessary, as other path sensitive checks rely
// on the core checkers.
for (unsigned i = 0; i < Checkers.size(); ++i) {
std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
if (Checkers[i].startswith("core") ||
(!Checkers[i].startswith("debug") && Filter.IsCheckEnabled(Checker)))
List.push_back(std::make_pair(Checkers[i], true));
}
}
private:
clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
StringRef File) LLVM_OVERRIDE {
AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
llvm::Regex CheckRegex(CheckRegexString);
// Always add all core checkers if any other static analyzer checks are
// enabled. This is currently necessary, as other path sensitive checks rely
// on the core checkers.
if (CheckRegex.match("clang-analyzer-"))
Options->CheckersControlList.push_back(std::make_pair("core", true));
// Run our regex against all possible static analyzer checkers.
// Note that debug checkers print values / run programs to visualize the CFG
// and are thus not applicable to clang-tidy in general.
#define GET_CHECKERS
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
if (!StringRef(FULLNAME).startswith("core") && \
!StringRef(FULLNAME).startswith("debug") && \
CheckRegex.match("clang-analyzer-" FULLNAME)) \
Options->CheckersControlList.push_back(std::make_pair(FULLNAME, true));
#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
#undef GET_CHECKERS
fillCheckersControlList(Options->CheckersControlList);
Options->AnalysisStoreOpt = RegionStoreModel;
Options->AnalysisDiagOpt = PD_TEXT;
Options->AnalyzeNestedBlocks = true;
@ -138,7 +158,7 @@ private:
return true;
}
std::string CheckRegexString;
ChecksFilter &Filter;
SmallVectorImpl<ClangTidyCheck *> &Checks;
ClangTidyContext &Context;
MatchFinder &Finder;
@ -146,9 +166,10 @@ private:
class ClangTidyActionFactory : public FrontendActionFactory {
public:
ClangTidyActionFactory(StringRef CheckRegexString, ClangTidyContext &Context)
: CheckRegexString(CheckRegexString), Context(Context) {
ClangTidyCheckFactories CheckFactories;
ClangTidyActionFactory(StringRef EnableChecksRegex,
StringRef DisableChecksRegex,
ClangTidyContext &Context)
: Filter(EnableChecksRegex, DisableChecksRegex), Context(Context) {
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I) {
@ -157,7 +178,7 @@ public:
}
SmallVector<ClangTidyCheck *, 16> Checks;
CheckFactories.createChecks(CheckRegexString, Checks);
CheckFactories.createChecks(Filter, Checks);
for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
E = Checks.end();
@ -168,18 +189,50 @@ public:
}
virtual FrontendAction *create() {
return new ClangTidyAction(CheckRegexString, Checks, Context, Finder);
return new ClangTidyAction(Filter, Checks, Context, Finder);
}
std::vector<std::string> getCheckNames() {
std::vector<std::string> CheckNames;
for (ClangTidyCheckFactories::FactoryMap::const_iterator
I = CheckFactories.begin(),
E = CheckFactories.end();
I != E; ++I) {
if (Filter.IsCheckEnabled(I->first))
CheckNames.push_back(I->first);
}
ClangTidyAction Action(Filter, Checks, Context, Finder);
ClangTidyAction::CheckersList AnalyzerChecks;
Action.fillCheckersControlList(AnalyzerChecks);
for (ClangTidyAction::CheckersList::const_iterator
I = AnalyzerChecks.begin(),
E = AnalyzerChecks.end();
I != E; ++I)
CheckNames.push_back(AnalyzerCheckerNamePrefix + I->first);
std::sort(CheckNames.begin(), CheckNames.end());
return CheckNames;
}
private:
std::string CheckRegexString;
ChecksFilter Filter;
SmallVector<ClangTidyCheck *, 8> Checks;
ClangTidyContext &Context;
MatchFinder Finder;
ClangTidyCheckFactories CheckFactories;
};
} // namespace
ChecksFilter::ChecksFilter(StringRef EnableChecksRegex,
StringRef DisableChecksRegex)
: EnableChecks(EnableChecksRegex), DisableChecks(DisableChecksRegex) {}
bool ChecksFilter::IsCheckEnabled(StringRef Name) {
return EnableChecks.match(Name) && !DisableChecks.match(Name);
}
ClangTidyMessage::ClangTidyMessage(StringRef Message) : Message(Message) {}
ClangTidyMessage::ClangTidyMessage(StringRef Message,
@ -217,12 +270,24 @@ void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
check(Result);
}
FrontendActionFactory *createClangTidyActionFactory(StringRef CheckRegexString,
ClangTidyContext &Context) {
return new ClangTidyActionFactory(CheckRegexString, Context);
FrontendActionFactory *
createClangTidyActionFactory(StringRef EnableChecksRegex,
StringRef DisableChecksRegex,
ClangTidyContext &Context) {
return new ClangTidyActionFactory(EnableChecksRegex, DisableChecksRegex,
Context);
}
void runClangTidy(StringRef CheckRegexString,
std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
StringRef DisableChecksRegex) {
SmallVector<ClangTidyError, 8> Errors;
clang::tidy::ClangTidyContext Context(&Errors);
ClangTidyActionFactory Factory(EnableChecksRegex, DisableChecksRegex,
Context);
return Factory.getCheckNames();
}
void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> Ranges,
SmallVectorImpl<ClangTidyError> *Errors) {
@ -233,7 +298,8 @@ void runClangTidy(StringRef CheckRegexString,
ClangTidyDiagnosticConsumer DiagConsumer(Context);
Tool.setDiagnosticConsumer(&DiagConsumer);
Tool.run(createClangTidyActionFactory(CheckRegexString, Context));
Tool.run(createClangTidyActionFactory(EnableChecksRegex, DisableChecksRegex,
Context));
}
static void reportDiagnostic(const ClangTidyMessage &Message,

View File

@ -85,13 +85,30 @@ private:
virtual void run(const ast_matchers::MatchFinder::MatchResult &Result);
};
/// \brief Filters checks by name.
class ChecksFilter {
public:
ChecksFilter(StringRef EnableChecksRegex, StringRef DisableChecksRegex);
bool IsCheckEnabled(StringRef Name);
private:
llvm::Regex EnableChecks;
llvm::Regex DisableChecks;
};
/// \brief Fills the list of check names that are enabled when the provided
/// filters are applied.
std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
StringRef DisableChecksRegex);
/// \brief Returns an action factory that runs the specified clang-tidy checks.
tooling::FrontendActionFactory *
createClangTidyActionFactory(StringRef CheckRegexString,
createClangTidyActionFactory(StringRef EnableChecksRegex,
StringRef DisableChecksRegex,
ClangTidyContext &Context);
/// \brief Run a set of clang-tidy checks on a set of files.
void runClangTidy(StringRef CheckRegexString,
void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> Ranges,
SmallVectorImpl<ClangTidyError> *Errors);

View File

@ -12,18 +12,13 @@
//===----------------------------------------------------------------------===//
#include "ClangTidyModule.h"
#include "llvm/Support/Regex.h"
namespace clang {
namespace tidy {
CheckFactoryBase::~CheckFactoryBase() {}
ClangTidyCheckFactories::~ClangTidyCheckFactories() {
for (std::map<std::string, CheckFactoryBase *>::iterator
I = Factories.begin(),
E = Factories.end();
I != E; ++I) {
for (FactoryMap::iterator I = Factories.begin(), E = Factories.end(); I != E;
++I) {
delete I->second;
}
}
@ -34,13 +29,10 @@ void ClangTidyCheckFactories::addCheckFactory(StringRef Name,
}
void ClangTidyCheckFactories::createChecks(
StringRef CheckRegexString, SmallVectorImpl<ClangTidyCheck *> &Checks) {
llvm::Regex CheckRegex(CheckRegexString);
for (std::map<std::string, CheckFactoryBase *>::iterator
I = Factories.begin(),
E = Factories.end();
I != E; ++I) {
if (CheckRegex.match(I->first))
ChecksFilter &Filter, SmallVectorImpl<ClangTidyCheck *> &Checks) {
for (FactoryMap::iterator I = Factories.begin(), E = Factories.end(); I != E;
++I) {
if (Filter.IsCheckEnabled(I->first))
Checks.push_back(I->second->createCheck());
}
}

View File

@ -26,7 +26,7 @@ namespace tidy {
/// this subclass in \c ClangTidyModule::addCheckFactories().
class CheckFactoryBase {
public:
virtual ~CheckFactoryBase();
virtual ~CheckFactoryBase() {}
virtual ClangTidyCheck *createCheck() = 0;
};
@ -84,12 +84,15 @@ public:
/// store them in \p Checks.
///
/// The caller takes ownership of the return \c ClangTidyChecks.
void createChecks(StringRef CheckRegexString,
void createChecks(ChecksFilter &Filter,
SmallVectorImpl<ClangTidyCheck *> &Checks);
typedef std::map<std::string, CheckFactoryBase *> FactoryMap;
FactoryMap::const_iterator begin() const { return Factories.begin(); }
FactoryMap::const_iterator end() const { return Factories.end(); }
private:
StringRef FilterRegex;
std::map<std::string, CheckFactoryBase *> Factories;
FactoryMap Factories;
};
} // end namespace tidy

View File

@ -17,7 +17,6 @@
#include "../ClangTidy.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include <vector>
using namespace clang::ast_matchers;
using namespace clang::driver;
@ -32,16 +31,34 @@ static cl::opt<std::string> Checks(
"checks",
cl::desc("Regular expression matching the names of the checks to be run."),
cl::init(".*"), cl::cat(ClangTidyCategory));
static cl::opt<std::string> DisableChecks(
"disable-checks",
cl::desc("Regular expression matching the names of the checks to disable."),
cl::init("clang-analyzer-alpha.*"), cl::cat(ClangTidyCategory));
static cl::opt<bool> Fix("fix", cl::desc("Fix detected errors if possible."),
cl::init(false), cl::cat(ClangTidyCategory));
// FIXME: Add option to list name/description of all checks.
static cl::opt<bool> ListChecks("list-checks",
cl::desc("List all enabled checks and exit."),
cl::init(false), cl::cat(ClangTidyCategory));
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory);
// FIXME: Allow using --list-checks without positional arguments.
if (ListChecks) {
std::vector<std::string> CheckNames =
clang::tidy::getCheckNames(Checks, DisableChecks);
llvm::outs() << "Enabled checks:";
for (unsigned i = 0; i < CheckNames.size(); ++i)
llvm::outs() << "\n " << CheckNames[i];
llvm::outs() << "\n\n";
return 0;
}
SmallVector<clang::tidy::ClangTidyError, 16> Errors;
clang::tidy::runClangTidy(Checks, OptionsParser.getCompilations(),
clang::tidy::runClangTidy(Checks, DisableChecks,
OptionsParser.getCompilations(),
OptionsParser.getSourcePathList(), &Errors);
clang::tidy::handleErrors(Errors, Fix);