Skip to content

Commit 92222dc

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 3a5d776 commit 92222dc

File tree

8 files changed

+830
-27
lines changed

8 files changed

+830
-27
lines changed

llvm/include/llvm/Support/YAMLGenerateSchema.h

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

llvm/include/llvm/Support/YAMLTraits.h

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,19 @@ struct unvalidatedMappingTraits
774774
bool, has_MappingTraits<T, Context>::value &&
775775
!has_MappingValidateTraits<T, Context>::value> {};
776776

777+
enum class IOKind : uint8_t {
778+
Outputting,
779+
Inputting,
780+
GeneratingSchema,
781+
};
782+
777783
// Base class for Input and Output.
778784
class IO {
779785
public:
780786
IO(void *Ctxt = nullptr);
781787
virtual ~IO();
782788

789+
virtual IOKind getKind() const = 0;
783790
virtual bool outputting() const = 0;
784791

785792
virtual unsigned beginSequence() = 0;
@@ -824,15 +831,17 @@ class IO {
824831

825832
template <typename T>
826833
void enumCase(T &Val, const char* Str, const T ConstVal) {
827-
if ( matchEnumScalar(Str, outputting() && Val == ConstVal) ) {
834+
if (matchEnumScalar(Str,
835+
getKind() == IOKind::Outputting && Val == ConstVal)) {
828836
Val = ConstVal;
829837
}
830838
}
831839

832840
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
833841
template <typename T>
834-
void enumCase(T &Val, const char* Str, const uint32_t ConstVal) {
835-
if ( matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal)) ) {
842+
void enumCase(T &Val, const char *Str, const uint32_t ConstVal) {
843+
if (matchEnumScalar(Str, getKind() == IOKind::Outputting &&
844+
Val == static_cast<T>(ConstVal))) {
836845
Val = ConstVal;
837846
}
838847
}
@@ -849,30 +858,34 @@ class IO {
849858
}
850859

851860
template <typename T>
852-
void bitSetCase(T &Val, const char* Str, const T ConstVal) {
853-
if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) {
861+
void bitSetCase(T &Val, const char *Str, const T ConstVal) {
862+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
863+
(Val & ConstVal) == ConstVal)) {
854864
Val = static_cast<T>(Val | ConstVal);
855865
}
856866
}
857867

858868
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
859869
template <typename T>
860870
void bitSetCase(T &Val, const char* Str, const uint32_t ConstVal) {
861-
if ( bitSetMatch(Str, outputting() && (Val & ConstVal) == ConstVal) ) {
871+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
872+
(Val & ConstVal) == ConstVal)) {
862873
Val = static_cast<T>(Val | ConstVal);
863874
}
864875
}
865876

866877
template <typename T>
867878
void maskedBitSetCase(T &Val, const char *Str, T ConstVal, T Mask) {
868-
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
879+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
880+
(Val & Mask) == ConstVal))
869881
Val = Val | ConstVal;
870882
}
871883

872884
template <typename T>
873885
void maskedBitSetCase(T &Val, const char *Str, uint32_t ConstVal,
874886
uint32_t Mask) {
875-
if (bitSetMatch(Str, outputting() && (Val & Mask) == ConstVal))
887+
if (bitSetMatch(Str, getKind() == IOKind::Outputting &&
888+
(Val & Mask) == ConstVal))
876889
Val = Val | ConstVal;
877890
}
878891

