Skip to content

Commit 28c5c4f

Browse files
synthesize child symbols for type aliases of private decls
rdar://141460819
1 parent c347b66 commit 28c5c4f

File tree

7 files changed

+145
-49
lines changed

7 files changed

+145
-49
lines changed

lib/SymbolGraphGen/Symbol.cpp

Lines changed: 7 additions & 5 deletions
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

Lines changed: 17 additions & 16 deletions
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

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,22 @@ void SymbolGraph::recordMemberRelationship(Symbol S) {
268268
}
269269
}
270270

271-
return recordEdge(S,
272-
Symbol(this, DC->getSelfNominalTypeDecl(), nullptr),
273-
RelationshipKind::MemberOf());
271+
if (Walker.PublicPrivateTypeAliases.contains(
272+
DC->getSelfNominalTypeDecl())) {
273+
// If our member target is a private type that has a public type alias,
274+
// point the membership to that type alias instead.
275+
return recordEdge(
276+
S,
277+
Symbol(
278+
this,
279+
Walker.PublicPrivateTypeAliases[DC->getSelfNominalTypeDecl()],
280+
nullptr),
281+
RelationshipKind::MemberOf());
282+
} else {
283+
return recordEdge(S,
284+
Symbol(this, DC->getSelfNominalTypeDecl(), nullptr),
285+
RelationshipKind::MemberOf());
286+
}
274287
case swift::DeclContextKind::AbstractClosureExpr:
275288
case swift::DeclContextKind::SerializedAbstractClosure:
276289
case swift::DeclContextKind::Initializer:
@@ -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

@@ -412,8 +429,11 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
412429
if (dropSynthesizedMembers &&
413430
!isImplicitlyPrivate(
414431
ParentDecl,
415-
/*IgnoreContext =*/ParentDecl->getModuleContext() ==
416-
StdlibModule)) {
432+
/*IgnoreContext =*/
433+
[&](const Decl *P) {
434+
return ParentDecl->getModuleContext() ==
435+
StdlibModule;
436+
})) {
417437
continue;
418438
}
419439
}
@@ -699,8 +719,8 @@ const ValueDecl *getProtocolRequirement(const ValueDecl *VD) {
699719

700720
}
701721

