Skip to content

Commit f5c531c

Browse files
only recurse public-private type aliases from Clang (#79996)
rdar://145980187
1 parent 96f073d commit f5c531c

File tree

3 files changed

+44
-17
lines changed

3 files changed

+44
-17
lines changed

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,28 @@ bool SymbolGraphASTWalker::walkToDeclPre(Decl *D, CharSourceRange Range) {
335335
}
336336
}
337337

338+
// If this is a Clang typedef of an underlying type that is being hidden (e.g. `typedef struct
339+
// _MyStruct { ... } MyStruct`) then copy in the child symbols from the underlying type to the
340+
// type alias.
338341
if (const auto *TD = dyn_cast_or_null<TypeAliasDecl>(VD)) {
339342
const auto InnerType = TD->getUnderlyingType();
340343
if (NominalTypeDecl *NTD = InnerType->getAnyNominal()) {
341344
// Only fold typedefs together if the inner type is from our module and it
342345
// otherwise isn't being shown
343346
if (isOurModule(NTD->getModuleContext()) &&
344347
!SG->canIncludeDeclAsNode(NTD)) {
345-
PublicPrivateTypeAliases.insert_or_assign(NTD, TD);
346-
synthesizeChildSymbols(NTD, TD);
348+
// We specifically only want to look for underlying types that are "embedded" in the typedef
349+
// definition, so let's pull out the Clang decl and check for that
350+
if (NTD->hasClangNode()) {
351+
if (const auto *ClangDecl = NTD->getClangNode().getAsDecl()) {
352+
if (const auto *ClangTagDecl = dyn_cast<clang::TagDecl>(ClangDecl)) {
353+
if (ClangTagDecl->isEmbeddedInDeclarator()) {
354+
PublicPrivateTypeAliases.insert_or_assign(NTD, TD);
355+
synthesizeChildSymbols(NTD, TD);
356+
}
357+
}
358+
}
359+
}
347360
}
348361
}
349362
}

test/SymbolGraph/Relationships/Synthesized/HiddenTypeAlias.swift

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,7 @@
1010
// _InnerType's type name should only appear in quotes like this once, in the declaration for OuterType
1111
// INNER-COUNT-1: "_InnerType"
1212

13-
// _InnerType.someFunc() as synthesized on OuterType
14-
// CHECK-DAG: "precise": "s:15HiddenTypeAlias06_InnerB0C8someFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a"
15-
16-
// someFunc is a member of OuterType
17-
// CHECK-DAG: "kind": "memberOf",{{[[:space:]]*}}"source": "s:15HiddenTypeAlias06_InnerB0C8someFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a",{{[[:space:]]*}}"target": "s:15HiddenTypeAlias05OuterB0a"
18-
19-
// OuterType conforms to SomeProtocol
20-
// CHECK-DAG: "kind": "conformsTo",{{[[:space:]]*}}"source": "s:15HiddenTypeAlias05OuterB0a",{{[[:space:]]*}}"target": "s:15HiddenTypeAlias12SomeProtocolP"
21-
22-
// OuterType "inherits from" BaseType
23-
// CHECK-DAG: "kind": "inheritsFrom",{{[[:space:]]*}}"source": "s:15HiddenTypeAlias05OuterB0a",{{[[:space:]]*}}"target": "s:15HiddenTypeAlias04BaseB0C"
24-
25-
// bonusFunc as a synthesized member of OuterType
26-
// CHECK-DAG: "precise": "s:15HiddenTypeAlias12SomeProtocolPAAE9bonusFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a",
27-
// CHECK-DAG: "kind": "memberOf",{{[[:space:]]*}}"source": "s:15HiddenTypeAlias12SomeProtocolPAAE9bonusFuncyyF::SYNTHESIZED::s:15HiddenTypeAlias05OuterB0a",{{[[:space:]]*}}"target": "s:15HiddenTypeAlias05OuterB0a",
13+
// CHECK-NOT: someFunc
2814

2915
public protocol SomeProtocol {}
3016

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name HiddenTypeAliasRecursive -emit-module -emit-module-path %t/
3+
// RUN: %target-swift-symbolgraph-extract -module-name HiddenTypeAliasRecursive -I %t -pretty-print -output-dir %t -v
4+
// RUN: %FileCheck %s --input-file %t/HiddenTypeAliasRecursive.symbols.json
5+
6+
// Ensure that mutually-recursive public-private type aliases don't cause infinite recursion in
7+
// SymbolGraphGen when generating symbol graphs. (rdar://145980187)
8+
9+
// HiddenClassB is not referenced by any public symbol, so it shouldn't appear in any symbol graph
10+
// CHECK-NOT: HiddenClassB
11+
12+
// HiddenClassA should only appear one time: in the declaration for PublicAlias
13+
// CHECK-COUNT-1: HiddenClassA
14+
// CHECK-NOT: HiddenClassA
15+
16+
@_documentation(visibility: private)
17+
public class HiddenClassA {
18+
public typealias ProblematicA = HiddenClassB
19+
public func funcA() {}
20+
}
21+
22+
@_documentation(visibility: private)
23+
public class HiddenClassB {
24+
public typealias ProblematicB = HiddenClassA
25+
public func funcB() {}
26+
}
27+
28+
public typealias PublicAlias = HiddenClassA

0 commit comments

Comments
 (0)