forked from OSchip/llvm-project
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:
parent
aa47d24a47
commit
fb9e92b167
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue