Skip to content

Commit 1c31d85

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 2e9494f commit 1c31d85

File tree

8 files changed

+833
-34
lines changed

8 files changed

+833
-34
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: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -775,12 +775,19 @@ struct unvalidatedMappingTraits
775775
bool, has_MappingTraits<T, Context>::value &&
776776
!has_MappingValidateTraits<T, Context>::value> {};
777777

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

790+
virtual IOKind getKind() const = 0;
784791
virtual bool outputting() const = 0;
785792

786793
virtual unsigned beginSequence() = 0;
@@ -825,15 +832,17 @@ class LLVM_ABI IO {
825832

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

833841
// allow anonymous enum values to be used with LLVM_YAML_STRONG_TYPEDEF
834842
template <typename T>
835-
void enumCase(T &Val, const char* Str, const uint32_t ConstVal) {
836-
if ( matchEnumScalar(Str, outputting() && Val == static_cast<T>(ConstVal)) ) {
843+
void enumCase(T &Val, const char *Str, const uint32_t ConstVal) {
844+
if (matchEnumScalar(Str, getKind() == IOKind::Outputting &&
845+
Val == static_cast<T>(ConstVal))) {
837846
Val = ConstVal;
838847
}
839848
}
@@ -850,30 +859,34 @@ class LLVM_ABI IO {
850859
}
851860

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

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

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

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

@@ -938,7 +951,8 @@ class LLVM_ABI IO {
938951
bool Required, Context &Ctx) {
939952
void *SaveInfo;
940953
bool UseDefault;
941-
const bool sameAsDefault = outputting() && Val == DefaultValue;
954+
const bool sameAsDefault =
955+
(getKind() == IOKind::Outputting) && Val == DefaultValue;
942956
if ( this->preflightKey(Key, Required, sameAsDefault, UseDefault,
943957
SaveInfo) ) {
944958
yamlize(*this, Val, Required, Ctx);
@@ -1000,14 +1014,13 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
10001014
template <typename T>
10011015
std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
10021016
EmptyContext &Ctx) {
1003-
if ( io.outputting() ) {
1017+
if (io.getKind() != IOKind::Inputting) {
10041018
SmallString<128> Storage;
10051019
raw_svector_ostream Buffer(Storage);
10061020
ScalarTraits<T>::output(Val, io.getContext(), Buffer);
10071021
StringRef Str = Buffer.str();
10081022
io.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
1009-
}
1010-
else {
1023+
} else {
10111024
StringRef Str;
10121025
io.scalarString(Str, ScalarTraits<T>::mustQuote(Str));
10131026
StringRef Result = ScalarTraits<T>::input(Str, io.getContext(), Val);
@@ -1020,7 +1033,7 @@ std::enable_if_t<has_ScalarTraits<T>::value, void> yamlize(IO &io, T &Val, bool,
10201033
template <typename T>
10211034
std::enable_if_t<has_BlockScalarTraits<T>::value, void>
10221035
yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
1023-
if (YamlIO.outputting()) {
1036+
if (YamlIO.getKind() != IOKind::Inputting) {
10241037
std::string Storage;
10251038
raw_string_ostream Buffer(Storage);
10261039
BlockScalarTraits<T>::output(Val, YamlIO.getContext(), Buffer);
@@ -1039,7 +1052,7 @@ yamlize(IO &YamlIO, T &Val, bool, EmptyContext &Ctx) {
10391052
template <typename T>
10401053
std::enable_if_t<has_TaggedScalarTraits<T>::value, void>
10411054
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1042-
if (io.outputting()) {
1055+
if (io.getKind() != IOKind::Inputting) {
10431056
std::string ScalarStorage, TagStorage;
10441057
raw_string_ostream ScalarBuffer(ScalarStorage), TagBuffer(TagStorage);
10451058
TaggedScalarTraits<T>::output(Val, io.getContext(), ScalarBuffer,
@@ -1081,15 +1094,15 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
10811094
io.beginFlowMapping();
10821095
else
10831096
io.beginMapping();
1084-
if (io.outputting()) {
1097+
if (io.getKind() == IOKind::Outputting) {
10851098
std::string Err = detail::doValidate(io, Val, Ctx);
10861099
if (!Err.empty()) {
10871100
errs() << Err << "\n";
10881101
assert(Err.empty() && "invalid struct trying to be written as yaml");
10891102
}
10901103
}
10911104
detail::doMapping(io, Val, Ctx);
1092-
if (!io.outputting()) {
1105+
if (io.getKind() == IOKind::Inputting) {
10931106
std::string Err = detail::doValidate(io, Val, Ctx);
10941107
if (!Err.empty())
10951108
io.setError(Err);
@@ -1134,7 +1147,7 @@ yamlize(IO &io, T &Val, bool, Context &Ctx) {
11341147
template <typename T>
11351148
std::enable_if_t<has_CustomMappingTraits<T>::value, void>
11361149
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1137-
if ( io.outputting() ) {
1150+
if (io.getKind() != IOKind::Inputting) {
11381151
io.beginMapping();
11391152
CustomMappingTraits<T>::output(io, Val);
11401153
io.endMapping();
@@ -1149,8 +1162,9 @@ yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
11491162
template <typename T>
11501163
std::enable_if_t<has_PolymorphicTraits<T>::value, void>
11511164
yamlize(IO &io, T &Val, bool, EmptyContext &Ctx) {
1152-
switch (io.outputting() ? PolymorphicTraits<T>::getKind(Val)
1153-
: io.getNodeKind()) {
1165+
switch ((io.getKind() == IOKind::Inputting)
1166+
? io.getNodeKind()
1167+
: PolymorphicTraits<T>::getKind(Val)) {
11541168
case NodeKind::Scalar:
11551169
return yamlize(io, PolymorphicTraits<T>::getAsScalar(Val), true, Ctx);
11561170
case NodeKind::Map:
@@ -1171,7 +1185,9 @@ std::enable_if_t<has_SequenceTraits<T>::value, void>
11711185
yamlize(IO &io, T &Seq, bool, Context &Ctx) {
11721186
if ( has_FlowTraits< SequenceTraits<T>>::value ) {
11731187
unsigned incnt = io.beginFlowSequence();
1174-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1188+
unsigned count = (io.getKind() == IOKind::Outputting)
1189+
? SequenceTraits<T>::size(io, Seq)
1190+
: incnt;
11751191
for(unsigned i=0; i < count; ++i) {
11761192
void *SaveInfo;
11771193
if ( io.preflightFlowElement(i, SaveInfo) ) {
@@ -1183,7 +1199,9 @@ yamlize(IO &io, T &Seq, bool, Context &Ctx) {
11831199
}
11841200
else {
11851201
unsigned incnt = io.beginSequence();
1186-
unsigned count = io.outputting() ? SequenceTraits<T>::size(io, Seq) : incnt;
1202+
unsigned count = (io.getKind() == IOKind::Outputting)
1203+
? SequenceTraits<T>::size(io, Seq)
1204+
: incnt;
11871205
for(unsigned i=0; i < count; ++i) {
11881206
void *SaveInfo;
11891207
if ( io.preflightElement(i, SaveInfo) ) {
@@ -1350,16 +1368,15 @@ template <typename TNorm, typename TFinal>
13501368
struct MappingNormalization {
13511369
MappingNormalization(IO &i_o, TFinal &Obj)
13521370
: io(i_o), BufPtr(nullptr), Result(Obj) {
1353-
if ( io.outputting() ) {
1371+
if (io.getKind() == IOKind::Outputting) {
13541372
BufPtr = new (&Buffer) TNorm(io, Obj);
1355-
}
1356-
else {
1373+
} else {
13571374
BufPtr = new (&Buffer) TNorm(io);
13581375
}
13591376
}
13601377

13611378
~MappingNormalization() {
1362-
if ( ! io.outputting() ) {
1379+
if (io.getKind() == IOKind::Inputting) {
13631380
Result = BufPtr->denormalize(io);
13641381
}
13651382
BufPtr->~TNorm();
@@ -1382,10 +1399,9 @@ template <typename TNorm, typename TFinal>
13821399
struct MappingNormalizationHeap {
13831400
MappingNormalizationHeap(IO &i_o, TFinal &Obj, BumpPtrAllocator *allocator)
13841401
: io(i_o), Result(Obj) {
1385-
if ( io.outputting() ) {
1402+
if (io.getKind() == IOKind::Outputting) {
13861403
BufPtr = new (&Buffer) TNorm(io, Obj);
1387-
}
1388-
else if (allocator) {
1404+
} else if (allocator) {
13891405
BufPtr = allocator->Allocate<TNorm>();
13901406
new (BufPtr) TNorm(io);
13911407
} else {
@@ -1394,10 +1410,9 @@ struct MappingNormalizationHeap {
13941410
}
13951411

13961412
~MappingNormalizationHeap() {
1397-
if ( io.outputting() ) {
1413+
if (io.getKind() == IOKind::Outputting) {
13981414
BufPtr->~TNorm();
1399-
}
1400-
else {
1415+
} else {
14011416
Result = BufPtr->denormalize(io);
14021417
}
14031418
}
@@ -1444,6 +1459,7 @@ class LLVM_ABI Input : public IO {
14441459
std::error_code error() override;
14451460

14461461
private:
1462+
IOKind getKind() const override;
14471463
bool outputting() const override;
14481464
bool mapTag(StringRef, bool) override;
14491465
void beginMapping() override;
@@ -1595,6 +1611,7 @@ class LLVM_ABI Output : public IO {
15951611
/// anyway.
15961612
void setWriteDefaultValues(bool Write) { WriteDefaultValues = Write; }
15971613

1614+
IOKind getKind() const override;
15981615
bool outputting() const override;
15991616
bool mapTag(StringRef, bool) override;
16001617
void beginMapping() override;
@@ -1680,8 +1697,8 @@ void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val,
16801697
assert(!DefaultValue && "std::optional<T> shouldn't have a value!");
16811698
void *SaveInfo;
16821699
bool UseDefault = true;
1683-
const bool sameAsDefault = outputting() && !Val;
1684-
if (!outputting() && !Val)
1700+
const bool sameAsDefault = (getKind() == IOKind::Outputting) && !Val;
1701+
if (!(getKind() == IOKind::Outputting) && !Val)
16851702
Val = T();
16861703
if (Val &&
16871704
this->preflightKey(Key, Required, sameAsDefault, UseDefault, SaveInfo)) {
@@ -1691,7 +1708,7 @@ void IO::processKeyWithDefault(const char *Key, std::optional<T> &Val,
16911708
// was requested, i.e. the DefaultValue will be assigned. The DefaultValue
16921709
// is usually None.
16931710
bool IsNone = false;
1694-
if (!outputting())
1711+
if (getKind() == IOKind::Inputting)
16951712
if (const auto *Node =
16961713
dyn_cast<ScalarNode>(((Input *)this)->getCurrentNode()))
16971714
// 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
@@ -275,6 +275,7 @@ add_llvm_component_library(LLVMSupport
275275
WithColor.cpp
276276
YAMLParser.cpp
277277
YAMLTraits.cpp
278+
YAMLGenerateSchema.cpp
278279
raw_os_ostream.cpp
279280
raw_ostream.cpp
280281
raw_socket_stream.cpp

0 commit comments

Comments
 (0)