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 @@ -70,7 +70,8 @@ NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap) { Options.reserve(OptionMap.size()); for (const auto &KeyValue : OptionMap) - Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value); + Options.emplace_back(std::string(KeyValue.getKey()), + KeyValue.getValue().Value); } ClangTidyOptions::OptionMap denormalize(IO &) { ClangTidyOptions::OptionMap Map; @@ -117,10 +118,58 @@ } } +struct ChecksVariant { + std::optional AsString; + std::optional> AsVector; +}; + +template <> +void yamlize(IO &IO, ChecksVariant &Checks, bool, EmptyContext &Ctx) { + if (!IO.outputting()) { + // Special case for reading from YAML + // Must support reading from both a string or a list + Input &I = reinterpret_cast(IO); + + if (isa(I.getCurrentNode()) || + isa(I.getCurrentNode())) { + Checks.AsString = std::string(); + yamlize(IO, *Checks.AsString, true, Ctx); + } else if (isa(I.getCurrentNode())) { + Checks.AsVector = std::vector(); + yamlize(IO, *Checks.AsVector, true, Ctx); + } else { + IO.setError("expected string or sequence"); + } + } +} + +static void mapChecks(IO &IO, std::optional &Checks) { + if (IO.outputting()) { + // Output as a string + IO.mapOptional("Checks", Checks); + } else { + // Input as either a string or a list + ChecksVariant ChecksAsVariant; + IO.mapOptional("Checks", ChecksAsVariant); + + // If we got the input as string, assign directly + if (ChecksAsVariant.AsString) { + Checks = ChecksAsVariant.AsString; + } + // If we got the input as a list, concatenate the items to create string + else if (ChecksAsVariant.AsVector) { + Checks = std::string(); + for (std::string const &check : *ChecksAsVariant.AsVector) { + *Checks += check + ","; + } + } + } +} + template <> struct MappingTraits { static void mapping(IO &IO, ClangTidyOptions &Options) { bool Ignored = false; - IO.mapOptional("Checks", Options.Checks); + mapChecks(IO, Options.Checks); IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors); IO.mapOptional("HeaderFileExtensions", Options.HeaderFileExtensions); IO.mapOptional("ImplementationFileExtensions", diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-bracket b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-bracket new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-bracket @@ -0,0 +1,6 @@ +Checks: [ + "-*", + "hicpp-uppercase-literal-suffix", + "hicpp-use-auto", + "hicpp-use-emplace", +] diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-dash b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-dash new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/config-file/config-file-list-dash @@ -0,0 +1,5 @@ +Checks: + - "-*" + - "hicpp-uppercase-literal-suffix" + - "hicpp-use-auto" + - "hicpp-use-emplace" diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/config-file.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/config-file.cpp --- a/clang-tools-extra/test/clang-tidy/infrastructure/config-file.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/config-file.cpp @@ -6,3 +6,15 @@ // CHECK-SPACES-NEXT: hicpp-use-auto // CHECK-SPACES-NEXT: hicpp-use-emplace // CHECK-SPACES-EMPTY: +// RUN: clang-tidy -config-file=%S/Inputs/config-file/config-file-list-dash --list-checks -- | FileCheck %s -check-prefix=CHECK-LIST-DASH +// CHECK-LIST-DASH: Enabled checks: +// CHECK-LIST-DASH-NEXT: hicpp-uppercase-literal-suffix +// CHECK-LIST-DASH-NEXT: hicpp-use-auto +// CHECK-LIST-DASH-NEXT: hicpp-use-emplace +// CHECK-LIST-DASH-EMPTY: +// RUN: clang-tidy -config-file=%S/Inputs/config-file/config-file-list-bracket --list-checks -- | FileCheck %s -check-prefix=CHECK-LIST-BRACKET +// CHECK-LIST-BRACKET: Enabled checks: +// CHECK-LIST-BRACKET-NEXT: hicpp-uppercase-literal-suffix +// CHECK-LIST-BRACKET-NEXT: hicpp-use-auto +// CHECK-LIST-BRACKET-NEXT: hicpp-use-emplace +// CHECK-LIST-BRACKET-EMPTY: