Skip to content

Commit b95dd6c

Browse files
committed
Fix curation of children of single-language topics
Fixes an issue where multi-language children of single-language pages are considered as single-language as well, causing them to not be curated in the render node when manually curating them. This also introduces the `DocumentationContext.sourceLanguages(for:)` API and adds documentation indicating that it should be used to query the available languages of a node instead of checking the reference's source languages. rdar://91484878
1 parent adc8503 commit b95dd6c

File tree

12 files changed

+620
-24
lines changed

12 files changed

+620
-24
lines changed

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
614614
tutorialArticles: [SemanticResult<TutorialArticle>],
615615
bundle: DocumentationBundle) {
616616

617-
let sourceLanguages = soleRootModuleReference?.sourceLanguages ?? [.swift]
617+
let sourceLanguages = soleRootModuleReference.map { self.sourceLanguages(for: $0) } ?? [.swift]
618618

619619
// Technologies
620620

@@ -1729,7 +1729,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
17291729

17301730
// Articles are available in the same languages the only root module is available in. If there is more
17311731
// than one module, we cannot determine what languages it's available in and default to Swift.
1732-
availableSourceLanguages: soleRootModuleReference?.sourceLanguages,
1732+
availableSourceLanguages: soleRootModuleReference.map { sourceLanguages(for: $0) },
17331733
kind: .article,
17341734
in: bundle
17351735
) else {
@@ -2294,6 +2294,23 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
22942294

22952295
throw ContextError.notFound(reference.url)
22962296
}
2297+
2298+
/// Returns the set of languages the entity corresponding to the given reference is available in.
2299+
///
2300+
/// - Precondition: The entity associated with the given reference must be registered in the context.
2301+
public func sourceLanguages(for reference: ResolvedTopicReference) -> Set<SourceLanguage> {
2302+
do {
2303+
// Look up the entity without its fragment. The documentation context does not keep track of page sections
2304+
// as nodes, and page sections are considered to be available in the same languages as the page they're
2305+
// defined in.
2306+
let referenceWithoutFragment = reference.withFragment(nil)
2307+
return try entity(with: referenceWithoutFragment).availableSourceLanguages
2308+
} catch ContextError.notFound {
2309+
preconditionFailure("Reference does not have an associated documentation node.")
2310+
} catch {
2311+
fatalError("Unexpected error when retrieving source languages: \(error)")
2312+
}
2313+
}
22972314

22982315
// MARK: - Relationship queries
22992316

