Skip to content

Commit 26ac575

Browse files
authored
Make --include-extended-types default for Swift 5.9 (#57)
* make --include-extended-types default for Swift 5.9 and introduce --exclude-extended-types flag * address review comments - mention default behavior earlier in docs - be more precise about method/function terminology in docs
1 parent 106fd09 commit 26ac575

File tree

12 files changed

+251
-59
lines changed

12 files changed

+251
-59
lines changed

IntegrationTests/Tests/TargetWithSwiftExtensionsTests.swift

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,31 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {
2525
try super.setUpWithError()
2626
}
2727

28-
func testGenerateDocumentationWithoutEnablementFlag() throws {
28+
func testGenerateDocumentationWithoutExtendedTypesFlag() throws {
2929
let result = try swiftPackage(
3030
"generate-documentation",
3131
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
3232
)
3333

34-
result.assertExitStatusEquals(0)
35-
XCTAssertEqual(result.referencedDocCArchives.count, 1)
34+
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)
3635

37-
let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first)
36+
#if swift(>=5.9)
37+
try assertDirectoryContentsWithExtendedTypes(dataDirectoryContents)
38+
#else
39+
try assertDirectoryContentsWithoutExtendedTypes(dataDirectoryContents)
40+
#endif
41+
}
42+
43+
func testGenerateDocumentationWithDisablementFlag() throws {
44+
let result = try swiftPackage(
45+
"generate-documentation",
46+
"--exclude-extended-types",
47+
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
48+
)
3849

39-
let dataDirectoryContents = try filesIn(.dataSubdirectory, of: doccArchiveURL)
50+
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)
4051

41-
XCTAssertEqual(
42-
Set(dataDirectoryContents.map(\.lastTwoPathComponents)),
43-
[
44-
"documentation/library.json",
45-
46-
"library/foo.json",
47-
"foo/foo().json",
48-
49-
"library/customfooconvertible.json",
50-
"customfooconvertible/asfoo.json",
51-
]
52-
)
52+
try assertDirectoryContentsWithoutExtendedTypes(dataDirectoryContents)
5353
}
5454

5555
func testGenerateDocumentationWithEnablementFlag() throws {
@@ -59,15 +59,17 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {
5959
workingDirectory: try setupTemporaryDirectoryForFixture(named: "LibraryTargetWithExtensionSymbols")
6060
)
6161

62-
result.assertExitStatusEquals(0)
63-
XCTAssertEqual(result.referencedDocCArchives.count, 1)
64-
65-
let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first)
66-
67-
let dataDirectoryContents = try filesIn(.dataSubdirectory, of: doccArchiveURL)
62+
let dataDirectoryContents = try unwrapDataDirectoryContents(of: result)
6863

64+
try assertDirectoryContentsWithExtendedTypes(dataDirectoryContents)
65+
}
66+
67+
func assertDirectoryContentsWithExtendedTypes(_ contents: [URL],
68+
_ message: @autoclosure () -> String = "",
69+
file: StaticString = #filePath,
70+
line: UInt = #line) throws {
6971
XCTAssertEqual(
70-
Set(dataDirectoryContents.map(\.lastTwoPathComponents)),
72+
Set(contents.map(\.lastTwoPathComponents)),
7173
[
7274
"documentation/library.json",
7375
"library/swift.json",
@@ -85,7 +87,43 @@ final class TargetWithSwiftExtensionsTests: ConcurrencyRequiringTestCase {
8587

8688
"library/customfooconvertible.json",
8789
"customfooconvertible/asfoo.json",
88-
]
90+
],
91+
message(),
92+
file: file,
93+
line: line
8994
)
9095
}
96+
97+
func assertDirectoryContentsWithoutExtendedTypes(_ contents: [URL],
98+
_ message: @autoclosure () -> String = "",
99+
file: StaticString = #filePath,
100+
line: UInt = #line) throws {
101+
XCTAssertEqual(
102+
Set(contents.map(\.lastTwoPathComponents)),
103+
[
104+
"documentation/library.json",
105+
106+
"library/foo.json",
107+
"foo/foo().json",
108+
109+
"library/customfooconvertible.json",
110+
"customfooconvertible/asfoo.json",
111+
],
112+
message(),
113+
file: file,
114+
line: line
115+
)
116+
}
117+
118+
func unwrapDataDirectoryContents(of result: SwiftInvocationResult,
119+
_ message: @autoclosure () -> String = "",
120+
file: StaticString = #filePath,
121+
line: UInt = #line) throws -> [URL] {
122+
result.assertExitStatusEquals(0)
123+
XCTAssertEqual(result.referencedDocCArchives.count, 1, message(), file: file, line: line)
124+
125+
let doccArchiveURL = try XCTUnwrap(result.referencedDocCArchives.first, message(), file: file, line: line)
126+
127+
return try filesIn(.dataSubdirectory, of: doccArchiveURL)
128+
}
91129
}

