Erick Velez 8050a6e073
[clang-doc] add support for concepts (#144430)
Add support for documenting concepts. This handles concepts and constraints on function and class templates.

Atomic constraints are not considered yet. We don't order constraints based on their conjunctive or disjunctive properties.
2025-06-20 17:39:31 -07:00

429 lines
16 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(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_concept:
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