diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -177,30 +177,7 @@ ClangTidyOptions DefaultOptions; }; -/// Implementation of ClangTidyOptions interface, which is used for -/// '-config' command-line option. -class ConfigOptionsProvider : public DefaultOptionsProvider { -public: - ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, - const ClangTidyOptions &DefaultOptions, - const ClangTidyOptions &ConfigOptions, - const ClangTidyOptions &OverrideOptions); - std::vector getRawOptions(llvm::StringRef FileName) override; - -private: - ClangTidyOptions ConfigOptions; - ClangTidyOptions OverrideOptions; -}; - -/// Implementation of the \c ClangTidyOptionsProvider interface, which -/// tries to find a configuration file in the closest parent directory of each -/// source file. -/// -/// By default, files named ".clang-tidy" will be considered, and the -/// \c clang::tidy::parseConfiguration function will be used for parsing, but a -/// custom set of configuration file names and parsing functions can be -/// specified using the appropriate constructor. -class FileOptionsProvider : public DefaultOptionsProvider { +class FileOptionsBaseProvider : public DefaultOptionsProvider { public: // A pair of configuration file base name and a function parsing // configuration from text in the corresponding format. @@ -227,6 +204,57 @@ /// take precedence over ".clang-tidy" if both reside in the same directory. typedef std::vector ConfigFileHandlers; + FileOptionsBaseProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + llvm::IntrusiveRefCntPtr FS = nullptr); + + FileOptionsBaseProvider(const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + const ConfigFileHandlers &ConfigHandlers); + +protected: + void addRawFileOptions(llvm::StringRef AbsolutePath, + std::vector &CurOptions); + + /// Try to read configuration files from \p Directory using registered + /// \c ConfigHandlers. + llvm::Optional tryReadConfigFile(llvm::StringRef Directory); + + llvm::StringMap CachedOptions; + ClangTidyOptions OverrideOptions; + ConfigFileHandlers ConfigHandlers; + llvm::IntrusiveRefCntPtr FS; +}; + +/// Implementation of ClangTidyOptions interface, which is used for +/// '-config' command-line option. +class ConfigOptionsProvider : public FileOptionsBaseProvider { +public: + ConfigOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &ConfigOptions, + const ClangTidyOptions &OverrideOptions, + llvm::IntrusiveRefCntPtr FS = nullptr); + std::vector getRawOptions(llvm::StringRef FileName) override; + +private: + ClangTidyOptions ConfigOptions; +}; + +/// Implementation of the \c ClangTidyOptionsProvider interface, which +/// tries to find a configuration file in the closest parent directory of each +/// source file. +/// +/// By default, files named ".clang-tidy" will be considered, and the +/// \c clang::tidy::parseConfiguration function will be used for parsing, but a +/// custom set of configuration file names and parsing functions can be +/// specified using the appropriate constructor. +class FileOptionsProvider : public FileOptionsBaseProvider { +public: /// Initializes the \c FileOptionsProvider instance. /// /// \param GlobalOptions are just stored and returned to the caller of @@ -266,16 +294,6 @@ const ConfigFileHandlers &ConfigHandlers); std::vector getRawOptions(llvm::StringRef FileName) override; - -protected: - /// Try to read configuration files from \p Directory using registered - /// \c ConfigHandlers. - llvm::Optional tryReadConfigFile(llvm::StringRef Directory); - - llvm::StringMap CachedOptions; - ClangTidyOptions OverrideOptions; - ConfigFileHandlers ConfigHandlers; - llvm::IntrusiveRefCntPtr FS; }; /// Parses LineFilter from JSON and stores it to the \p Options. diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -193,14 +193,27 @@ const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &ConfigOptions, - const ClangTidyOptions &OverrideOptions) - : DefaultOptionsProvider(GlobalOptions, DefaultOptions), - ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {} + const ClangTidyOptions &OverrideOptions, + llvm::IntrusiveRefCntPtr FS) + : FileOptionsBaseProvider(GlobalOptions, DefaultOptions, OverrideOptions, + FS), + ConfigOptions(ConfigOptions) {} std::vector ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) { std::vector RawOptions = DefaultOptionsProvider::getRawOptions(FileName); + if (ConfigOptions.InheritParentConfig.getValueOr(false)) { + LLVM_DEBUG(llvm::dbgs() + << "Getting options for file " << FileName << "...\n"); + assert(FS && "FS must be set."); + + llvm::SmallString<128> AbsoluteFilePath(FileName); + + if (!FS->makeAbsolute(AbsoluteFilePath)) { + addRawFileOptions(AbsoluteFilePath, RawOptions); + } + } RawOptions.emplace_back(ConfigOptions, OptionsSourceTypeConfigCommandLineOption); RawOptions.emplace_back(OverrideOptions, @@ -208,7 +221,7 @@ return RawOptions; } -FileOptionsProvider::FileOptionsProvider( +FileOptionsBaseProvider::FileOptionsBaseProvider( const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions, @@ -220,36 +233,21 @@ ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration); } -FileOptionsProvider::FileOptionsProvider( +FileOptionsBaseProvider::FileOptionsBaseProvider( const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions, - const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers) + const FileOptionsBaseProvider::ConfigFileHandlers &ConfigHandlers) : DefaultOptionsProvider(GlobalOptions, DefaultOptions), OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {} -// FIXME: This method has some common logic with clang::format::getStyle(). -// Consider pulling out common bits to a findParentFileWithName function or -// similar. -std::vector -FileOptionsProvider::getRawOptions(StringRef FileName) { - LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName - << "...\n"); - assert(FS && "FS must be set."); - - llvm::SmallString<128> AbsoluteFilePath(FileName); +void FileOptionsBaseProvider::addRawFileOptions( + llvm::StringRef AbsolutePath, std::vector &CurOptions) { + auto CurSize = CurOptions.size(); - if (FS->makeAbsolute(AbsoluteFilePath)) - return {}; - - std::vector RawOptions = - DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str()); - OptionsSource CommandLineOptions(OverrideOptions, - OptionsSourceTypeCheckCommandLineOption); - size_t FirstFileConfig = RawOptions.size(); // Look for a suitable configuration file in all parent directories of the // file. Start with the immediate parent directory and move up. - StringRef Path = llvm::sys::path::parent_path(AbsoluteFilePath.str()); + StringRef Path = llvm::sys::path::parent_path(AbsolutePath); for (StringRef CurrentPath = Path; !CurrentPath.empty(); CurrentPath = llvm::sys::path::parent_path(CurrentPath)) { llvm::Optional Result; @@ -272,21 +270,58 @@ } CachedOptions[Path] = *Result; - RawOptions.push_back(*Result); - if (!Result->first.InheritParentConfig || - !*Result->first.InheritParentConfig) + CurOptions.push_back(*Result); + if (!Result->first.InheritParentConfig.getValueOr(false)) break; } } // Reverse order of file configs because closer configs should have higher // priority. - std::reverse(RawOptions.begin() + FirstFileConfig, RawOptions.end()); + std::reverse(CurOptions.begin() + CurSize, CurOptions.end()); +} + +FileOptionsProvider::FileOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + llvm::IntrusiveRefCntPtr VFS) + : FileOptionsBaseProvider(GlobalOptions, DefaultOptions, OverrideOptions, + VFS){}; + +FileOptionsProvider::FileOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + const FileOptionsBaseProvider::ConfigFileHandlers &ConfigHandlers) + : FileOptionsBaseProvider(GlobalOptions, DefaultOptions, OverrideOptions, + ConfigHandlers) {} + +// FIXME: This method has some common logic with clang::format::getStyle(). +// Consider pulling out common bits to a findParentFileWithName function or +// similar. +std::vector +FileOptionsProvider::getRawOptions(StringRef FileName) { + LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName + << "...\n"); + assert(FS && "FS must be set."); + + llvm::SmallString<128> AbsoluteFilePath(FileName); + + if (FS->makeAbsolute(AbsoluteFilePath)) + return {}; + + std::vector RawOptions = + DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str()); + addRawFileOptions(AbsoluteFilePath, RawOptions); + OptionsSource CommandLineOptions(OverrideOptions, + OptionsSourceTypeCheckCommandLineOption); + RawOptions.push_back(CommandLineOptions); return RawOptions; } llvm::Optional -FileOptionsProvider::tryReadConfigFile(StringRef Directory) { +FileOptionsBaseProvider::tryReadConfigFile(StringRef Directory) { assert(!Directory.empty()); if (!llvm::sys::fs::is_directory(Directory)) { diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -292,14 +292,13 @@ OverrideOptions.SystemHeaders = SystemHeaders; if (FormatStyle.getNumOccurrences() > 0) OverrideOptions.FormatStyle = FormatStyle; - if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { return std::make_unique( GlobalOptions, ClangTidyOptions::getDefaults().mergeWith(DefaultOptions, 0), - *ParsedConfig, OverrideOptions); + *ParsedConfig, OverrideOptions, std::move(FS)); } else { llvm::errs() << "Error: invalid configuration specified.\n" << ParsedConfig.getError().message() << "\n"; diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp --- a/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/config-files.cpp @@ -18,7 +18,7 @@ // RUN: clang-tidy -dump-config %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-CHILD4 // CHECK-CHILD4: Checks: {{.*}}modernize-loop-convert,modernize-use-using,llvm-qualified-auto // CHECK-CHILD4: - key: llvm-qualified-auto.AddConstToQualified -// CHECK-CHILD4-NEXT: value: '1 +// CHECK-CHILD4-NEXT: value: '1' // CHECK-CHILD4: - key: modernize-loop-convert.MaxCopySize // CHECK-CHILD4-NEXT: value: '20' // CHECK-CHILD4: - key: modernize-loop-convert.MinConfidence @@ -30,3 +30,23 @@ // CHECK-EXPLAIN: 'llvm-qualified-auto' is enabled in the {{.*}}{{[/\\]}}Inputs{{[/\\]}}config-files{{[/\\]}}4{{[/\\]}}44{{[/\\]}}.clang-tidy. // CHECK-EXPLAIN: 'modernize-loop-convert' is enabled in the {{.*}}{{[/\\]}}Inputs{{[/\\]}}config-files{{[/\\]}}4{{[/\\]}}.clang-tidy. // CHECK-EXPLAIN: 'modernize-use-using' is enabled in the {{.*}}{{[/\\]}}Inputs{{[/\\]}}config-files{{[/\\]}}4{{[/\\]}}.clang-tidy. + +// RUN: clang-tidy -dump-config \ +// RUN: --config='{InheritParentConfig: true, \ +// RUN: Checks: -llvm-qualified-auto, \ +// RUN: CheckOptions: [{key: modernize-loop-convert.MaxCopySize, value: 21}]}' \ +// RUN: %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-CHILD5 +// CHECK-CHILD5: Checks: {{.*}}modernize-loop-convert,modernize-use-using,llvm-qualified-auto,-llvm-qualified-auto +// CHECK-CHILD5: - key: modernize-loop-convert.MaxCopySize +// CHECK-CHILD5-NEXT: value: '21' +// CHECK-CHILD5: - key: modernize-loop-convert.MinConfidence +// CHECK-CHILD5-NEXT: value: reasonable +// CHECK-CHILD5: - key: modernize-use-using.IgnoreMacros +// CHECK-CHILD5-NEXT: value: '0' + +// RUN: clang-tidy -dump-config \ +// RUN: --config='{InheritParentConfig: false, \ +// RUN: Checks: -llvm-qualified-auto}' \ +// RUN: %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-CHILD6 +// CHECK-CHILD6: Checks: {{.*}}-llvm-qualified-auto +// CHECK-CHILD6-NOT: - key: modernize-use-using.IgnoreMacros