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(Value, "Public" , clang::AccessSpecifier::AS_public); |
42 | IO.enumCase(Value, "Protected" , clang::AccessSpecifier::AS_protected); |
43 | IO.enumCase(Value, "Private" , clang::AccessSpecifier::AS_private); |
44 | IO.enumCase(Value, "None" , 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(Value, "Struct" , clang::TagTypeKind::TTK_Struct); |
51 | IO.enumCase(Value, "Interface" , clang::TagTypeKind::TTK_Interface); |
52 | IO.enumCase(Value, "Union" , clang::TagTypeKind::TTK_Union); |
53 | IO.enumCase(Value, "Class" , clang::TagTypeKind::TTK_Class); |
54 | IO.enumCase(Value, "Enum" , clang::TagTypeKind::TTK_Enum); |
55 | } |
56 | }; |
57 | |
58 | template <> struct ScalarEnumerationTraits<InfoType> { |
59 | static void enumeration(IO &IO, InfoType &Value) { |
60 | IO.enumCase(Value, "Namespace" , InfoType::IT_namespace); |
61 | IO.enumCase(Value, "Record" , InfoType::IT_record); |
62 | IO.enumCase(Value, "Function" , InfoType::IT_function); |
63 | IO.enumCase(Value, "Enum" , InfoType::IT_enum); |
64 | IO.enumCase(Value, "Default" , 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(toStringRef(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(Scalar); |
96 | return StringRef(); |
97 | } |
98 | |
99 | static SymbolID StringToSymbol(llvm::StringRef Value) { |
100 | SymbolID USR; |
101 | std::string HexString = fromHex(Value); |
102 | std::copy(HexString.begin(), HexString.end(), 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("Type" , I.Type, Reference()); |
113 | } |
114 | |
115 | static void FieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) { |
116 | TypeInfoMapping(IO, I); |
117 | IO.mapOptional("Name" , I.Name, SmallString<16>()); |
118 | IO.mapOptional("DefaultValue" , I.DefaultValue, SmallString<16>()); |
119 | } |
120 | |
121 | static void InfoMapping(IO &IO, Info &I) { |
122 | IO.mapRequired("USR" , I.USR); |
123 | IO.mapOptional("Name" , I.Name, SmallString<16>()); |
124 | IO.mapOptional("Path" , I.Path, SmallString<128>()); |
125 | IO.mapOptional("Namespace" , I.Namespace, llvm::SmallVector<Reference, 4>()); |
126 | IO.mapOptional("Description" , I.Description); |
127 | } |
128 | |
129 | static void SymbolInfoMapping(IO &IO, SymbolInfo &I) { |
130 | InfoMapping(IO, I); |
131 | IO.mapOptional("DefLocation" , I.DefLoc, std::optional<Location>()); |
132 | IO.mapOptional("Location" , I.Loc, llvm::SmallVector<Location, 2>()); |
133 | } |
134 | |
135 | static void RecordInfoMapping(IO &IO, RecordInfo &I) { |
136 | SymbolInfoMapping(IO, I); |
137 | IO.mapOptional("TagType" , I.TagType); |
138 | IO.mapOptional("IsTypeDef" , I.IsTypeDef, false); |
139 | IO.mapOptional("Members" , I.Members); |
140 | IO.mapOptional("Bases" , I.Bases); |
141 | IO.mapOptional("Parents" , I.Parents, llvm::SmallVector<Reference, 4>()); |
142 | IO.mapOptional("VirtualParents" , I.VirtualParents, |
143 | llvm::SmallVector<Reference, 4>()); |
144 | IO.mapOptional("ChildRecords" , I.Children.Records, std::vector<Reference>()); |
145 | IO.mapOptional("ChildFunctions" , I.Children.Functions); |
146 | IO.mapOptional("ChildEnums" , I.Children.Enums); |
147 | IO.mapOptional("ChildTypedefs" , I.Children.Typedefs); |
148 | IO.mapOptional("Template" , I.Template); |
149 | } |
150 | |
151 | static void (IO &IO, CommentInfo &I) { |
152 | IO.mapOptional("Kind" , I.Kind, SmallString<16>()); |
153 | IO.mapOptional("Text" , I.Text, SmallString<64>()); |
154 | IO.mapOptional("Name" , I.Name, SmallString<16>()); |
155 | IO.mapOptional("Direction" , I.Direction, SmallString<8>()); |
156 | IO.mapOptional("ParamName" , I.ParamName, SmallString<16>()); |
157 | IO.mapOptional("CloseName" , I.CloseName, SmallString<16>()); |
158 | IO.mapOptional("SelfClosing" , I.SelfClosing, false); |
159 | IO.mapOptional("Explicit" , I.Explicit, false); |
160 | IO.mapOptional("Args" , I.Args, llvm::SmallVector<SmallString<16>, 4>()); |
161 | IO.mapOptional("AttrKeys" , I.AttrKeys, |
162 | llvm::SmallVector<SmallString<16>, 4>()); |
163 | IO.mapOptional("AttrValues" , I.AttrValues, |
164 | llvm::SmallVector<SmallString<16>, 4>()); |
165 | IO.mapOptional("Children" , 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("LineNumber" , Loc.LineNumber, 0); |
173 | IO.mapOptional("Filename" , Loc.Filename, SmallString<32>()); |
174 | } |
175 | }; |
176 | |
177 | template <> struct MappingTraits<Reference> { |
178 | static void mapping(IO &IO, Reference &Ref) { |
179 | IO.mapOptional("Type" , Ref.RefType, InfoType::IT_default); |
180 | IO.mapOptional("Name" , Ref.Name, SmallString<16>()); |
181 | IO.mapOptional("QualName" , Ref.QualName, SmallString<16>()); |
182 | IO.mapOptional("USR" , Ref.USR, SymbolID()); |
183 | IO.mapOptional("Path" , Ref.Path, 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("Name" , I.Name, SmallString<16>()); |
195 | IO.mapOptional("DefaultValue" , I.DefaultValue, 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("Access" , I.Access, clang::AccessSpecifier::AS_none); |
206 | IO.mapOptional("Description" , 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("ChildNamespaces" , I.Children.Namespaces, |
214 | std::vector<Reference>()); |
215 | IO.mapOptional("ChildRecords" , I.Children.Records, |
216 | std::vector<Reference>()); |
217 | IO.mapOptional("ChildFunctions" , I.Children.Functions); |
218 | IO.mapOptional("ChildEnums" , I.Children.Enums); |
219 | IO.mapOptional("ChildTypedefs" , 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("IsVirtual" , I.IsVirtual, 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("Access" , I.Access, clang::AccessSpecifier::AS_none); |
235 | IO.mapOptional("IsParent" , I.IsParent, false); |
236 | } |
237 | }; |
238 | |
239 | template <> struct MappingTraits<EnumValueInfo> { |
240 | static void mapping(IO &IO, EnumValueInfo &I) { |
241 | IO.mapOptional("Name" , I.Name); |
242 | IO.mapOptional("Value" , I.Value); |
243 | IO.mapOptional("Expr" , I.ValueExpr, 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("Scoped" , I.Scoped, false); |
251 | IO.mapOptional("BaseType" , I.BaseType); |
252 | IO.mapOptional("Members" , 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("Underlying" , I.Underlying.Type); |
260 | IO.mapOptional("IsUsing" , I.IsUsing, false); |
261 | } |
262 | }; |
263 | |
264 | template <> struct MappingTraits<FunctionInfo> { |
265 | static void mapping(IO &IO, FunctionInfo &I) { |
266 | SymbolInfoMapping(IO, I); |
267 | IO.mapOptional("IsMethod" , I.IsMethod, false); |
268 | IO.mapOptional("Parent" , I.Parent, Reference()); |
269 | IO.mapOptional("Params" , I.Params); |
270 | IO.mapOptional("ReturnType" , 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("Access" , I.Access, clang::AccessSpecifier::AS_none); |
275 | IO.mapOptional("Template" , I.Template); |
276 | } |
277 | }; |
278 | |
279 | template <> struct MappingTraits<TemplateParamInfo> { |
280 | static void mapping(IO &IO, TemplateParamInfo &I) { |
281 | IO.mapOptional("Contents" , I.Contents); |
282 | } |
283 | }; |
284 | |
285 | template <> struct MappingTraits<TemplateSpecializationInfo> { |
286 | static void mapping(IO &IO, TemplateSpecializationInfo &I) { |
287 | IO.mapOptional("SpecializationOf" , I.SpecializationOf); |
288 | IO.mapOptional("Params" , I.Params); |
289 | } |
290 | }; |
291 | |
292 | template <> struct MappingTraits<TemplateInfo> { |
293 | static void mapping(IO &IO, TemplateInfo &I) { |
294 | IO.mapOptional("Params" , I.Params); |
295 | IO.mapOptional("Specialization" , I.Specialization, |
296 | 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); |
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(RootDir, Path); |
343 | if (Info->IT == InfoType::IT_namespace && Info->Name.empty()) { |
344 | llvm::sys::path::append(Path, "index.yaml" ); |
345 | } else { |
346 | llvm::sys::path::append(Path, 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(FileErr, "Error opening file '%s'" , |
353 | Path.c_str()); |
354 | } |
355 | |
356 | if (llvm::Error Err = generateDocForInfo(Info, 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(llvm::inconvertibleErrorCode(), |
385 | "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 | |