Allow per-file clang-tidy options.
Summary: This patch makes it possible for clang-tidy clients to provide different options for different translation units. The option, which doesn't make sense to be file-dependent, was moved to a separate ClangTidyGlobalOptions struct. Added parsing of ClangTidyOptions. Reviewers: klimek Reviewed By: klimek Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D3979 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@210260 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
e04d0b1c60
Коммит
ac560ec8cb
|
@ -169,51 +169,63 @@ private:
|
|||
unsigned AppliedFixes;
|
||||
};
|
||||
|
||||
class ClangTidyASTConsumer : public MultiplexConsumer {
|
||||
public:
|
||||
ClangTidyASTConsumer(const SmallVectorImpl<ASTConsumer *> &Consumers,
|
||||
std::unique_ptr<ast_matchers::MatchFinder> Finder,
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
|
||||
: MultiplexConsumer(Consumers), Finder(std::move(Finder)),
|
||||
Checks(std::move(Checks)) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ast_matchers::MatchFinder> Finder;
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
|
||||
ClangTidyContext &Context, const ClangTidyOptions &Options)
|
||||
: Context(Context), CheckFactories(new ClangTidyCheckFactories),
|
||||
Options(Options) {
|
||||
ClangTidyContext &Context)
|
||||
: Context(Context), CheckFactories(new ClangTidyCheckFactories) {
|
||||
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
|
||||
E = ClangTidyModuleRegistry::end();
|
||||
I != E; ++I) {
|
||||
std::unique_ptr<ClangTidyModule> Module(I->instantiate());
|
||||
Module->addCheckFactories(*CheckFactories);
|
||||
}
|
||||
|
||||
CheckFactories->createChecks(Context.getChecksFilter(), Checks);
|
||||
|
||||
for (ClangTidyCheck *Check : Checks) {
|
||||
Check->setContext(&Context);
|
||||
Check->registerMatchers(&Finder);
|
||||
}
|
||||
}
|
||||
|
||||
ClangTidyASTConsumerFactory::~ClangTidyASTConsumerFactory() {
|
||||
for (ClangTidyCheck *Check : Checks)
|
||||
delete Check;
|
||||
}
|
||||
|
||||
clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
|
||||
clang::CompilerInstance &Compiler, StringRef File) {
|
||||
// FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
|
||||
// modify Compiler.
|
||||
Context.setSourceManager(&Compiler.getSourceManager());
|
||||
for (ClangTidyCheck *Check : Checks)
|
||||
Context.setCurrentFile(File);
|
||||
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
|
||||
ChecksFilter &Filter = Context.getChecksFilter();
|
||||
CheckFactories->createChecks(Filter, Checks);
|
||||
|
||||
std::unique_ptr<ast_matchers::MatchFinder> Finder(
|
||||
new ast_matchers::MatchFinder);
|
||||
for (auto &Check : Checks) {
|
||||
Check->setContext(&Context);
|
||||
Check->registerMatchers(&*Finder);
|
||||
Check->registerPPCallbacks(Compiler);
|
||||
}
|
||||
|
||||
SmallVector<ASTConsumer *, 2> Consumers;
|
||||
if (!CheckFactories->empty())
|
||||
Consumers.push_back(Finder.newASTConsumer());
|
||||
if (!Checks.empty())
|
||||
Consumers.push_back(Finder->newASTConsumer());
|
||||
|
||||
AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
|
||||
// FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
|
||||
// to true.
|
||||
AnalyzerOptions->Config["cfg-temporary-dtors"] =
|
||||
Options.AnalyzeTemporaryDtors ? "true" : "false";
|
||||
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
|
||||
|
||||
AnalyzerOptions->CheckersControlList = getCheckersControlList();
|
||||
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
|
||||
if (!AnalyzerOptions->CheckersControlList.empty()) {
|
||||
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
|
||||
AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
|
||||
|
@ -226,17 +238,19 @@ clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
|
|||
new AnalyzerDiagnosticConsumer(Context));
|
||||
Consumers.push_back(AnalysisConsumer);
|
||||
}
|
||||
return new MultiplexConsumer(Consumers);
|
||||
return new ClangTidyASTConsumer(Consumers, std::move(Finder),
|
||||
std::move(Checks));
|
||||
}
|
||||
|
||||
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
|
||||
std::vector<std::string>
|
||||
ClangTidyASTConsumerFactory::getCheckNames(ChecksFilter &Filter) {
|
||||
std::vector<std::string> CheckNames;
|
||||
for (const auto &CheckFactory : *CheckFactories) {
|
||||
if (Context.getChecksFilter().isCheckEnabled(CheckFactory.first))
|
||||
if (Filter.isCheckEnabled(CheckFactory.first))
|
||||
CheckNames.push_back(CheckFactory.first);
|
||||
}
|
||||
|
||||
for (const auto &AnalyzerCheck : getCheckersControlList())
|
||||
for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
|
||||
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
|
||||
|
||||
std::sort(CheckNames.begin(), CheckNames.end());
|
||||
|
@ -244,15 +258,15 @@ std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
|
|||
}
|
||||
|
||||
ClangTidyASTConsumerFactory::CheckersList
|
||||
ClangTidyASTConsumerFactory::getCheckersControlList() {
|
||||
ClangTidyASTConsumerFactory::getCheckersControlList(ChecksFilter &Filter) {
|
||||
CheckersList List;
|
||||
|
||||
bool AnalyzerChecksEnabled = false;
|
||||
for (StringRef CheckName : StaticAnalyzerChecks) {
|
||||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
AnalyzerChecksEnabled |=
|
||||
Context.getChecksFilter().isCheckEnabled(Checker) &&
|
||||
!CheckName.startswith("debug");
|
||||
AnalyzerChecksEnabled =
|
||||
AnalyzerChecksEnabled ||
|
||||
(!CheckName.startswith("debug") && Filter.isCheckEnabled(Checker));
|
||||
}
|
||||
|
||||
if (AnalyzerChecksEnabled) {
|
||||
|
@ -267,8 +281,7 @@ ClangTidyASTConsumerFactory::getCheckersControlList() {
|
|||
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
|
||||
|
||||
if (CheckName.startswith("core") ||
|
||||
(!CheckName.startswith("debug") &&
|
||||
Context.getChecksFilter().isCheckEnabled(Checker)))
|
||||
(!CheckName.startswith("debug") && Filter.isCheckEnabled(Checker)))
|
||||
List.push_back(std::make_pair(CheckName, true));
|
||||
}
|
||||
}
|
||||
|
@ -291,17 +304,18 @@ void ClangTidyCheck::setName(StringRef Name) {
|
|||
}
|
||||
|
||||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
|
||||
clang::tidy::ClangTidyContext Context(Options);
|
||||
ClangTidyASTConsumerFactory Factory(Context, Options);
|
||||
return Factory.getCheckNames();
|
||||
clang::tidy::ClangTidyContext Context(
|
||||
new DefaultOptionsProvider(ClangTidyGlobalOptions(), Options));
|
||||
ClangTidyASTConsumerFactory Factory(Context);
|
||||
return Factory.getCheckNames(Context.getChecksFilter());
|
||||
}
|
||||
|
||||
ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
|
||||
ClangTidyStats runClangTidy(ClangTidyOptionsProvider *OptionsProvider,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors) {
|
||||
ClangTool Tool(Compilations, InputFiles);
|
||||
clang::tidy::ClangTidyContext Context(Options);
|
||||
clang::tidy::ClangTidyContext Context(OptionsProvider);
|
||||
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
||||
|
||||
Tool.setDiagnosticConsumer(&DiagConsumer);
|
||||
|
@ -328,7 +342,7 @@ ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
|
|||
ClangTidyASTConsumerFactory *ConsumerFactory;
|
||||
};
|
||||
|
||||
Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context, Options)));
|
||||
Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context)));
|
||||
*Errors = Context.getErrors();
|
||||
return Context.getStats();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Tooling/Refactoring.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
|
@ -95,26 +96,21 @@ class ClangTidyCheckFactories;
|
|||
|
||||
class ClangTidyASTConsumerFactory {
|
||||
public:
|
||||
ClangTidyASTConsumerFactory(ClangTidyContext &Context,
|
||||
const ClangTidyOptions &Options);
|
||||
~ClangTidyASTConsumerFactory();
|
||||
ClangTidyASTConsumerFactory(ClangTidyContext &Context);
|
||||
|
||||
/// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
|
||||
clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
|
||||
StringRef File);
|
||||
|
||||
/// \brief Get the list of enabled checks.
|
||||
std::vector<std::string> getCheckNames();
|
||||
std::vector<std::string> getCheckNames(ChecksFilter &Filter);
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::string, bool> > CheckersList;
|
||||
CheckersList getCheckersControlList();
|
||||
CheckersList getCheckersControlList(ChecksFilter &Filter);
|
||||
|
||||
SmallVector<ClangTidyCheck *, 8> Checks;
|
||||
ClangTidyContext &Context;
|
||||
ast_matchers::MatchFinder Finder;
|
||||
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
|
||||
ClangTidyOptions Options;
|
||||
};
|
||||
|
||||
/// \brief Fills the list of check names that are enabled when the provided
|
||||
|
@ -122,10 +118,13 @@ private:
|
|||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
|
||||
|
||||
/// \brief Run a set of clang-tidy checks on a set of files.
|
||||
ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors);
|
||||
///
|
||||
/// Takes ownership of the \c OptionsProvider.
|
||||
ClangTidyStats
|
||||
runClangTidy(ClangTidyOptionsProvider *OptionsProvider,
|
||||
const tooling::CompilationDatabase &Compilations,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
std::vector<ClangTidyError> *Errors);
|
||||
|
||||
// FIXME: This interface will need to be significantly extended to be useful.
|
||||
// FIXME: Implement confidence levels for displaying/fixing errors.
|
||||
|
|
|
@ -154,8 +154,12 @@ bool ChecksFilter::isCheckEnabled(StringRef Name, bool Enabled) {
|
|||
return Enabled;
|
||||
}
|
||||
|
||||
ClangTidyContext::ClangTidyContext(const ClangTidyOptions &Options)
|
||||
: DiagEngine(nullptr), Options(Options), Filter(Options.Checks) {}
|
||||
ClangTidyContext::ClangTidyContext(ClangTidyOptionsProvider *OptionsProvider)
|
||||
: DiagEngine(nullptr), OptionsProvider(OptionsProvider) {
|
||||
// Before the first translation unit we can get errors related to command-line
|
||||
// parsing, use empty string for the file name in this case.
|
||||
setCurrentFile("");
|
||||
}
|
||||
|
||||
DiagnosticBuilder ClangTidyContext::diag(
|
||||
StringRef CheckName, SourceLocation Loc, StringRef Description,
|
||||
|
@ -190,6 +194,24 @@ void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
|
|||
DiagEngine->setSourceManager(SourceMgr);
|
||||
}
|
||||
|
||||
void ClangTidyContext::setCurrentFile(StringRef File) {
|
||||
CurrentFile = File;
|
||||
CheckFilter.reset(new ChecksFilter(getOptions().Checks));
|
||||
}
|
||||
|
||||
const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
|
||||
return OptionsProvider->getGlobalOptions();
|
||||
}
|
||||
|
||||
const ClangTidyOptions &ClangTidyContext::getOptions() const {
|
||||
return OptionsProvider->getOptions(CurrentFile);
|
||||
}
|
||||
|
||||
ChecksFilter &ClangTidyContext::getChecksFilter() {
|
||||
assert(CheckFilter != nullptr);
|
||||
return *CheckFilter;
|
||||
}
|
||||
|
||||
/// \brief Store a \c ClangTidyError.
|
||||
void ClangTidyContext::storeError(const ClangTidyError &Error) {
|
||||
Errors.push_back(Error);
|
||||
|
@ -204,8 +226,8 @@ StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
|
|||
}
|
||||
|
||||
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
|
||||
: Context(Ctx), HeaderFilter(Ctx.getOptions().HeaderFilterRegex),
|
||||
LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false) {
|
||||
: Context(Ctx), LastErrorRelatesToUserCode(false),
|
||||
LastErrorPassesLineFilter(false) {
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
Diags.reset(new DiagnosticsEngine(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
|
||||
|
@ -274,11 +296,18 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
|||
checkFilters(Info.getLocation());
|
||||
}
|
||||
|
||||
void ClangTidyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
|
||||
const Preprocessor *PP) {
|
||||
// Before the first translation unit we don't need HeaderFilter, as we
|
||||
// shouldn't get valid source locations in diagnostics.
|
||||
HeaderFilter.reset(new llvm::Regex(Context.getOptions().HeaderFilterRegex));
|
||||
}
|
||||
|
||||
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
|
||||
unsigned LineNumber) const {
|
||||
if (Context.getOptions().LineFilter.empty())
|
||||
if (Context.getGlobalOptions().LineFilter.empty())
|
||||
return true;
|
||||
for (const FileFilter& Filter : Context.getOptions().LineFilter) {
|
||||
for (const FileFilter& Filter : Context.getGlobalOptions().LineFilter) {
|
||||
if (FileName.endswith(Filter.Name)) {
|
||||
if (Filter.LineRanges.empty())
|
||||
return true;
|
||||
|
@ -319,10 +348,15 @@ void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
|
|||
}
|
||||
|
||||
StringRef FileName(File->getName());
|
||||
assert(LastErrorRelatesToUserCode || Sources.isInMainFile(Location) ||
|
||||
HeaderFilter != nullptr);
|
||||
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
|
||||
Sources.isInMainFile(Location) ||
|
||||
HeaderFilter->match(FileName);
|
||||
|
||||
unsigned LineNumber = Sources.getExpansionLineNumber(Location);
|
||||
LastErrorRelatesToUserCode |=
|
||||
Sources.isInMainFile(Location) || HeaderFilter.match(FileName);
|
||||
LastErrorPassesLineFilter |= passesLineFilter(FileName, LineNumber);
|
||||
LastErrorPassesLineFilter =
|
||||
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -67,12 +67,13 @@ struct ClangTidyError {
|
|||
/// \brief Filters checks by name.
|
||||
class ChecksFilter {
|
||||
public:
|
||||
// GlobList is a comma-separated list of globs (only '*' metacharacter is
|
||||
// supported) with optional '-' prefix to denote exclusion.
|
||||
/// \brief \p GlobList is a comma-separated list of globs (only '*'
|
||||
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
|
||||
ChecksFilter(StringRef GlobList);
|
||||
// Returns true if the check with the specified Name should be enabled.
|
||||
// The result is the last matching glob's Positive flag. If Name is not
|
||||
// matched by any globs, the check is not enabled.
|
||||
|
||||
/// \brief Returns \c true if the check with the specified \p Name should be
|
||||
/// enabled. The result is the last matching glob's Positive flag. If \p Name
|
||||
/// is not matched by any globs, the check is not enabled.
|
||||
bool isCheckEnabled(StringRef Name) { return isCheckEnabled(Name, false); }
|
||||
|
||||
private:
|
||||
|
@ -83,6 +84,8 @@ private:
|
|||
std::unique_ptr<ChecksFilter> NextFilter;
|
||||
};
|
||||
|
||||
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
|
||||
/// run.
|
||||
struct ClangTidyStats {
|
||||
ClangTidyStats()
|
||||
: ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
|
||||
|
@ -111,7 +114,10 @@ struct ClangTidyStats {
|
|||
/// \endcode
|
||||
class ClangTidyContext {
|
||||
public:
|
||||
ClangTidyContext(const ClangTidyOptions &Options);
|
||||
/// \brief Initializes \c ClangTidyContext instance.
|
||||
///
|
||||
/// Takes ownership of the \c OptionsProvider.
|
||||
ClangTidyContext(ClangTidyOptionsProvider *OptionsProvider);
|
||||
|
||||
/// \brief Report any errors detected using this method.
|
||||
///
|
||||
|
@ -122,37 +128,55 @@ public:
|
|||
StringRef Message,
|
||||
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
|
||||
|
||||
/// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
|
||||
/// correctly.
|
||||
///
|
||||
/// This is called from the \c ClangTidyCheck base class.
|
||||
void setDiagnosticsEngine(DiagnosticsEngine *Engine);
|
||||
|
||||
/// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
|
||||
///
|
||||
/// This is called from the \c ClangTidyCheck base class.
|
||||
void setSourceManager(SourceManager *SourceMgr);
|
||||
|
||||
/// \brief Should be called when starting to process new translation unit.
|
||||
void setCurrentFile(StringRef File);
|
||||
|
||||
/// \brief Returns the name of the clang-tidy check which produced this
|
||||
/// diagnostic ID.
|
||||
StringRef getCheckName(unsigned DiagnosticID) const;
|
||||
|
||||
ChecksFilter &getChecksFilter() { return Filter; }
|
||||
const ClangTidyOptions &getOptions() const { return Options; }
|
||||
/// \brief Returns check filter for the \c CurrentFile.
|
||||
ChecksFilter &getChecksFilter();
|
||||
|
||||
/// \brief Returns global options.
|
||||
const ClangTidyGlobalOptions &getGlobalOptions() const;
|
||||
|
||||
/// \brief Returns options for \c CurrentFile.
|
||||
const ClangTidyOptions &getOptions() const;
|
||||
|
||||
/// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
|
||||
/// counters.
|
||||
const ClangTidyStats &getStats() const { return Stats; }
|
||||
|
||||
/// \brief Returns all collected errors.
|
||||
const std::vector<ClangTidyError> &getErrors() const { return Errors; }
|
||||
|
||||
/// \brief Clears collected errors.
|
||||
void clearErrors() { Errors.clear(); }
|
||||
|
||||
private:
|
||||
friend class ClangTidyDiagnosticConsumer; // Calls storeError().
|
||||
// Calls setDiagnosticsEngine() and storeError().
|
||||
friend class ClangTidyDiagnosticConsumer;
|
||||
|
||||
/// \brief Store a \c ClangTidyError.
|
||||
/// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
|
||||
/// correctly.
|
||||
void setDiagnosticsEngine(DiagnosticsEngine *Engine);
|
||||
|
||||
/// \brief Store an \p Error.
|
||||
void storeError(const ClangTidyError &Error);
|
||||
|
||||
std::vector<ClangTidyError> Errors;
|
||||
DiagnosticsEngine *DiagEngine;
|
||||
ClangTidyOptions Options;
|
||||
ChecksFilter Filter;
|
||||
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
|
||||
|
||||
std::string CurrentFile;
|
||||
std::unique_ptr<ChecksFilter> CheckFilter;
|
||||
|
||||
ClangTidyStats Stats;
|
||||
|
||||
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
|
||||
|
@ -173,18 +197,25 @@ public:
|
|||
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
||||
const Diagnostic &Info) override;
|
||||
|
||||
// Flushes the internal diagnostics buffer to the ClangTidyContext.
|
||||
/// \brief Sets \c HeaderFilter to the value configured for this file.
|
||||
void BeginSourceFile(const LangOptions &LangOpts,
|
||||
const Preprocessor *PP) override;
|
||||
|
||||
/// \brief Flushes the internal diagnostics buffer to the ClangTidyContext.
|
||||
void finish() override;
|
||||
|
||||
private:
|
||||
void finalizeLastError();
|
||||
|
||||
/// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
|
||||
/// according to the diagnostic \p Location.
|
||||
void checkFilters(SourceLocation Location);
|
||||
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
|
||||
|
||||
ClangTidyContext &Context;
|
||||
llvm::Regex HeaderFilter;
|
||||
std::unique_ptr<DiagnosticsEngine> Diags;
|
||||
SmallVector<ClangTidyError, 8> Errors;
|
||||
std::unique_ptr<llvm::Regex> HeaderFilter;
|
||||
bool LastErrorRelatesToUserCode;
|
||||
bool LastErrorPassesLineFilter;
|
||||
};
|
||||
|
|
|
@ -27,12 +27,13 @@ void ClangTidyCheckFactories::addCheckFactory(StringRef Name,
|
|||
}
|
||||
|
||||
void ClangTidyCheckFactories::createChecks(
|
||||
ChecksFilter &Filter, SmallVectorImpl<ClangTidyCheck *> &Checks) {
|
||||
ChecksFilter &Filter,
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
|
||||
for (const auto &Factory : Factories) {
|
||||
if (Filter.isCheckEnabled(Factory.first)) {
|
||||
ClangTidyCheck *Check = Factory.second->createCheck();
|
||||
Check->setName(Factory.first);
|
||||
Checks.push_back(Check);
|
||||
Checks.emplace_back(Check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public:
|
|||
///
|
||||
/// The caller takes ownership of the return \c ClangTidyChecks.
|
||||
void createChecks(ChecksFilter &Filter,
|
||||
SmallVectorImpl<ClangTidyCheck *> &Checks);
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
|
||||
|
||||
typedef std::map<std::string, CheckFactoryBase *> FactoryMap;
|
||||
FactoryMap::const_iterator begin() const { return Factories.begin(); }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ClangTidyOptions.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
using clang::tidy::ClangTidyOptions;
|
||||
using clang::tidy::FileFilter;
|
||||
|
||||
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
|
||||
|
@ -46,6 +47,14 @@ template <> struct MappingTraits<FileFilter> {
|
|||
}
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<ClangTidyOptions> {
|
||||
static void mapping(IO &IO, ClangTidyOptions &Options) {
|
||||
IO.mapOptional("Checks", Options.Checks);
|
||||
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
|
||||
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yaml
|
||||
} // namespace llvm
|
||||
|
||||
|
@ -54,11 +63,18 @@ namespace tidy {
|
|||
|
||||
/// \brief Parses -line-filter option and stores it to the \c Options.
|
||||
llvm::error_code parseLineFilter(const std::string &LineFilter,
|
||||
clang::tidy::ClangTidyOptions &Options) {
|
||||
clang::tidy::ClangTidyGlobalOptions &Options) {
|
||||
llvm::yaml::Input Input(LineFilter);
|
||||
Input >> Options.LineFilter;
|
||||
return Input.error();
|
||||
}
|
||||
|
||||
llvm::error_code parseConfiguration(const std::string &Config,
|
||||
clang::tidy::ClangTidyOptions &Options) {
|
||||
llvm::yaml::Input Input(Config);
|
||||
Input >> Options;
|
||||
return Input.error();
|
||||
}
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/system_error.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
@ -18,32 +19,82 @@
|
|||
namespace clang {
|
||||
namespace tidy {
|
||||
|
||||
/// \brief Contains a list of line ranges in a single file.
|
||||
struct FileFilter {
|
||||
/// \brief File name.
|
||||
std::string Name;
|
||||
// LineRange is a pair<start, end> (inclusive).
|
||||
|
||||
/// \brief LineRange is a pair<start, end> (inclusive).
|
||||
typedef std::pair<unsigned, unsigned> LineRange;
|
||||
|
||||
/// \brief A list of line ranges in this file, for which we show warnings.
|
||||
std::vector<LineRange> LineRanges;
|
||||
};
|
||||
|
||||
/// \brief Contains options for clang-tidy.
|
||||
/// \brief Global options. These options are neither stored nor read from
|
||||
/// configuration files.
|
||||
struct ClangTidyGlobalOptions {
|
||||
/// \brief Output warnings from certain line ranges of certain files only. If
|
||||
/// this list is emtpy, it won't be applied.
|
||||
std::vector<FileFilter> LineFilter;
|
||||
};
|
||||
|
||||
/// \brief Contains options for clang-tidy. These options may be read from
|
||||
/// configuration files, and may be different for different translation units.
|
||||
struct ClangTidyOptions {
|
||||
/// \brief Allow all checks and no headers by default.
|
||||
ClangTidyOptions() : Checks("*"), AnalyzeTemporaryDtors(false) {}
|
||||
|
||||
/// \brief Checks filter.
|
||||
std::string Checks;
|
||||
|
||||
// Output warnings from headers matching this filter. Warnings from main files
|
||||
// will always be displayed.
|
||||
/// \brief Output warnings from headers matching this filter. Warnings from
|
||||
/// main files will always be displayed.
|
||||
std::string HeaderFilterRegex;
|
||||
|
||||
// Output warnings from certain line ranges of certain files only. If this
|
||||
// list is emtpy, it won't be applied.
|
||||
std::vector<FileFilter> LineFilter;
|
||||
|
||||
/// \brief Turns on temporary destructor-based analysis.
|
||||
bool AnalyzeTemporaryDtors;
|
||||
};
|
||||
|
||||
/// \brief Parses LineFilter from JSON and stores it to the \c Options.
|
||||
/// \brief Abstract interface for retrieving various ClangTidy options.
|
||||
class ClangTidyOptionsProvider {
|
||||
public:
|
||||
virtual ~ClangTidyOptionsProvider() {}
|
||||
|
||||
/// \brief Returns global options, which are independent of the file.
|
||||
virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0;
|
||||
|
||||
/// \brief Returns options applying to a specific translation unit with the
|
||||
/// specified \p FileName.
|
||||
virtual const ClangTidyOptions &getOptions(llvm::StringRef FileName) = 0;
|
||||
};
|
||||
|
||||
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
|
||||
/// returns the same options for all files.
|
||||
class DefaultOptionsProvider : public ClangTidyOptionsProvider {
|
||||
public:
|
||||
DefaultOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
|
||||
const ClangTidyOptions &Options)
|
||||
: GlobalOptions(GlobalOptions), DefaultOptions(Options) {}
|
||||
const ClangTidyGlobalOptions &getGlobalOptions() override {
|
||||
return GlobalOptions;
|
||||
}
|
||||
const ClangTidyOptions &getOptions(llvm::StringRef) override {
|
||||
return DefaultOptions;
|
||||
}
|
||||
|
||||
private:
|
||||
ClangTidyGlobalOptions GlobalOptions;
|
||||
ClangTidyOptions DefaultOptions;
|
||||
};
|
||||
|
||||
/// \brief Parses LineFilter from JSON and stores it to the \p Options.
|
||||
llvm::error_code parseLineFilter(const std::string &LineFilter,
|
||||
clang::tidy::ClangTidyOptions &Options);
|
||||
clang::tidy::ClangTidyGlobalOptions &Options);
|
||||
|
||||
/// \brief Parses configuration from JSON and stores it to the \p Options.
|
||||
llvm::error_code parseConfiguration(const std::string &Config,
|
||||
clang::tidy::ClangTidyOptions &Options);
|
||||
|
||||
} // end namespace tidy
|
||||
} // end namespace clang
|
||||
|
|
|
@ -108,17 +108,19 @@ static void printStats(const clang::tidy::ClangTidyStats &Stats) {
|
|||
int main(int argc, const char **argv) {
|
||||
CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory);
|
||||
|
||||
clang::tidy::ClangTidyOptions Options;
|
||||
Options.Checks = DefaultChecks + Checks;
|
||||
Options.HeaderFilterRegex = HeaderFilter;
|
||||
Options.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
|
||||
clang::tidy::ClangTidyGlobalOptions GlobalOptions;
|
||||
if (llvm::error_code Err =
|
||||
clang::tidy::parseLineFilter(LineFilter, Options)) {
|
||||
clang::tidy::parseLineFilter(LineFilter, GlobalOptions)) {
|
||||
llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";
|
||||
llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
clang::tidy::ClangTidyOptions Options;
|
||||
Options.Checks = DefaultChecks + Checks;
|
||||
Options.HeaderFilterRegex = HeaderFilter;
|
||||
Options.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
|
||||
|
||||
std::vector<std::string> EnabledChecks = clang::tidy::getCheckNames(Options);
|
||||
|
||||
// FIXME: Allow using --list-checks without positional arguments.
|
||||
|
@ -136,10 +138,13 @@ int main(int argc, const char **argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Implement configuration file reading and a "real" options provider.
|
||||
auto OptionsProvider =
|
||||
new clang::tidy::DefaultOptionsProvider(GlobalOptions, Options);
|
||||
std::vector<clang::tidy::ClangTidyError> Errors;
|
||||
clang::tidy::ClangTidyStats Stats =
|
||||
clang::tidy::runClangTidy(Options, OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList(), &Errors);
|
||||
clang::tidy::ClangTidyStats Stats = clang::tidy::runClangTidy(
|
||||
OptionsProvider, OptionsParser.getCompilations(),
|
||||
OptionsParser.getSourcePathList(), &Errors);
|
||||
clang::tidy::handleErrors(Errors, Fix);
|
||||
|
||||
printStats(Stats);
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace tidy {
|
|||
namespace test {
|
||||
|
||||
TEST(ParseLineFilter, EmptyFilter) {
|
||||
ClangTidyOptions Options;
|
||||
ClangTidyGlobalOptions Options;
|
||||
EXPECT_FALSE(parseLineFilter("", Options));
|
||||
EXPECT_TRUE(Options.LineFilter.empty());
|
||||
EXPECT_FALSE(parseLineFilter("[]", Options));
|
||||
|
@ -14,7 +14,7 @@ TEST(ParseLineFilter, EmptyFilter) {
|
|||
}
|
||||
|
||||
TEST(ParseLineFilter, InvalidFilter) {
|
||||
ClangTidyOptions Options;
|
||||
ClangTidyGlobalOptions Options;
|
||||
// TODO: Figure out why parsing succeeds here.
|
||||
EXPECT_FALSE(parseLineFilter("asdf", Options));
|
||||
EXPECT_TRUE(Options.LineFilter.empty());
|
||||
|
@ -30,7 +30,7 @@ TEST(ParseLineFilter, InvalidFilter) {
|
|||
}
|
||||
|
||||
TEST(ParseLineFilter, ValidFilter) {
|
||||
ClangTidyOptions Options;
|
||||
ClangTidyGlobalOptions Options;
|
||||
llvm::error_code Error = parseLineFilter(
|
||||
"[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"
|
||||
"{\"name\":\"file2.h\"},"
|
||||
|
@ -54,6 +54,18 @@ TEST(ParseLineFilter, ValidFilter) {
|
|||
EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second);
|
||||
}
|
||||
|
||||
TEST(ParseConfiguration, ValidConfiguration) {
|
||||
ClangTidyOptions Options;
|
||||
llvm::error_code Error = parseConfiguration("Checks: \"-*,misc-*\"\n"
|
||||
"HeaderFilterRegex: \".*\"\n"
|
||||
"AnalyzeTemporaryDtors: true\n",
|
||||
Options);
|
||||
EXPECT_FALSE(Error);
|
||||
EXPECT_EQ("-*,misc-*", Options.Checks);
|
||||
EXPECT_EQ(".*", Options.HeaderFilterRegex);
|
||||
EXPECT_TRUE(Options.AnalyzeTemporaryDtors);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
|
|
@ -43,8 +43,8 @@ template <typename T>
|
|||
std::string runCheckOnCode(StringRef Code,
|
||||
std::vector<ClangTidyError> *Errors = nullptr) {
|
||||
T Check;
|
||||
ClangTidyOptions Options;
|
||||
ClangTidyContext Context(Options);
|
||||
ClangTidyContext Context(
|
||||
new DefaultOptionsProvider(ClangTidyGlobalOptions(), ClangTidyOptions()));
|
||||
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
||||
Check.setContext(&Context);
|
||||
std::vector<std::string> ArgCXX11(1, "-std=c++11");
|
||||
|
|
Загрузка…
Ссылка в новой задаче