Files
TheRock/compiler/amd-llvm/clang-tools-extra/clang-doc/Representation.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

760 lines
27 KiB
C++

///===-- Representation.cpp - ClangDoc Representation -----------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the merging of different types of infos. The data in the
// calling Info is preserved during a merge unless that field is empty or
// default. In that case, the data from the parameter Info is used to replace
// the empty or default data.
//
// For most fields, the first decl seen provides the data. Exceptions to this
// include the location and description fields, which are collections of data on
// all decls related to a given definition. All other fields are ignored in new
// decls unless the first seen decl didn't, for whatever reason, incorporate
// data on that field (e.g. a forward declared class wouldn't have information
// on members on the forward declaration, but would have the class name).
//
//===----------------------------------------------------------------------===//
#include "Representation.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
namespace clang {
namespace doc {
// Thread local arenas usable in each thread pool
thread_local llvm::BumpPtrAllocator TransientArena;
thread_local llvm::BumpPtrAllocator PersistentArena;
ConcurrentStringPool &getGlobalStringPool() {
static ConcurrentStringPool GlobalPool;
return GlobalPool;
}
CommentKind stringToCommentKind(llvm::StringRef KindStr) {
static const llvm::StringMap<CommentKind> KindMap = {
{"FullComment", CommentKind::CK_FullComment},
{"ParagraphComment", CommentKind::CK_ParagraphComment},
{"TextComment", CommentKind::CK_TextComment},
{"InlineCommandComment", CommentKind::CK_InlineCommandComment},
{"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},
{"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},
{"BlockCommandComment", CommentKind::CK_BlockCommandComment},
{"ParamCommandComment", CommentKind::CK_ParamCommandComment},
{"TParamCommandComment", CommentKind::CK_TParamCommandComment},
{"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},
{"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},
{"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},
};
auto It = KindMap.find(KindStr);
if (It != KindMap.end()) {
return It->second;
}
return CommentKind::CK_Unknown;
}
llvm::StringRef commentKindToString(CommentKind Kind) {
switch (Kind) {
case CommentKind::CK_FullComment:
return "FullComment";
case CommentKind::CK_ParagraphComment:
return "ParagraphComment";
case CommentKind::CK_TextComment:
return "TextComment";
case CommentKind::CK_InlineCommandComment:
return "InlineCommandComment";
case CommentKind::CK_HTMLStartTagComment:
return "HTMLStartTagComment";
case CommentKind::CK_HTMLEndTagComment:
return "HTMLEndTagComment";
case CommentKind::CK_BlockCommandComment:
return "BlockCommandComment";
case CommentKind::CK_ParamCommandComment:
return "ParamCommandComment";
case CommentKind::CK_TParamCommandComment:
return "TParamCommandComment";
case CommentKind::CK_VerbatimBlockComment:
return "VerbatimBlockComment";
case CommentKind::CK_VerbatimBlockLineComment:
return "VerbatimBlockLineComment";
case CommentKind::CK_VerbatimLineComment:
return "VerbatimLineComment";
case CommentKind::CK_Unknown:
return "Unknown";
}
llvm_unreachable("Unhandled CommentKind");
}
const SymbolID EmptySID = SymbolID();
template <typename T>
static llvm::Expected<Info *> reduce(SmallVectorImpl<Info *> &Values) {
if (Values.empty() || !Values[0])
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"no value to reduce");
T *Merged = allocateTransient<T>(Values[0]->USR);
for (auto &I : Values)
Merged->merge(std::move(*static_cast<T *>(I)));
return Merged;
}
template <typename T>
static void reduceChildren(DocList<T> &Children, DocList<T> &&ChildrenToMerge) {
while (!ChildrenToMerge.empty()) {
T *Ptr = ChildrenToMerge.front().Ptr;
ChildrenToMerge.pop_front();
auto It = llvm::find_if(
Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
if (It == Children.end()) {
InfoNode<T> *NewNode = allocateListNodePersistent<T>(Ptr->USR);
NewNode->Ptr->merge(std::move(*Ptr));
Children.push_back(*NewNode);
} else {
It->Ptr->merge(std::move(*Ptr));
}
}
}
template <>
void reduceChildren<Reference>(DocList<Reference> &Children,
DocList<Reference> &&ChildrenToMerge) {
while (!ChildrenToMerge.empty()) {
Reference *Ptr = ChildrenToMerge.front().Ptr;
ChildrenToMerge.pop_front();
auto It = llvm::find_if(
Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
if (It == Children.end()) {
InfoNode<Reference> *NewNode = allocateListNodePersistent<Reference>();
NewNode->Ptr->USR = Ptr->USR;
NewNode->Ptr->RefType = Ptr->RefType;
NewNode->Ptr->merge(std::move(*Ptr));
Children.push_back(*NewNode);
} else {
It->Ptr->merge(std::move(*Ptr));
}
}
}
template <typename T>
static void mergeUnkeyed(DocList<T> &Target, DocList<T> &&Source) {
while (!Source.empty()) {
T *Ptr = Source.front().Ptr;
Source.pop_front();
if (!llvm::any_of(Target,
[Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
Target.push_back(*allocateListNodePersistent<T>(*Ptr));
}
}
}
template <>
void mergeUnkeyed<CommentInfo>(DocList<CommentInfo> &Target,
DocList<CommentInfo> &&Source) {
while (!Source.empty()) {
CommentInfo *Ptr = Source.front().Ptr;
Source.pop_front();
if (llvm::none_of(Target,
[Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
Target.push_back(
*allocateListNodePersistent<CommentInfo>(*Ptr, PersistentArena));
}
}
}
llvm::Error mergeSingleInfo(doc::Info *&Reduced, doc::Info *NewInfo,
llvm::BumpPtrAllocator &Arena) {
if (!Reduced) {
switch (NewInfo->IT) {
case InfoType::IT_namespace:
Reduced = allocatePtr<NamespaceInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_record:
Reduced = allocatePtr<RecordInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_enum:
Reduced = allocatePtr<EnumInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_function:
Reduced = allocatePtr<FunctionInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_typedef:
Reduced = allocatePtr<TypedefInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_concept:
Reduced = allocatePtr<ConceptInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_variable:
Reduced = allocatePtr<VarInfo>(Arena, NewInfo->USR);
break;
case InfoType::IT_friend:
Reduced = allocatePtr<FriendInfo>(Arena, NewInfo->USR);
break;
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unknown info type");
}
}
if (Reduced->IT != NewInfo->IT)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"info types mismatch");
switch (Reduced->IT) {
case InfoType::IT_namespace:
static_cast<NamespaceInfo *>(Reduced)->merge(
std::move(*static_cast<NamespaceInfo *>(NewInfo)));
break;
case InfoType::IT_record:
static_cast<RecordInfo *>(Reduced)->merge(
std::move(*static_cast<RecordInfo *>(NewInfo)));
break;
case InfoType::IT_enum:
static_cast<EnumInfo *>(Reduced)->merge(
std::move(*static_cast<EnumInfo *>(NewInfo)));
break;
case InfoType::IT_function:
static_cast<FunctionInfo *>(Reduced)->merge(
std::move(*static_cast<FunctionInfo *>(NewInfo)));
break;
case InfoType::IT_typedef:
static_cast<TypedefInfo *>(Reduced)->merge(
std::move(*static_cast<TypedefInfo *>(NewInfo)));
break;
case InfoType::IT_concept:
static_cast<ConceptInfo *>(Reduced)->merge(
std::move(*static_cast<ConceptInfo *>(NewInfo)));
break;
case InfoType::IT_variable:
static_cast<VarInfo *>(Reduced)->merge(
std::move(*static_cast<VarInfo *>(NewInfo)));
break;
case InfoType::IT_friend:
static_cast<FriendInfo *>(Reduced)->merge(
std::move(*static_cast<FriendInfo *>(NewInfo)));
break;
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unknown info type");
}
return llvm::Error::success();
}
// Dispatch function.
llvm::Expected<Info *> mergeInfos(SmallVectorImpl<Info *> &Values) {
if (Values.empty() || !Values[0])
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"no info values to merge");
switch (Values[0]->IT) {
case InfoType::IT_namespace:
return reduce<NamespaceInfo>(Values);
case InfoType::IT_record:
return reduce<RecordInfo>(Values);
case InfoType::IT_enum:
return reduce<EnumInfo>(Values);
case InfoType::IT_function:
return reduce<FunctionInfo>(Values);
case InfoType::IT_typedef:
return reduce<TypedefInfo>(Values);
case InfoType::IT_concept:
return reduce<ConceptInfo>(Values);
case InfoType::IT_variable:
return reduce<VarInfo>(Values);
case InfoType::IT_friend:
return reduce<FriendInfo>(Values);
case InfoType::IT_default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected info type");
}
llvm_unreachable("unhandled enumerator");
}
TemplateSpecializationInfo::TemplateSpecializationInfo(
const TemplateSpecializationInfo &Other, llvm::BumpPtrAllocator &Arena)
: SpecializationOf(Other.SpecializationOf) {
Params = allocateArray(Other.Params, Arena);
}
TemplateInfo::TemplateInfo(const TemplateInfo &Other,
llvm::BumpPtrAllocator &Arena) {
Params = allocateArray(Other.Params, Arena);
if (Other.Specialization)
Specialization = TemplateSpecializationInfo(*Other.Specialization, Arena);
Constraints = allocateArray(Other.Constraints, Arena);
}
bool CommentInfo::operator==(const CommentInfo &Other) const {
auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
SelfClosing, Explicit, AttrKeys, AttrValues, Args);
auto SecondCI =
std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
Other.ParamName, Other.CloseName, Other.SelfClosing,
Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
if (FirstCI != SecondCI || Children.size() != Other.Children.size())
return false;
return llvm::equal(Children, Other.Children);
}
bool CommentInfo::operator<(const CommentInfo &Other) const {
auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
SelfClosing, Explicit, AttrKeys, AttrValues, Args);
auto SecondCI =
std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,
Other.ParamName, Other.CloseName, Other.SelfClosing,
Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);
if (FirstCI < SecondCI)
return true;
if (FirstCI == SecondCI) {
return std::lexicographical_compare(Children.begin(), Children.end(),
Other.Children.begin(),
Other.Children.end());
}
return false;
}
CommentInfo::CommentInfo(const CommentInfo &Other,
llvm::BumpPtrAllocator &Arena) {
Kind = Other.Kind;
Direction = Other.Direction;
Name = Other.Name;
ParamName = Other.ParamName;
CloseName = Other.CloseName;
SelfClosing = Other.SelfClosing;
Explicit = Other.Explicit;
Text = Other.Text;
AttrKeys = allocateArray(Other.AttrKeys, Arena);
AttrValues = allocateArray(Other.AttrValues, Arena);
Args = allocateArray(Other.Args, Arena);
if (!Other.Children.empty()) {
CommentInfo *NewArray = Arena.Allocate<CommentInfo>(Other.Children.size());
for (size_t Idx = 0; Idx < Other.Children.size(); ++Idx) {
new (NewArray + Idx) CommentInfo(Other.Children[Idx], Arena);
}
Children = llvm::ArrayRef<CommentInfo>(NewArray, Other.Children.size());
}
}
static llvm::SmallString<64>
calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
const StringRef &Name, const StringRef &CurrentPath) {
llvm::SmallString<64> FilePath;
if (CurrentPath != Path) {
// iterate back to the top
for (llvm::sys::path::const_iterator I =
llvm::sys::path::begin(CurrentPath);
I != llvm::sys::path::end(CurrentPath); ++I)
llvm::sys::path::append(FilePath, "..");
llvm::sys::path::append(FilePath, Path);
}
// Namespace references have a Path to the parent namespace, but
// the file is actually in the subdirectory for the namespace.
if (Type == doc::InfoType::IT_namespace)
llvm::sys::path::append(FilePath, Name);
return llvm::sys::path::relative_path(FilePath);
}
StringRef Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
return internString(
calculateRelativeFilePath(RefType, Path, Name, CurrentPath));
}
StringRef Reference::getFileBaseName() const {
if (RefType == InfoType::IT_namespace)
return "index";
return Name;
}
StringRef Info::getRelativeFilePath(const StringRef &CurrentPath) const {
return internString(
calculateRelativeFilePath(IT, Path, extractName(), CurrentPath));
}
StringRef Info::getFileBaseName() const {
if (IT == InfoType::IT_namespace)
return "index";
return extractName();
}
bool Reference::mergeable(const Reference &Other) {
return RefType == Other.RefType && USR == Other.USR;
}
void Reference::merge(Reference &&Other) {
assert(mergeable(Other));
assert(RefType != InfoType::IT_default &&
"Merging reference with default InfoType");
if (Name.empty())
Name = Other.Name;
if (Path.empty())
Path = Other.Path;
if (QualName.empty())
QualName = Other.QualName;
if (DocumentationFileName.empty())
DocumentationFileName = Other.DocumentationFileName;
}
bool FriendInfo::mergeable(const FriendInfo &Other) {
return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;
}
void FriendInfo::merge(FriendInfo &&Other) {
assert(mergeable(Other));
Ref.merge(std::move(Other.Ref));
SymbolInfo::merge(std::move(Other));
}
FriendInfo::FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena)
: SymbolInfo(Other, Arena) {
Ref = Other.Ref;
if (Other.Template)
Template.emplace(*Other.Template, Arena);
if (Other.ReturnType)
ReturnType = Other.ReturnType;
if (!Other.Params.empty())
Params = allocateArray(Other.Params, Arena);
IsClass = Other.IsClass;
}
Info::Info(const Info &Other, llvm::BumpPtrAllocator &Arena)
: Path(Other.Path), Name(Other.Name),
DocumentationFileName(Other.DocumentationFileName), USR(Other.USR),
ParentUSR(Other.ParentUSR), IT(Other.IT) {
Namespace = allocateArray(Other.Namespace, Arena);
if (!Other.Description.empty()) {
for (const auto &Desc : Other.Description) {
CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
}
}
}
void Info::mergeBase(Info &&Other) {
assert(mergeable(Other));
assert(IT != InfoType::IT_default && "Merging info with default InfoType");
if (USR == EmptySID)
USR = Other.USR;
if (Name == "")
Name = Other.Name;
if (Path == "")
Path = Other.Path;
if (Namespace.empty() && !Other.Namespace.empty())
Namespace = allocateArray(Other.Namespace, PersistentArena);
// Unconditionally extend the description, since each decl may have a comment.
mergeUnkeyed(Description, std::move(Other.Description));
if (ParentUSR == EmptySID)
ParentUSR = Other.ParentUSR;
if (DocumentationFileName.empty())
DocumentationFileName = Other.DocumentationFileName;
}
bool Info::mergeable(const Info &Other) {
return IT == Other.IT && USR == Other.USR;
}
SymbolInfo::SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena)
: Info(Other, Arena), DefLoc(Other.DefLoc), MangledName(Other.MangledName),
IsStatic(Other.IsStatic) {
if (!Other.Loc.empty()) {
for (const auto &L : Other.Loc) {
Location *NewL = allocatePtr<Location>(Arena, L);
Loc.push_back(*allocateListNode<Location>(Arena, NewL));
}
}
}
void SymbolInfo::merge(SymbolInfo &&Other) {
assert(mergeable(Other));
if (!DefLoc)
DefLoc = std::move(Other.DefLoc);
if (MangledName.empty())
MangledName = std::move(Other.MangledName);
// Unconditionally extend the list of locations, since we want all of them.
mergeUnkeyed(Loc, std::move(Other.Loc));
mergeBase(std::move(Other));
if (!IsStatic)
IsStatic = Other.IsStatic;
}
NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
: Info(InfoType::IT_namespace, USR, Name, Path) {}
void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
// Reduce children if necessary.
reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
reduceChildren(Children.Variables, std::move(Other.Children.Variables));
mergeBase(std::move(Other));
}
RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
: SymbolInfo(InfoType::IT_record, USR, Name, Path) {}
// FIXME: This constructor is currently unsafe for cross-arena copies of
// populated records. Because a default copy of ScopeChildren will shallow-copy
// the intrusive pointers, leading to a use-after-free when the TransientArena
// is reset. Subsequent patches will address this by deep-copying children
// individually via reduceChildren.
RecordInfo::RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena)
: SymbolInfo(Other, Arena), TagType(Other.TagType),
IsTypeDef(Other.IsTypeDef) {
Members = deepCopyArray(Other.Members, Arena);
Parents = allocateArray(Other.Parents, Arena);
VirtualParents = allocateArray(Other.VirtualParents, Arena);
Bases = deepCopyArray(Other.Bases, Arena);
Friends = deepCopyArray(Other.Friends, Arena);
}
MemberTypeInfo::MemberTypeInfo(const MemberTypeInfo &Other,
llvm::BumpPtrAllocator &Arena)
: FieldTypeInfo(Other), Access(Other.Access), IsStatic(Other.IsStatic) {
if (!Other.Description.empty()) {
for (const auto &Desc : Other.Description) {
CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
}
}
}
void RecordInfo::merge(RecordInfo &&Other) {
assert(mergeable(Other));
if (!llvm::to_underlying(TagType))
TagType = Other.TagType;
IsTypeDef = IsTypeDef || Other.IsTypeDef;
if (Members.empty() && !Other.Members.empty())
Members = deepCopyArray(Other.Members, PersistentArena);
if (Bases.empty() && !Other.Bases.empty())
Bases = deepCopyArray(Other.Bases, PersistentArena);
if (Parents.empty() && !Other.Parents.empty())
Parents = allocateArray(Other.Parents, PersistentArena);
if (VirtualParents.empty() && !Other.VirtualParents.empty())
VirtualParents = allocateArray(Other.VirtualParents, PersistentArena);
if (Friends.empty() && !Other.Friends.empty())
Friends = deepCopyArray(Other.Friends, PersistentArena);
// Reduce children if necessary.
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
if (!Template && Other.Template)
Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
EnumValueInfo::EnumValueInfo(const EnumValueInfo &Other,
llvm::BumpPtrAllocator &Arena)
: Name(Other.Name), Value(Other.Value), ValueExpr(Other.ValueExpr) {
if (!Other.Description.empty()) {
for (const auto &Desc : Other.Description) {
CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
}
}
}
void EnumInfo::merge(EnumInfo &&Other) {
assert(mergeable(Other));
if (!Scoped)
Scoped = Other.Scoped;
if (!BaseType && Other.BaseType)
BaseType = std::move(Other.BaseType);
if (Members.empty() && !Other.Members.empty())
Members = deepCopyArray(Other.Members, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
void FunctionInfo::merge(FunctionInfo &&Other) {
assert(mergeable(Other));
if (!IsMethod)
IsMethod = Other.IsMethod;
if (!Access)
Access = Other.Access;
if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
ReturnType = std::move(Other.ReturnType);
if (Parent.USR == EmptySID && Parent.Name == "")
Parent = std::move(Other.Parent);
if (Params.empty() && !Other.Params.empty())
Params = allocateArray(Other.Params, PersistentArena);
if (!Template && Other.Template)
Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
void TypedefInfo::merge(TypedefInfo &&Other) {
assert(mergeable(Other));
if (!IsUsing)
IsUsing = Other.IsUsing;
if (Underlying.Type.Name == "")
Underlying = Other.Underlying;
if (!Template && Other.Template)
Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
void ConceptInfo::merge(ConceptInfo &&Other) {
assert(mergeable(Other));
if (!IsType)
IsType = Other.IsType;
if (ConstraintExpression.empty())
ConstraintExpression = std::move(Other.ConstraintExpression);
if (Template.Constraints.empty() && !Other.Template.Constraints.empty())
Template.Constraints =
allocateArray(Other.Template.Constraints, PersistentArena);
if (Template.Params.empty() && !Other.Template.Params.empty())
Template.Params = allocateArray(Other.Template.Params, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
void VarInfo::merge(VarInfo &&Other) {
assert(mergeable(Other));
if (!IsStatic)
IsStatic = Other.IsStatic;
if (Type.Type.USR == EmptySID && Type.Type.Name == "")
Type = std::move(Other.Type);
SymbolInfo::merge(std::move(Other));
}
BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
BaseRecordInfo::BaseRecordInfo(const BaseRecordInfo &Other,
llvm::BumpPtrAllocator &Arena)
: RecordInfo(Other, Arena), Access(Other.Access),
IsVirtual(Other.IsVirtual), IsParent(Other.IsParent) {}
BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
bool IsVirtual, AccessSpecifier Access,
bool IsParent)
: RecordInfo(USR, Name, Path), Access(Access), IsVirtual(IsVirtual),
IsParent(IsParent) {}
StringRef Info::extractName() const {
if (!Name.empty())
return Name;
switch (IT) {
case InfoType::IT_namespace:
// Cover the case where the project contains a base namespace called
// 'GlobalNamespace' (i.e. a namespace at the same level as the global
// namespace, which would conflict with the hard-coded global namespace name
// below.)
if (Name == "GlobalNamespace" && Namespace.empty())
return "@GlobalNamespace";
// The case of anonymous namespaces is taken care of in serialization,
// so here we can safely assume an unnamed namespace is the global
// one.
return "GlobalNamespace";
case InfoType::IT_record:
return internString("@nonymous_record_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_enum:
return internString("@nonymous_enum_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_typedef:
return internString("@nonymous_typedef_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_function:
return internString("@nonymous_function_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_concept:
return internString("@nonymous_concept_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_variable:
return internString("@nonymous_variable_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_friend:
return internString("@nonymous_friend_" + toHex(llvm::toStringRef(USR)));
case InfoType::IT_default:
return internString("@nonymous_" + toHex(llvm::toStringRef(USR)));
}
llvm_unreachable("Invalid InfoType.");
return "";
}
// Order is based on the Name attribute: case insensitive order
bool Index::operator<(const Index &Other) const {
// Start with case-insensitive (e.g., 'apple' < 'Zebra').
// This prevents 'Zebra' from appearing before 'apple' due to ASCII values,
// where uppercase letters have a lower numeric value than lowercase.
int Cmp = Name.compare_insensitive(Other.Name);
if (Cmp != 0)
return Cmp < 0;
// If names are identical, we fall back to standard string comparison where
// uppercase precedes lowercase (e.g., 'Apple' < 'apple').
return Name < Other.Name;
}
std::vector<const Index *> Index::getSortedChildren() const {
std::vector<const Index *> SortedChildren;
SortedChildren.reserve(Children.size());
for (const auto &[_, C] : Children)
SortedChildren.push_back(&C);
llvm::sort(SortedChildren,
[](const Index *A, const Index *B) { return *A < *B; });
return SortedChildren;
}
void Index::sort() {
for (auto &[_, C] : Children)
C.sort();
}
ClangDocContext::ClangDocContext(
tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly,
StringRef OutDirectory, StringRef SourceRoot, StringRef RepositoryUrl,
StringRef RepositoryLinePrefix, StringRef Base,
std::vector<std::string> UserStylesheets, clang::DiagnosticsEngine &Diags,
OutputFormatTy Format, bool FTimeTrace, bool Pretty)
: ECtx(ECtx), ProjectName(ProjectName), OutDirectory(OutDirectory),
SourceRoot(std::string(SourceRoot)), UserStylesheets(UserStylesheets),
Base(Base), Diags(Diags), Format(Format), PublicOnly(PublicOnly),
FTimeTrace(FTimeTrace), Pretty(Pretty) {
llvm::SmallString<128> SourceRootDir(SourceRoot);
if (SourceRoot.empty())
// If no SourceRoot was provided the current path is used as the default
llvm::sys::fs::current_path(SourceRootDir);
this->SourceRoot = std::string(SourceRootDir);
if (!RepositoryUrl.empty()) {
this->RepositoryUrl = std::string(RepositoryUrl);
if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&
!RepositoryUrl.starts_with("https://"))
this->RepositoryUrl->insert(0, "https://");
if (!RepositoryLinePrefix.empty())
this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);
}
}
void ScopeChildren::sort() {
Namespaces.sort();
Records.sort();
Functions.sort();
Enums.sort();
Typedefs.sort();
Concepts.sort();
Variables.sort();
}
} // namespace doc
} // namespace clang