Skip to content

Commit 081b05a

Browse files
author
Harlan Haskins
committed
[api-digester] Don’t synthesize type nodes with no public members
Upon seeing an extension for a type outside the current module, the digester creates a dummy type node and puts all the extensions’ members and conformances in that type. This allows us to track new API endpoints that are retroactively added. Unfortunately, if there are no public members/conformances (only internal or private ones), the type itself ends up in the SDK dump without any public children. This causes an issue when you dump the SDK from a parseable interface, where the internal extension was not printed.
1 parent 92416e7 commit 081b05a

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Func _SmallString.computeIsASCII() has been removed
22
Func _SmallString.withMutableCapacity(_:) has been removed
33
Struct _StringObject.Discriminator has been removed
4+
Struct __swift_stdlib_UErrorCode has been removed
45
Var _SmallString.discriminator has been removed
56
Var _StringObject.CountAndFlags._storage has declared type change from UInt to UInt64
67
Var _StringObject.CountAndFlags.countMask has declared type change from UInt to UInt64
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %empty-directory(%t.mod)
2+
// RUN: %empty-directory(%t.sdk)
3+
// RUN: %empty-directory(%t.module-cache)
4+
// RUN: %swift -emit-module -o %t.mod/cake.swiftmodule %S/Inputs/cake.swift -parse-as-library -I %S/Inputs/ClangCake %clang-importer-sdk-nosource
5+
// RUN: %swift -emit-module -o %t.mod/main.swiftmodule %s -parse-as-library -I %t.mod -I %S/Inputs/ClangCake %clang-importer-sdk-nosource
6+
// RUN: %api-digester -dump-sdk -module main -o %t.dump.json -module-cache-path %t.module-cache -swift-only -I %t.mod -I %S/Inputs/ClangCake %clang-importer-sdk-nosource
7+
// RUN: %FileCheck %s < %t.dump.json
8+
9+
import cake
10+
11+
// CHECK: publicSymbol
12+
public func publicSymbol() {}
13+
14+
internal protocol InternalProto {}
15+
public protocol PublicProto {}
16+
17+
// This internal extension doesn't declare any new members on S1, so it
18+
// shouldn't show up at all in the output.
19+
// CHECK-NOT: S1
20+
internal extension S1 {
21+
var x: Int { return 4 }
22+
}
23+
24+
// There should only be one conformance declared: the conformance of C0 to
25+
// PublicProto. InternalProto should not show up in the list.
26+
// CHECK: "name": "C0",
27+
// CHECK: "conformances": [
28+
// CHECK-NEXT: {
29+
// CHECK-NEXT: "kind": "Conformance",
30+
// CHECK-NEXT: "name": "PublicProto",
31+
// CHECK-NEXT: "printedName": "PublicProto"
32+
// CHECK-NEXT: }
33+
// CHECK-NEXT: ]
34+
extension C0: InternalProto {
35+
}
36+
extension C0: PublicProto {
37+
}

tools/swift-api-digester/ModuleAnalyzerNodes.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,11 +1368,31 @@ SwiftDeclCollector::constructExternalExtensionNode(NominalTypeDecl *NTD,
13681368
ArrayRef<ExtensionDecl*> AllExts) {
13691369
auto *TypeNode = SDKNodeInitInfo(Ctx, NTD).createSDKNode(SDKNodeKind::DeclType);
13701370
addConformancesToTypeDecl(cast<SDKNodeDeclType>(TypeNode), NTD);
1371+
1372+
bool anyConformancesAdded = false;
13711373
// The members of the extensions are the only members of this synthesized type.
13721374
for (auto *Ext: AllExts) {
13731375
HandledExtensions.insert(Ext);
13741376
addMembersToRoot(TypeNode, Ext);
1377+
1378+
// Keep track if we've declared any conformances in this extension.
1379+
// FIXME: This is too conservative. We only _really_ care if this extension
1380+
// declares a conformance to any public protocols outside the module
1381+
// where the extended type originated. Eventually this should be
1382+
// updated to filter extensions that declare conformances to internal
1383+
// protocols that either don't inherit from any protocols or only
1384+
// inherit from other internal protocols. It should also consider
1385+
// conditional conformances with internal requirements that are still
1386+
// part of the ABI.
1387+
if (!Ext->getInherited().empty())
1388+
anyConformancesAdded = true;
13751389
}
1390+
1391+
// If none of the extensions added any public members or conformances, don't
1392+
// synthesize the type node.
1393+
if (TypeNode->getChildrenCount() == 0 && !anyConformancesAdded)
1394+
return nullptr;
1395+
13761396
return TypeNode;
13771397
}
13781398

@@ -1532,7 +1552,8 @@ void SwiftDeclCollector::lookupVisibleDecls(ArrayRef<ModuleDecl *> Modules) {
15321552
}
15331553
}
15341554
for (auto Pair: ExtensionMap) {
1535-
RootNode->addChild(constructExternalExtensionNode(Pair.first, Pair.second));
1555+
if (auto child = constructExternalExtensionNode(Pair.first, Pair.second))
1556+
RootNode->addChild(child);
15361557
}
15371558
}
15381559

tools/swift-api-digester/ModuleAnalyzerNodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer {
648648

649649
void printTopLevelNames();
650650

651+
/// Adds all conformances from the provided NominalTypeDecl to the provided
652+
/// SDK node for that type decl.
651653
void addConformancesToTypeDecl(SDKNodeDeclType *Root, NominalTypeDecl* NTD);
652654
void addMembersToRoot(SDKNode *Root, IterableDeclContext *Context);
653655

0 commit comments

Comments
 (0)