diff options
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 65c10f4cfef..5a3af2c2af8 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<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();  } diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h index 51fb2fe9a2a..8b181fc8c90 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 <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. diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index 625f92ae8a0..807e48b0d7c 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<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 { diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 926a3b43a00..4dac7b6cd8e 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<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;  }; diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp index b79194d44ef..40812a2eba1 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<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);      }    }  } diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h index 00a5a8d4a63..625be7aa482 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<ClangTidyCheck *> &Checks); +                    std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);    typedef std::map<std::string, CheckFactoryBase *> 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 fcf66ee8a61..93e12ca0870 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<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 diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 60b38eabf1a..1a5a92e9e3e 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 <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 diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index f338805fcfd..3447a971264 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<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); diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/clang-tools-extra/unittests/clang-tidy/ClangTidyOptionsTest.cpp index 84de275e00d..0a294d5e119 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 7c97e73af50..6fb031811f3 100644 --- a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h @@ -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");  | 

