xref: /llvm-project/clang-tools-extra/clang-doc/YAMLGenerator.cpp (revision edd690b02e16e991393bf7f67631196942369aed)
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> {
enumerationllvm::yaml::ScalarEnumerationTraits40   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> {
enumerationllvm::yaml::ScalarEnumerationTraits49   static void enumeration(IO &IO, clang::TagTypeKind &Value) {
50     IO.enumCase(Value, "Struct", clang::TagTypeKind::Struct);
51     IO.enumCase(Value, "Interface", clang::TagTypeKind::Interface);
52     IO.enumCase(Value, "Union", clang::TagTypeKind::Union);
53     IO.enumCase(Value, "Class", clang::TagTypeKind::Class);
54     IO.enumCase(Value, "Enum", clang::TagTypeKind::Enum);
55   }
56 };
57 
58 template <> struct ScalarEnumerationTraits<InfoType> {
enumerationllvm::yaml::ScalarEnumerationTraits59   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 
outputllvm::yaml::ScalarTraits71   static void output(const SmallString<U> &S, void *, llvm::raw_ostream &OS) {
72     for (const auto &C : S)
73       OS << C;
74   }
75 
inputllvm::yaml::ScalarTraits76   static StringRef input(StringRef Scalar, void *, SmallString<U> &Value) {
77     Value.assign(Scalar.begin(), Scalar.end());
78     return StringRef();
79   }
80 
mustQuotellvm::yaml::ScalarTraits81   static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
82 };
83 
84 template <> struct ScalarTraits<std::array<unsigned char, 20>> {
85 
outputllvm::yaml::ScalarTraits86   static void output(const std::array<unsigned char, 20> &S, void *,
87                      llvm::raw_ostream &OS) {
88     OS << toHex(toStringRef(S));
89   }
90 
inputllvm::yaml::ScalarTraits91   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 
StringToSymbolllvm::yaml::ScalarTraits99   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 
mustQuotellvm::yaml::ScalarTraits106   static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
107 };
108 
109 // Helper functions to map infos to YAML.
110 
TypeInfoMapping(IO & IO,TypeInfo & I)111 static void TypeInfoMapping(IO &IO, TypeInfo &I) {
112   IO.mapOptional("Type", I.Type, Reference());
113 }
114 
FieldTypeInfoMapping(IO & IO,FieldTypeInfo & I)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 
InfoMapping(IO & IO,Info & I)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 
SymbolInfoMapping(IO & IO,SymbolInfo & I)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 
RecordInfoMapping(IO & IO,RecordInfo & I)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 
CommentInfoMapping(IO & IO,CommentInfo & I)151 static void CommentInfoMapping(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> {
mappingllvm::yaml::MappingTraits171   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> {
mappingllvm::yaml::MappingTraits178   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> {
mappingllvm::yaml::MappingTraits188   static void mapping(IO &IO, TypeInfo &I) { TypeInfoMapping(IO, I); }
189 };
190 
191 template <> struct MappingTraits<FieldTypeInfo> {
mappingllvm::yaml::MappingTraits192   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> {
mappingllvm::yaml::MappingTraits200   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> {
mappingllvm::yaml::MappingTraits211   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> {
mappingllvm::yaml::MappingTraits224   static void mapping(IO &IO, RecordInfo &I) { RecordInfoMapping(IO, I); }
225 };
226 
227 template <> struct MappingTraits<BaseRecordInfo> {
mappingllvm::yaml::MappingTraits228   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> {
mappingllvm::yaml::MappingTraits240   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> {
mappingllvm::yaml::MappingTraits248   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> {
mappingllvm::yaml::MappingTraits257   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> {
mappingllvm::yaml::MappingTraits265   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> {
mappingllvm::yaml::MappingTraits280   static void mapping(IO &IO, TemplateParamInfo &I) {
281     IO.mapOptional("Contents", I.Contents);
282   }
283 };
284 
285 template <> struct MappingTraits<TemplateSpecializationInfo> {
mappingllvm::yaml::MappingTraits286   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> {
mappingllvm::yaml::MappingTraits293   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> {
mappingllvm::yaml::MappingTraits301   static void mapping(IO &IO, CommentInfo &I) { CommentInfoMapping(IO, I); }
302 };
303 
304 template <> struct MappingTraits<std::unique_ptr<CommentInfo>> {
mappingllvm::yaml::MappingTraits305   static void mapping(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
generateDocs(StringRef RootDir,llvm::StringMap<std::unique_ptr<doc::Info>> Infos,const ClangDocContext & CDCtx)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 
generateDocForInfo(Info * I,llvm::raw_ostream & OS,const ClangDocContext & CDCtx)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