Skip to content

[clang-doc] serialize friends #146165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ static llvm::Error decodeRecord(const Record &R, InfoType &Field,
case InfoType::IT_typedef:
case InfoType::IT_concept:
case InfoType::IT_variable:
case InfoType::IT_friend:
Field = IT;
return llvm::Error::success();
}
Expand All @@ -111,6 +112,7 @@ static llvm::Error decodeRecord(const Record &R, FieldId &Field,
case FieldId::F_child_namespace:
case FieldId::F_child_record:
case FieldId::F_concept:
case FieldId::F_friend:
case FieldId::F_default:
Field = F;
return llvm::Error::success();
Expand Down Expand Up @@ -450,6 +452,15 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
}
}

static llvm::Error parseRecord(const Record &R, unsigned ID, StringRef Blob,
FriendInfo *F) {
if (ID == FRIEND_IS_CLASS) {
return decodeRecord(R, F->IsClass, Blob);
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for Friend");
}

template <typename T> static llvm::Expected<CommentInfo *> getCommentInfo(T I) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain CommentInfo");
Expand Down Expand Up @@ -525,6 +536,18 @@ template <> llvm::Error addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) {
return llvm::Error::success();
}

template <> llvm::Error addTypeInfo(FriendInfo *I, FieldTypeInfo &&T) {
if (!I->Params)
I->Params.emplace();
I->Params->emplace_back(std::move(T));
return llvm::Error::success();
}

template <> llvm::Error addTypeInfo(FriendInfo *I, TypeInfo &&T) {
I->ReturnType.emplace(std::move(T));
return llvm::Error::success();
}

template <> llvm::Error addTypeInfo(EnumInfo *I, TypeInfo &&T) {
I->BaseType = std::move(T);
return llvm::Error::success();
Expand Down Expand Up @@ -667,6 +690,16 @@ llvm::Error addReference(ConstraintInfo *I, Reference &&R, FieldId F) {
"ConstraintInfo cannot contain this Reference");
}

template <>
llvm::Error addReference(FriendInfo *Friend, Reference &&R, FieldId F) {
if (F == FieldId::F_friend) {
Friend->Ref = std::move(R);
return llvm::Error::success();
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Friend cannot contain this Reference");
}

template <typename T, typename ChildInfoType>
static void addChild(T I, ChildInfoType &&R) {
llvm::errs() << "invalid child type for info";
Expand Down Expand Up @@ -700,6 +733,9 @@ template <> void addChild(RecordInfo *I, EnumInfo &&R) {
template <> void addChild(RecordInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, FriendInfo &&R) {
I->Friends.emplace_back(std::move(R));
}

// Other types of children:
template <> void addChild(EnumInfo *I, EnumValueInfo &&R) {
Expand Down Expand Up @@ -741,6 +777,9 @@ template <> void addTemplate(FunctionInfo *I, TemplateInfo &&P) {
template <> void addTemplate(ConceptInfo *I, TemplateInfo &&P) {
I->Template = std::move(P);
}
template <> void addTemplate(FriendInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}

// Template specializations go only into template records.
template <typename T>
Expand Down Expand Up @@ -921,6 +960,10 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
case BI_VAR_BLOCK_ID: {
return handleSubBlock<VarInfo>(ID, I, CreateAddFunc(addChild<T, VarInfo>));
}
case BI_FRIEND_BLOCK_ID: {
return handleSubBlock<FriendInfo>(ID, I,
CreateAddFunc(addChild<T, FriendInfo>));
}
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid subblock type");
Expand Down Expand Up @@ -1032,6 +1075,8 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
return createInfo<FunctionInfo>(ID);
case BI_VAR_BLOCK_ID:
return createInfo<VarInfo>(ID);
case BI_FRIEND_BLOCK_ID:
return createInfo<FriendInfo>(ID);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"cannot create info");
Expand Down Expand Up @@ -1072,6 +1117,7 @@ ClangDocBitcodeReader::readBitcode() {
case BI_TYPEDEF_BLOCK_ID:
case BI_CONCEPT_BLOCK_ID:
case BI_VAR_BLOCK_ID:
case BI_FRIEND_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID: {
auto InfoOrErr = readBlockToInfo(ID);
if (!InfoOrErr)
Expand Down
27 changes: 24 additions & 3 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
{BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"},
{BI_CONSTRAINT_BLOCK_ID, "ConstraintBlock"},
{BI_CONCEPT_BLOCK_ID, "ConceptBlock"},
{BI_VAR_BLOCK_ID, "VarBlock"}};
{BI_VAR_BLOCK_ID, "VarBlock"},
{BI_FRIEND_BLOCK_ID, "FriendBlock"}};
assert(Inits.size() == BlockIdCount);
for (const auto &Init : Inits)
BlockIdNameMap[Init.first] = Init.second;
Expand Down Expand Up @@ -224,7 +225,8 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{VAR_USR, {"USR", &genSymbolIdAbbrev}},
{VAR_NAME, {"Name", &genStringAbbrev}},
{VAR_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
{VAR_IS_STATIC, {"IsStatic", &genBoolAbbrev}}};
{VAR_IS_STATIC, {"IsStatic", &genBoolAbbrev}},
{FRIEND_IS_CLASS, {"IsClass", &genBoolAbbrev}}};

assert(Inits.size() == RecordIdCount);
for (const auto &Init : Inits) {
Expand Down Expand Up @@ -293,7 +295,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
CONCEPT_CONSTRAINT_EXPRESSION}},
// Constraint Block
{BI_CONSTRAINT_BLOCK_ID, {CONSTRAINT_EXPRESSION}},
{BI_VAR_BLOCK_ID, {VAR_NAME, VAR_USR, VAR_DEFLOCATION, VAR_IS_STATIC}}};
{BI_VAR_BLOCK_ID, {VAR_NAME, VAR_USR, VAR_DEFLOCATION, VAR_IS_STATIC}},
{BI_FRIEND_BLOCK_ID, {FRIEND_IS_CLASS}}};

// AbbreviationMap

Expand Down Expand Up @@ -476,6 +479,19 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
emitRecord((unsigned)Field, REFERENCE_FIELD);
}

