Skip to content

Commit cc399e1

Browse files
committed
[llvm][Support] Add YAMLGenerateSchema for producing YAML schemas
Introduced a way for generating schema for validating input YAML. This can be useful to see the full structure of the input YAML file for different llvm based tools that use existing YAML parser, for example clang-format, clang-tidy e.t.c. This commit also can be useful for yaml-language-server.
1 parent a516cc0 commit cc399e1

File tree

8 files changed

+862
-25
lines changed

8 files changed

+862
-25
lines changed

llvm/include/llvm/Support/YAMLGenerateSchema.h

Lines changed: 400 additions & 0 deletions
Large diffs are not rendered by default.

llvm/include/llvm/Support/YAMLTraits.h

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -683,12 +683,19 @@ struct unvalidatedMappingTraits
683683
!has_MappingValidateTraits<T, Context>::value> {
684684
};
685685

686+
enum class IOKind : uint8_t {
687+
Outputting,
688+
Inputting,
689+
GeneratingSchema,
690+
};
691+
686692
// Base class for Input and Output.
687693
class LLVM_ABI IO {
688694
public:
689695
IO(void *Ctxt = nullptr);
690696
virtual ~IO();
691697

698+
virtual IOKind getKind() const = 0;
692699
virtual bool outputting() const = 0;
693700

694701
virtual unsigned beginSequence() = 0;
@@ -732,15 +739,17 @@ class LLVM_ABI IO {
732739
virtual void setAllowUnknownKeys(bool Allow);
733740

734741
template <typename T> void enumCase(T &Val, StringRef Str, const T ConstVal) {
735-
if (matchEnumScalar(Str, outputting() && Val == ConstVal)) {
742+
if (matchEnumScalar(Str,
743+
getKind() == IOKind::Outputting && Val == ConstVal)) {
736744
Val = ConstVal;
737745
}
738746
}
739747

740748
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
741749
template <typename T>
742750
void enumCase(T &Val, StringRef Str, const uint32_t ConstVal) {
743-
if (matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal))) {
751+
if (matchEnumScalar(Str, getKind() == IOKind::Outputting &&
752+
Val == static_cast<T>(ConstVal))) {
744753
Val = ConstVal;
745754
}
746755
}
@@ -757,29 +766,33 @@ class LLVM_ABI IO {
757766

758767
template <typename T>
759768
void bitSetCase(T &Val, StringRef Str, const T ConstVal) {
760-
if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) {
769+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
770+
(Val & ConstVal) == ConstVal)) {
761771
Val = static_cast<T>(Val | ConstVal);
762772
}
763773
}
764774

765775
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
766776
template <typename T>
767777
void bitSetCase(T &Val, StringRef Str, const uint32_t ConstVal) {
768-
if (bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal)) {
778+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
779+
(Val & ConstVal) == ConstVal)) {
769780
Val = static_cast<T>(Val | ConstVal);
770781
}
771782
}
772783

773784
template <typename T>
774785
void maskedBitSetCase(T &Val, StringRef Str, T ConstVal, T Mask) {
775-
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
786+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
787+
(Val & Mask) == ConstVal))
776788
Val = Val | ConstVal;
777789
}
778790

779791
template <typename T>
780792
void maskedBitSetCase(T &Val, StringRef Str, uint32_t ConstVal,
781793
uint32_t Mask) {
782-
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
794+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
795+
(Val & Mask) == ConstVal))
783796
Val = Val | ConstVal;
784797
}
785798