702-
bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
703-
bool IgnoreContext) const {
722+
bool SymbolGraph::isImplicitlyPrivate(
723+
const Decl *D, llvm::function_ref<bool(const Decl *)> IgnoreContext) const {
704724
// Don't record unconditionally private declarations
705725
if (D->isPrivateSystemDecl(/*treatNonBuiltinProtocolsAsPublic=*/false)) {
706726
return true;
@@ -808,16 +828,15 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
808828
if (IsGlobalSIMDType) {
809829
return true;
810830
}
811-
812-
if (IgnoreContext) {
813-
return false;
814-
}
815831
}
816832

817833
// Check up the parent chain. Anything inside a privately named
818834
// thing is also private. We could be looking at the `B` of `_A.B`.
819835
if (const auto *DC = D->getDeclContext()) {
820836
if (const auto *Parent = DC->getAsDecl()) {
837+
if (IgnoreContext && IgnoreContext(Parent))
838+
return false;
839+
821840
// Exception: Children of underscored protocols should be considered
822841
// public, even though the protocols themselves aren't. This way,
823842
// synthesized copies of those symbols are correctly added to the public
@@ -852,7 +871,11 @@ bool SymbolGraph::isUnconditionallyUnavailableOnAllPlatforms(const Decl *D) cons
852871
}
853872

854873
/// Returns `true` if the symbol should be included as a node in the graph.
855-
bool SymbolGraph::canIncludeDeclAsNode(const Decl *D) const {
874+
bool SymbolGraph::canIncludeDeclAsNode(const Decl *D,
875+
const Decl *PublicAncestorDecl) const {
876+
if (PublicAncestorDecl && D == PublicAncestorDecl)
877+
return true;
878+
856879
// If this decl isn't in this module or module that this module imported with `@_exported`, don't record it,
857880
// as it will appear elsewhere in its module's symbol graph.
858881
if (D->getModuleContext()->getName() != M.getName() && !Walker.isConsideredExportedImported(D)) {
@@ -866,6 +889,8 @@ bool SymbolGraph::canIncludeDeclAsNode(const Decl *D) const {
866889
} else {
867890
return false;
868891
}
869-
return !isImplicitlyPrivate(cast<ValueDecl>(D))
870-
&& !isUnconditionallyUnavailableOnAllPlatforms(cast<ValueDecl>(D));
892+
return !isImplicitlyPrivate(
893+
cast<ValueDecl>(D), /*IgnoreContext=*/
894+
[&](const Decl *P) { return P == PublicAncestorDecl; }) &&
895+
!isUnconditionallyUnavailableOnAllPlatforms(cast<ValueDecl>(D));
871896
}

lib/SymbolGraphGen/SymbolGraph.h

Lines changed: 11 additions & 5 deletions
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

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ static bool isUnavailableOrObsoletedOnPlatform(const Decl *D) {
119119
}
120120

121121
bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
122+
if (SynthesizedChildrenBaseDecl && D == SynthesizedChildrenBaseDecl)
123+
return true;
124+
122125
if (isUnavailableOrObsoletedOnPlatform(D)) {
123126
return false;
124127
}
@@ -239,7 +242,7 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
239242

240243
auto *VD = cast<ValueDecl>(D);
241244

242-
if (!SG->canIncludeDeclAsNode(VD)) {
245+
if (!BaseDecl && !SG->canIncludeDeclAsNode(VD)) {
243246
return false;
244247
}
245248

@@ -270,8 +273,21 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
270273
}
271274
}
272275

276+
if (const auto *TD = dyn_cast_or_null<TypeAliasDecl>(VD)) {
277+
const auto InnerType = TD->getUnderlyingType();
278+
if (NominalTypeDecl *NTD = InnerType->getAnyNominal()) {
279+
// Only fold typedefs together if the inner type is from our module and it
280+
// otherwise isn't being shown
281+
if (isOurModule(NTD->getModuleContext()) &&
282+
!SG->canIncludeDeclAsNode(NTD)) {
283+
PublicPrivateTypeAliases[NTD] = TD;
284+
synthesizeChildSymbols(NTD, TD);
285+
}
286+
}
287+
}
288+
273289
// Otherwise, record this in the main module `M`'s symbol graph.
274-
SG->recordNode(Symbol(SG, VD, nullptr));
290+
SG->recordNode(Symbol(SG, VD, BaseDecl));
275291

276292
return true;
277293
}
@@ -340,3 +356,15 @@ bool SymbolGraphASTWalker::shouldBeRecordedAsExtension(
340356
!isExportedImportedModule(
341357
ED->getExtendedNominal()->getModuleContext());
342358
}
359+
360+
bool SymbolGraphASTWalker::synthesizeChildSymbols(Decl *D,
361+
const ValueDecl *BD) {
362+
BaseDecl = BD;
363+
SynthesizedChildrenBaseDecl = D;
364+
SWIFT_DEFER {
365+
BaseDecl = nullptr;
366+
SynthesizedChildrenBaseDecl = nullptr;
367+
};
368+
369+
return walk(D);
370+
}

lib/SymbolGraphGen/SymbolGraphASTWalker.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
5959
/// A map of modules whose types were extended by the main module of interest `M`.
6060
llvm::StringMap<SymbolGraph *> ExtendedModuleGraphs;
6161

62+
/// A temporary pointer to a base decl when crawling symbols to synthesize.
63+
const ValueDecl *BaseDecl;
64+
65+
/// A temporary pointer to the top-level decl being crawled when synthesizing
66+
/// child symbols.
67+
const Decl *SynthesizedChildrenBaseDecl;
68+
69+
/// Maps any internal symbol with a public type alias of that symbol.
70+
llvm::DenseMap<const ValueDecl *, const ValueDecl *> PublicPrivateTypeAliases;
71+
6272
// MARK: - Initialization
6373

6474
SymbolGraphASTWalker(
@@ -103,6 +113,10 @@ struct SymbolGraphASTWalker : public SourceEntityWalker {
103113

104114
// MARK: - Utilities
105115

116+
/// Walk the given decl and add its children as synthesized children of the
117+
/// given base decl.
118+
bool synthesizeChildSymbols(Decl *D, const ValueDecl *BaseDecl);
119+
106120
/// Returns whether the given declaration was itself imported via an `@_exported import`
107121
/// statement, or if it is an extension or child symbol of something else that was.
108122
virtual bool isConsideredExportedImported(const Decl *D) const;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name HiddenTypeAlias -emit-module -emit-module-path %t/
3+
// RUN: %target-swift-symbolgraph-extract -module-name HiddenTypeAlias -I %t -pretty-print -output-dir %t
4+
// RUN: %FileCheck %s --input-file %t/HiddenTypeAlias.symbols.json
5+
6+
// Ensure that public type aliases of effectively-private symbols inherit the child symbols of the
7+
// inner type.
8+
9+
// CHECK-NOT: "OuterType._InnerType"
10+
// CHECK-DAG: "title": "OuterType"
11+
12+
// CHECK-DAG: "precise": "s:15HiddenTypeAlias06_InnerB0V8someFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a"
13+
14+
// CHECK-DAG: "kind": "memberOf",{{[[:space:]]*}}"source": "s:15HiddenTypeAlias06_InnerB0V8someFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a",{{[[:space:]]*}}"target": "s:15HiddenTypeAlias05OuterB0a"
15+
16+
public struct _InnerType {
17+
public func someFunc() {}
18+
}
19+
20+
public typealias OuterType = _InnerType

0 commit comments

Comments
 (0)