1 | //===-- YAMLGenerator.cpp - ClangDoc YAML -----------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // Implementation of the YAML generator, converting decl info into YAML output. |
9 | //===----------------------------------------------------------------------===// |
10 | |
11 | #include "Generators.h" |
12 | #include "Representation.h" |
13 | #include "llvm/Support/YAMLTraits.h" |
14 | #include "llvm/Support/raw_ostream.h" |
15 | #include <optional> |
16 | |
17 | using namespace clang::doc; |
18 | |
19 | // These define YAML traits for decoding the listed values within a vector. |
20 | LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo) |
21 | LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) |
22 | LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) |
23 | LLVM_YAML_IS_SEQUENCE_VECTOR(Location) |
24 | LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) |
25 | LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) |
26 | LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) |
27 | LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo) |
28 | LLVM_YAML_IS_SEQUENCE_VECTOR(TemplateParamInfo) |
29 | LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo) |
30 | LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo) |
31 | LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>) |
32 | LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) |
33 | |
34 | namespace llvm { |
35 | namespace yaml { |
36 | |
37 | // Enumerations to YAML output. |
38 | |
39 | template <> struct ScalarEnumerationTraits<clang::AccessSpecifier> { |
40 | static void enumeration(IO &IO, clang::AccessSpecifier &Value) { |
41 | IO.enumCase(Val&: Value, Str: "Public" , ConstVal: clang::AccessSpecifier::AS_public); |
42 | IO.enumCase(Val&: Value, Str: "Protected" , ConstVal: clang::AccessSpecifier::AS_protected); |
43 | IO.enumCase(Val&: Value, Str: "Private" , ConstVal: clang::AccessSpecifier::AS_private); |
44 | IO.enumCase(Val&: Value, Str: "None" , ConstVal: clang::AccessSpecifier::AS_none); |
45 | } |
46 | }; |
47 | |
48 | template <> struct ScalarEnumerationTraits<clang::TagTypeKind> { |
49 | static void enumeration(IO &IO, clang::TagTypeKind &Value) { |
50 | IO.enumCase(Val&: Value, Str: "Struct" , ConstVal: clang::TagTypeKind::Struct); |
51 | IO.enumCase(Val&: Value, Str: "Interface" , ConstVal: clang::TagTypeKind::Interface); |
52 | IO.enumCase(Val&: Value, Str: "Union" , ConstVal: clang::TagTypeKind::Union); |
53 | IO.enumCase(Val&: Value, Str: "Class" , ConstVal: clang::TagTypeKind::Class); |
54 | IO.enumCase(Val&: Value, Str: "Enum" , ConstVal: clang::TagTypeKind::Enum); |
55 | } |
56 | }; |
57 | |
58 | template <> struct ScalarEnumerationTraits<InfoType> { |
59 | static void enumeration(IO &IO, InfoType &Value) { |
60 | IO.enumCase(Val&: Value, Str: "Namespace" , ConstVal: InfoType::IT_namespace); |
61 | IO.enumCase(Val&: Value, Str: "Record" , ConstVal: InfoType::IT_record); |
62 | IO.enumCase(Val&: Value, Str: "Function" , ConstVal: InfoType::IT_function); |
63 | IO.enumCase(Val&: Value, Str: "Enum" , ConstVal: InfoType::IT_enum); |
64 | IO.enumCase(Val&: Value, Str: "Default" , ConstVal: InfoType::IT_default); |
65 | } |
66 | }; |
67 | |
68 | // Scalars to YAML output. |
69 | template <unsigned U> struct ScalarTraits<SmallString<U>> { |
70 | |
71 | static void output(const SmallString<U> &S, void *, llvm::raw_ostream &OS) { |
72 | for (const auto &C : S) |
73 | OS << C; |
74 | } |
75 | |
76 | static StringRef input(StringRef Scalar, void *, SmallString<U> &Value) { |
77 | Value.assign(Scalar.begin(), Scalar.end()); |
78 | return StringRef(); |
79 | } |
80 | |
81 | static QuotingType mustQuote(StringRef) { return QuotingType::Single; } |
82 | }; |
83 | |
84 | template <> struct ScalarTraits<std::array<unsigned char, 20>> { |
85 | |
86 | static void output(const std::array<unsigned char, 20> &S, void *, |
87 | llvm::raw_ostream &OS) { |
88 | OS << toHex(Input: toStringRef(Input: S)); |
89 | } |
90 | |
91 | static StringRef input(StringRef Scalar, void *, |
92 | std::array<unsigned char, 20> &Value) { |
93 | if (Scalar.size() != 40) |
94 | return "Error: Incorrect scalar size for USR." ; |
95 | Value = StringToSymbol(Value: Scalar); |
96 | return StringRef(); |
97 | } |
98 | |
99 | static SymbolID StringToSymbol(llvm::StringRef Value) { |
100 | SymbolID USR; |
101 | std::string HexString = fromHex(Input: Value); |
102 | std::copy(first: HexString.begin(), last: HexString.end(), result: USR.begin()); |
103 | return SymbolID(USR); |
104 | } |
105 | |
106 | static QuotingType mustQuote(StringRef) { return QuotingType::Single; } |
107 | }; |
108 | |
109 | // Helper functions to map infos to YAML. |
110 | |
111 | static void TypeInfoMapping(IO &IO, TypeInfo &I) { |
112 | IO.mapOptional(Key: "Type" , Val&: I.Type, Default: Reference()); |
113 | } |
114 | |
115 | static void FieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) { |
116 | TypeInfoMapping(IO, I); |
117 | IO.mapOptional(Key: "Name" , Val&: I.Name, Default: SmallString<16>()); |
118 | IO.mapOptional(Key: "DefaultValue" , Val&: I.DefaultValue, Default: SmallString<16>()); |
119 | } |
120 | |
121 | static void InfoMapping(IO &IO, Info &I) { |
122 | IO.mapRequired(Key: "USR" , Val&: I.USR); |
123 | IO.mapOptional(Key: "Name" , Val&: I.Name, Default: SmallString<16>()); |
124 | IO.mapOptional(Key: "Path" , Val&: I.Path, Default: SmallString<128>()); |
125 | IO.mapOptional(Key: "Namespace" , Val&: I.Namespace, Default: llvm::SmallVector<Reference, 4>()); |
126 | IO.mapOptional(Key: "Description" , Val&: I.Description); |
127 | } |
128 | |
129 | static void SymbolInfoMapping(IO &IO, SymbolInfo &I) { |
130 | InfoMapping(IO, I); |
131 | IO.mapOptional(Key: "DefLocation" , Val&: I.DefLoc, Default: std::optional<Location>()); |
132 | IO.mapOptional(Key: "Location" , Val&: I.Loc, Default: llvm::SmallVector<Location, 2>()); |
133 | } |
134 | |
135 | static void RecordInfoMapping(IO &IO, RecordInfo &I) { |
136 | SymbolInfoMapping(IO, I); |
137 | IO.mapOptional(Key: "TagType" , Val&: I.TagType); |
138 | IO.mapOptional(Key: "IsTypeDef" , Val&: I.IsTypeDef, Default: false); |
139 | IO.mapOptional(Key: "Members" , Val&: I.Members); |
140 | IO.mapOptional(Key: "Bases" , Val&: I.Bases); |
141 | IO.mapOptional(Key: "Parents" , Val&: I.Parents, Default: llvm::SmallVector<Reference, 4>()); |
142 | IO.mapOptional(Key: "VirtualParents" , Val&: I.VirtualParents, |
143 | Default: llvm::SmallVector<Reference, 4>()); |
144 | IO.mapOptional(Key: "ChildRecords" , Val&: I.Children.Records, Default: std::vector<Reference>()); |
145 | IO.mapOptional(Key: "ChildFunctions" , Val&: I.Children.Functions); |
146 | IO.mapOptional(Key: "ChildEnums" , Val&: I.Children.Enums); |
147 | IO.mapOptional(Key: "ChildTypedefs" , Val&: I.Children.Typedefs); |
148 | IO.mapOptional(Key: "Template" , Val&: I.Template); |
149 | } |
150 | |
151 | static void (IO &IO, CommentInfo &I) { |
152 | IO.mapOptional(Key: "Kind" , Val&: I.Kind, Default: SmallString<16>()); |
153 | IO.mapOptional(Key: "Text" , Val&: I.Text, Default: SmallString<64>()); |
154 | IO.mapOptional(Key: "Name" , Val&: I.Name, Default: SmallString<16>()); |
155 | IO.mapOptional(Key: "Direction" , Val&: I.Direction, Default: SmallString<8>()); |
156 | IO.mapOptional(Key: "ParamName" , Val&: I.ParamName, Default: SmallString<16>()); |
157 | IO.mapOptional(Key: "CloseName" , Val&: I.CloseName, Default: SmallString<16>()); |
158 | IO.mapOptional(Key: "SelfClosing" , Val&: I.SelfClosing, Default: false); |
159 | IO.mapOptional(Key: "Explicit" , Val&: I.Explicit, Default: false); |
160 | IO.mapOptional(Key: "Args" , Val&: I.Args, Default: llvm::SmallVector<SmallString<16>, 4>()); |
161 | IO.mapOptional(Key: "AttrKeys" , Val&: I.AttrKeys, |
162 | Default: llvm::SmallVector<SmallString<16>, 4>()); |
163 | IO.mapOptional(Key: "AttrValues" , Val&: I.AttrValues, |
164 | Default: llvm::SmallVector<SmallString<16>, 4>()); |
165 | IO.mapOptional(Key: "Children" , Val&: I.Children); |
166 | } |
167 | |
168 | // Template specialization to YAML traits for Infos. |
169 | |
170 | template <> struct MappingTraits<Location> { |
171 | static void mapping(IO &IO, Location &Loc) { |
172 | IO.mapOptional(Key: "LineNumber" , Val&: Loc.LineNumber, Default: 0); |
173 | IO.mapOptional(Key: "Filename" , Val&: Loc.Filename, Default: SmallString<32>()); |
174 | } |
175 | }; |
176 | |
177 | template <> struct MappingTraits<Reference> { |
178 | static void mapping(IO &IO, Reference &Ref) { |
179 | IO.mapOptional(Key: "Type" , Val&: Ref.RefType, Default: InfoType::IT_default); |
180 | IO.mapOptional(Key: "Name" , Val&: Ref.Name, Default: SmallString<16>()); |
181 | IO.mapOptional(Key: "QualName" , Val&: Ref.QualName, Default: SmallString<16>()); |
182 | IO.mapOptional(Key: "USR" , Val&: Ref.USR, Default: SymbolID()); |
183 | IO.mapOptional(Key: "Path" , Val&: Ref.Path, Default: SmallString<128>()); |
184 | } |
185 | }; |
186 | |
187 | template <> struct MappingTraits<TypeInfo> { |
188 | static void mapping(IO &IO, TypeInfo &I) { TypeInfoMapping(IO, I); } |
189 | }; |
190 | |
191 | template <> struct MappingTraits<FieldTypeInfo> { |
192 | static void mapping(IO &IO, FieldTypeInfo &I) { |
193 | TypeInfoMapping(IO, I); |
194 | IO.mapOptional(Key: "Name" , Val&: I.Name, Default: SmallString<16>()); |
195 | IO.mapOptional(Key: "DefaultValue" , Val&: I.DefaultValue, Default: SmallString<16>()); |
196 | } |
197 | }; |
198 | |
199 | template <> struct MappingTraits<MemberTypeInfo> { |
200 | static void mapping(IO &IO, MemberTypeInfo &I) { |
201 | FieldTypeInfoMapping(IO, I); |
202 | // clang::AccessSpecifier::AS_none is used as the default here because it's |
203 | // the AS that shouldn't be part of the output. Even though AS_public is the |
204 | // default in the struct, it should be displayed in the YAML output. |
205 | IO.mapOptional(Key: "Access" , Val&: I.Access, Default: clang::AccessSpecifier::AS_none); |
206 | IO.mapOptional(Key: "Description" , Val&: I.Description); |
207 | } |
208 | }; |
209 | |
210 | template <> struct MappingTraits<NamespaceInfo> { |
211 | static void mapping(IO &IO, NamespaceInfo &I) { |
212 | InfoMapping(IO, I); |
213 | IO.mapOptional(Key: "ChildNamespaces" , Val&: I.Children.Namespaces, |
214 | Default: std::vector<Reference>()); |
215 | IO.mapOptional(Key: "ChildRecords" , Val&: I.Children.Records, |
216 | Default: std::vector<Reference>()); |
217 | IO.mapOptional(Key: "ChildFunctions" , Val&: I.Children.Functions); |
218 | IO.mapOptional(Key: "ChildEnums" , Val&: I.Children.Enums); |
219 | IO.mapOptional(Key: "ChildTypedefs" , Val&: I.Children.Typedefs); |
220 | } |
221 | }; |
222 | |
223 | template <> struct MappingTraits<RecordInfo> { |
224 | static void mapping(IO &IO, RecordInfo &I) { RecordInfoMapping(IO, I); } |
225 | }; |
226 | |
227 | template <> struct MappingTraits<BaseRecordInfo> { |
228 | static void mapping(IO &IO, BaseRecordInfo &I) { |
229 | RecordInfoMapping(IO, I); |
230 | IO.mapOptional(Key: "IsVirtual" , Val&: I.IsVirtual, Default: false); |
231 | // clang::AccessSpecifier::AS_none is used as the default here because it's |
232 | // the AS that shouldn't be part of the output. Even though AS_public is the |
233 | // default in the struct, it should be displayed in the YAML output. |
234 | IO.mapOptional(Key: "Access" , Val&: I.Access, Default: clang::AccessSpecifier::AS_none); |
235 | IO.mapOptional(Key: "IsParent" , Val&: I.IsParent, Default: false); |
236 | } |
237 | }; |
238 | |
239 | template <> struct MappingTraits<EnumValueInfo> { |
240 | static void mapping(IO &IO, EnumValueInfo &I) { |
241 | IO.mapOptional(Key: "Name" , Val&: I.Name); |
242 | IO.mapOptional(Key: "Value" , Val&: I.Value); |
243 | IO.mapOptional(Key: "Expr" , Val&: I.ValueExpr, Default: SmallString<16>()); |
244 | } |
245 | }; |
246 | |
247 | template <> struct MappingTraits<EnumInfo> { |
248 | static void mapping(IO &IO, EnumInfo &I) { |
249 | SymbolInfoMapping(IO, I); |
250 | IO.mapOptional(Key: "Scoped" , Val&: I.Scoped, Default: false); |
251 | IO.mapOptional(Key: "BaseType" , Val&: I.BaseType); |
252 | IO.mapOptional(Key: "Members" , Val&: I.Members); |
253 | } |
254 | }; |
255 | |
256 | template <> struct MappingTraits<TypedefInfo> { |
257 | static void mapping(IO &IO, TypedefInfo &I) { |
258 | SymbolInfoMapping(IO, I); |
259 | IO.mapOptional(Key: "Underlying" , Val&: I.Underlying.Type); |
260 | IO.mapOptional(Key: "IsUsing" , Val&: I.IsUsing, Default: false); |
261 | } |
262 | }; |
263 | |
264 | template <> struct MappingTraits<FunctionInfo> { |
265 | static void mapping(IO &IO, FunctionInfo &I) { |
266 | SymbolInfoMapping(IO, I); |
267 | IO.mapOptional(Key: "IsMethod" , Val&: I.IsMethod, Default: false); |
268 | IO.mapOptional(Key: "Parent" , Val&: I.Parent, Default: Reference()); |
269 | IO.mapOptional(Key: "Params" , Val&: I.Params); |
270 | IO.mapOptional(Key: "ReturnType" , Val&: I.ReturnType); |
271 | // clang::AccessSpecifier::AS_none is used as the default here because it's |
272 | // the AS that shouldn't be part of the output. Even though AS_public is the |
273 | // default in the struct, it should be displayed in the YAML output. |
274 | IO.mapOptional(Key: "Access" , Val&: I.Access, Default: clang::AccessSpecifier::AS_none); |
275 | IO.mapOptional(Key: "Template" , Val&: I.Template); |
276 | } |
277 | }; |
278 | |
279 | template <> struct MappingTraits<TemplateParamInfo> { |
280 | static void mapping(IO &IO, TemplateParamInfo &I) { |
281 | IO.mapOptional(Key: "Contents" , Val&: I.Contents); |
282 | } |
283 | }; |
284 | |
285 | template <> struct MappingTraits<TemplateSpecializationInfo> { |
286 | static void mapping(IO &IO, TemplateSpecializationInfo &I) { |
287 | IO.mapOptional(Key: "SpecializationOf" , Val&: I.SpecializationOf); |
288 | IO.mapOptional(Key: "Params" , Val&: I.Params); |
289 | } |
290 | }; |
291 | |
292 | template <> struct MappingTraits<TemplateInfo> { |
293 | static void mapping(IO &IO, TemplateInfo &I) { |
294 | IO.mapOptional(Key: "Params" , Val&: I.Params); |
295 | IO.mapOptional(Key: "Specialization" , Val&: I.Specialization, |
296 | Default: std::optional<TemplateSpecializationInfo>()); |
297 | } |
298 | }; |
299 | |
300 | template <> struct MappingTraits<CommentInfo> { |
301 | static void (IO &IO, CommentInfo &I) { CommentInfoMapping(IO, I); } |
302 | }; |
303 | |
304 | template <> struct MappingTraits<std::unique_ptr<CommentInfo>> { |
305 | static void (IO &IO, std::unique_ptr<CommentInfo> &I) { |
306 | if (I) |
307 | CommentInfoMapping(IO, I&: *I); |
308 | } |
309 | }; |
310 | |
311 | } // end namespace yaml |
312 | } // end namespace llvm |
313 | |
314 | namespace clang { |
315 | namespace doc { |
316 | |
317 | /// Generator for YAML documentation. |
318 | class YAMLGenerator : public Generator { |
319 | public: |
320 | static const char *Format; |
321 | |
322 | llvm::Error generateDocs(StringRef RootDir, |
323 | llvm::StringMap<std::unique_ptr<doc::Info>> Infos, |
324 | const ClangDocContext &CDCtx) override; |
325 | llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, |
326 | const ClangDocContext &CDCtx) override; |
327 | }; |
328 | |
329 | const char *YAMLGenerator::Format = "yaml" ; |
330 | |
331 | llvm::Error |
332 | YAMLGenerator::generateDocs(StringRef RootDir, |
333 | llvm::StringMap<std::unique_ptr<doc::Info>> Infos, |
334 | const ClangDocContext &CDCtx) { |
335 | for (const auto &Group : Infos) { |
336 | doc::Info *Info = Group.getValue().get(); |
337 | |
338 | // Output file names according to the USR except the global namesapce. |
339 | // Anonymous namespaces are taken care of in serialization, so here we can |
340 | // safely assume an unnamed namespace is the global one. |
341 | llvm::SmallString<128> Path; |
342 | llvm::sys::path::native(path: RootDir, result&: Path); |
343 | if (Info->IT == InfoType::IT_namespace && Info->Name.empty()) { |
344 | llvm::sys::path::append(path&: Path, a: "index.yaml" ); |
345 | } else { |
346 | llvm::sys::path::append(path&: Path, a: Group.getKey() + ".yaml" ); |
347 | } |
348 | |
349 | std::error_code FileErr; |
350 | llvm::raw_fd_ostream InfoOS(Path, FileErr, llvm::sys::fs::OF_None); |
351 | if (FileErr) { |
352 | return llvm::createStringError(EC: FileErr, Fmt: "Error opening file '%s'" , |
353 | Vals: Path.c_str()); |
354 | } |
355 | |
356 | if (llvm::Error Err = generateDocForInfo(I: Info, OS&: InfoOS, CDCtx)) { |
357 | return Err; |
358 | } |
359 | } |
360 | |
361 | return llvm::Error::success(); |
362 | } |
363 | |
364 | llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS, |
365 | const ClangDocContext &CDCtx) { |
366 | llvm::yaml::Output InfoYAML(OS); |
367 | switch (I->IT) { |
368 | case InfoType::IT_namespace: |
369 | InfoYAML << *static_cast<clang::doc::NamespaceInfo *>(I); |
370 | break; |
371 | case InfoType::IT_record: |
372 | InfoYAML << *static_cast<clang::doc::RecordInfo *>(I); |
373 | break; |
374 | case InfoType::IT_enum: |
375 | InfoYAML << *static_cast<clang::doc::EnumInfo *>(I); |
376 | break; |
377 | case InfoType::IT_function: |
378 | InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I); |
379 | break; |
380 | case InfoType::IT_typedef: |
381 | InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I); |
382 | break; |
383 | case InfoType::IT_default: |
384 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
385 | Msg: "unexpected InfoType" ); |
386 | } |
387 | return llvm::Error::success(); |
388 | } |
389 | |
390 | static GeneratorRegistry::Add<YAMLGenerator> YAML(YAMLGenerator::Format, |
391 | "Generator for YAML output." ); |
392 | |
393 | // This anchor is used to force the linker to link in the generated object file |
394 | // and thus register the generator. |
395 | volatile int YAMLGeneratorAnchorSource = 0; |
396 | |
397 | } // namespace doc |
398 | } // namespace clang |
399 | |