@@ -942,7 +955,8 @@ class IO {
942955
bool Required, Context &Ctx) {
943956
void *SaveInfo;
944957
bool UseDefault;
945-
const bool sameAsDefault = outputting() && Val == DefaultValue;
958+
const bool sameAsDefault =
959+
(getKind() == IOKind::Outputting) && Val == DefaultValue;
946960
if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault,
947961
SaveInfo) ) {
948962
yamlize(*this, Val, Required, Ctx);
@@ -1004,7 +1018,7 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
10041018
template <typename T>
10051019
std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
10061020
EmptyContext &Ctx) {
1007-
if ( io.outputting() ) {
1021+
if (io.getKind() != IOKind::Inputting) {
10081022
SmallString<128> Storage;
10091023
raw_svector_ostream Buffer(Storage);
10101024
ScalarTraits<T>::output(Val, io.getContext(), Buffer);
@@ -1024,7 +1038,7 @@ std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
10241038
template <typename T>
10251039
std::enable_if_t<has_BlockScalarTraits<T>::value, void>
10261040
yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
1027-
if (YamlIO.outputting()) {
1041+
if (YamlIO.getKind() != IOKind::Inputting) {
10281042
std::string Storage;
10291043
raw_string_ostream Buffer(Storage);
10301044
BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer);
@@ -1043,7 +1057,7 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
10431057
template <typename T>
10441058
std::enable_if_t<has_TaggedScalarTraits<T>::value, void>
10451059
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1046-
if (io.outputting()) {
1060+
if (io.getKind() != IOKind::Inputting) {
10471061
std::string ScalarStorage, TagStorage;
10481062
raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage);
10491063
TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer,
@@ -1085,15 +1099,15 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
10851099
io.beginFlowMapping();
10861100
else
10871101
io.beginMapping();
1088-
if (io.outputting()) {
1102+
if (io.getKind() == IOKind::Outputting) {
10891103
std::string Err = detail::doValidate(io, Val, Ctx);
10901104
if (!Err.empty()) {
10911105
errs() << Err << "\n";
10921106
assert(Err.empty() && "invalid struct trying to be written as yaml");
10931107
}
10941108
}
10951109
detail::doMapping(io, Val, Ctx);
1096-
if (!io.outputting()) {
1110+
if (io.getKind() == IOKind::Inputting) {
10971111
std::string Err = detail::doValidate(io, Val, Ctx);
10981112
if (!Err.empty())
10991113
io.setError(Err);
@@ -1113,7 +1127,7 @@ yamlizeMappingEnumInput(IO &io, T &Val) {
11131127
template <typename T, typename Context>
11141128
std::enable_if_t<has_MappingEnumInputTraits<T, Context>::value, bool>
11151129
yamlizeMappingEnumInput(IO &io, T &Val) {
1116-
if (io.outputting())
1130+
if (io.getKind() != IOKind::Inputting)
11171131
return false;
11181132

11191133
io.beginEnumScalar();
@@ -1142,7 +1156,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
11421156
template <typename T>
11431157
std::enable_if_t<has_CustomMappingTraits<T>::value, void>
11441158
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1145-
if ( io.outputting() ) {
1159+
if (io.getKind() != IOKind::Inputting) {
11461160
io.beginMapping();
11471161
CustomMappingTraits<T>::output(io, Val);
11481162
io.endMapping();
@@ -1157,8 +1171,9 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
11571171
template <typename T>
11581172
std::enable_if_t<has_PolymorphicTraits<T>::value, void>
11591173
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1160-
switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val)
1161-
: io.getNodeKind()) {
1174+
switch ((io.getKind() == IOKind::Inputting)
1175+
? io.getNodeKind()
1176+
: PolymorphicTraits<T>::getKind(Val)) {
11621177
case NodeKind::Scalar:
11631178
return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx);
11641179
case NodeKind::Map:
@@ -1179,7 +1194,9 @@ std::enable_if_t<has_SequenceTraits<T>::value, void>
11791194
yamlize(IO &io, T &Seq, bool, Context &Ctx) {
11801195
if ( has_FlowTraits< SequenceTraits<T>>::value ) {
11811196
unsigned incnt = io.beginFlowSequence();
1182-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1197+
unsigned count = (io.getKind() == IOKind::Outputting)
1198+
? SequenceTraits<T>::size(io, Seq)
1199+
: incnt;
11831200
for(unsigned i=0; i < count; ++i) {
11841201
void *SaveInfo;
11851202
if ( io.preflightFlowElement(i, SaveInfo) ) {
@@ -1191,7 +1208,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) {
11911208
}
11921209
else {
11931210
unsigned incnt = io.beginSequence();
1194-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1211+
unsigned count = (io.getKind() == IOKind::Outputting)
1212+
? SequenceTraits<T>::size(io, Seq)
1213+
: incnt;
11951214
for(unsigned i=0; i < count; ++i) {
11961215
void *SaveInfo;
11971216
if ( io.preflightElement(i, SaveInfo) ) {
@@ -1358,7 +1377,7 @@ template <typename TNorm, typename TFinal>
13581377
struct MappingNormalization {
13591378
MappingNormalization(IO &i_o, TFinal &Obj)
13601379
: io(i_o), BufPtr(nullptr), Result(Obj) {
1361-
if ( io.outputting() ) {
1380+
if (io.getKind() == IOKind::Outputting) {
13621381
BufPtr = new (&Buffer) TNorm(io, Obj);
13631382
}
13641383
else {
@@ -1367,7 +1386,7 @@ struct MappingNormalization {
13671386
}
13681387

13691388
~MappingNormalization() {
1370-
if ( ! io.outputting() ) {
1389+
if (io.getKind() == IOKind::Inputting) {
13711390
Result = BufPtr->denormalize(io);
13721391
}
13731392
BufPtr->~TNorm();
@@ -1390,7 +1409,7 @@ template <typename TNorm, typename TFinal>
13901409
struct MappingNormalizationHeap {
13911410
MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator)
13921411
: io(i_o), Result(Obj) {
1393-
if ( io.outputting() ) {
1412+
if (io.getKind() == IOKind::Outputting) {
13941413
BufPtr = new (&Buffer) TNorm(io, Obj);
13951414
}
13961415
else if (allocator) {
@@ -1402,7 +1421,7 @@ struct MappingNormalizationHeap {
14021421
}
14031422

14041423
~MappingNormalizationHeap() {
1405-
if ( io.outputting() ) {
1424+
if (io.getKind() == IOKind::Outputting) {
14061425
BufPtr->~TNorm();
14071426
}
14081427
else {
@@ -1452,6 +1471,7 @@ class Input : public IO {
14521471
std::error_code error() override;
14531472

14541473
private:
1474+
IOKind getKind() const override;
14551475
bool outputting() const override;
14561476
bool mapTag(StringRef, bool) override;
14571477
void beginMapping() override;
@@ -1603,6 +1623,7 @@ class Output : public IO {
16031623
/// anyway.
16041624
void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; }
16051625

1626+
IOKind getKind() const override;
16061627
bool outputting() const override;
16071628
bool mapTag(StringRef, bool) override;
16081629
void beginMapping() override;
@@ -1688,8 +1709,8 @@ void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val,
16881709
assert(!DefaultValue && "std::optional<T> shouldn't have a value!");
16891710
void *SaveInfo;
16901711
bool UseDefault = true;
1691-
const bool sameAsDefault = outputting() && !Val;
1692-
if (!outputting() && !Val)
1712+
const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val;
1713+
if (!(getKind() == IOKind::Outputting) && !Val)
16931714
Val = T();
16941715
if (Val &&
16951716
this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) {
@@ -1699,7 +1720,7 @@ void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val,
16991720
// was requested, i.e. the DefaultValue will be assigned. The DefaultValue
17001721
// is usually None.
17011722
bool IsNone = false;
1702-
if (!outputting())
1723+
if (getKind() == IOKind::Inputting)
17031724
if (const auto *Node =
17041725
dyn_cast<ScalarNode>(((Input *)this)->getCurrentNode()))
17051726
// 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
@@ -272,6 +272,7 @@ add_llvm_component_library(LLVMSupport
272272
WithColor.cpp
273273
YAMLParser.cpp
274274
YAMLTraits.cpp
275+
YAMLGenerateSchema.cpp
275276
raw_os_ostream.cpp
276277
raw_ostream.cpp
277278
raw_socket_stream.cpp

0 commit comments

Comments
 (0)