Skip to content

Records store friends as relationships #894

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
Apr 4, 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
1 change: 0 additions & 1 deletion include/mrdocs/Metadata/Info/Enum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <mrdocs/Metadata/Source.hpp>
#include <mrdocs/Metadata/Type.hpp>
#include <mrdocs/Dom/LazyArray.hpp>
#include <ranges>

namespace clang::mrdocs {

Expand Down
29 changes: 12 additions & 17 deletions include/mrdocs/Metadata/Info/Friend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,20 @@
namespace clang::mrdocs {

/** Info for friend declarations.

- Friendship is not transitive
- Friendship is not inherited
- Access specifiers have no effect on the meaning of friend declarations
*/
struct FriendInfo final
: InfoCommonBase<InfoKind::Friend>
{
/** Befriended symbol.
*/
SymbolID FriendSymbol = SymbolID::invalid;
SymbolID id = SymbolID::invalid;

/** Befriended type.
*/
Polymorphic<TypeInfo> FriendType;

//--------------------------------------------

explicit FriendInfo(SymbolID ID) noexcept
: InfoCommonBase(ID)
{
}
Polymorphic<TypeInfo> Type;
};

MRDOCS_DECL
Expand All @@ -52,20 +48,19 @@ tag_invoke(
FriendInfo const& I,
DomCorpus const* domCorpus)
{
tag_invoke(t, io, dynamic_cast<Info const&>(I), domCorpus);
if (I.FriendSymbol)
if (I.id)
{
io.defer("name", [&I, domCorpus]{
return dom::ValueFrom(I.FriendSymbol, domCorpus).get("name");
return dom::ValueFrom(I.id, domCorpus).get("name");
});
io.map("symbol", I.FriendSymbol);
io.map("symbol", I.id);
}
else if (I.FriendType)
else if (I.Type)
{
io.defer("name", [&]{
return dom::ValueFrom(I.FriendType, domCorpus).get("name");
return dom::ValueFrom(I.Type, domCorpus).get("name");
});
io.map("type", I.FriendType);
io.map("type", I.Type);
}
}

Expand Down
1 change: 0 additions & 1 deletion include/mrdocs/Metadata/Info/InfoNodes.inc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ INFO(Enum)
INFO(EnumConstant)
INFO(Typedef)
INFO(Variable)
INFO(Friend)
INFO(Guide)
INFO(NamespaceAlias)
INFO(Using)
Expand Down
15 changes: 10 additions & 5 deletions include/mrdocs/Metadata/Info/Record.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <mrdocs/Metadata/Info.hpp>
#include <mrdocs/Metadata/Source.hpp>
#include <mrdocs/Metadata/Template.hpp>
#include <mrdocs/Metadata/Info/Friend.hpp>
#include <mrdocs/Dom.hpp>
#include <mrdocs/Dom/LazyObject.hpp>
#include <mrdocs/Dom/LazyArray.hpp>
Expand Down Expand Up @@ -46,7 +47,6 @@ struct RecordTranche
std::vector<SymbolID> StaticVariables;
std::vector<SymbolID> Concepts;
std::vector<SymbolID> Guides;
std::vector<SymbolID> Friends;
std::vector<SymbolID> Usings;
};

Expand All @@ -60,7 +60,7 @@ allMembers(RecordTranche const& T)
{
// This is a trick to emulate views::concat in C++20
return std::views::transform(
std::views::iota(0, 12),
std::views::iota(0, 11),
[&T](int const i) -> auto const&
{
switch (i) {
Expand All @@ -74,8 +74,7 @@ allMembers(RecordTranche const& T)
case 7: return T.StaticVariables;
case 8: return T.Concepts;
case 9: return T.Guides;
case 10: return T.Friends;
case 11: return T.Usings;
case 10: return T.Usings;
default: throw std::out_of_range("Invalid index");
}
}
Expand All @@ -102,7 +101,6 @@ tag_invoke(
io.map("staticVariables", dom::LazyArray(I.StaticVariables, domCorpus));
io.map("concepts", dom::LazyArray(I.Concepts, domCorpus));
io.map("guides", dom::LazyArray(I.Guides, domCorpus));
io.map("friends", dom::LazyArray(I.Friends, domCorpus));
io.map("usings", dom::LazyArray(I.Usings, domCorpus));
}

Expand Down Expand Up @@ -295,8 +293,14 @@ struct RecordInfo final
*/
std::vector<SymbolID> Derived;

/** Lists of members.
*/
RecordInterface Interface;

/** List of friends.
*/
std::vector<FriendInfo> Friends;

//--------------------------------------------

explicit RecordInfo(SymbolID const& ID) noexcept
Expand Down Expand Up @@ -354,6 +358,7 @@ tag_invoke(
io.map("derived", dom::LazyArray(I.Derived, domCorpus));
io.map("interface", I.Interface);
io.map("template", I.Template);
io.map("friends", dom::LazyArray(I.Friends, domCorpus));
}

/** Map the RecordInfo to a @ref dom::Value object.
Expand Down
3 changes: 0 additions & 3 deletions mrdocs.rnc
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,7 @@ grammar
Friend =
element friend
{
Access ?,
ID,
Location *,
Javadoc ?,
(
element befriended
{
Expand Down
27 changes: 23 additions & 4 deletions share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
{{! Base classes }}
{{#if (any_of_by symbol.bases "isPublic")}}
{{#> markup/dynamic-level-h }}Base Classes{{/markup/dynamic-level-h}}
[,cols=2]
[cols=2]
|===
| Name
| Description
Expand All @@ -58,7 +58,7 @@
{{#if symbol.constants}}
{{#> markup/dynamic-level-h }}Members{{/markup/dynamic-level-h}}

[,cols=2]
[cols=2]
|===
| Name
| Description
Expand All @@ -73,6 +73,25 @@
|===

{{/if}}
{{/if}}
{{! Friends }}
{{#if symbol.friends}}
{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h}}
[cols=2]
|===
| Name
| Description
{{#each symbol.friends }}
{{#if symbol}}
| {{#>markup/code}}{{> symbol/name symbol link=symbol }}{{/markup/code}}
| {{> javadoc/inline-brief symbol.doc.brief }}
{{else}}
| {{#>markup/code}}{{> type/declarator type }}{{/markup/code}}
|
{{/if}}
{{/each}}
|===

{{/if}}
{{! Using directives }}
{{#if symbol.usingDirectives}}
Expand All @@ -82,7 +101,7 @@
{{! Related symbols }}
{{#if symbol.doc.related}}
{{#> markup/dynamic-level-h }}Non-Member Functions{{/markup/dynamic-level-h}}
[,cols=2]
[cols=2]
|===
| Name
| Description
Expand All @@ -96,7 +115,7 @@
{{! Derived classes }}
{{#if symbol.derived}}
{{#> markup/dynamic-level-h }}Derived Classes{{/markup/dynamic-level-h}}
[,cols=2]
[cols=2]
|===
| Name
| Description
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
{{>symbol/members-table members=tranche.staticFunctions title=(concat (select label (concat label " ") "") "Static Member Functions")}}
{{>symbol/members-table members=tranche.variables title=(concat (select label (concat label " ") "") "Data Members")}}
{{>symbol/members-table members=tranche.staticVariables title=(concat (select label (concat label " ") "") "Static Data Members")}}
{{>symbol/members-table members=tranche.friends title=(concat (select label (concat label " ") "") "Friends")}}
{{>symbol/members-table members=tranche.aliases title=(concat (select label (concat label " ") "") "Aliases")}}
{{>symbol/members-table members=tranche.usings title=(concat (select label (concat label " ") "") "Using Declarations")}}
{{/if}}
28 changes: 28 additions & 0 deletions share/mrdocs/addons/generator/html/partials/symbol.html.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@
</table>
</div>
{{/if}}
{{! Friends }}
{{#if symbol.friends}}
<div>
{{#> markup/dynamic-level-h }}Friends{{/markup/dynamic-level-h}}
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{{#each symbol.friends }}
<tr>
{{#if symbol}}
<td>{{#>markup/code}}{{> symbol/name symbol link=symbol }}{{/markup/code}}</td>
<td>{{> javadoc/inline-brief symbol.doc.brief }}</td>
{{else}}
<td>{{#>markup/code}}{{> type/declarator type }}{{/markup/code}}</td>
<td></td>
{{/if}}
</tr>
{{/each}}
|===
</tbody>
</table>
</div>
{{/if}}
{{! Using directives }}
{{#if symbol.usingDirectives}}
<div>
Expand Down
68 changes: 41 additions & 27 deletions src/lib/AST/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,26 @@ populate(
B.isVirtual());
}
}

// Iterate over the friends of the class
if (D->hasDefinition() && D->hasFriends())
{
for (FriendDecl const* FD : D->friends())
{
// Check if the friend is a fundamental type
// Declaring a fundamental type like `int` as a friend of a
// class or struct does not have any practical effect. Thus,
// it's not considered part of the public API.
if (TypeSourceInfo const* TSI = FD->getFriendType())
{
Type const* T = TSI->getType().getTypePtrOrNull();
MRDOCS_CHECK_OR_CONTINUE(!T || !T->isBuiltinType());
}
FriendInfo F;
populate(F, FD);
I.Friends.push_back(std::move(F));
}
}
}

void
Expand Down Expand Up @@ -1113,32 +1133,31 @@ populate(
FriendInfo& I,
FriendDecl const* D)
{
// A NamedDecl nominated by a FriendDecl
// will be one of the following:
// - FunctionDecl
// - FunctionTemplateDecl
// - ClassTemplateDecl
if (NamedDecl* ND = D->getFriendDecl())
if (TypeSourceInfo const* TSI = D->getFriendType())
{
generateID(ND, I.FriendSymbol);
// If this is a friend function declaration naming
// a previously undeclared function, traverse it.
// in addition to this, traverse the declaration if
// it's a class templates first declared as a friend
if((ND->isFunctionOrFunctionTemplate() &&
ND->getFriendObjectKind() == Decl::FOK_Undeclared) ||
(isa<ClassTemplateDecl>(ND) && ND->isFirstDecl()))
{
traverse(cast<Decl>(ND));
}
I.Type = toTypeInfo(TSI->getType());
MRDOCS_CHECK_OR(I.Type->isNamed());
auto const& NTI = dynamic_cast<NamedTypeInfo&>(*I.Type);
MRDOCS_CHECK_OR(NTI.Name);
I.id = NTI.Name->id;
}
// Since a friend declaration which name non-class types
// will be ignored, a type nominated by a FriendDecl can
// essentially be anything
if (TypeSourceInfo const* TSI = D->getFriendType())
else if (NamedDecl const* ND = D->getFriendDecl())
{
I.FriendType = toTypeInfo(TSI->getType());
// ND can be a function or a class
ScopeExitRestore s(mode_, Dependency);
if (Info const* SI = traverse(dyn_cast<Decl>(ND)))
{
I.id = SI->id;
}
}
// The newly traversed info might need to inherit the
// documentation from the FriendDecl when the friend
// is the only declaration.
MRDOCS_CHECK_OR(isDocumented(D));
Info* TI = find(I.id);
MRDOCS_CHECK_OR(TI);
MRDOCS_CHECK_OR(!TI->javadoc);
populate(TI->javadoc, D);
}

void
Expand Down Expand Up @@ -1779,11 +1798,6 @@ addMember(RecordTranche& T, Info const& Member)
addMember(T.Guides, *U);
return;
}
if (auto const* U = Member.asFriendPtr())
{
addMember(T.Friends, *U);
return;
}
if (auto const* U = Member.asUsingPtr())
{
addMember(T.Usings, *U);
Expand Down
4 changes: 4 additions & 0 deletions src/lib/AST/ASTVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ class ASTVisitor
*/
TraversalMode mode_ = Regular;

/* A map which stores the Info types created by each decl.
*/
std::unordered_map<FriendDecl const*, Info const*> friendDecls_;

public:
/** Constructor for ASTVisitor.

Expand Down
5 changes: 0 additions & 5 deletions src/lib/AST/ClangHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,6 @@ template <>
struct InfoTypeFor<FieldDecl>
: std::type_identity<VariableInfo> {};

// Extract FriendInfo from FriendDecl
template <>
struct InfoTypeFor<FriendDecl>
: std::type_identity<FriendInfo> {};

// Extract GuideInfo from CXXDeductionGuideDecl
template <>
struct InfoTypeFor<CXXDeductionGuideDecl>
Expand Down
Loading
Loading