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 @@ -106,6 +106,12 @@ /// Add extra compilation arguments to the start of the list. llvm::Optional ExtraArgsBefore; + + /// Only used in the FileOptionsProvider. If true, FileOptionsProvider will + /// take a configuration file in the parent directory (if any exists) and + /// apply this config file on top of the parent one. If false or missing, + /// only this configuration file will be used. + llvm::Optional InheritParentConfig; }; /// Abstract interface for retrieving various ClangTidy 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 @@ -92,6 +92,7 @@ IO.mapOptional("CheckOptions", NOpts->Options); IO.mapOptional("ExtraArgs", Options.ExtraArgs); IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore); + IO.mapOptional("InheritParentConfig", Options.InheritParentConfig); } }; @@ -237,6 +238,7 @@ 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()); @@ -256,15 +258,21 @@ while (Path != CurrentPath) { LLVM_DEBUG(llvm::dbgs() << "Caching configuration for path " << Path << ".\n"); - CachedOptions[Path] = *Result; + if (!CachedOptions.count(Path)) + CachedOptions[Path] = *Result; Path = llvm::sys::path::parent_path(Path); } CachedOptions[Path] = *Result; RawOptions.push_back(*Result); - break; + if (!Result->first.InheritParentConfig || + !*Result->first.InheritParentConfig) + break; } } + // Reverse order of file configs because closer configs should have higher + // priority. + std::reverse(RawOptions.begin() + FirstFileConfig, RawOptions.end()); RawOptions.push_back(CommandLineOptions); return RawOptions; } 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 @@ -36,17 +36,20 @@ Configuration files: clang-tidy attempts to read configuration for each source file from a .clang-tidy file located in the closest parent directory of the source - file. If any configuration options have a corresponding command-line - option, command-line option takes precedence. The effective - configuration can be inspected using -dump-config: + file. If InheritParentConfig is true in a config file, the configuration file + in the parent directory (if any exists) will be taken and current config file + will be applied on top of the parent one. If any configuration options have + a corresponding command-line option, command-line option takes precedence. + The effective configuration can be inspected using -dump-config: $ clang-tidy -dump-config --- - Checks: '-*,some-check' - WarningsAsErrors: '' - HeaderFilterRegex: '' - FormatStyle: none - User: user + Checks: '-*,some-check' + WarningsAsErrors: '' + HeaderFilterRegex: '' + FormatStyle: none + InheritParentConfig: true + User: user CheckOptions: - key: some-check.SomeOption value: 'some value' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/3/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: 'from-child3' +HeaderFilterRegex: 'child3' diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/.clang-tidy new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/.clang-tidy @@ -0,0 +1,6 @@ +Checks: '-*,modernize-loop-convert' +CheckOptions: + - key: modernize-loop-convert.MaxCopySize + value: '10' + - key: modernize-loop-convert.MinConfidence + value: reasonable diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/44/.clang-tidy b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/44/.clang-tidy new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-files/4/44/.clang-tidy @@ -0,0 +1,7 @@ +InheritParentConfig: true +Checks: 'llvm-qualified-auto' +CheckOptions: + - key: modernize-loop-convert.MaxCopySize + value: '20' + - key: llvm-qualified-auto.AddConstToQualified + value: '1' 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 @@ -7,6 +7,23 @@ // RUN: clang-tidy -dump-config %S/Inputs/config-files/2/- -- | FileCheck %s -check-prefix=CHECK-CHILD2 // CHECK-CHILD2: Checks: {{.*}}from-parent // CHECK-CHILD2: HeaderFilterRegex: parent +// RUN: clang-tidy -dump-config %S/Inputs/config-files/3/- -- | FileCheck %s -check-prefix=CHECK-CHILD3 +// CHECK-CHILD3: Checks: {{.*}}from-parent,from-child3 +// CHECK-CHILD3: HeaderFilterRegex: child3 // RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE // CHECK-COMMAND-LINE: Checks: {{.*}}from-parent,from-command-line // CHECK-COMMAND-LINE: HeaderFilterRegex: from command line + +// For this test we have to use names of the real checks because otherwise values are ignored. +// RUN: clang-tidy -dump-config %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-CHILD4 +// CHECK-CHILD4: Checks: {{.*}}modernize-loop-convert,llvm-qualified-auto +// CHECK-CHILD4: - key: llvm-qualified-auto.AddConstToQualified +// CHECK-CHILD4-NEXT: value: '1 +// CHECK-CHILD4: - key: modernize-loop-convert.MaxCopySize +// CHECK-CHILD4-NEXT: value: '20' +// CHECK-CHILD4: - key: modernize-loop-convert.MinConfidence +// CHECK-CHILD4-NEXT: value: reasonable + +// RUN: clang-tidy --explain-config %S/Inputs/config-files/4/44/- -- | FileCheck %s -check-prefix=CHECK-EXPLAIN +// 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.