Sources/SwiftDocC/Infrastructure/Symbol Graph/GeneratedDocumentationTopics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ enum GeneratedDocumentationTopics {
7373
let automaticCurationSourceLanguage: SourceLanguage
7474
let automaticCurationSourceLanguages: Set<SourceLanguage>
7575
automaticCurationSourceLanguage = identifiers.first?.sourceLanguage ?? .swift
76-
automaticCurationSourceLanguages = Set(identifiers.flatMap(\.sourceLanguages))
76+
automaticCurationSourceLanguages = Set(identifiers.flatMap { identifier in context.sourceLanguages(for: identifier) })
7777

7878
// Create the collection topic reference
7979
let collectionReference = ResolvedTopicReference(
@@ -95,7 +95,7 @@ enum GeneratedDocumentationTopics {
9595
if let symbol = node.semantic as? Symbol {
9696
for trait in node.availableVariantTraits {
9797
guard let language = trait.interfaceLanguage,
98-
collectionReference.sourceLanguages.lazy.map(\.id).contains(language)
98+
automaticCurationSourceLanguages.lazy.map(\.id).contains(language)
9999
else {
100100
// If the collection is not available in this trait, don't curate it in this symbol's variant.
101101
continue

Sources/SwiftDocC/Model/Identifier.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public struct ResolvedTopicReference: Hashable, Codable, Equatable, CustomString
130130
}
131131

132132
/// The source languages for which this topic is relevant.
133+
///
134+
/// > Important: The source languages associated with the reference may not be the same as the available source languages of its
135+
/// corresponding ``DocumentationNode``. If you need to query the source languages associated with a documentation node, use
136+
/// ``DocumentationContext/sourceLanguages(for:)`` instead.
133137
public var sourceLanguages: Set<SourceLanguage> {
134138
return _storage.sourceLanguages
135139
}

Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ public struct RenderNodeTranslator: SemanticVisitor {
896896
return true
897897
}
898898

899-
return reference.sourceLanguages
899+
return context.sourceLanguages(for: reference)
900900
.contains { sourceLanguage in
901901
allowedTraits.contains { trait in
902902
trait.interfaceLanguage == sourceLanguage.id

Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,7 @@ let expected = """
15031503
}
15041504

15051505
XCTAssertEqual(
1506-
context.soleRootModuleReference?.sourceLanguages,
1506+
context.soleRootModuleReference.map { context.sourceLanguages(for: $0) },
15071507
[.swift],
15081508
"Expected the module to have language 'Swift' since it has 0 symbols."
15091509
)

Tests/SwiftDocCTests/Model/SemaToRenderNodeMultiLanguageTests.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,35 @@ class SemaToRenderNodeMixedLanguageTests: XCTestCase {
737737
)
738738
}
739739

740+
func testMultiLanguageChildOfSingleParentSymbolIsCuratedInMultiLanguage() throws {
741+
throw XCTSkip("Skipped due to flakiness in generating disambiguation prefixes due to rdar://91505520")
742+
743+
// let outputConsumer = try mixedLanguageFrameworkConsumer(
744+
// bundleName: "MixedLanguageFrameworkSingleLanguageParent"
745+
// )
746+
//
747+
// let topLevelFrameworkPage = try outputConsumer.renderNode(withTitle: "MixedLanguageFramework")
748+
//
749+
// XCTAssertEqual(
750+
// topLevelFrameworkPage.topicSections.flatMap(\.identifiers),
751+
// [
752+
// "doc://org.swift.MixedLanguageFramework/documentation/MixedLanguageFramework/MyError-swift.struct/Code-swift.enum",
753+
// "doc://org.swift.MixedLanguageFramework/documentation/MixedLanguageFramework/MyError-swift.struct",
754+
// "doc://org.swift.MixedLanguageFramework/documentation/MixedLanguageFramework/MyErrorDomain",
755+
// ]
756+
// )
757+
//
758+
// let objectiveCTopLevelFrameworkPage = try renderNodeApplyingObjectiveCVariantOverrides(to: topLevelFrameworkPage)
759+
//
760+
// XCTAssertEqual(
761+
// objectiveCTopLevelFrameworkPage.topicSections.flatMap(\.identifiers),
762+
// [
763+
// "doc://org.swift.MixedLanguageFramework/documentation/MixedLanguageFramework/MyError-swift.struct/Code-swift.enum",
764+
// "doc://org.swift.MixedLanguageFramework/documentation/MixedLanguageFramework/MyErrorDomain",
765+
// ]
766+
// )
767+
}
768+
740769
func assertExpectedContent(
741770
_ renderNode: RenderNode,
742771
sourceLanguage expectedSourceLanguage: String,
@@ -933,10 +962,11 @@ extension TestRenderNodeOutputConsumer {
933962

934963
fileprivate extension SemaToRenderNodeMixedLanguageTests {
935964
func mixedLanguageFrameworkConsumer(
965+
bundleName: String = "MixedLanguageFramework",
936966
configureBundle: ((URL) throws -> Void)? = nil
937967
) throws -> TestRenderNodeOutputConsumer {
938968
let (bundleURL, _, context) = try testBundleAndContext(
939-
copying: "MixedLanguageFramework",
969+
copying: bundleName,
940970
configureBundle: configureBundle
941971
)
942972

Tests/SwiftDocCTests/Rendering/RenderNodeTranslatorSymbolVariantsTests.swift

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,23 +1153,7 @@ class RenderNodeTranslatorSymbolVariantsTests: XCTestCase {
11531153
sourceLanguage: .swift
11541154
)
11551155

1156-
let newReference = ResolvedTopicReference(
1157-
bundleIdentifier: bundleIdentifier,
1158-
path: symbolPath,
1159-
sourceLanguages: [.swift, .objectiveC]
1160-
)
1161-
1162-
let node = try XCTUnwrap(context.topicGraph.nodes[newReference])
1163-
1164-
let newNode = TopicGraph.Node(
1165-
reference: newReference,
1166-
kind: node.kind,
1167-
source: node.source,
1168-
title: node.title
1169-
)
1170-
1171-
context.topicGraph.nodes[reference] = nil
1172-
context.topicGraph.nodes[newReference] = newNode
1156+
context.documentationCache[reference]?.availableSourceLanguages = [.swift, .objectiveC]
11731157
}
11741158
}
11751159

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleName</key>
6+
<string>MixedLanguageFramework</string>
7+
<key>CFBundleDisplayName</key>
8+
<string>MixedLanguageFramework</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>org.swift.MixedLanguageFramework</string>
11+
<key>CFBundleVersion</key>
12+
<string>0.1.0</string>
13+
</dict>
14+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# ``MixedLanguageFramework``
2+
3+
## Topics
4+
5+
### Errors
6+
7+
- ``MyError-swift.struct/Code-swift.enum``
8+
9+
<!-- Copyright (c) 2022 Apple Inc and the Swift Project authors. All Rights Reserved. -->
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2022 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
// This is the header corresponding to the symbol graph files
12+
// generated in this catalog.
13+
14+
#import <Foundation/Foundation.h>
15+
16+
const NSErrorDomain MyErrorDomain;
17+
18+
// This generates a Swift-only struct for MyError which automatically
19+
// curates a multi-language enumeration for `MyError.Code` / `MyError`.
20+
21+
typedef NS_ERROR_ENUM(MyErrorDomain, MyError) {
22+
MyErrorUnknown = 1,
23+
};
24+
25+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"metadata": {
3+
"formatVersion": {
4+
"major": 0,
5+
"minor": 5,
6+
"patch": 0
7+
},
8+
"generator": "SymbolKit"
9+
},
10+
"module": {
11+
"name": "MixedLanguageFramework",
12+
"platform": {
13+
"architecture": "x86_64",
14+
"operatingSystem": {
15+
"minimumVersion": {
16+
"major": 12,
17+
"minor": 4,
18+
"patch": 0
19+
},
20+
"name": "macos"
21+
},
22+
"vendor": "apple"
23+
}
24+
},
25+
"relationships": [
26+
{
27+
"kind": "memberOf",
28+
"source": "c:@E@MyError@MyErrorUnknown",
29+
"target": "c:@E@MyError",
30+
"targetFallback": null
31+
}
32+
],
33+
"symbols": [
34+
{
35+
"accessLevel": "public",
36+
"identifier": {
37+
"interfaceLanguage": "occ",
38+
"precise": "c:@E@MyError"
39+
},
40+
"kind": {
41+
"displayName": "Enumeration",
42+
"identifier": "enum"
43+
},
44+
"location": {
45+
"position": {
46+
"character": 8,
47+
"line": 14
48+
},
49+
"uri": "MixedLanguageFramework.h"
50+
},
51+
"names": {
52+
"title": "MyError"
53+
},
54+
"pathComponents": [
55+
"MyError"
56+
]
57+
},
58+
{
59+
"accessLevel": "public",
60+
"identifier": {
61+
"interfaceLanguage": "occ",
62+
"precise": "c:@E@MyError@MyErrorUnknown"
63+
},
64+
"kind": {
65+
"displayName": "Enumeration Case",
66+
"identifier": "enum.case"
67+
},
68+
"names": {
69+
"title": "MyErrorUnknown"
70+
},
71+
"pathComponents": [
72+
"MyError",
73+
"MyErrorUnknown"
74+
]
75+
},
76+
{
77+
"accessLevel": "public",
78+
"identifier": {
79+
"interfaceLanguage": "occ",
80+
"precise": "c:@MyErrorDomain"
81+
},
82+
"kind": {
83+
"displayName": "Global Variable",
84+
"identifier": "var"
85+
},
86+
"names": {
87+
"title": "MyErrorDomain"
88+
},
89+
"pathComponents": [
90+
"MyErrorDomain"
91+
]
92+
}
93+
]
94+
}

0 commit comments

Comments
 (0)