Skip to content

Commit b1871fb

Browse files
[SymbolGraphGen] synthesize child symbols for type aliases of private decls (swiftlang#78959)
1 parent 7eada7d commit b1871fb

File tree

8 files changed

+226
-62
lines changed

8 files changed

+226
-62
lines changed

lib/SymbolGraphGen/Symbol.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,21 @@ using namespace swift;
3737
using namespace symbolgraphgen;
3838

3939
Symbol::Symbol(SymbolGraph *Graph, const ExtensionDecl *ED,
40-
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
40+
const ValueDecl *SynthesizedBaseTypeDecl, Type BaseType)
4141
: Symbol::Symbol(Graph, nullptr, ED, SynthesizedBaseTypeDecl, BaseType) {}
4242

4343
Symbol::Symbol(SymbolGraph *Graph, const ValueDecl *VD,
44-
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
44+
const ValueDecl *SynthesizedBaseTypeDecl, Type BaseType)
4545
: Symbol::Symbol(Graph, VD, nullptr, SynthesizedBaseTypeDecl, BaseType) {}
4646

4747
Symbol::Symbol(SymbolGraph *Graph, const ValueDecl *VD, const ExtensionDecl *ED,
48-
const NominalTypeDecl *SynthesizedBaseTypeDecl, Type BaseType)
48+
const ValueDecl *SynthesizedBaseTypeDecl, Type BaseType)
4949
: Graph(Graph), D(VD), BaseType(BaseType),
5050
SynthesizedBaseTypeDecl(SynthesizedBaseTypeDecl) {
51-
if (!BaseType && SynthesizedBaseTypeDecl)
52-
BaseType = SynthesizedBaseTypeDecl->getDeclaredInterfaceType();
51+
if (!BaseType && SynthesizedBaseTypeDecl) {
52+
if (const auto *NTD = dyn_cast<NominalTypeDecl>(SynthesizedBaseTypeDecl))
53+
BaseType = NTD->getDeclaredInterfaceType();
54+
}
5355
if (D == nullptr) {
5456
D = ED;
5557
}

lib/SymbolGraphGen/Symbol.h

+17-16
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ class Symbol {
3535
/// Either a ValueDecl* or ExtensionDecl*.
3636
const Decl *D;
3737
Type BaseType;
38-
const NominalTypeDecl *SynthesizedBaseTypeDecl;
38+
const ValueDecl *SynthesizedBaseTypeDecl;
3939

4040
Symbol(SymbolGraph *Graph, const ValueDecl *VD, const ExtensionDecl *ED,
41-
const NominalTypeDecl *SynthesizedBaseTypeDecl,
41+
const ValueDecl *SynthesizedBaseTypeDecl,
4242
Type BaseTypeForSubstitution = Type());
4343

4444
swift::DeclName getName(const Decl *D) const;
@@ -87,11 +87,11 @@ class Symbol {
8787

8888
public:
8989
Symbol(SymbolGraph *Graph, const ExtensionDecl *ED,
90-
const NominalTypeDecl *SynthesizedBaseTypeDecl,
90+
const ValueDecl *SynthesizedBaseTypeDecl,
9191
Type BaseTypeForSubstitution = Type());
9292

9393
Symbol(SymbolGraph *Graph, const ValueDecl *VD,
94-
const NominalTypeDecl *SynthesizedBaseTypeDecl,
94+
const ValueDecl *SynthesizedBaseTypeDecl,
9595
Type BaseTypeForSubstitution = Type());
9696

9797
void serialize(llvm::json::OStream &OS) const;
@@ -108,7 +108,7 @@ class Symbol {
108108
return BaseType;
109109
}
110110

111-
const NominalTypeDecl *getSynthesizedBaseTypeDecl() const {
111+
const ValueDecl *getSynthesizedBaseTypeDecl() const {
112112
return SynthesizedBaseTypeDecl;
113113
}
114114

@@ -175,27 +175,28 @@ using SymbolGraph = swift::symbolgraphgen::SymbolGraph;
175175

176176
template <> struct DenseMapInfo<Symbol> {
177177
static inline Symbol getEmptyKey() {
178-
return Symbol {
179-
DenseMapInfo<SymbolGraph *>::getEmptyKey(),
180-
DenseMapInfo<const swift::ValueDecl *>::getEmptyKey(),
181-
DenseMapInfo<const swift::NominalTypeDecl *>::getTombstoneKey(),
182-
DenseMapInfo<swift::Type>::getEmptyKey(),
178+
return Symbol{
179+
DenseMapInfo<SymbolGraph *>::getEmptyKey(),
180+
DenseMapInfo<const swift::ValueDecl *>::getEmptyKey(),
181+
DenseMapInfo<const swift::ValueDecl *>::getTombstoneKey(),
182+
DenseMapInfo<swift::Type>::getEmptyKey(),
183183
};
184184
}
185185
static inline Symbol getTombstoneKey() {
186-
return Symbol {
187-
DenseMapInfo<SymbolGraph *>::getTombstoneKey(),
188-
DenseMapInfo<const swift::ValueDecl *>::getTombstoneKey(),
189-
DenseMapInfo<const swift::NominalTypeDecl *>::getTombstoneKey(),
190-
DenseMapInfo<swift::Type>::getTombstoneKey(),
186+
return Symbol{
187+
DenseMapInfo<SymbolGraph *>::getTombstoneKey(),
188+
DenseMapInfo<const swift::ValueDecl *>::getTombstoneKey(),
189+
DenseMapInfo<const swift::ValueDecl *>::getTombstoneKey(),
190+
DenseMapInfo<swift::Type>::getTombstoneKey(),
191191
};
192192
}
193193
static unsigned getHashValue(const Symbol S) {
194194
unsigned H = 0;
195195
H ^= DenseMapInfo<SymbolGraph *>::getHashValue(S.getGraph());
196196
H ^=
197197
DenseMapInfo<const swift::Decl *>::getHashValue(S.getLocalSymbolDecl());
198-
H ^= DenseMapInfo<const swift::NominalTypeDecl *>::getHashValue(S.getSynthesizedBaseTypeDecl());
198+
H ^= DenseMapInfo<const swift::ValueDecl *>::getHashValue(
199+
S.getSynthesizedBaseTypeDecl());
199200
H ^= DenseMapInfo<swift::Type>::getHashValue(S.getBaseType());
200201
return H;
201202
}

lib/SymbolGraphGen/SymbolGraph.cpp

+79-34
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,18 @@ void SymbolGraph::recordEdge(Symbol Source,
232232

233233
void SymbolGraph::recordMemberRelationship(Symbol S) {
234234
const auto *DC = S.getLocalSymbolDecl()->getDeclContext();
235+
const ValueDecl *ParentDecl = DC->getSelfNominalTypeDecl();
236+
if (!ParentDecl) {
237+
// If we couldn't look up the type the member is declared on (e.g.
238+
// because the member is declared in an extension whose extended type
239+
// doesn't exist), don't record a memberOf relationship.
240+
return;
241+
}
242+
if (const auto *PublicDecl = Walker.PublicPrivateTypeAliases[ParentDecl]) {
243+
// If our member target is a private type that has a public type alias,
244+
// point the membership to that type alias instead.
245+
ParentDecl = PublicDecl;
246+
}
235247
switch (DC->getContextKind()) {
236248
case DeclContextKind::GenericTypeDecl:
237249
case DeclContextKind::ExtensionDecl:
@@ -250,13 +262,6 @@ void SymbolGraph::recordMemberRelationship(Symbol S) {
250262
return;
251263
}
252264

253-
if (DC->getSelfNominalTypeDecl() == nullptr) {
254-
// If we couldn't look up the type the member is declared on (e.g.
255-
// because the member is declared in an extension whose extended type
256-
// doesn't exist), don't record a memberOf relationship.
257-
return;
258-
}
259-
260265
// If this is an extension to an external type, we use the extension
261266
// symbol itself as the target.
262267
if (auto const *Extension =
@@ -268,8 +273,7 @@ void SymbolGraph::recordMemberRelationship(Symbol S) {
268273
}
269274
}
270275

271-
return recordEdge(S,
272-
Symbol(this, DC->getSelfNominalTypeDecl(), nullptr),
276+
return recordEdge(S, Symbol(this, ParentDecl, nullptr),
273277
RelationshipKind::MemberOf());
274278
case swift::DeclContextKind::AbstractClosureExpr:
275279
case swift::DeclContextKind::SerializedAbstractClosure:
@@ -322,7 +326,16 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
322326
bool dropSynthesizedMembers = !Walker.Options.EmitSynthesizedMembers ||
323327
Walker.Options.SkipProtocolImplementations;
324328

325-
const auto D = S.getLocalSymbolDecl();
329+
const auto *D = S.getLocalSymbolDecl();
330+
331+
// If this symbol is a public type alias to a private symbol, collect
332+
// synthesized members for the underlying type.
333+
if (const auto *TD = dyn_cast<TypeAliasDecl>(D)) {
334+
const auto *NTD = TD->getUnderlyingType()->getAnyNominal();
335+
if (NTD && Walker.PublicPrivateTypeAliases[NTD] == D)
336+
D = NTD;
337+
}
338+
326339
const NominalTypeDecl *OwningNominal = nullptr;
327340
if (const auto *ThisNominal = dyn_cast<NominalTypeDecl>(D)) {
328341
OwningNominal = ThisNominal;
@@ -375,9 +388,11 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
375388
// that protocol would otherwise be hidden.
376389
if (auto *Nominal = Info.Ext->getExtendedNominal()) {
377390
if (dropSynthesizedMembers &&
378-
!isImplicitlyPrivate(
379-
Nominal, /*IgnoreContext =*/Nominal->getModuleContext() ==
380-
StdlibModule))
391+
!isImplicitlyPrivate(Nominal, /*IgnoreContext =*/
392+
[&](const Decl *P) {
393+
return Nominal->getModuleContext() ==
394+
StdlibModule;
395+
}))
381396
continue;
382397
} else if (dropSynthesizedMembers) {
383398
continue;
@@ -392,10 +407,12 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
392407
// There can be synthesized members on effectively private
393408
// protocols or things that conform to them. We don't want to
394409
// include those.
395-
if (isImplicitlyPrivate(SynthMember,
396-
/*IgnoreContext =*/
397-
SynthMember->getModuleContext() ==
398-
StdlibModule)) {
410+
if (isImplicitlyPrivate(
411+
SynthMember,
412+
/*IgnoreContext =*/
413+
[&](const Decl *P) {
414+
return SynthMember->getModuleContext() == StdlibModule;
415+
})) {
399416
continue;
400417
}
401418

@@ -404,22 +421,29 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
404421
continue;
405422
}
406423

407-
Symbol Source(this, SynthMember, OwningNominal);
424+
const ValueDecl *BaseDecl = OwningNominal;
425+
if (Walker.PublicPrivateTypeAliases.contains(BaseDecl))
426+
BaseDecl = Walker.PublicPrivateTypeAliases[BaseDecl];
427+
428+
Symbol Source(this, SynthMember, BaseDecl);
408429

409430
if (auto *InheritedDecl = Source.getInheritedDecl()) {
410431
if (auto *ParentDecl =
411432
InheritedDecl->getDeclContext()->getAsDecl()) {
412433
if (dropSynthesizedMembers &&
413434
!isImplicitlyPrivate(
414435
ParentDecl,
415-
/*IgnoreContext =*/ParentDecl->getModuleContext() ==
416-
StdlibModule)) {
436+
/*IgnoreContext =*/
437+
[&](const Decl *P) {
438+
return ParentDecl->getModuleContext() ==
439+
StdlibModule;
440+
})) {
417441
continue;
418442
}
419443
}
420444
}
421445

422-
auto ExtendedSG = Walker.getModuleSymbolGraph(OwningNominal);
446+
auto ExtendedSG = Walker.getModuleSymbolGraph(BaseDecl);
423447

424448
ExtendedSG->Nodes.insert(Source);
425449

@@ -432,7 +456,15 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
432456

433457
void
434458
SymbolGraph::recordInheritanceRelationships(Symbol S) {
435-
const auto D = S.getLocalSymbolDecl();
459+
const auto *D = S.getLocalSymbolDecl();
460+
461+
// If this is a public type alias for a private symbol, gather inheritance
462+
// for the underlying type instead.
463+
if (const auto *TD = dyn_cast<TypeAliasDecl>(D)) {
464+
const auto *NTD = TD->getUnderlyingType()->getAnyNominal();
465+
if (NTD && Walker.PublicPrivateTypeAliases[NTD] == D)
466+
D = NTD;
467+
}
436468

437469
ClassDecl *Super = nullptr;
438470
if (auto *CD = dyn_cast<ClassDecl>(D))
@@ -441,8 +473,7 @@ SymbolGraph::recordInheritanceRelationships(Symbol S) {
441473
Super = PD->getSuperclassDecl();
442474

443475
if (Super) {
444-
recordEdge(Symbol(this, cast<ValueDecl>(D), nullptr),
445-
Symbol(this, Super, nullptr),
476+
recordEdge(S, Symbol(this, Super, nullptr),
446477
RelationshipKind::InheritsFrom());
447478
}
448479
}
@@ -521,7 +552,16 @@ void SymbolGraph::recordOptionalRequirementRelationships(Symbol S) {
521552
}
522553

523554
void SymbolGraph::recordConformanceRelationships(Symbol S) {
524-
const auto D = S.getLocalSymbolDecl();
555+
const auto *D = S.getLocalSymbolDecl();
556+
557+
// If this is a public type alias for a private symbol, gather conformances
558+
// for the underlying type instead.
559+
if (const auto *TD = dyn_cast<TypeAliasDecl>(D)) {
560+
const auto *NTD = TD->getUnderlyingType()->getAnyNominal();
561+
if (NTD && Walker.PublicPrivateTypeAliases[NTD] == D)
562+
D = NTD;
563+
}
564+
525565
if (const auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
526566
if (auto *PD = dyn_cast<ProtocolDecl>(NTD)) {
527567
for (auto *inherited : PD->getAllInheritedProtocols()) {
@@ -699,8 +739,8 @@ const ValueDecl *getProtocolRequirement(const ValueDecl *VD) {
699739

700740
}
701741

702-
bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
703-
bool IgnoreContext) const {
742+
bool SymbolGraph::isImplicitlyPrivate(
743+
const Decl *D, llvm::function_ref<bool(const Decl *)> IgnoreContext) const {
704744
// Don't record unconditionally private declarations
705745
if (D->isPrivateSystemDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) {
706746
return true;
@@ -808,16 +848,15 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
808848
if (IsGlobalSIMDType) {
809849
return true;
810850
}
811-
812-
if (IgnoreContext) {
813-
return false;
814-
}
815851
}
816852

817853
// Check up the parent chain. Anything inside a privately named
818854
// thing is also private. We could be looking at the `B` of `_A.B`.
819855
if (const auto *DC = D->getDeclContext()) {
820856
if (const auto *Parent = DC->getAsDecl()) {
857+
if (IgnoreContext && IgnoreContext(Parent))
858+
return false;
859+
821860
// Exception: Children of underscored protocols should be considered
822861
// public, even though the protocols themselves aren't. This way,
823862
// synthesized copies of those symbols are correctly added to the public
@@ -850,7 +889,11 @@ bool SymbolGraph::isUnconditionallyUnavailableOnAllPlatforms(const Decl *D) cons
850889
}
851890

852891
/// Returns `true` if the symbol should be included as a node in the graph.
853-
bool SymbolGraph::canIncludeDeclAsNode(const Decl *D) const {
892+
bool SymbolGraph::canIncludeDeclAsNode(const Decl *D,
893+
const Decl *PublicAncestorDecl) const {
894+
if (PublicAncestorDecl && D == PublicAncestorDecl)
895+
return true;
896+
854897
// If this decl isn't in this module or module that this module imported with `@_exported`, don't record it,
855898
// as it will appear elsewhere in its module's symbol graph.
856899
if (D->getModuleContext()->getName() != M.getName() && !Walker.isConsideredExportedImported(D)) {
@@ -864,6 +907,8 @@ bool SymbolGraph::canIncludeDeclAsNode(const Decl *D) const {
864907
} else {
865908
return false;
866909
}
867-
return !isImplicitlyPrivate(cast<ValueDecl>(D))
868-
&& !isUnconditionallyUnavailableOnAllPlatforms(cast<ValueDecl>(D));
910+
return !isImplicitlyPrivate(
911+
cast<ValueDecl>(D), /*IgnoreContext=*/
912+
[&](const Decl *P) { return P == PublicAncestorDecl; }) &&
913+
!isUnconditionallyUnavailableOnAllPlatforms(cast<ValueDecl>(D));
869914
}

lib/SymbolGraphGen/SymbolGraph.h

+11-5
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,12 @@ struct SymbolGraph {
220220
/// implicitly internal/private, such as underscore prefixes,
221221
/// and checking every named parent context as well.
222222
///
223-
/// \param IgnoreContext If `true`, don't consider
224-
/// the context of the declaration to determine whether it is implicitly private.
225-
bool isImplicitlyPrivate(const Decl *D,
226-
bool IgnoreContext = false) const;
223+
/// \param IgnoreContext A function ref that receives the parent decl
224+
/// and returns whether or not the context should be ignored when determining
225+
/// privacy.
226+
bool isImplicitlyPrivate(
227+
const Decl *D,
228+
llvm::function_ref<bool(const Decl *)> IgnoreContext = nullptr) const;
227229

228230
/// Returns `true` if the declaration has an availability attribute
229231
/// that marks it as unconditionally unavailable on all platforms (i.e., where
@@ -232,7 +234,11 @@ struct SymbolGraph {
232234

233235
/// Returns `true` if the declaration should be included as a node
234236
/// in the graph.
235-
bool canIncludeDeclAsNode(const Decl *D) const;
237+
///
238+
/// If `PublicAncestorDecl` is set and is an ancestor of `D`, that declaration
239+
/// is considered to be public, regardless of its surrounding context.
240+
bool canIncludeDeclAsNode(const Decl *D,
241+
const Decl *PublicAncestorDecl = nullptr) const;
236242

237243
/// Returns `true` if the declaration is a requirement of a protocol
238244
/// or is a default implementation of a protocol

0 commit comments

Comments
 (0)