void ClangDocBitcodeWriter::emitBlock(const FriendInfo &R) {
StreamSubBlockGuard Block(Stream, BI_FRIEND_BLOCK_ID);
emitBlock(R.Ref, FieldId::F_friend);
emitRecord(R.IsClass, FRIEND_IS_CLASS);
if (R.Template)
emitBlock(*R.Template);
if (R.Params)
for (const auto &P : *R.Params)
emitBlock(P);
if (R.ReturnType)
emitBlock(*R.ReturnType);
}

void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
Expand Down Expand Up @@ -628,6 +644,8 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
emitBlock(C);
if (I.Template)
emitBlock(*I.Template);
for (const auto &C : I.Friends)
emitBlock(C);
}

void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
Expand Down Expand Up @@ -744,6 +762,9 @@ bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
case InfoType::IT_variable:
emitBlock(*static_cast<VarInfo *>(I));
break;
case InfoType::IT_friend:
emitBlock(*static_cast<FriendInfo *>(I));
break;
case InfoType::IT_default:
llvm::errs() << "Unexpected info, unable to write.\n";
return true;
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/clang-doc/BitcodeWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum BlockId {
BI_TYPEDEF_BLOCK_ID,
BI_CONCEPT_BLOCK_ID,
BI_VAR_BLOCK_ID,
BI_FRIEND_BLOCK_ID,
BI_LAST,
BI_FIRST = BI_VERSION_BLOCK_ID
};
Expand Down Expand Up @@ -153,6 +154,7 @@ enum RecordId {
VAR_NAME,
VAR_DEFLOCATION,
VAR_IS_STATIC,
FRIEND_IS_CLASS,
RI_LAST,
RI_FIRST = VERSION
};
Expand All @@ -169,7 +171,8 @@ enum class FieldId {
F_type,
F_child_namespace,
F_child_record,
F_concept
F_concept,
F_friend
};

