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:
Alexander Kornienko 2014-06-05 13:31:45 +00:00
Родитель e04d0b1c60
Коммит ac560ec8cb
11 изменённых файлов: 266 добавлений и 103 удалений

Просмотреть файл

@ -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");