Plugins/SharedPackagePluginExtensions/PackageManager+getSymbolGraphsForDocC.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@ extension PackageManager {
4949
// Modify the symbol graph options with the custom ones
5050
for customSymbolGraphOption in customSymbolGraphOptions {
5151
switch customSymbolGraphOption {
52-
case .extendedTypes:
52+
case .extendedTypes.positive:
5353
#if swift(>=5.8)
5454
symbolGraphOptions.emitExtensionBlocks = true
5555
#else
5656
print("warning: detected '--include-extended-types' option, which is incompatible with your swift version (required: 5.8)")
57+
#endif
58+
case .extendedTypes.negative:
59+
#if swift(>=5.8)
60+
symbolGraphOptions.emitExtensionBlocks = false
61+
#else
62+
print("warning: detected '--exclude-extended-types' option, which is incompatible with your swift version (required: 5.8)")
5763
#endif
5864
case .skipSynthesizedSymbols:
5965
symbolGraphOptions.includeSynthesized = false

Plugins/SharedPackagePluginExtensions/Target+defaultSymbolGraphOptions.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ extension SwiftSourceModuleTarget {
2424
targetMinimumAccessLevel = .public
2525
}
2626

27+
#if swift(>=5.9)
28+
let emitExtensionBlockSymbolDefault = true
29+
#else
30+
let emitExtensionBlockSymbolDefault = false
31+
#endif
32+
2733
return PackageManager.SymbolGraphOptions(
2834
minimumAccessLevel: targetMinimumAccessLevel,
2935
includeSynthesized: true,
3036
includeSPI: false,
31-
emitExtensionBlocks: false
37+
emitExtensionBlocks: emitExtensionBlockSymbolDefault
3238
)
3339
}
3440
}

Sources/SwiftDocCPluginDocumentation/SwiftDocCPlugin.docc/Generating Documentation for Extended Types.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,36 @@ Generate documentation for the extensions you make to types from other modules.
44

55
## Overview
66

7-
By default, the Swift-DocC plugin ignores extensions you make to types that are not from the module you're generating documentation for.
7+
The Swift-DocC plugin allows you to document extensions you make to types that are not from the module you're generating documentation for.
88

9-
To include documentation for extended types, add the `--include-extended-types` flag to your invocations:
9+
As of Swift 5.9, extension support is enabled by default. If you're using an older version of Swift or would like to configure this behavior pass the `--include-extended-types` or `--exclude-extended-types` flag to enable or disable the feature, respectively:
1010

1111
$ swift package generate-documentation --include-extended-types
1212

13-
> Note: Extension support is available when using Swift 5.8 or later and the Swift-DocC plugin 1.2 or later.
13+
$ swift package generate-documentation --exclude-extended-types
1414

15-
## Understanding What is Included by Default
15+
> Note: Extension support is available when using Swift 5.8 or later and the Swift-DocC plugin 1.2 or later. Extension support is enabled by default starting with Swift 5.9 and the Swift-DocC plugin 1.3.
1616
17-
Not everything that is declared in an extension is hidden behind the `--include-extended-types` flag. If the extension is declared in the same target as the type it is extending, the extension's contents will be included in the documentation by default.
17+
## Understanding What is an Extended Type
18+
19+
Not every type you add an extension to is an extended type. If the extension is declared in the same target as the type it is extending, the extension's contents will always be included in the documentation. Only extensions you make to types from other targets are represented as an external type in your documentation archive.
1820

1921
```swift
2022
public struct Sloth { }
2123

2224
extension Sloth {
23-
// This function is included in the
24-
// documentation by default.
25+
// This method is always included
26+
// in the documentation.
2527
public func wake() { /* ... */ }
2628
}
2729

2830
// `Collection` is from the standard library,
2931
// not the `SlothCreator` library, so this is
3032
// what we call an "extended type".
3133
extension Collection where Element == Sloth {
32-
// This property is not included in
33-
// the documentation by default.
34+
// This property is only included in
35+
// the documentation if extension
36+
// support is enabled.
3437
public func wake() {
3538
for sloth in self {
3639
sloth.wake()

Sources/SwiftDocCPluginUtilities/HelpInformation.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,13 @@ public enum HelpInformation {
5656
#endif
5757

5858
for flag in supportedPluginFlags {
59+
var flagListText = flag.positive.parsedValues.sorted().joined(separator: ", ")
60+
if !flag.negative.parsedValues.isEmpty {
61+
flagListText += " / \(flag.negative.parsedValues.sorted().joined(separator: ", "))"
62+
}
63+
5964
helpText += """
60-
\(flag.parsedValues.sorted().joined(separator: ", "))
65+
\(flagListText)
6166
\(flag.abstract)
6267
\(flag.description)
6368

Sources/SwiftDocCPluginUtilities/ParsedArguments.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ public struct ParsedArguments {
156156

157157
let symbolGraphArguments = arguments.filter(for: .dumpSymbolGraph)
158158

159-
self.symbolGraphArguments = ParsedArguments.ArgumentConsumer.dumpSymbolGraph.flags.filter { option in
160-
option.parsedValues.contains(where: symbolGraphArguments.contains)
159+
self.symbolGraphArguments = ParsedArguments.ArgumentConsumer.dumpSymbolGraph.flags.compactMap {
160+
$0.value(for: symbolGraphArguments)
161161
}
162162
}
163163

Sources/SwiftDocCPluginUtilities/PluginFlags/ExtendedTypesFlag.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,30 @@
77
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
extension PluginFlag {
10-
/// Include extended types in documentation archives.
10+
/// Include or exclude extended types in documentation archives.
1111
///
12-
/// Enables the extension block symbol format when calling the
12+
/// Enables/disables the extension block symbol format when calling the
1313
/// dump symbol graph API.
1414
///
1515
/// - Note: This flag is only available starting from Swift 5.8. It should
1616
/// be hidden from the `--help` command for lower toolchain versions.
1717
/// However, we do not hide the flag entirely, because this enables us to give
1818
/// a more precise warning when accidentally used with Swift 5.7 or lower.
1919
static let extendedTypes = PluginFlag(
20-
parsedValues: [
20+
positiveValues: [
2121
"--include-extended-types",
2222
],
23-
abstract: "Include extended types from other modules in the produced DocC archive.",
24-
description: """
25-
Allows documenting symbols that a target adds to its dependencies.
26-
""",
23+
negativeValues: [
24+
"--exclude-extended-types",
25+
],
26+
abstract: "Control whether extended types from other modules are shown in the produced DocC archive. (default: \(Self.default))",
27+
description: "Allows documenting symbols that a target adds to its dependencies.",
2728
argumentTransformation: { $0 }
2829
)
30+
31+
#if swift(>=5.9)
32+
private static let `default` = "--include-extended-types"
33+
#else
34+
private static let `default` = "--exclude-extended-types"
35+
#endif
2936
}

Sources/SwiftDocCPluginUtilities/PluginFlags/PluginFlag+Equatable.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
extension PluginFlag: Equatable {
1010
static func ==(lhs: PluginFlag, rhs: PluginFlag) -> Bool {
11-
return lhs.parsedValues == rhs.parsedValues
11+
return lhs.positive.parsedValues == rhs.positive.parsedValues
12+
&& lhs.negative.parsedValues == rhs.negative.parsedValues
1213
}
1314
}

Sources/SwiftDocCPluginUtilities/PluginFlags/PluginFlag.swift

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ struct PluginFlag: ArgumentsTransforming {
1313
/// The string values that will be parsed when detecting this flag.
1414
///
1515
/// For example, this might be `["--disable-index"]`.
16-
let parsedValues: Set<String>
16+
var parsedValues: Set<String> {
17+
positiveValues.union(negativeValues)
18+
}
19+
20+
private let positiveValues: Set<String>
21+
22+
private let negativeValues: Set<String>
1723

1824
/// A short, user-facing description of this flag.
1925
let abstract: String
@@ -23,6 +29,41 @@ struct PluginFlag: ArgumentsTransforming {
2329

2430
let argumentTransformation: (Arguments) -> Arguments
2531

32+
/// A version of this flag that only parses its positive values.
33+
var positive: Self {
34+
.init(positiveValues: positiveValues,
35+
negativeValues: [],
36+
abstract: abstract,
37+
description: description,
38+
argumentTransformation: argumentTransformation)
39+
}
40+
41+
/// A version of this flag that only parses its negative values.
42+
var negative: Self {
43+
.init(positiveValues: [],
44+
negativeValues: negativeValues,
45+
abstract: abstract,
46+
description: description,
47+
argumentTransformation: argumentTransformation)
48+
}
49+
50+
/// Returns either the ``positive`` or ``negative`` version
51+
/// of this flag, depending on the last element in `arguments` that
52+
/// triggers this flag.
53+
func value(for arguments: Arguments) -> Self? {
54+
guard let last = arguments
55+
.filter({ parsedValues.contains($0) })
56+
.last else {
57+
return nil
58+
}
59+
60+
if positiveValues.contains(last) {
61+
return positive
62+
} else {
63+
return negative
64+
}
65+
}
66+
2667
/// Transforms the given set of arguments if they include any of this flag's
2768
/// parsed values.
2869
///
@@ -49,20 +90,44 @@ struct PluginFlag: ArgumentsTransforming {
4990
/// Create a new command-line flag.
5091
///
5192
/// - Parameters:
52-
/// - parsedValues: The string values that should be parsed to detect this flag.
93+
/// - positiveValues: The string values that should be parsed to enable this flag.
94+
/// - negativeValues: The string values that should be parsed to disable this flag.
5395
/// - abstract: The user-facing description of this flag.
5496
/// - description: An expanded, user-facing description of this flag.
5597
/// - argumentTransformation: A closure that can be applied to a given
5698
/// set of parsed arguments if the arguments include any of the this flag's parsed values.
5799
init(
58-
parsedValues: Set<String>,
100+
positiveValues: Set<String>,
101+
negativeValues: Set<String>,
59102
abstract: String,
60103
description: String,
61104
argumentTransformation: @escaping (Arguments) -> Arguments
62105
) {
63-
self.parsedValues = parsedValues
106+
self.positiveValues = positiveValues
107+
self.negativeValues = negativeValues
64108
self.abstract = abstract
65109
self.description = description
66110
self.argumentTransformation = argumentTransformation
67111
}
112+
113+
/// Create a new command-line flag.
114+
///
115+
/// - Parameters:
116+
/// - parsedValues: The string values that should be parsed to detect this flag.
117+
/// - abstract: The user-facing description of this flag.
118+
/// - description: An expanded, user-facing description of this flag.
119+
/// - argumentTransformation: A closure that can be applied to a given
120+
/// set of parsed arguments if the arguments include any of the this flag's parsed values.
121+
init(
122+
parsedValues: Set<String>,
123+
abstract: String,
124+
description: String,
125+
argumentTransformation: @escaping (Arguments) -> Arguments
126+
) {
127+
self.init(positiveValues: parsedValues,
128+
negativeValues: [],
129+
abstract: abstract,
130+
description: description,
131+
argumentTransformation: argumentTransformation)
132+
}
68133
}

0 commit comments

Comments
 (0)