class ClangDocBitcodeWriter {
Expand Down Expand Up @@ -201,6 +204,7 @@ class ClangDocBitcodeWriter {
void emitBlock(const ConceptInfo &T);
void emitBlock(const ConstraintInfo &T);
void emitBlock(const Reference &B, FieldId F);
void emitBlock(const FriendInfo &R);
void emitBlock(const VarInfo &B);

private:
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
break;
case InfoType::IT_concept:
case InfoType::IT_variable:
case InfoType::IT_friend:
break;
case InfoType::IT_default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
Expand Down Expand Up @@ -1018,6 +1019,8 @@ static std::string getRefType(InfoType IT) {
return "concept";
case InfoType::IT_variable:
return "variable";
case InfoType::IT_friend:
return "friend";
}
llvm_unreachable("Unknown InfoType");
}
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
case InfoType::IT_concept:
break;
case InfoType::IT_variable:
case InfoType::IT_friend:
break;
case InfoType::IT_default:
return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
Expand Down
23 changes: 21 additions & 2 deletions clang-tools-extra/clang-doc/JSONGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ static void serializeArray(const Container &Records, Object &Obj,
static auto SerializeInfoLambda = [](const auto &Info, Object &Object) {
serializeInfo(Info, Object);
};
static auto SerializeReferenceLambda = [](const Reference &Ref,
Object &Object) {
static auto SerializeReferenceLambda = [](const auto &Ref, Object &Object) {
serializeReference(Ref, Object);
};

Expand Down Expand Up @@ -365,6 +364,22 @@ static void serializeInfo(const BaseRecordInfo &I, Object &Obj,
Obj["IsParent"] = I.IsParent;
}

static void serializeInfo(const FriendInfo &I, Object &Obj) {
auto FriendRef = Object();
serializeReference(I.Ref, FriendRef);
Obj["Reference"] = std::move(FriendRef);
Obj["IsClass"] = I.IsClass;
if (I.Template)
serializeInfo(I.Template.value(), Obj);
if (I.Params)
serializeArray(I.Params.value(), Obj, "Params", SerializeInfoLambda);
if (I.ReturnType) {
auto ReturnTypeObj = Object();
serializeInfo(I.ReturnType.value(), ReturnTypeObj);
Obj["ReturnType"] = std::move(ReturnTypeObj);
}
}

static void serializeInfo(const RecordInfo &I, json::Object &Obj,
const std::optional<StringRef> &RepositoryUrl) {
serializeCommonAttributes(I, Obj, RepositoryUrl);
Expand Down Expand Up @@ -436,6 +451,9 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
if (I.Template)
serializeInfo(I.Template.value(), Obj);

if (!I.Friends.empty())
serializeArray(I.Friends, Obj, "Friends", SerializeInfoLambda);

serializeCommonChildren(I.Children, Obj, RepositoryUrl);
}

Expand Down Expand Up @@ -525,6 +543,7 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
case InfoType::IT_function:
case InfoType::IT_typedef:
case InfoType::IT_variable:
case InfoType::IT_friend:
break;
case InfoType::IT_default:
return createStringError(inconvertibleErrorCode(), "unexpected info type");
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/MDGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ static llvm::Error genIndex(ClangDocContext &CDCtx) {
case InfoType::IT_variable:
Type = "Variable";
break;
case InfoType::IT_friend:
Type = "Friend";
break;
case InfoType::IT_default:
Type = "Other";
}
Expand Down Expand Up @@ -472,6 +475,7 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
break;
case InfoType::IT_concept:
case InfoType::IT_variable:
case InfoType::IT_friend:
break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clang-doc/Representation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
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");
Expand Down Expand Up @@ -247,6 +249,15 @@ void Reference::merge(Reference &&Other) {
Path = Other.Path;
}

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));
}

void Info::mergeBase(Info &&Other) {
assert(mergeable(Other));
if (USR == EmptySID)
Expand Down Expand Up @@ -313,6 +324,8 @@ void RecordInfo::merge(RecordInfo &&Other) {
Parents = std::move(Other.Parents);
if (VirtualParents.empty())
VirtualParents = std::move(Other.VirtualParents);
if (Friends.empty())
Friends = std::move(Other.Friends);
// Reduce children if necessary.
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
Expand Down Expand Up @@ -422,6 +435,9 @@ llvm::SmallString<16> Info::extractName() const {
case InfoType::IT_variable:
return llvm::SmallString<16>("@nonymous_variable_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_friend:
return llvm::SmallString<16>("@nonymous_friend_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_default:
return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
}
Expand Down
21 changes: 20 additions & 1 deletion clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ enum class InfoType {
IT_enum,
IT_typedef,
IT_concept,
IT_variable
IT_variable,
IT_friend
};

enum class CommentKind {
Expand Down Expand Up @@ -379,6 +380,22 @@ struct SymbolInfo : public Info {
bool IsStatic = false;
};

struct FriendInfo : SymbolInfo {
FriendInfo() : SymbolInfo(InfoType::IT_friend) {}
FriendInfo(SymbolID USR) : SymbolInfo(InfoType::IT_friend, USR) {}
FriendInfo(const InfoType IT, const SymbolID &USR,
const StringRef Name = StringRef())
: SymbolInfo(IT, USR, Name) {}
bool mergeable(const FriendInfo &Other);
void merge(FriendInfo &&Other);

Reference Ref;
std::optional<TemplateInfo> Template;
std::optional<TypeInfo> ReturnType;
std::optional<SmallVector<FieldTypeInfo, 4>> Params;
bool IsClass = false;
};

struct VarInfo : SymbolInfo {
VarInfo() : SymbolInfo(InfoType::IT_variable) {}
explicit VarInfo(SymbolID USR) : SymbolInfo(InfoType::IT_variable, USR) {}
Expand Down Expand Up @@ -454,6 +471,8 @@ struct RecordInfo : public SymbolInfo {
Bases; // List of base/parent records; this includes inherited methods and
// attributes

std::vector<FriendInfo> Friends;

ScopeChildren Children;
};

Expand Down
Loading