diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp index 21455db7c7e7b..213dcd59ba536 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -16,6 +16,7 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/Path.h" +#include "llvm/Support/YAMLGenerateSchema.h" #include "llvm/Support/YAMLTraits.h" #include #include @@ -87,7 +88,7 @@ struct NOptionMap { template <> void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool, EmptyContext &Ctx) { - if (IO.outputting()) { + if (IO.getKind() == IOKind::Outputting) { // Ensure check options are sorted std::vector> SortedOptions; SortedOptions.reserve(Val.size()); @@ -108,7 +109,7 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool, IO.postflightKey(SaveInfo); } IO.endMapping(); - } else { + } else if (IO.getKind() == IOKind::Inputting) { // We need custom logic here to support the old method of specifying check // options using a list of maps containing key and value keys. auto &I = reinterpret_cast(IO); @@ -128,6 +129,11 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool, } else { IO.setError("expected a sequence or map"); } + } else { + MappingNormalization NOpts(IO, + Val); + EmptyContext Ctx; + yamlize(IO, NOpts->Options, true, Ctx); } } @@ -184,7 +190,7 @@ struct ChecksVariant { }; template <> void yamlize(IO &IO, ChecksVariant &Val, bool, EmptyContext &Ctx) { - if (!IO.outputting()) { + if (IO.getKind() == IOKind::Inputting) { // Special case for reading from YAML // Must support reading from both a string or a list auto &I = reinterpret_cast(IO); @@ -197,6 +203,9 @@ template <> void yamlize(IO &IO, ChecksVariant &Val, bool, EmptyContext &Ctx) { } else { IO.setError("expected string or sequence"); } + } else if (IO.getKind() == IOKind::GeneratingSchema) { + Val.AsVector = std::vector(); + yamlize(IO, *Val.AsVector, true, Ctx); } } @@ -543,6 +552,12 @@ parseConfiguration(llvm::MemoryBufferRef Config) { return Options; } +void dumpConfigurationYAMLSchema(llvm::raw_ostream &Stream) { + ClangTidyOptions Options; + llvm::yaml::GenerateSchema GS(Stream); + GS << Options; +} + static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx) { (*reinterpret_cast(Ctx))(Diag); } diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 2aae92f1d9eb3..353bf7e5c435e 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -343,6 +343,9 @@ std::error_code parseLineFilter(llvm::StringRef LineFilter, llvm::ErrorOr parseConfiguration(llvm::MemoryBufferRef Config); +/// Dumps configuration YAML Schema to \p Stream +void dumpConfigurationYAMLSchema(llvm::raw_ostream &Stream); + using DiagCallback = llvm::function_ref; llvm::ErrorOr diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 64157f530b8c0..7c6fa7f5c40b9 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -355,6 +355,12 @@ see https://clang.llvm.org/extra/clang-tidy/QueryBasedCustomChecks.html. cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt DumpYAMLSchema("dump-yaml-schema", desc(R"( +Dumps configuration YAML Schema in JSON format to +stdout. +)"), + cl::init(false), + cl::cat(ClangTidyCategory)); namespace clang::tidy { static void printStats(const ClangTidyStats &Stats) { @@ -684,6 +690,11 @@ int clangTidyMain(int argc, const char **argv) { return 0; } + if (DumpYAMLSchema) { + dumpConfigurationYAMLSchema(llvm::outs()); + return 0; + } + if (VerifyConfig) { std::vector RawOptions = OptionsProvider->getRawOptions(FileName); diff --git a/llvm/include/llvm/Support/YAMLGenerateSchema.h b/llvm/include/llvm/Support/YAMLGenerateSchema.h new file mode 100644 index 0000000000000..ce699092c0e8e --- /dev/null +++ b/llvm/include/llvm/Support/YAMLGenerateSchema.h @@ -0,0 +1,400 @@ +//===- llvm/Support/YAMLGenerateSchema.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H +#define LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H + +#include "llvm/Support/Casting.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { + +namespace json { +class Value; +} + +namespace yaml { + +class GenerateSchema : public IO { +public: + GenerateSchema(raw_ostream &RO); + ~GenerateSchema() override = default; + + IOKind getKind() const override; + bool outputting() const override; + bool mapTag(StringRef, bool) override; + void beginMapping() override; + void endMapping() override; + bool preflightKey(StringRef, bool, bool, bool &, void *&) override; + void postflightKey(void *) override; + std::vector keys() override; + void beginFlowMapping() override; + void endFlowMapping() override; + unsigned beginSequence() override; + void endSequence() override; + bool preflightElement(unsigned, void *&) override; + void postflightElement(void *) override; + unsigned beginFlowSequence() override; + bool preflightFlowElement(unsigned, void *&) override; + void postflightFlowElement(void *) override; + void endFlowSequence() override; + void beginEnumScalar() override; + bool matchEnumScalar(StringRef, bool) override; + bool matchEnumFallback() override; + void endEnumScalar() override; + bool beginBitSetScalar(bool &) override; + bool bitSetMatch(StringRef, bool) override; + void endBitSetScalar() override; + void scalarString(StringRef &, QuotingType) override; + void blockScalarString(StringRef &) override; + void scalarTag(std::string &) override; + NodeKind getNodeKind() override; + void setError(const Twine &message) override; + std::error_code error() override; + bool canElideEmptySequence() override; + + bool preflightDocument(); + void postflightDocument(); + + class SchemaNode { + public: + virtual json::Value toJSON() const = 0; + + virtual ~SchemaNode() = default; + }; + + enum class PropertyKind : uint8_t { + UserDefined, + Properties, + AdditionalProperties, + Required, + Optional, + Type, + Enum, + Items, + FlowStyle, + }; + + class SchemaProperty : public SchemaNode { + StringRef Name; + PropertyKind Kind; + + public: + SchemaProperty(StringRef Name, PropertyKind Kind) + : Name(Name), Kind(Kind) {} + + PropertyKind getKind() const { return Kind; } + + StringRef getName() const { return Name; } + }; + + class Schema; + + class UserDefinedProperty final : public SchemaProperty { + Schema *Value; + + public: + UserDefinedProperty(StringRef Name, Schema *Value) + : SchemaProperty(Name, PropertyKind::UserDefined), Value(Value) {} + + Schema *getSchema() const { return Value; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::UserDefined; + } + }; + + class PropertiesProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + PropertiesProperty() + : SchemaProperty("properties", PropertyKind::Properties) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Properties; + } + }; + + class AdditionalPropertiesProperty final : public SchemaProperty { + Schema *Value; + + public: + AdditionalPropertiesProperty(Schema *Value = nullptr) + : SchemaProperty("additionalProperties", + PropertyKind::AdditionalProperties), + Value(Value) {} + + Schema *getSchema() const { return Value; } + + void setSchema(Schema *S) { Value = S; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::AdditionalProperties; + } + }; + + class RequiredProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + RequiredProperty() : SchemaProperty("required", PropertyKind::Required) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Required; + } + }; + + class OptionalProperty final : public SchemaProperty, + SmallVector { + public: + using BaseVector = SmallVector; + + OptionalProperty() : SchemaProperty("optional", PropertyKind::Optional) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Optional; + } + }; + + class TypeProperty final : public SchemaProperty { + StringRef Value; + + public: + TypeProperty(StringRef Value) + : SchemaProperty("type", PropertyKind::Type), Value(Value) {} + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Type; + } + }; + + class EnumProperty final : public SchemaProperty, SmallVector { + public: + using BaseVector = SmallVector; + + EnumProperty() : SchemaProperty("enum", PropertyKind::Enum) {} + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Enum; + } + }; + + class ItemsProperty final : public SchemaProperty { + Schema *Value; + + public: + ItemsProperty(Schema *Value = nullptr) + : SchemaProperty("items", PropertyKind::Items), Value(Value) {} + + Schema *getSchema() const { return Value; } + + void setSchema(Schema *S) { Value = S; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::Items; + } + }; + + enum class FlowStyle : bool { + Block, + Flow, + }; + + class FlowStyleProperty final : public SchemaProperty { + FlowStyle Style; + + public: + FlowStyleProperty(FlowStyle Style = FlowStyle::Block) + : SchemaProperty("flowStyle", PropertyKind::FlowStyle), Style(Style) {} + + void setStyle(FlowStyle S) { Style = S; } + + FlowStyle getStyle() const { return Style; } + + json::Value toJSON() const override; + + static bool classof(const SchemaProperty *Property) { + return Property->getKind() == PropertyKind::FlowStyle; + } + }; + + class Schema final : public SchemaNode, SmallVector { + public: + using BaseVector = SmallVector; + + Schema() = default; + + using BaseVector::begin; + using BaseVector::emplace_back; + using BaseVector::end; + using BaseVector::size; + + json::Value toJSON() const override; + }; + +private: + std::vector> SchemaNodes; + SmallVector Schemas; + raw_ostream &RO; + SchemaNode *Root = nullptr; + + template + PropertyType *createProperty(PropertyArgs &&...Args) { + std::unique_ptr UPtr = + std::make_unique(std::forward(Args)...); + PropertyType *Ptr = UPtr.get(); + SchemaNodes.emplace_back(std::move(UPtr)); + return Ptr; + } + + template + PropertyType *getOrCreateProperty(Schema &S, PropertyArgs... Args) { + auto Found = std::find_if(S.begin(), S.end(), [](SchemaProperty *Property) { + return isa(Property); + }); + if (Found != S.end()) { + return cast(*Found); + } + PropertyType *Created = + createProperty(std::forward(Args)...); + S.emplace_back(Created); + return Created; + } + + Schema *createSchema() { + std::unique_ptr UPtr = std::make_unique(); + Schema *Ptr = UPtr.get(); + SchemaNodes.emplace_back(std::move(UPtr)); + return Ptr; + } + + Schema *getTopSchema() const { + return Schemas.empty() ? nullptr : Schemas.back(); + } +}; + +// Define non-member operator<< so that Output can stream out document list. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &DocList) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, DocumentListTraits::element(Gen, DocList, 0), true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a map. +template +inline std::enable_if_t::value, + GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Map) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Map, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a sequence. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Seq) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Seq, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a block scalar. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a string map. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Define non-member operator<< so that Output can stream out a polymorphic +// type. +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &Val) { + EmptyContext Ctx; + Gen.preflightDocument(); + yamlize(Gen, Val, true, Ctx); + Gen.postflightDocument(); + return Gen; +} + +// Provide better error message about types missing a trait specialization +template +inline std::enable_if_t::value, GenerateSchema &> +operator<<(GenerateSchema &Gen, T &seq) { + char missing_yaml_trait_for_type[sizeof(MissingTrait)]; + return Gen; +} + +} // namespace yaml + +} // namespace llvm + +#endif // LLVM_SUPPORT_YAMLGENERATE_SCHEMA_H diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h index 3d36f41ca1a04..f92e26e6424a1 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -145,6 +145,7 @@ enum class QuotingType { None, Single, Double }; /// return StringRef(); /// } /// static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +/// static constexpr StringRef typeName = "string"; /// }; template struct ScalarTraits { // Must provide: @@ -158,6 +159,9 @@ template struct ScalarTraits { // // Function to determine if the value should be quoted. // static QuotingType mustQuote(StringRef); + // + // Optional, for GeneratingSchema: + // static constexpr StringRef typeName = "string"; }; /// This class should be specialized by type that requires custom conversion @@ -175,6 +179,7 @@ template struct ScalarTraits { /// // return empty string on success, or error string /// return StringRef(); /// } +/// static constexpr StringRef typeName = "string"; /// }; template struct BlockScalarTraits { // Must provide: @@ -189,6 +194,7 @@ template struct BlockScalarTraits { // Optional: // static StringRef inputTag(T &Val, std::string Tag) // static void outputTag(const T &Val, raw_ostream &Out) + // static constexpr StringRef typeName = "string"; }; /// This class should be specialized by type that requires custom conversion @@ -211,6 +217,7 @@ template struct BlockScalarTraits { /// static QuotingType mustQuote(const MyType &Value, StringRef) { /// return QuotingType::Single; /// } +/// static constexpr StringRef typeName = "integer"; /// }; template struct TaggedScalarTraits { // Must provide: @@ -226,6 +233,9 @@ template struct TaggedScalarTraits { // // Function to determine if the value should be quoted. // static QuotingType mustQuote(const T &Value, StringRef Scalar); + // + // Optional: + // static constexpr StringRef typeName = "string"; }; /// This class should be specialized by any type that needs to be converted @@ -442,6 +452,14 @@ template struct has_CustomMappingTraits { is_detected>::value; }; +// Test if typeName is defined on type T. +template struct has_TypeNameTraits { + template + using check = std::is_same; + + static constexpr bool value = is_detected::value; +}; + // Test if flow is defined on type T. template struct has_FlowTraits { template using check = decltype(&U::flow); @@ -683,12 +701,19 @@ struct unvalidatedMappingTraits !has_MappingValidateTraits::value> { }; +enum class IOKind : uint8_t { + Outputting, + Inputting, + GeneratingSchema, +}; + // Base class for Input and Output. class LLVM_ABI IO { public: IO(void *Ctxt = nullptr); virtual ~IO(); + virtual IOKind getKind() const = 0; virtual bool outputting() const = 0; virtual unsigned beginSequence() = 0; @@ -732,7 +757,8 @@ class LLVM_ABI IO { virtual void setAllowUnknownKeys(bool Allow); template void enumCase(T &Val, StringRef Str, const T ConstVal) { - if (matchEnumScalar(Str, outputting() && Val == ConstVal)) { + if (matchEnumScalar(Str, + getKind() == IOKind::Outputting && Val == ConstVal)) { Val = ConstVal; } } @@ -740,7 +766,8 @@ class LLVM_ABI IO { // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void enumCase(T &Val, StringRef Str, const uint32_t ConstVal) { - if (matchEnumScalar(Str, outputting() && Val == static_cast(ConstVal))) { + if (matchEnumScalar(Str, getKind() == IOKind::Outputting && + Val == static_cast(ConstVal))) { Val = ConstVal; } } @@ -757,7 +784,8 @@ class LLVM_ABI IO { template void bitSetCase(T &Val, StringRef Str, const T ConstVal) { - if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) { + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & ConstVal) == ConstVal)) { Val = static_cast(Val | ConstVal); } } @@ -765,21 +793,24 @@ class LLVM_ABI IO { // allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF template void bitSetCase(T &Val, StringRef Str, const uint32_t ConstVal) { - if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) { + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & ConstVal) == ConstVal)) { Val = static_cast(Val | ConstVal); } } template void maskedBitSetCase(T &Val, StringRef Str, T ConstVal, T Mask) { - if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & Mask) == ConstVal)) Val = Val | ConstVal; } template void maskedBitSetCase(T &Val, StringRef Str, uint32_t ConstVal, uint32_t Mask) { - if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal)) + if (bitSetMatch(Str, getKind() == IOKind::Outputting && + (Val & Mask) == ConstVal)) Val = Val | ConstVal; } @@ -844,7 +875,8 @@ class LLVM_ABI IO { bool Required, Context &Ctx) { void *SaveInfo; bool UseDefault; - const bool sameAsDefault = outputting() && Val == DefaultValue; + const bool sameAsDefault = + (getKind() == IOKind::Outputting) && Val == DefaultValue; if (this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { yamlize(*this, Val, Required, Ctx); @@ -905,45 +937,57 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { SmallString<128> Storage; raw_svector_ostream Buffer(Storage); ScalarTraits::output(Val, io.getContext(), Buffer); StringRef Str = Buffer.str(); io.scalarString(Str, ScalarTraits::mustQuote(Str)); - } else { + } else if (io.getKind() == IOKind::Inputting) { StringRef Str; io.scalarString(Str, ScalarTraits::mustQuote(Str)); StringRef Result = ScalarTraits::input(Str, io.getContext(), Val); if (!Result.empty()) { io.setError(Twine(Result)); } + } else { + StringRef TypeName = "string"; + if constexpr (has_TypeNameTraits>::value) { + TypeName = ScalarTraits::typeName; + } + io.scalarString(TypeName, QuotingType::None); } } template std::enable_if_t::value, void> yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) { - if (YamlIO.outputting()) { + if (YamlIO.getKind() == IOKind::Outputting) { std::string Storage; raw_string_ostream Buffer(Storage); BlockScalarTraits::output(Val, YamlIO.getContext(), Buffer); StringRef Str(Storage); YamlIO.blockScalarString(Str); - } else { + } else if (YamlIO.getKind() == IOKind::Inputting) { StringRef Str; YamlIO.blockScalarString(Str); StringRef Result = BlockScalarTraits::input(Str, YamlIO.getContext(), Val); if (!Result.empty()) YamlIO.setError(Twine(Result)); + } else { + StringRef TypeName = "string"; + if constexpr (has_TypeNameTraits>::value) { + TypeName = ScalarTraits::typeName; + } + YamlIO.blockScalarString(TypeName); } } template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { std::string ScalarStorage, TagStorage; raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage); TaggedScalarTraits::output(Val, io.getContext(), ScalarBuffer, @@ -952,7 +996,7 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { StringRef ScalarStr(ScalarStorage); io.scalarString(ScalarStr, TaggedScalarTraits::mustQuote(Val, ScalarStr)); - } else { + } else if (io.getKind() == IOKind::Inputting) { std::string Tag; io.scalarTag(Tag); StringRef Str; @@ -962,6 +1006,12 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { if (!Result.empty()) { io.setError(Twine(Result)); } + } else { + StringRef TypeName = "string"; + if constexpr (has_TypeNameTraits>::value) { + TypeName = ScalarTraits::typeName; + } + io.scalarString(TypeName, QuotingType::None); } } @@ -985,7 +1035,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { io.beginFlowMapping(); else io.beginMapping(); - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { std::string Err = detail::doValidate(io, Val, Ctx); if (!Err.empty()) { errs() << Err << "\n"; @@ -993,7 +1043,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { } } detail::doMapping(io, Val, Ctx); - if (!io.outputting()) { + if (io.getKind() == IOKind::Inputting) { std::string Err = detail::doValidate(io, Val, Ctx); if (!Err.empty()) io.setError(Err); @@ -1007,7 +1057,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { template bool yamlizeMappingEnumInput(IO &io, T &Val) { if constexpr (has_MappingEnumInputTraits::value) { - if (io.outputting()) + if (io.getKind() == IOKind::Outputting) return false; io.beginEnumScalar(); @@ -1038,23 +1088,28 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) { template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { io.beginMapping(); CustomMappingTraits::output(io, Val); io.endMapping(); - } else { + } else if (io.getKind() == IOKind::Inputting) { io.beginMapping(); for (StringRef key : io.keys()) CustomMappingTraits::inputOne(io, key, Val); io.endMapping(); + } else { + io.beginMapping(); + CustomMappingTraits::inputOne(io, "additionalProperties", Val); + io.endMapping(); } } template std::enable_if_t::value, void> yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) { - switch (io.outputting() ? PolymorphicTraits::getKind(Val) - : io.getNodeKind()) { + switch (io.getKind() == IOKind::Outputting + ? PolymorphicTraits::getKind(Val) + : io.getNodeKind()) { case NodeKind::Scalar: return yamlize(io, PolymorphicTraits::getAsScalar(Val), true, Ctx); case NodeKind::Map: @@ -1075,7 +1130,9 @@ std::enable_if_t::value, void> yamlize(IO &io, T &Seq, bool, Context &Ctx) { if (has_FlowTraits>::value) { unsigned incnt = io.beginFlowSequence(); - unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; + unsigned count = io.getKind() == IOKind::Outputting + ? SequenceTraits::size(io, Seq) + : incnt; for (unsigned i = 0; i < count; ++i) { void *SaveInfo; if (io.preflightFlowElement(i, SaveInfo)) { @@ -1086,7 +1143,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) { io.endFlowSequence(); } else { unsigned incnt = io.beginSequence(); - unsigned count = io.outputting() ? SequenceTraits::size(io, Seq) : incnt; + unsigned count = io.getKind() == IOKind::Outputting + ? SequenceTraits::size(io, Seq) + : incnt; for (unsigned i = 0; i < count; ++i) { void *SaveInfo; if (io.preflightElement(i, SaveInfo)) { @@ -1102,6 +1161,7 @@ template <> struct ScalarTraits { LLVM_ABI static void output(const bool &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, bool &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "boolean"; }; template <> struct ScalarTraits { @@ -1120,60 +1180,70 @@ template <> struct ScalarTraits { LLVM_ABI static void output(const uint8_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, uint8_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const uint16_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, uint16_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const uint32_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, uint32_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const uint64_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, uint64_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const int8_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, int8_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const int16_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, int16_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const int32_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, int32_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const int64_t &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, int64_t &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const float &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, float &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "number"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const double &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, double &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "number"; }; // For endian types, we use existing scalar Traits class for the underlying @@ -1201,6 +1271,10 @@ struct ScalarTraits::mustQuote(Str); } + + static constexpr StringRef typeName = has_TypeNameTraits::value + ? ScalarTraits::typeName + : "string"; }; template @@ -1239,7 +1313,7 @@ struct ScalarBitSetTraits< template struct MappingNormalization { MappingNormalization(IO &i_o, TFinal &Obj) : io(i_o), BufPtr(nullptr), Result(Obj) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr = new (&Buffer) TNorm(io, Obj); } else { BufPtr = new (&Buffer) TNorm(io); @@ -1247,7 +1321,7 @@ template struct MappingNormalization { } ~MappingNormalization() { - if (!io.outputting()) { + if (io.getKind() != IOKind::Outputting) { Result = BufPtr->denormalize(io); } BufPtr->~TNorm(); @@ -1269,7 +1343,7 @@ template struct MappingNormalization { template struct MappingNormalizationHeap { MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator) : io(i_o), Result(Obj) { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr = new (&Buffer) TNorm(io, Obj); } else if (allocator) { BufPtr = allocator->Allocate(); @@ -1280,7 +1354,7 @@ template struct MappingNormalizationHeap { } ~MappingNormalizationHeap() { - if (io.outputting()) { + if (io.getKind() == IOKind::Outputting) { BufPtr->~TNorm(); } else { Result = BufPtr->denormalize(io); @@ -1327,6 +1401,7 @@ class LLVM_ABI Input : public IO { std::error_code error() override; private: + IOKind getKind() const override; bool outputting() const override; bool mapTag(StringRef, bool) override; void beginMapping() override; @@ -1478,6 +1553,7 @@ class LLVM_ABI Output : public IO { /// anyway. void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; } + IOKind getKind() const override; bool outputting() const override; bool mapTag(StringRef, bool) override; void beginMapping() override; @@ -1563,8 +1639,8 @@ void IO::processKeyWithDefault(StringRef Key, std::optional &Val, assert(!DefaultValue && "std::optional shouldn't have a value!"); void *SaveInfo; bool UseDefault = true; - const bool sameAsDefault = outputting() && !Val; - if (!outputting() && !Val) + const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val; + if (getKind() != IOKind::Outputting && !Val) Val = T(); if (Val && this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) { @@ -1574,7 +1650,7 @@ void IO::processKeyWithDefault(StringRef Key, std::optional &Val, // was requested, i.e. the DefaultValue will be assigned. The DefaultValue // is usually None. bool IsNone = false; - if (!outputting()) + if (getKind() == IOKind::Inputting) if (const auto *Node = dyn_cast(((Input *)this)->getCurrentNode())) // We use rtrim to ignore possible white spaces that might exist when a @@ -1631,24 +1707,28 @@ template <> struct ScalarTraits { LLVM_ABI static void output(const Hex8 &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, Hex8 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const Hex16 &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, Hex16 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const Hex32 &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, Hex32 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { LLVM_ABI static void output(const Hex64 &, void *, raw_ostream &); LLVM_ABI static StringRef input(StringRef, void *, Hex64 &); static QuotingType mustQuote(StringRef) { return QuotingType::None; } + static constexpr StringRef typeName = "integer"; }; template <> struct ScalarTraits { diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 671a5fe941cef..6efb8c29249d6 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -282,6 +282,7 @@ add_llvm_component_library(LLVMSupport WithColor.cpp YAMLParser.cpp YAMLTraits.cpp + YAMLGenerateSchema.cpp raw_os_ostream.cpp raw_ostream.cpp raw_ostream_proxy.cpp diff --git a/llvm/lib/Support/YAMLGenerateSchema.cpp b/llvm/lib/Support/YAMLGenerateSchema.cpp new file mode 100644 index 0000000000000..b77d30d9364a4 --- /dev/null +++ b/llvm/lib/Support/YAMLGenerateSchema.cpp @@ -0,0 +1,285 @@ +//===- lib/Support/YAMLGenerateSchema.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/YAMLGenerateSchema.h" +#include "llvm/Support/JSON.h" + +using namespace llvm; +using namespace yaml; + +//===----------------------------------------------------------------------===// +// GenerateSchema +//===----------------------------------------------------------------------===// + +GenerateSchema::GenerateSchema(raw_ostream &RO) : RO(RO) {} + +IOKind GenerateSchema::getKind() const { return IOKind::GeneratingSchema; } + +bool GenerateSchema::outputting() const { return false; } + +bool GenerateSchema::mapTag(StringRef, bool) { return false; } + +void GenerateSchema::beginMapping() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("object"); + Top->emplace_back(Type); + FlowStyleProperty *FlowStyle = createProperty(); + Top->emplace_back(FlowStyle); +} + +void GenerateSchema::endMapping() {} + +bool GenerateSchema::preflightKey(StringRef Key, bool Required, + bool SameAsDefault, bool &UseDefault, + void *&SaveInfo) { + Schema *Top = getTopSchema(); + assert(Top); + if (Key == "additionalProperties") { + AdditionalPropertiesProperty *Additional = + getOrCreateProperty(*Top); + Schema *S = createSchema(); + Additional->setSchema(S); + Schemas.push_back(S); + return true; + } + + if (Required) { + RequiredProperty *Req = getOrCreateProperty(*Top); + Req->emplace_back(Key); + } else { + OptionalProperty *Opt = getOrCreateProperty(*Top); + Opt->emplace_back(Key); + } + PropertiesProperty *Properties = + getOrCreateProperty(*Top); + Schema *S = createSchema(); + UserDefinedProperty *UserDefined = + createProperty(Key, S); + Properties->emplace_back(UserDefined); + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightKey(void *) { + assert(!Schemas.empty()); + Schemas.pop_back(); +} + +std::vector GenerateSchema::keys() { return {}; } + +void GenerateSchema::beginFlowMapping() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("object"); + Top->emplace_back(Type); + FlowStyleProperty *FlowStyle = + createProperty(FlowStyle::Flow); + Top->emplace_back(FlowStyle); +} + +void GenerateSchema::endFlowMapping() {} + +unsigned GenerateSchema::beginSequence() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("array"); + Top->emplace_back(Type); + ItemsProperty *Items = createProperty(); + Top->emplace_back(Items); + FlowStyleProperty *FlowStyle = createProperty(); + Top->emplace_back(FlowStyle); + return 1; +} + +void GenerateSchema::endSequence() {} + +bool GenerateSchema::preflightElement(unsigned, void *&) { + Schema *Top = getTopSchema(); + assert(Top); + Schema *S = createSchema(); + ItemsProperty *Items = getOrCreateProperty(*Top); + Items->setSchema(S); + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightElement(void *) { + assert(!Schemas.empty()); + Schemas.pop_back(); +} + +unsigned GenerateSchema::beginFlowSequence() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("array"); + Top->emplace_back(Type); + ItemsProperty *Items = createProperty(); + Top->emplace_back(Items); + FlowStyleProperty *FlowStyle = + createProperty(FlowStyle::Flow); + Top->emplace_back(FlowStyle); + return 1; +} + +bool GenerateSchema::preflightFlowElement(unsigned Arg1, void *&Arg2) { + return preflightElement(Arg1, Arg2); +} + +void GenerateSchema::postflightFlowElement(void *Arg1) { + postflightElement(Arg1); +} + +void GenerateSchema::endFlowSequence() { endSequence(); } + +void GenerateSchema::beginEnumScalar() { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty("string"); + Top->emplace_back(Type); + EnumProperty *Enum = createProperty(); + Top->emplace_back(Enum); +} + +bool GenerateSchema::matchEnumScalar(StringRef Val, bool) { + Schema *Top = getTopSchema(); + assert(Top); + EnumProperty *Enum = getOrCreateProperty(*Top); + Enum->emplace_back(Val); + return false; +} + +bool GenerateSchema::matchEnumFallback() { return false; } + +void GenerateSchema::endEnumScalar() {} + +bool GenerateSchema::beginBitSetScalar(bool &) { + beginEnumScalar(); + return true; +} + +bool GenerateSchema::bitSetMatch(StringRef Val, bool Arg) { + return matchEnumScalar(Val, Arg); +} + +void GenerateSchema::endBitSetScalar() { endEnumScalar(); } + +void GenerateSchema::scalarString(StringRef &Val, QuotingType) { + Schema *Top = getTopSchema(); + assert(Top); + TypeProperty *Type = createProperty(Val); + Top->emplace_back(Type); +} + +void GenerateSchema::blockScalarString(StringRef &S) { + scalarString(S, QuotingType::None); +} + +void GenerateSchema::scalarTag(std::string &val) {} + +NodeKind GenerateSchema::getNodeKind() { report_fatal_error("invalid call"); } + +void GenerateSchema::setError(const Twine &) {} + +std::error_code GenerateSchema::error() { return {}; } + +bool GenerateSchema::canElideEmptySequence() { return false; } + +// These are only used by operator<<. They could be private +// if that templated operator could be made a friend. + +bool GenerateSchema::preflightDocument() { + Schema *S = createSchema(); + Root = S; + Schemas.push_back(S); + return true; +} + +void GenerateSchema::postflightDocument() { + assert(!Schemas.empty()); + Schemas.pop_back(); + json::Value JSONValue = Root->toJSON(); + RO << llvm::formatv("{0:2}\n", JSONValue); +} + +json::Value GenerateSchema::UserDefinedProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::PropertiesProperty::toJSON() const { + json::Object JSONObject; + for (UserDefinedProperty *Property : *this) { + json::Value JSONValue = Property->toJSON(); + JSONObject.try_emplace(Property->getName().data(), std::move(JSONValue)); + } + return JSONObject; +} + +json::Value GenerateSchema::AdditionalPropertiesProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::RequiredProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::OptionalProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::TypeProperty::toJSON() const { + json::Value JSONValue(Value.data()); + return JSONValue; +} + +json::Value GenerateSchema::EnumProperty::toJSON() const { + json::Array JSONArray; + for (StringRef Value : *this) { + json::Value JSONValue(Value.data()); + JSONArray.emplace_back(std::move(JSONValue)); + } + return JSONArray; +} + +json::Value GenerateSchema::ItemsProperty::toJSON() const { + return Value->toJSON(); +} + +json::Value GenerateSchema::FlowStyleProperty::toJSON() const { + StringRef Value; + switch (Style) { + case FlowStyle::Block: + Value = "block"; + break; + case FlowStyle::Flow: + Value = "flow"; + break; + } + json::Value JSONValue(Value.data()); + return JSONValue; +} + +json::Value GenerateSchema::Schema::toJSON() const { + json::Object JSONObject; + for (SchemaProperty *Value : *this) { + json::Value JSONValue = Value->toJSON(); + StringRef Key = Value->getName(); + JSONObject.try_emplace(Key.data(), std::move(JSONValue)); + } + return JSONObject; +} diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp index 95a41eafdf5e4..bea05378ac8f3 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -74,6 +74,8 @@ Input::~Input() = default; std::error_code Input::error() { return EC; } +IOKind Input::getKind() const { return IOKind::Inputting; } + bool Input::outputting() const { return false; } @@ -485,6 +487,8 @@ Output::Output(raw_ostream &yout, void *context, int WrapColumn) Output::~Output() = default; +IOKind Output::getKind() const { return IOKind::Outputting; } + bool Output::outputting() const { return true; } diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 21f10eb610f11..850661de95264 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -112,6 +112,7 @@ add_llvm_unittest(SupportTests VirtualOutputFileTest.cpp WithColorTest.cpp YAMLIOTest.cpp + YAMLGenerateSchemaTest.cpp YAMLParserTest.cpp buffer_ostream_test.cpp formatted_raw_ostream_test.cpp diff --git a/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp b/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp new file mode 100644 index 0000000000000..d76b5298145a9 --- /dev/null +++ b/llvm/unittests/Support/YAMLGenerateSchemaTest.cpp @@ -0,0 +1,124 @@ +//===- YAMLGenerateSchemaTest.cpp - Tests for Generating YAML Schema ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/YAMLGenerateSchema.h" +#include "llvm/Support/YAMLTraits.h" +#include +#include + +#include "gtest/gtest.h" + +using namespace llvm; + +enum class ColorTy { + White, + Black, + Blue, +}; + +struct Baby { + std::string Name; + ColorTy Color; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(Baby) + +struct Animal { + std::string Name; + std::optional Age; + std::vector Babies; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(Animal) + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, ColorTy &value) { + io.enumCase(value, "white", ColorTy::White); + io.enumCase(value, "black", ColorTy::Black); + io.enumCase(value, "blue", ColorTy::Blue); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, Baby &info) { + io.mapRequired("name", info.Name); + io.mapRequired("color", info.Color); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, Animal &info) { + io.mapRequired("name", info.Name); + io.mapOptional("age", info.Age); + io.mapOptional("babies", info.Babies); + } +}; + +} // namespace yaml +} // namespace llvm + +TEST(ObjectYAMLGenerateSchema, SimpleSchema) { + std::string String; + raw_string_ostream OS(String); + std::vector Animals; + yaml::GenerateSchema Gen(OS); + Gen << Animals; + StringRef YAMLSchema = R"({ + "flowStyle": "block", + "items": { + "flowStyle": "block", + "optional": [ + "age", + "babies" + ], + "properties": { + "age": { + "type": "integer" + }, + "babies": { + "flowStyle": "block", + "items": { + "flowStyle": "block", + "properties": { + "color": { + "enum": [ + "white", + "black", + "blue" + ], + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "color" + ], + "type": "object" + }, + "type": "array" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" +} +)"; + EXPECT_EQ(String.c_str(), YAMLSchema.str()); +} diff --git a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn index df9ddf91f2c49..dd3932751a194 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn @@ -178,6 +178,7 @@ static_library("Support") { "WithColor.cpp", "YAMLParser.cpp", "YAMLTraits.cpp", + "YAMLGenerateSchema.cpp", "Z3Solver.cpp", "circular_raw_ostream.cpp", "raw_os_ostream.cpp",