|  | //===-- YAMLGenerator.cpp - ClangDoc YAML -----------------------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // Implementation of the YAML generator, converting decl info into YAML output. | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Generators.h" | 
|  | #include "Representation.h" | 
|  | #include "llvm/Support/YAMLTraits.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include <optional> | 
|  |  | 
|  | using namespace clang::doc; | 
|  |  | 
|  | // These define YAML traits for decoding the listed values within a vector. | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(Location) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(TemplateParamInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>) | 
|  | LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) | 
|  |  | 
|  | namespace llvm { | 
|  | namespace yaml { | 
|  |  | 
|  | // Enumerations to YAML output. | 
|  |  | 
|  | template <> struct ScalarEnumerationTraits<clang::AccessSpecifier> { | 
|  | static void enumeration(IO &IO, clang::AccessSpecifier &Value) { | 
|  | IO.enumCase(Value, "Public", clang::AccessSpecifier::AS_public); | 
|  | IO.enumCase(Value, "Protected", clang::AccessSpecifier::AS_protected); | 
|  | IO.enumCase(Value, "Private", clang::AccessSpecifier::AS_private); | 
|  | IO.enumCase(Value, "None", clang::AccessSpecifier::AS_none); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct ScalarEnumerationTraits<clang::TagTypeKind> { | 
|  | static void enumeration(IO &IO, clang::TagTypeKind &Value) { | 
|  | IO.enumCase(Value, "Struct", clang::TagTypeKind::Struct); | 
|  | IO.enumCase(Value, "Interface", clang::TagTypeKind::Interface); | 
|  | IO.enumCase(Value, "Union", clang::TagTypeKind::Union); | 
|  | IO.enumCase(Value, "Class", clang::TagTypeKind::Class); | 
|  | IO.enumCase(Value, "Enum", clang::TagTypeKind::Enum); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct ScalarEnumerationTraits<InfoType> { | 
|  | static void enumeration(IO &IO, InfoType &Value) { | 
|  | IO.enumCase(Value, "Namespace", InfoType::IT_namespace); | 
|  | IO.enumCase(Value, "Record", InfoType::IT_record); | 
|  | IO.enumCase(Value, "Function", InfoType::IT_function); | 
|  | IO.enumCase(Value, "Enum", InfoType::IT_enum); | 
|  | IO.enumCase(Value, "Default", InfoType::IT_default); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct ScalarEnumerationTraits<clang::doc::CommentKind> { | 
|  | static void enumeration(IO &IO, clang::doc::CommentKind &Value) { | 
|  | IO.enumCase(Value, "FullComment", clang::doc::CommentKind::CK_FullComment); | 
|  | IO.enumCase(Value, "ParagraphComment", | 
|  | clang::doc::CommentKind::CK_ParagraphComment); | 
|  | IO.enumCase(Value, "TextComment", clang::doc::CommentKind::CK_TextComment); | 
|  | IO.enumCase(Value, "InlineCommandComment", | 
|  | clang::doc::CommentKind::CK_InlineCommandComment); | 
|  | IO.enumCase(Value, "HTMLStartTagComment", | 
|  | clang::doc::CommentKind::CK_HTMLStartTagComment); | 
|  | IO.enumCase(Value, "HTMLEndTagComment", | 
|  | clang::doc::CommentKind::CK_HTMLEndTagComment); | 
|  | IO.enumCase(Value, "BlockCommandComment", | 
|  | clang::doc::CommentKind::CK_BlockCommandComment); | 
|  | IO.enumCase(Value, "ParamCommandComment", | 
|  | clang::doc::CommentKind::CK_ParamCommandComment); | 
|  | IO.enumCase(Value, "TParamCommandComment", | 
|  | clang::doc::CommentKind::CK_TParamCommandComment); | 
|  | IO.enumCase(Value, "VerbatimBlockComment", | 
|  | clang::doc::CommentKind::CK_VerbatimBlockComment); | 
|  | IO.enumCase(Value, "VerbatimBlockLineComment", | 
|  | clang::doc::CommentKind::CK_VerbatimBlockLineComment); | 
|  | IO.enumCase(Value, "VerbatimLineComment", | 
|  | clang::doc::CommentKind::CK_VerbatimLineComment); | 
|  | IO.enumCase(Value, "Unknown", clang::doc::CommentKind::CK_Unknown); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Scalars to YAML output. | 
|  | template <unsigned U> struct ScalarTraits<SmallString<U>> { | 
|  |  | 
|  | static void output(const SmallString<U> &S, void *, llvm::raw_ostream &OS) { | 
|  | for (const auto &C : S) | 
|  | OS << C; | 
|  | } | 
|  |  | 
|  | static StringRef input(StringRef Scalar, void *, SmallString<U> &Value) { | 
|  | Value.assign(Scalar.begin(), Scalar.end()); | 
|  | return StringRef(); | 
|  | } | 
|  |  | 
|  | static QuotingType mustQuote(StringRef) { return QuotingType::Single; } | 
|  | }; | 
|  |  | 
|  | template <> struct ScalarTraits<std::array<unsigned char, 20>> { | 
|  |  | 
|  | static void output(const std::array<unsigned char, 20> &S, void *, | 
|  | llvm::raw_ostream &OS) { | 
|  | OS << toHex(toStringRef(S)); | 
|  | } | 
|  |  | 
|  | static StringRef input(StringRef Scalar, void *, | 
|  | std::array<unsigned char, 20> &Value) { | 
|  | if (Scalar.size() != 40) | 
|  | return "Error: Incorrect scalar size for USR."; | 
|  | Value = stringToSymbol(Scalar); | 
|  | return StringRef(); | 
|  | } | 
|  |  | 
|  | static SymbolID stringToSymbol(llvm::StringRef Value) { | 
|  | SymbolID USR; | 
|  | std::string HexString = fromHex(Value); | 
|  | std::copy(HexString.begin(), HexString.end(), USR.begin()); | 
|  | return SymbolID(USR); | 
|  | } | 
|  |  | 
|  | static QuotingType mustQuote(StringRef) { return QuotingType::Single; } | 
|  | }; | 
|  |  | 
|  | // Helper functions to map infos to YAML. | 
|  |  | 
|  | static void typeInfoMapping(IO &IO, TypeInfo &I) { | 
|  | IO.mapOptional("Type", I.Type, Reference()); | 
|  | } | 
|  |  | 
|  | static void fieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) { | 
|  | typeInfoMapping(IO, I); | 
|  | IO.mapOptional("Name", I.Name, SmallString<16>()); | 
|  | IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>()); | 
|  | } | 
|  |  | 
|  | static void infoMapping(IO &IO, Info &I) { | 
|  | IO.mapRequired("USR", I.USR); | 
|  | IO.mapOptional("Name", I.Name, SmallString<16>()); | 
|  | IO.mapOptional("Path", I.Path, SmallString<128>()); | 
|  | IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector<Reference, 4>()); | 
|  | IO.mapOptional("Description", I.Description); | 
|  | } | 
|  |  | 
|  | static void symbolInfoMapping(IO &IO, SymbolInfo &I) { | 
|  | infoMapping(IO, I); | 
|  | IO.mapOptional("DefLocation", I.DefLoc, std::optional<Location>()); | 
|  | IO.mapOptional("Location", I.Loc, llvm::SmallVector<Location, 2>()); | 
|  | } | 
|  |  | 
|  | static void recordInfoMapping(IO &IO, RecordInfo &I) { | 
|  | symbolInfoMapping(IO, I); | 
|  | IO.mapOptional("TagType", I.TagType); | 
|  | IO.mapOptional("IsTypeDef", I.IsTypeDef, false); | 
|  | IO.mapOptional("Members", I.Members); | 
|  | IO.mapOptional("Bases", I.Bases); | 
|  | IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>()); | 
|  | IO.mapOptional("VirtualParents", I.VirtualParents, | 
|  | llvm::SmallVector<Reference, 4>()); | 
|  | IO.mapOptional("ChildRecords", I.Children.Records, std::vector<Reference>()); | 
|  | IO.mapOptional("ChildFunctions", I.Children.Functions); | 
|  | IO.mapOptional("ChildEnums", I.Children.Enums); | 
|  | IO.mapOptional("ChildTypedefs", I.Children.Typedefs); | 
|  | IO.mapOptional("Template", I.Template); | 
|  | } | 
|  |  | 
|  | static void commentInfoMapping(IO &IO, CommentInfo &I) { | 
|  | IO.mapOptional("Kind", I.Kind, CommentKind::CK_Unknown); | 
|  | IO.mapOptional("Text", I.Text, SmallString<64>()); | 
|  | IO.mapOptional("Name", I.Name, SmallString<16>()); | 
|  | IO.mapOptional("Direction", I.Direction, SmallString<8>()); | 
|  | IO.mapOptional("ParamName", I.ParamName, SmallString<16>()); | 
|  | IO.mapOptional("CloseName", I.CloseName, SmallString<16>()); | 
|  | IO.mapOptional("SelfClosing", I.SelfClosing, false); | 
|  | IO.mapOptional("Explicit", I.Explicit, false); | 
|  | IO.mapOptional("Args", I.Args, llvm::SmallVector<SmallString<16>, 4>()); | 
|  | IO.mapOptional("AttrKeys", I.AttrKeys, | 
|  | llvm::SmallVector<SmallString<16>, 4>()); | 
|  | IO.mapOptional("AttrValues", I.AttrValues, | 
|  | llvm::SmallVector<SmallString<16>, 4>()); | 
|  | IO.mapOptional("Children", I.Children); | 
|  | } | 
|  |  | 
|  | // Template specialization to YAML traits for Infos. | 
|  |  | 
|  | template <> struct MappingTraits<Location> { | 
|  | static void mapping(IO &IO, Location &Loc) { | 
|  | IO.mapOptional("LineNumber", Loc.StartLineNumber, 0); | 
|  | IO.mapOptional("Filename", Loc.Filename, SmallString<32>()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<Reference> { | 
|  | static void mapping(IO &IO, Reference &Ref) { | 
|  | IO.mapOptional("Type", Ref.RefType, InfoType::IT_default); | 
|  | IO.mapOptional("Name", Ref.Name, SmallString<16>()); | 
|  | IO.mapOptional("QualName", Ref.QualName, SmallString<16>()); | 
|  | IO.mapOptional("USR", Ref.USR, SymbolID()); | 
|  | IO.mapOptional("Path", Ref.Path, SmallString<128>()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<TypeInfo> { | 
|  | static void mapping(IO &IO, TypeInfo &I) { typeInfoMapping(IO, I); } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<FieldTypeInfo> { | 
|  | static void mapping(IO &IO, FieldTypeInfo &I) { | 
|  | typeInfoMapping(IO, I); | 
|  | IO.mapOptional("Name", I.Name, SmallString<16>()); | 
|  | IO.mapOptional("DefaultValue", I.DefaultValue, SmallString<16>()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<MemberTypeInfo> { | 
|  | static void mapping(IO &IO, MemberTypeInfo &I) { | 
|  | fieldTypeInfoMapping(IO, I); | 
|  | // clang::AccessSpecifier::AS_none is used as the default here because it's | 
|  | // the AS that shouldn't be part of the output. Even though AS_public is the | 
|  | // default in the struct, it should be displayed in the YAML output. | 
|  | IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); | 
|  | IO.mapOptional("Description", I.Description); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<NamespaceInfo> { | 
|  | static void mapping(IO &IO, NamespaceInfo &I) { | 
|  | infoMapping(IO, I); | 
|  | IO.mapOptional("ChildNamespaces", I.Children.Namespaces, | 
|  | std::vector<Reference>()); | 
|  | IO.mapOptional("ChildRecords", I.Children.Records, | 
|  | std::vector<Reference>()); | 
|  | IO.mapOptional("ChildFunctions", I.Children.Functions); | 
|  | IO.mapOptional("ChildEnums", I.Children.Enums); | 
|  | IO.mapOptional("ChildTypedefs", I.Children.Typedefs); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<RecordInfo> { | 
|  | static void mapping(IO &IO, RecordInfo &I) { recordInfoMapping(IO, I); } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<BaseRecordInfo> { | 
|  | static void mapping(IO &IO, BaseRecordInfo &I) { | 
|  | recordInfoMapping(IO, I); | 
|  | IO.mapOptional("IsVirtual", I.IsVirtual, false); | 
|  | // clang::AccessSpecifier::AS_none is used as the default here because it's | 
|  | // the AS that shouldn't be part of the output. Even though AS_public is the | 
|  | // default in the struct, it should be displayed in the YAML output. | 
|  | IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); | 
|  | IO.mapOptional("IsParent", I.IsParent, false); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<EnumValueInfo> { | 
|  | static void mapping(IO &IO, EnumValueInfo &I) { | 
|  | IO.mapOptional("Name", I.Name); | 
|  | IO.mapOptional("Value", I.Value); | 
|  | IO.mapOptional("Expr", I.ValueExpr, SmallString<16>()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<EnumInfo> { | 
|  | static void mapping(IO &IO, EnumInfo &I) { | 
|  | symbolInfoMapping(IO, I); | 
|  | IO.mapOptional("Scoped", I.Scoped, false); | 
|  | IO.mapOptional("BaseType", I.BaseType); | 
|  | IO.mapOptional("Members", I.Members); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<TypedefInfo> { | 
|  | static void mapping(IO &IO, TypedefInfo &I) { | 
|  | symbolInfoMapping(IO, I); | 
|  | IO.mapOptional("Underlying", I.Underlying.Type); | 
|  | IO.mapOptional("IsUsing", I.IsUsing, false); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<FunctionInfo> { | 
|  | static void mapping(IO &IO, FunctionInfo &I) { | 
|  | symbolInfoMapping(IO, I); | 
|  | IO.mapOptional("IsMethod", I.IsMethod, false); | 
|  | IO.mapOptional("Parent", I.Parent, Reference()); | 
|  | IO.mapOptional("Params", I.Params); | 
|  | IO.mapOptional("ReturnType", I.ReturnType); | 
|  | // clang::AccessSpecifier::AS_none is used as the default here because it's | 
|  | // the AS that shouldn't be part of the output. Even though AS_public is the | 
|  | // default in the struct, it should be displayed in the YAML output. | 
|  | IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); | 
|  | IO.mapOptional("Template", I.Template); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<TemplateParamInfo> { | 
|  | static void mapping(IO &IO, TemplateParamInfo &I) { | 
|  | IO.mapOptional("Contents", I.Contents); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<TemplateSpecializationInfo> { | 
|  | static void mapping(IO &IO, TemplateSpecializationInfo &I) { | 
|  | IO.mapOptional("SpecializationOf", I.SpecializationOf); | 
|  | IO.mapOptional("Params", I.Params); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<TemplateInfo> { | 
|  | static void mapping(IO &IO, TemplateInfo &I) { | 
|  | IO.mapOptional("Params", I.Params); | 
|  | IO.mapOptional("Specialization", I.Specialization, | 
|  | std::optional<TemplateSpecializationInfo>()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<CommentInfo> { | 
|  | static void mapping(IO &IO, CommentInfo &I) { commentInfoMapping(IO, I); } | 
|  | }; | 
|  |  | 
|  | template <> struct MappingTraits<std::unique_ptr<CommentInfo>> { | 
|  | static void mapping(IO &IO, std::unique_ptr<CommentInfo> &I) { | 
|  | if (I) | 
|  | commentInfoMapping(IO, *I); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // end namespace yaml | 
|  | } // end namespace llvm | 
|  |  | 
|  | namespace clang { | 
|  | namespace doc { | 
|  |  | 
|  | /// Generator for YAML documentation. | 
|  | class YAMLGenerator : public Generator { | 
|  | public: | 
|  | static const char *Format; | 
|  |  | 
|  | llvm::Error generateDocs(StringRef RootDir, | 
|  | llvm::StringMap<std::unique_ptr<doc::Info>> Infos, | 
|  | const ClangDocContext &CDCtx) override; | 
|  | llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS, | 
|  | const ClangDocContext &CDCtx) override; | 
|  | }; | 
|  |  | 
|  | const char *YAMLGenerator::Format = "yaml"; | 
|  |  | 
|  | llvm::Error | 
|  | YAMLGenerator::generateDocs(StringRef RootDir, | 
|  | llvm::StringMap<std::unique_ptr<doc::Info>> Infos, | 
|  | const ClangDocContext &CDCtx) { | 
|  | for (const auto &Group : Infos) { | 
|  | doc::Info *Info = Group.getValue().get(); | 
|  |  | 
|  | // Output file names according to the USR except the global namesapce. | 
|  | // Anonymous namespaces are taken care of in serialization, so here we can | 
|  | // safely assume an unnamed namespace is the global one. | 
|  | llvm::SmallString<128> Path; | 
|  | llvm::sys::path::native(RootDir, Path); | 
|  | if (Info->IT == InfoType::IT_namespace && Info->Name.empty()) { | 
|  | llvm::sys::path::append(Path, "index.yaml"); | 
|  | } else { | 
|  | llvm::sys::path::append(Path, Group.getKey() + ".yaml"); | 
|  | } | 
|  |  | 
|  | std::error_code FileErr; | 
|  | llvm::raw_fd_ostream InfoOS(Path, FileErr, llvm::sys::fs::OF_Text); | 
|  | if (FileErr) { | 
|  | return llvm::createStringError(FileErr, "Error opening file '%s'", | 
|  | Path.c_str()); | 
|  | } | 
|  |  | 
|  | if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) { | 
|  | return Err; | 
|  | } | 
|  | } | 
|  |  | 
|  | return llvm::Error::success(); | 
|  | } | 
|  |  | 
|  | llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS, | 
|  | const ClangDocContext &CDCtx) { | 
|  | llvm::yaml::Output InfoYAML(OS); | 
|  | switch (I->IT) { | 
|  | case InfoType::IT_namespace: | 
|  | InfoYAML << *static_cast<clang::doc::NamespaceInfo *>(I); | 
|  | break; | 
|  | case InfoType::IT_record: | 
|  | InfoYAML << *static_cast<clang::doc::RecordInfo *>(I); | 
|  | break; | 
|  | case InfoType::IT_enum: | 
|  | InfoYAML << *static_cast<clang::doc::EnumInfo *>(I); | 
|  | break; | 
|  | case InfoType::IT_function: | 
|  | InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I); | 
|  | break; | 
|  | case InfoType::IT_typedef: | 
|  | InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I); | 
|  | break; | 
|  | case InfoType::IT_default: | 
|  | return llvm::createStringError(llvm::inconvertibleErrorCode(), | 
|  | "unexpected InfoType"); | 
|  | } | 
|  | return llvm::Error::success(); | 
|  | } | 
|  |  | 
|  | static GeneratorRegistry::Add<YAMLGenerator> YAML(YAMLGenerator::Format, | 
|  | "Generator for YAML output."); | 
|  |  | 
|  | // This anchor is used to force the linker to link in the generated object file | 
|  | // and thus register the generator. | 
|  | volatile int YAMLGeneratorAnchorSource = 0; | 
|  |  | 
|  | } // namespace doc | 
|  | } // namespace clang |