@@ -844,7 +857,8 @@ class LLVM_ABI IO {
844857
bool Required, Context &Ctx) {
845858
void *SaveInfo;
846859
bool UseDefault;
847-
const bool sameAsDefault = outputting() && Val == DefaultValue;
860+
const bool sameAsDefault =
861+
(getKind() == IOKind::Outputting) && Val == DefaultValue;
848862
if (this->preflightKey(Key, Required, sameAsDefault, UseDefault,
849863
SaveInfo)) {
850864
yamlize(*this, Val, Required, Ctx);
@@ -905,7 +919,7 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
905919
template <typename T>
906920
std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
907921
EmptyContext &Ctx) {
908-
if (io.outputting()) {
922+
if (io.getKind() == IOKind::Outputting) {
909923
SmallString<128> Storage;
910924
raw_svector_ostream Buffer(Storage);
911925
ScalarTraits<T>::output(Val, io.getContext(), Buffer);
@@ -924,7 +938,7 @@ std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
924938
template <typename T>
925939
std::enable_if_t<has_BlockScalarTraits<T>::value, void>
926940
yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
927-
if (YamlIO.outputting()) {
941+
if (YamlIO.getKind() == IOKind::Outputting) {
928942
std::string Storage;
929943
raw_string_ostream Buffer(Storage);
930944
BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer);
@@ -943,7 +957,7 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
943957
template <typename T>
944958
std::enable_if_t<has_TaggedScalarTraits<T>::value, void>
945959
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
946-
if (io.outputting()) {
960+
if (io.getKind() == IOKind::Outputting) {
947961
std::string ScalarStorage, TagStorage;
948962
raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage);
949963
TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer,
@@ -985,15 +999,15 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
985999
io.beginFlowMapping();
9861000
else
9871001
io.beginMapping();
988-
if (io.outputting()) {
1002+
if (io.getKind() == IOKind::Outputting) {
9891003
std::string Err = detail::doValidate(io, Val, Ctx);
9901004
if (!Err.empty()) {
9911005
errs() << Err << "\n";
9921006
assert(Err.empty() && "invalid struct trying to be written as yaml");
9931007
}
9941008
}
9951009
detail::doMapping(io, Val, Ctx);
996-
if (!io.outputting()) {
1010+
if (io.getKind() == IOKind::Inputting) {
9971011
std::string Err = detail::doValidate(io, Val, Ctx);
9981012
if (!Err.empty())
9991013
io.setError(Err);
@@ -1007,7 +1021,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
10071021
template <typename T, typename Context>
10081022
bool yamlizeMappingEnumInput(IO &io, T &Val) {
10091023
if constexpr (has_MappingEnumInputTraits<T, Context>::value) {
1010-
if (io.outputting())
1024+
if (io.getKind() == IOKind::Outputting)
10111025
return false;
10121026

10131027
io.beginEnumScalar();
@@ -1038,7 +1052,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
10381052
template <typename T>
10391053
std::enable_if_t<has_CustomMappingTraits<T>::value, void>
10401054
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1041-
if (io.outputting()) {
1055+
if (io.getKind() == IOKind::Outputting) {
10421056
io.beginMapping();
10431057
CustomMappingTraits<T>::output(io, Val);
10441058
io.endMapping();
@@ -1053,8 +1067,9 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
10531067
template <typename T>
10541068
std::enable_if_t<has_PolymorphicTraits<T>::value, void>
10551069
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1056-
switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val)
1057-
: io.getNodeKind()) {
1070+
switch (io.getKind() == IOKind::Outputting
1071+
? PolymorphicTraits<T>::getKind(Val)
1072+
: io.getNodeKind()) {
10581073
case NodeKind::Scalar:
10591074
return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx);
10601075
case NodeKind::Map:
@@ -1075,7 +1090,9 @@ std::enable_if_t<has_SequenceTraits<T>::value, void>
10751090
yamlize(IO &io, T &Seq, bool, Context &Ctx) {
10761091
if (has_FlowTraits<SequenceTraits<T>>::value) {
10771092
unsigned incnt = io.beginFlowSequence();
1078-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1093+
unsigned count = io.getKind() == IOKind::Outputting
1094+
? SequenceTraits<T>::size(io, Seq)
1095+
: incnt;
10791096
for (unsigned i = 0; i < count; ++i) {
10801097
void *SaveInfo;
10811098
if (io.preflightFlowElement(i, SaveInfo)) {
@@ -1086,7 +1103,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) {
10861103
io.endFlowSequence();
10871104
} else {
10881105
unsigned incnt = io.beginSequence();
1089-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1106+
unsigned count = io.getKind() == IOKind::Outputting
1107+
? SequenceTraits<T>::size(io, Seq)
1108+
: incnt;
10901109
for (unsigned i = 0; i < count; ++i) {
10911110
void *SaveInfo;
10921111
if (io.preflightElement(i, SaveInfo)) {
@@ -1239,15 +1258,15 @@ struct ScalarBitSetTraits<
12391258
template <typename TNorm, typename TFinal> struct MappingNormalization {
12401259
MappingNormalization(IO &i_o, TFinal &Obj)
12411260
: io(i_o), BufPtr(nullptr), Result(Obj) {
1242-
if (io.outputting()) {
1261+
if (io.getKind() == IOKind::Outputting) {
12431262
BufPtr = new (&Buffer) TNorm(io, Obj);
12441263
} else {
12451264
BufPtr = new (&Buffer) TNorm(io);
12461265
}
12471266
}
12481267

12491268
~MappingNormalization() {
1250-
if (!io.outputting()) {
1269+
if (io.getKind() != IOKind::Outputting) {
12511270
Result = BufPtr->denormalize(io);
12521271
}
12531272
BufPtr->~TNorm();
@@ -1269,7 +1288,7 @@ template <typename TNorm, typename TFinal> struct MappingNormalization {
12691288
template <typename TNorm, typename TFinal> struct MappingNormalizationHeap {
12701289
MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator)
12711290
: io(i_o), Result(Obj) {
1272-
if (io.outputting()) {
1291+
if (io.getKind() == IOKind::Outputting) {
12731292
BufPtr = new (&Buffer) TNorm(io, Obj);
12741293
} else if (allocator) {
12751294
BufPtr = allocator->Allocate<TNorm>();
@@ -1280,7 +1299,7 @@ template <typename TNorm, typename TFinal> struct MappingNormalizationHeap {
12801299
}
12811300

12821301
~MappingNormalizationHeap() {
1283-
if (io.outputting()) {
1302+
if (io.getKind() == IOKind::Outputting) {
12841303
BufPtr->~TNorm();
12851304
} else {
12861305
Result = BufPtr->denormalize(io);
@@ -1327,6 +1346,7 @@ class LLVM_ABI Input : public IO {
13271346
std::error_code error() override;
13281347

13291348
private:
1349+
IOKind getKind() const override;
13301350
bool outputting() const override;
13311351
bool mapTag(StringRef, bool) override;
13321352
void beginMapping() override;
@@ -1478,6 +1498,7 @@ class LLVM_ABI Output : public IO {
14781498
/// anyway.
14791499
void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; }
14801500

1501+
IOKind getKind() const override;
14811502
bool outputting() const override;
14821503
bool mapTag(StringRef, bool) override;
14831504
void beginMapping() override;
@@ -1563,8 +1584,8 @@ void IO::processKeyWithDefault(StringRef Key, std::optional<T> &Val,
15631584
assert(!DefaultValue && "std::optional<T> shouldn't have a value!");
15641585
void *SaveInfo;
15651586
bool UseDefault = true;
1566-
const bool sameAsDefault = outputting() && !Val;
1567-
if (!outputting() && !Val)
1587+
const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val;
1588+
if (getKind() != IOKind::Outputting && !Val)
15681589
Val = T();
15691590
if (Val &&
15701591
this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) {
@@ -1574,7 +1595,7 @@ void IO::processKeyWithDefault(StringRef Key, std::optional<T> &Val,
15741595
// was requested, i.e. the DefaultValue will be assigned. The DefaultValue
15751596
// is usually None.
15761597
bool IsNone = false;
1577-
if (!outputting())
1598+
if (getKind() == IOKind::Inputting)
15781599
if (const auto *Node =
15791600
dyn_cast<ScalarNode>(((Input *)this)->getCurrentNode()))
15801601
// We use rtrim to ignore possible white spaces that might exist when a

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ add_llvm_component_library(LLVMSupport
282282
WithColor.cpp
283283
YAMLParser.cpp
284284
YAMLTraits.cpp
285+
YAMLGenerateSchema.cpp
285286
raw_os_ostream.cpp
286287
raw_ostream.cpp
287288
raw_ostream_proxy.cpp

0 commit comments

Comments
 (0)