430 lines · cpp
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-exception6//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 17using namespace clang::doc;18 19// These define YAML traits for decoding the listed values within a vector.20LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo)21LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo)22LLVM_YAML_IS_SEQUENCE_VECTOR(Reference)23LLVM_YAML_IS_SEQUENCE_VECTOR(Location)24LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)25LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)26LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)27LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo)28LLVM_YAML_IS_SEQUENCE_VECTOR(TemplateParamInfo)29LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo)30LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)31LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)32LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)33 34namespace llvm {35namespace yaml {36 37// Enumerations to YAML output.38 39template <> 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 48template <> struct ScalarEnumerationTraits<clang::TagTypeKind> {49 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 58template <> 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 68template <> struct ScalarEnumerationTraits<clang::doc::CommentKind> {69 static void enumeration(IO &IO, clang::doc::CommentKind &Value) {70 IO.enumCase(Value, "FullComment", clang::doc::CommentKind::CK_FullComment);71 IO.enumCase(Value, "ParagraphComment",72 clang::doc::CommentKind::CK_ParagraphComment);73 IO.enumCase(Value, "TextComment", clang::doc::CommentKind::CK_TextComment);74 IO.enumCase(Value, "InlineCommandComment",75 clang::doc::CommentKind::CK_InlineCommandComment);76 IO.enumCase(Value, "HTMLStartTagComment",77 clang::doc::CommentKind::CK_HTMLStartTagComment);78 IO.enumCase(Value, "HTMLEndTagComment",79 clang::doc::CommentKind::CK_HTMLEndTagComment);80 IO.enumCase(Value, "BlockCommandComment",81 clang::doc::CommentKind::CK_BlockCommandComment);82 IO.enumCase(Value, "ParamCommandComment",83 clang::doc::CommentKind::CK_ParamCommandComment);84 IO.enumCase(Value, "TParamCommandComment",85 clang::doc::CommentKind::CK_TParamCommandComment);86 IO.enumCase(Value, "VerbatimBlockComment",87 clang::doc::CommentKind::CK_VerbatimBlockComment);88 IO.enumCase(Value, "VerbatimBlockLineComment",89 clang::doc::CommentKind::CK_VerbatimBlockLineComment);90 IO.enumCase(Value, "VerbatimLineComment",91 clang::doc::CommentKind::CK_VerbatimLineComment);92 IO.enumCase(Value, "Unknown", clang::doc::CommentKind::CK_Unknown);93 }94};95 96// Scalars to YAML output.97template <unsigned U> struct ScalarTraits<SmallString<U>> {98 99 static void output(const SmallString<U> &S, void *, llvm::raw_ostream &OS) {100 for (const auto &C : S)101 OS << C;102 }103 104 static StringRef input(StringRef Scalar, void *, SmallString<U> &Value) {105 Value.assign(Scalar.begin(), Scalar.end());106 return StringRef();107 }108 109 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }110};111 112template <> struct ScalarTraits<std::array<unsigned char, 20>> {113 114 static void output(const std::array<unsigned char, 20> &S, void *,115 llvm::raw_ostream &OS) {116 OS << toHex(toStringRef(S));117 }118 119 static StringRef input(StringRef Scalar, void *,120 std::array<unsigned char, 20> &Value) {121 if (Scalar.size() != 40)122 return "Error: Incorrect scalar size for USR.";123 Value = stringToSymbol(Scalar);124 return StringRef();125 }126 127 static SymbolID stringToSymbol(llvm::StringRef Value) {128 SymbolID USR;129 std::string HexString = fromHex(Value);130 std::copy(HexString.begin(), HexString.end(), USR.begin());131 return SymbolID(USR);132 }133 134 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }135};136 137// Helper functions to map infos to YAML.138 139static void typeInfoMapping(IO &IO, TypeInfo &I) {140 IO.mapOptional("Type", I.Type, Reference());141}142 143static void fieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) {144 typeInfoMapping(IO, I);145 IO.mapOptional("Name", I.Name, SmallString<16>());146 IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>());147}148 149static void infoMapping(IO &IO, Info &I) {150 IO.mapRequired("USR", I.USR);151 IO.mapOptional("Name", I.Name, SmallString<16>());152 IO.mapOptional("Path", I.Path, SmallString<128>());153 IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector<Reference, 4>());154 IO.mapOptional("Description", I.Description);155}156 157static void symbolInfoMapping(IO &IO, SymbolInfo &I) {158 infoMapping(IO, I);159 IO.mapOptional("DefLocation", I.DefLoc, std::optional<Location>());160 IO.mapOptional("Location", I.Loc, llvm::SmallVector<Location, 2>());161}162 163static void recordInfoMapping(IO &IO, RecordInfo &I) {164 symbolInfoMapping(IO, I);165 IO.mapOptional("TagType", I.TagType);166 IO.mapOptional("IsTypeDef", I.IsTypeDef, false);167 IO.mapOptional("Members", I.Members);168 IO.mapOptional("Bases", I.Bases);169 IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>());170 IO.mapOptional("VirtualParents", I.VirtualParents,171 llvm::SmallVector<Reference, 4>());172 IO.mapOptional("ChildRecords", I.Children.Records, std::vector<Reference>());173 IO.mapOptional("ChildFunctions", I.Children.Functions);174 IO.mapOptional("ChildEnums", I.Children.Enums);175 IO.mapOptional("ChildTypedefs", I.Children.Typedefs);176 IO.mapOptional("Template", I.Template);177}178 179static void commentInfoMapping(IO &IO, CommentInfo &I) {180 IO.mapOptional("Kind", I.Kind, CommentKind::CK_Unknown);181 IO.mapOptional("Text", I.Text, SmallString<64>());182 IO.mapOptional("Name", I.Name, SmallString<16>());183 IO.mapOptional("Direction", I.Direction, SmallString<8>());184 IO.mapOptional("ParamName", I.ParamName, SmallString<16>());185 IO.mapOptional("CloseName", I.CloseName, SmallString<16>());186 IO.mapOptional("SelfClosing", I.SelfClosing, false);187 IO.mapOptional("Explicit", I.Explicit, false);188 IO.mapOptional("Args", I.Args, llvm::SmallVector<SmallString<16>, 4>());189 IO.mapOptional("AttrKeys", I.AttrKeys,190 llvm::SmallVector<SmallString<16>, 4>());191 IO.mapOptional("AttrValues", I.AttrValues,192 llvm::SmallVector<SmallString<16>, 4>());193 IO.mapOptional("Children", I.Children);194}195 196// Template specialization to YAML traits for Infos.197 198template <> struct MappingTraits<Location> {199 static void mapping(IO &IO, Location &Loc) {200 IO.mapOptional("LineNumber", Loc.StartLineNumber, 0);201 IO.mapOptional("Filename", Loc.Filename, SmallString<32>());202 }203};204 205template <> struct MappingTraits<Reference> {206 static void mapping(IO &IO, Reference &Ref) {207 IO.mapOptional("Type", Ref.RefType, InfoType::IT_default);208 IO.mapOptional("Name", Ref.Name, SmallString<16>());209 IO.mapOptional("QualName", Ref.QualName, SmallString<16>());210 IO.mapOptional("USR", Ref.USR, SymbolID());211 IO.mapOptional("Path", Ref.Path, SmallString<128>());212 }213};214 215template <> struct MappingTraits<TypeInfo> {216 static void mapping(IO &IO, TypeInfo &I) { typeInfoMapping(IO, I); }217};218 219template <> struct MappingTraits<FieldTypeInfo> {220 static void mapping(IO &IO, FieldTypeInfo &I) {221 typeInfoMapping(IO, I);222 IO.mapOptional("Name", I.Name, SmallString<16>());223 IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>());224 }225};226 227template <> struct MappingTraits<MemberTypeInfo> {228 static void mapping(IO &IO, MemberTypeInfo &I) {229 fieldTypeInfoMapping(IO, I);230 // clang::AccessSpecifier::AS_none is used as the default here because it's231 // the AS that shouldn't be part of the output. Even though AS_public is the232 // default in the struct, it should be displayed in the YAML output.233 IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);234 IO.mapOptional("Description", I.Description);235 }236};237 238template <> struct MappingTraits<NamespaceInfo> {239 static void mapping(IO &IO, NamespaceInfo &I) {240 infoMapping(IO, I);241 IO.mapOptional("ChildNamespaces", I.Children.Namespaces,242 std::vector<Reference>());243 IO.mapOptional("ChildRecords", I.Children.Records,244 std::vector<Reference>());245 IO.mapOptional("ChildFunctions", I.Children.Functions);246 IO.mapOptional("ChildEnums", I.Children.Enums);247 IO.mapOptional("ChildTypedefs", I.Children.Typedefs);248 }249};250 251template <> struct MappingTraits<RecordInfo> {252 static void mapping(IO &IO, RecordInfo &I) { recordInfoMapping(IO, I); }253};254 255template <> struct MappingTraits<BaseRecordInfo> {256 static void mapping(IO &IO, BaseRecordInfo &I) {257 recordInfoMapping(IO, I);258 IO.mapOptional("IsVirtual", I.IsVirtual, false);259 // clang::AccessSpecifier::AS_none is used as the default here because it's260 // the AS that shouldn't be part of the output. Even though AS_public is the261 // default in the struct, it should be displayed in the YAML output.262 IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);263 IO.mapOptional("IsParent", I.IsParent, false);264 }265};266 267template <> struct MappingTraits<EnumValueInfo> {268 static void mapping(IO &IO, EnumValueInfo &I) {269 IO.mapOptional("Name", I.Name);270 IO.mapOptional("Value", I.Value);271 IO.mapOptional("Expr", I.ValueExpr, SmallString<16>());272 }273};274 275template <> struct MappingTraits<EnumInfo> {276 static void mapping(IO &IO, EnumInfo &I) {277 symbolInfoMapping(IO, I);278 IO.mapOptional("Scoped", I.Scoped, false);279 IO.mapOptional("BaseType", I.BaseType);280 IO.mapOptional("Members", I.Members);281 }282};283 284template <> struct MappingTraits<TypedefInfo> {285 static void mapping(IO &IO, TypedefInfo &I) {286 symbolInfoMapping(IO, I);287 IO.mapOptional("Underlying", I.Underlying.Type);288 IO.mapOptional("IsUsing", I.IsUsing, false);289 }290};291 292template <> struct MappingTraits<FunctionInfo> {293 static void mapping(IO &IO, FunctionInfo &I) {294 symbolInfoMapping(IO, I);295 IO.mapOptional("IsMethod", I.IsMethod, false);296 IO.mapOptional("Parent", I.Parent, Reference());297 IO.mapOptional("Params", I.Params);298 IO.mapOptional("ReturnType", I.ReturnType);299 // clang::AccessSpecifier::AS_none is used as the default here because it's300 // the AS that shouldn't be part of the output. Even though AS_public is the301 // default in the struct, it should be displayed in the YAML output.302 IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);303 IO.mapOptional("Template", I.Template);304 }305};306 307template <> struct MappingTraits<TemplateParamInfo> {308 static void mapping(IO &IO, TemplateParamInfo &I) {309 IO.mapOptional("Contents", I.Contents);310 }311};312 313template <> struct MappingTraits<TemplateSpecializationInfo> {314 static void mapping(IO &IO, TemplateSpecializationInfo &I) {315 IO.mapOptional("SpecializationOf", I.SpecializationOf);316 IO.mapOptional("Params", I.Params);317 }318};319 320template <> struct MappingTraits<TemplateInfo> {321 static void mapping(IO &IO, TemplateInfo &I) {322 IO.mapOptional("Params", I.Params);323 IO.mapOptional("Specialization", I.Specialization,324 std::optional<TemplateSpecializationInfo>());325 }326};327 328template <> struct MappingTraits<CommentInfo> {329 static void mapping(IO &IO, CommentInfo &I) { commentInfoMapping(IO, I); }330};331 332template <> struct MappingTraits<std::unique_ptr<CommentInfo>> {333 static void mapping(IO &IO, std::unique_ptr<CommentInfo> &I) {334 if (I)335 commentInfoMapping(IO, *I);336 }337};338 339} // end namespace yaml340} // end namespace llvm341 342namespace clang {343namespace doc {344 345/// Generator for YAML documentation.346class YAMLGenerator : public Generator {347public:348 static const char *Format;349 350 llvm::Error generateDocumentation(351 StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,352 const ClangDocContext &CDCtx, std::string DirName) override;353 llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,354 const ClangDocContext &CDCtx) override;355};356 357const char *YAMLGenerator::Format = "yaml";358 359llvm::Error YAMLGenerator::generateDocumentation(360 StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,361 const ClangDocContext &CDCtx, std::string DirName) {362 for (const auto &Group : Infos) {363 doc::Info *Info = Group.getValue().get();364 365 // Output file names according to the USR except the global namesapce.366 // Anonymous namespaces are taken care of in serialization, so here we can367 // safely assume an unnamed namespace is the global one.368 llvm::SmallString<128> Path;369 llvm::sys::path::native(RootDir, Path);370 if (Info->IT == InfoType::IT_namespace && Info->Name.empty()) {371 llvm::sys::path::append(Path, "index.yaml");372 } else {373 llvm::sys::path::append(Path, Group.getKey() + ".yaml");374 }375 376 std::error_code FileErr;377 llvm::raw_fd_ostream InfoOS(Path, FileErr, llvm::sys::fs::OF_Text);378 if (FileErr) {379 return llvm::createStringError(FileErr, "Error opening file '%s'",380 Path.c_str());381 }382 383 if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {384 return Err;385 }386 }387 388 return llvm::Error::success();389}390 391llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,392 const ClangDocContext &CDCtx) {393 llvm::yaml::Output InfoYAML(OS);394 switch (I->IT) {395 case InfoType::IT_namespace:396 InfoYAML << *static_cast<clang::doc::NamespaceInfo *>(I);397 break;398 case InfoType::IT_record:399 InfoYAML << *static_cast<clang::doc::RecordInfo *>(I);400 break;401 case InfoType::IT_enum:402 InfoYAML << *static_cast<clang::doc::EnumInfo *>(I);403 break;404 case InfoType::IT_function:405 InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);406 break;407 case InfoType::IT_typedef:408 InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I);409 break;410 case InfoType::IT_concept:411 case InfoType::IT_variable:412 case InfoType::IT_friend:413 break;414 case InfoType::IT_default:415 return llvm::createStringError(llvm::inconvertibleErrorCode(),416 "unexpected InfoType");417 }418 return llvm::Error::success();419}420 421static GeneratorRegistry::Add<YAMLGenerator> YAML(YAMLGenerator::Format,422 "Generator for YAML output.");423 424// This anchor is used to force the linker to link in the generated object file425// and thus register the generator.426volatile int YAMLGeneratorAnchorSource = 0;427 428} // namespace doc429} // namespace clang430