[llvm-branch-commits] [clang-tools-extra] [clang-doc] serialize friends (PR #146165)
Erick Velez via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jun 27 14:58:05 PDT 2025
https://github.com/evelez7 created https://github.com/llvm/llvm-project/pull/146165
None
>From a373ecb94c6137fb8de62226b9cfbfe8c8840564 Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvelez7 at gmail.com>
Date: Thu, 26 Jun 2025 20:54:03 -0700
Subject: [PATCH] [clang-doc] serialize friends
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 46 +++++++++++
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 27 ++++++-
clang-tools-extra/clang-doc/BitcodeWriter.h | 6 +-
clang-tools-extra/clang-doc/HTMLGenerator.cpp | 3 +
.../clang-doc/HTMLMustacheGenerator.cpp | 1 +
clang-tools-extra/clang-doc/JSONGenerator.cpp | 23 +++++-
clang-tools-extra/clang-doc/MDGenerator.cpp | 4 +
.../clang-doc/Representation.cpp | 16 ++++
clang-tools-extra/clang-doc/Representation.h | 21 ++++-
clang-tools-extra/clang-doc/Serialize.cpp | 53 +++++++++++++
clang-tools-extra/clang-doc/YAMLGenerator.cpp | 1 +
.../test/clang-doc/json/class.cpp | 76 +++++++++----------
.../unittests/clang-doc/BitcodeTest.cpp | 2 +
13 files changed, 234 insertions(+), 45 deletions(-)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index fd6f40cff1a4e..2cbf8bf6b2879 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -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();
}
@@ -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();
@@ -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");
@@ -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();
@@ -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";
@@ -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) {
@@ -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>
@@ -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");
@@ -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");
@@ -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)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 006ddda1b75e7..3cc0d4ad332f0 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -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;
@@ -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) {
@@ -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
@@ -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);
@@ -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) {
@@ -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;
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index f1325094f957a..d09ec4ca34006 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -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
};
@@ -153,6 +154,7 @@ enum RecordId {
VAR_NAME,
VAR_DEFLOCATION,
VAR_IS_STATIC,
+ FRIEND_IS_CLASS,
RI_LAST,
RI_FIRST = VERSION
};
@@ -169,7 +171,8 @@ enum class FieldId {
F_type,
F_child_namespace,
F_child_record,
- F_concept
+ F_concept,
+ F_friend
};
class ClangDocBitcodeWriter {
@@ -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:
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index c4303d287da9e..8294ff9118558 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -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(),
@@ -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");
}
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index c611c946b3937..7aeaa1b7cf67d 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -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");
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 1f6167f7f9b8d..0e1a0cc347e45 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -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);
};
@@ -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);
@@ -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);
}
@@ -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");
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 608a7f6d4a9d3..6f16f5bd2f528 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -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";
}
@@ -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(),
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 5b94d37d868b4..ba53329a41789 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -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");
@@ -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)
@@ -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));
@@ -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)));
}
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 59874f0cfcedf..fe5cc48069d58 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -46,7 +46,8 @@ enum class InfoType {
IT_enum,
IT_typedef,
IT_concept,
- IT_variable
+ IT_variable,
+ IT_friend
};
enum class CommentKind {
@@ -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) {}
@@ -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;
};
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 7a9cb8a1eddb9..12ef8891c720e 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -7,9 +7,12 @@
//===----------------------------------------------------------------------===//
#include "Serialize.h"
+#include "../clangd/CodeCompletionStrings.h"
#include "BitcodeWriter.h"
+
#include "clang/AST/Attr.h"
#include "clang/AST/Comment.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringExtras.h"
@@ -403,6 +406,7 @@ std::string serialize(std::unique_ptr<Info> &I) {
return serialize(*static_cast<ConceptInfo *>(I.get()));
case InfoType::IT_variable:
return serialize(*static_cast<VarInfo *>(I.get()));
+ case InfoType::IT_friend:
case InfoType::IT_typedef:
case InfoType::IT_default:
return "";
@@ -556,6 +560,7 @@ static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) {
case InfoType::IT_typedef:
case InfoType::IT_concept:
case InfoType::IT_variable:
+ case InfoType::IT_friend:
break;
}
llvm_unreachable("Invalid reference type for parent namespace");
@@ -947,6 +952,53 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};
}
+static void parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
+ if (D->hasDefinition() && D->hasFriends())
+ for (const FriendDecl *FD : D->friends()) {
+ if (FD->isUnsupportedFriend())
+ continue;
+
+ FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
+ const auto *ActualDecl = FD->getFriendDecl();
+ if (!ActualDecl) {
+ const auto *FriendTypeInfo = FD->getFriendType();
+ if (!FriendTypeInfo)
+ continue;
+ ActualDecl = FriendTypeInfo->getType()->getAsCXXRecordDecl();
+
+ if (!ActualDecl)
+ continue;
+ F.IsClass = true;
+ }
+
+ if (const auto *ActualTD = dyn_cast_or_null<TemplateDecl>(ActualDecl)) {
+ if (isa<RecordDecl>(ActualTD->getTemplatedDecl()))
+ F.IsClass = true;
+ F.Template.emplace();
+ for (const auto *Param : ActualTD->getTemplateParameters()->asArray())
+ F.Template->Params.emplace_back(
+ getSourceCode(Param, Param->getSourceRange()));
+ ActualDecl = ActualTD->getTemplatedDecl();
+ }
+
+ if (auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(ActualDecl)) {
+ FunctionInfo TempInfo;
+ parseParameters(TempInfo, FuncDecl);
+ F.Params.emplace();
+ F.Params = std::move(TempInfo.Params);
+ F.ReturnType = getTypeInfoForType(FuncDecl->getReturnType(),
+ FuncDecl->getLangOpts());
+ }
+
+ F.Ref = Reference(getUSRForDecl(ActualDecl),
+ ActualDecl->getNameAsString(), InfoType::IT_default,
+ ActualDecl->getQualifiedNameAsString(),
+ getInfoRelativePath(ActualDecl));
+
+ RI.Friends.push_back(std::move(F));
+ }
+}
+
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
bool PublicOnly) {
@@ -970,6 +1022,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
// TODO: remove first call to parseBases, that function should be deleted
parseBases(*RI, C);
parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);
+ parseFriends(*RI, C);
}
RI->Path = getInfoRelativePath(RI->Namespace);
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index 3ca4d4947fa97..eeccdd804b669 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -410,6 +410,7 @@ llvm::Error YAMLGenerator::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(),
diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp b/clang-tools-extra/test/clang-doc/json/class.cpp
index 0715fcefbb785..ae47da75edccb 100644
--- a/clang-tools-extra/test/clang-doc/json/class.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class.cpp
@@ -89,44 +89,44 @@ struct MyClass {
// CHECK-NEXT: "USR": "{{[0-9A-F]*}}"
// CHECK-NEXT: }
// CHECK-NEXT: ],
-// CHECK-NOT: "Friends": [
-// CHECK-NOT: {
-// CHECK-NOT: "IsClass": false,
-// CHECK-NOT: "Params": [
-// CHECK-NOT: {
-// CHECK-NOT: "Name": "",
-// CHECK-NOT: "Type": "int"
-// CHECK-NOT: }
-// CHECK-NOT: ],
-// CHECK-NOT: "Reference": {
-// CHECK-NOT: "Name": "friendFunction",
-// CHECK-NOT: "Path": "",
-// CHECK-NOT: "QualName": "friendFunction",
-// CHECK-NOT: "USR": "{{[0-9A-F]*}}"
-// CHECK-NOT: },
-// CHECK-NOT: "ReturnType": {
-// CHECK-NOT: "IsBuiltIn": true,
-// CHECK-NOT: "IsTemplate": false,
-// CHECK-NOT: "Name": "void",
-// CHECK-NOT: "QualName": "void",
-// CHECK-NOT: "USR": "0000000000000000000000000000000000000000"
-// CHECK-NOT: },
-// CHECK-NOT: "Template": {
-// CHECK-NOT: "Parameters": [
-// CHECK-NOT: "typename T"
-// CHECK-NOT: ]
-// CHECK-NOT: }
-// CHECK-NOT: },
-// CHECK-NOT: {
-// CHECK-NOT: "IsClass": true,
-// CHECK-NOT: "Reference": {
-// CHECK-NOT: "Name": "Foo",
-// CHECK-NOT: "Path": "GlobalNamespace",
-// CHECK-NOT: "QualName": "Foo",
-// CHECK-NOT: "USR": "{{[0-9A-F]*}}"
-// CHECK-NOT: },
-// CHECK-NOT: },
-// CHECK-NOT: ],
+// CHECK-NEXT: "Friends": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "IsClass": false,
+// CHECK-NEXT: "Params": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "Name": "",
+// CHECK-NEXT: "Type": "int"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
+// CHECK-NEXT: "Reference": {
+// CHECK-NEXT: "Name": "friendFunction",
+// CHECK-NEXT: "Path": "",
+// CHECK-NEXT: "QualName": "friendFunction",
+// CHECK-NEXT: "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT: },
+// CHECK-NEXT: "ReturnType": {
+// CHECK-NEXT: "IsBuiltIn": true,
+// CHECK-NEXT: "IsTemplate": false,
+// CHECK-NEXT: "Name": "void",
+// CHECK-NEXT: "QualName": "void",
+// CHECK-NEXT: "USR": "0000000000000000000000000000000000000000"
+// CHECK-NEXT: },
+// CHECK-NEXT: "Template": {
+// CHECK-NEXT: "Parameters": [
+// CHECK-NEXT: "typename T"
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: },
+// CHECK-NEXT: {
+// CHECK-NEXT: "IsClass": true,
+// CHECK-NEXT: "Reference": {
+// CHECK-NEXT: "Name": "Foo",
+// CHECK-NEXT: "Path": "GlobalNamespace",
+// CHECK-NEXT: "QualName": "Foo",
+// CHECK-NEXT: "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
// COM: FIXME: FullName is not emitted correctly.
// CHECK-NEXT: "FullName": "",
// CHECK-NEXT: "IsTypedef": false,
diff --git a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
index 323431c4fc9e0..d88b0c91bb9a9 100644
--- a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
@@ -41,6 +41,8 @@ static std::string writeInfo(Info *I) {
return writeInfo(*static_cast<ConceptInfo *>(I));
case InfoType::IT_variable:
return writeInfo(*static_cast<VarInfo *>(I));
+ case InfoType::IT_friend:
+ return writeInfo(*static_cast<FriendInfo *>(I));
case InfoType::IT_default:
return "";
}
More information about the llvm-branch-commits
mailing list