From a46952221e44cb53b707bca433b5177191de779d Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Thu, 5 Jun 2014 13:31:45 +0000 Subject: [PATCH] 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 llvm-svn: 210260 --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 84 +++++++++++++--------- clang-tools-extra/clang-tidy/ClangTidy.h | 23 +++--- .../clang-tidy/ClangTidyDiagnosticConsumer.cpp | 52 +++++++++++--- .../clang-tidy/ClangTidyDiagnosticConsumer.h | 71 ++++++++++++------ clang-tools-extra/clang-tidy/ClangTidyModule.cpp | 5 +- clang-tools-extra/clang-tidy/ClangTidyModule.h | 2 +- clang-tools-extra/clang-tidy/ClangTidyOptions.cpp | 18 ++++- clang-tools-extra/clang-tidy/ClangTidyOptions.h | 71 +++++++++++++++--- .../clang-tidy/tool/ClangTidyMain.cpp | 21 +++--- .../unittests/clang-tidy/ClangTidyOptionsTest.cpp | 18 ++++- .../unittests/clang-tidy/ClangTidyTest.h | 4 +- 11 files changed, 266 insertions(+), 103 deletions(-) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 65c10f4..5a3af2c 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -169,51 +169,63 @@ private: unsigned AppliedFixes; }; +class ClangTidyASTConsumer : public MultiplexConsumer { +public: + ClangTidyASTConsumer(const SmallVectorImpl &Consumers, + std::unique_ptr Finder, + std::vector> Checks) + : MultiplexConsumer(Consumers), Finder(std::move(Finder)), + Checks(std::move(Checks)) {} + +private: + std::unique_ptr Finder; + std::vector> 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 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> Checks; + ChecksFilter &Filter = Context.getChecksFilter(); + CheckFactories->createChecks(Filter, Checks); + + std::unique_ptr Finder( + new ast_matchers::MatchFinder); + for (auto &Check : Checks) { + Check->setContext(&Context); + Check->registerMatchers(&*Finder); Check->registerPPCallbacks(Compiler); + } SmallVector 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 ClangTidyASTConsumerFactory::getCheckNames() { +std::vector +ClangTidyASTConsumerFactory::getCheckNames(ChecksFilter &Filter) { std::vector 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 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 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 InputFiles, std::vector *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(); } diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h index 51fb2fe..8b181fc 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.h +++ b/clang-tools-extra/clang-tidy/ClangTidy.h @@ -16,6 +16,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Refactoring.h" +#include #include 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 getCheckNames(); + std::vector getCheckNames(ChecksFilter &Filter); private: typedef std::vector > CheckersList; - CheckersList getCheckersControlList(); + CheckersList getCheckersControlList(ChecksFilter &Filter); - SmallVector Checks; ClangTidyContext &Context; - ast_matchers::MatchFinder Finder; std::unique_ptr CheckFactories; - ClangTidyOptions Options; }; /// \brief Fills the list of check names that are enabled when the provided @@ -122,10 +118,13 @@ private: std::vector 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 InputFiles, - std::vector *Errors); +/// +/// Takes ownership of the \c OptionsProvider. +ClangTidyStats +runClangTidy(ClangTidyOptionsProvider *OptionsProvider, + const tooling::CompilationDatabase &Compilations, + ArrayRef InputFiles, + std::vector *Errors); // FIXME: This interface will need to be significantly extended to be useful. // FIXME: Implement confidence levels for displaying/fixing errors. diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index 625f92ae..807e48b 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -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 DiagOpts = new DiagnosticOptions(); Diags.reset(new DiagnosticsEngine( IntrusiveRefCntPtr(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 { diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 926a3b4..4dac7b6 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -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 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 &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 Errors; DiagnosticsEngine *DiagEngine; - ClangTidyOptions Options; - ChecksFilter Filter; + std::unique_ptr OptionsProvider; + + std::string CurrentFile; + std::unique_ptr CheckFilter; + ClangTidyStats Stats; llvm::DenseMap 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 Diags; SmallVector Errors; + std::unique_ptr HeaderFilter; bool LastErrorRelatesToUserCode; bool LastErrorPassesLineFilter; }; diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp index b79194d..40812a2 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp @@ -27,12 +27,13 @@ void ClangTidyCheckFactories::addCheckFactory(StringRef Name, } void ClangTidyCheckFactories::createChecks( - ChecksFilter &Filter, SmallVectorImpl &Checks) { + ChecksFilter &Filter, + std::vector> &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); } } } diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h index 00a5a8d..625be7a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.h +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.h @@ -87,7 +87,7 @@ public: /// /// The caller takes ownership of the return \c ClangTidyChecks. void createChecks(ChecksFilter &Filter, - SmallVectorImpl &Checks); + std::vector> &Checks); typedef std::map FactoryMap; FactoryMap::const_iterator begin() const { return Factories.begin(); } diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp index fcf66ee..93e12ca 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -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 { } }; +template <> struct MappingTraits { + 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 diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 60b38ea..1a5a92e 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -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 #include @@ -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 (inclusive). + + /// \brief LineRange is a pair (inclusive). typedef std::pair LineRange; + + /// \brief A list of line ranges in this file, for which we show warnings. std::vector 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 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 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 diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index f338805..3447a97 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -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 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 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); diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp index 84de275..0a294d5 100644 --- a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp @@ -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 diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h index 7c97e73..6fb0318 100644 --- a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h @@ -43,8 +43,8 @@ template std::string runCheckOnCode(StringRef Code, std::vector *Errors = nullptr) { T Check; - ClangTidyOptions Options; - ClangTidyContext Context(Options); + ClangTidyContext Context( + new DefaultOptionsProvider(ClangTidyGlobalOptions(), ClangTidyOptions())); ClangTidyDiagnosticConsumer DiagConsumer(Context); Check.setContext(&Context); std::vector ArgCXX11(1, "-std=c++11"); -- 2.7.4