Files
llvm-project/clang-tools-extra/clang-doc/YAMLGenerator.cpp
Paul Kirth 22e6a261fa Revert clang-doc arena merging patches (#191668)
This is a set of squashed reverts of recent clang doc patches, since its
breaking something on Darwin builders:
https://lab.llvm.org/buildbot/#/builders/23/builds/19172

Revert "[clang-doc][nfc] Default initialize all StringRef members
(#191641)"

This reverts commit 155b9b354c.

Revert "[clang-doc] Initialize StringRef members in Info types
(#191637)"

This reverts commit 489dab3827.

Revert "[clang-doc] Initialize member variable (#191570)"

This reverts commit 5d64a44a84.

Revert "[clang-doc] Merge data into persistent memory (#190056)"

This reverts commit 21e0034c69.

Revert "[clang-doc] Support deep copy between arenas for merging
(#190055)"

This reverts commit c70dae8b0c.
2026-04-13 15:40:40 +00:00

542 lines
18 KiB
C++

//===-- 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(OwnedPtr<CommentInfo>)
namespace llvm {
namespace yaml {
template <typename T> struct SequenceTraits<llvm::ArrayRef<T>> {
static size_t size(IO &io, llvm::ArrayRef<T> &seq) { return seq.size(); }
static T &element(IO &io, llvm::ArrayRef<T> &seq, size_t index) {
return const_cast<T &>(seq[index]);
}
};
// 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 <> struct ScalarTraits<SymbolID> {
static void output(const SymbolID &S, void *, llvm::raw_ostream &OS) {
OS << toHex(toStringRef(S));
}
static StringRef input(StringRef Scalar, void *, SymbolID &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; }
};
/// A wrapper for StringRef to force YAML traits to single-quote the string.
struct QuotedString {
StringRef Ref;
QuotedString() = default;
QuotedString(StringRef R) : Ref(R) {}
operator StringRef() const { return Ref; }
bool operator==(const QuotedString &Other) const { return Ref == Other.Ref; }
};
template <> struct ScalarTraits<QuotedString> {
static void output(const QuotedString &S, void *, llvm::raw_ostream &OS) {
OS << S.Ref;
}
static StringRef input(StringRef Scalar, void *, QuotedString &Value) {
Value.Ref = Scalar;
return StringRef();
}
static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
};
} // end namespace yaml
} // end namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::QuotedString)
namespace llvm {
namespace yaml {
// 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);
QuotedString QName(I.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
I.Name = QName.Ref;
QuotedString QDefault(I.DefaultValue);
IO.mapOptional("DefaultValue", QDefault, QuotedString(StringRef()));
if (!IO.outputting())
I.DefaultValue = QDefault.Ref;
}
static void infoMapping(IO &IO, Info &I) {
IO.mapRequired("USR", I.USR);
QuotedString QName(I.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
I.Name = QName.Ref;
QuotedString QPath(I.Path);
IO.mapOptional("Path", QPath, QuotedString(StringRef()));
if (!IO.outputting())
I.Path = QPath.Ref;
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, OwningVec<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);
QuotedString QText(I.Text);
IO.mapOptional("Text", QText, QuotedString(StringRef()));
if (!IO.outputting())
I.Text = QText.Ref;
QuotedString QName(I.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
I.Name = QName.Ref;
QuotedString QDirection(I.Direction);
IO.mapOptional("Direction", QDirection, QuotedString(StringRef()));
if (!IO.outputting())
I.Direction = QDirection.Ref;
QuotedString QParamName(I.ParamName);
IO.mapOptional("ParamName", QParamName, QuotedString(StringRef()));
if (!IO.outputting())
I.ParamName = QParamName.Ref;
QuotedString QCloseName(I.CloseName);
IO.mapOptional("CloseName", QCloseName, QuotedString(StringRef()));
if (!IO.outputting())
I.CloseName = QCloseName.Ref;
IO.mapOptional("SelfClosing", I.SelfClosing, false);
IO.mapOptional("Explicit", I.Explicit, false);
std::vector<QuotedString> QArgs;
if (IO.outputting()) {
for (auto &S : I.Args)
QArgs.push_back(QuotedString(S));
}
IO.mapOptional("Args", QArgs, std::vector<QuotedString>());
std::vector<QuotedString> QAttrKeys;
if (IO.outputting()) {
for (auto &S : I.AttrKeys)
QAttrKeys.push_back(QuotedString(S));
}
IO.mapOptional("AttrKeys", QAttrKeys, std::vector<QuotedString>());
std::vector<QuotedString> QAttrValues;
if (IO.outputting()) {
for (auto &S : I.AttrValues)
QAttrValues.push_back(QuotedString(S));
}
IO.mapOptional("AttrValues", QAttrValues, std::vector<QuotedString>());
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);
QuotedString QFilename(Loc.Filename);
IO.mapOptional("Filename", QFilename, QuotedString(StringRef()));
if (!IO.outputting())
Loc.Filename = QFilename.Ref;
}
};
template <> struct MappingTraits<Reference> {
static void mapping(IO &IO, Reference &Ref) {
IO.mapOptional("Type", Ref.RefType, InfoType::IT_default);
QuotedString QName(Ref.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
Ref.Name = QName.Ref;
QuotedString QQualName(Ref.QualName);
IO.mapOptional("QualName", QQualName, QuotedString(StringRef()));
if (!IO.outputting())
Ref.QualName = QQualName.Ref;
IO.mapOptional("USR", Ref.USR, SymbolID());
QuotedString QPath(Ref.Path);
IO.mapOptional("Path", QPath, QuotedString(StringRef()));
if (!IO.outputting())
Ref.Path = QPath.Ref;
}
};
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);
QuotedString QName(I.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
I.Name = QName.Ref;
QuotedString QDefault(I.DefaultValue);
IO.mapOptional("DefaultValue", QDefault, QuotedString(StringRef()));
if (!IO.outputting())
I.DefaultValue = QDefault.Ref;
}
};
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);
std::vector<Reference> TempNamespaces;
for (const auto &N : I.Children.Namespaces)
TempNamespaces.push_back(N);
IO.mapOptional("ChildNamespaces", TempNamespaces, std::vector<Reference>());
IO.mapOptional("ChildRecords", I.Children.Records, OwningVec<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) {
QuotedString QName(I.Name);
IO.mapOptional("Name", QName, QuotedString(StringRef()));
if (!IO.outputting())
I.Name = QName.Ref;
QuotedString QValue(I.Value);
IO.mapOptional("Value", QValue, QuotedString(StringRef()));
if (!IO.outputting())
I.Value = QValue.Ref;
QuotedString QExpr(I.ValueExpr);
IO.mapOptional("Expr", QExpr, QuotedString(StringRef()));
if (!IO.outputting())
I.ValueExpr = QExpr.Ref;
}
};
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) {
QuotedString QContents(I.Contents);
IO.mapOptional("Contents", QContents, QuotedString(StringRef()));
if (!IO.outputting())
I.Contents = QContents.Ref;
}
};
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<OwnedPtr<CommentInfo>> {
static void mapping(IO &IO, OwnedPtr<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 generateDocumentation(
StringRef RootDir, llvm::StringMap<doc::OwnedPtr<doc::Info>> Infos,
const ClangDocContext &CDCtx, std::string DirName) override;
llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
const ClangDocContext &CDCtx) override;
};
const char *YAMLGenerator::Format = "yaml";
llvm::Error YAMLGenerator::generateDocumentation(
StringRef RootDir, llvm::StringMap<doc::OwnedPtr<doc::Info>> Infos,
const ClangDocContext &CDCtx, std::string DirName) {
for (const auto &Group : Infos) {
doc::Info *Info = getPtr(Group.getValue());
// 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_concept:
case InfoType::IT_variable:
case InfoType::IT_friend:
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