Skip to content

Commit 9ff6286

Browse files
committed
SwiftExtract: extract AccessLevelMode into SwiftExtractConfigurationShared
Reviewer asked: "why does swift-java need to map at all, can it not use this exact enum?" — yes, after introducing a small shared target. `SwiftJavaConfigurationShared` is intentionally lightweight (stdlib + Foundation only): it must be symlinked into each plugin's source tree because SwiftPM plugins can't have target dependencies. Pulling SwiftSyntax in via `SwiftExtract` would balloon plugin builds. Instead, introduce a sibling `SwiftExtractConfigurationShared` target that holds nothing but the small `AccessLevelMode` enum. Both `SwiftExtract` and `SwiftJavaConfigurationShared` depend on it; the plugin symlink discipline (`Plugins/PluginsShared/SwiftExtractConfigurationShared`) mirrors the existing `SwiftJavaConfigurationShared` symlink. Effects: - `AccessLevelMode` is the single, shared enum. swift-java's `Configuration` uses it directly via `@_exported import`, retiring `JExtractMinimumAccessLevelMode` and the four-line mapping switch in `Configuration+SwiftExtract.swift` (now an identity passthrough). - `SwiftJavaConfigurationShared/Configuration.swift` guards the import with `#if canImport(SwiftExtractConfigurationShared)` so plugin builds (which inline the file alongside `AccessLevelMode.swift` via symlink rather than as a separate module) still compile. - `AccessLevelMode` gains `@nonexhaustive` (SE-0487, gated with `#if compiler(>=6.2)`) per reviewer request, so adding cases in the future is non-breaking. `Codable` conformance is added so it can replace `JExtractMinimumAccessLevelMode` in the on-disk `Configuration` JSON without changing the wire format. - The CLI's `@Option var minimumInputAccessLevelMode` and the `ExpressibleByArgument` conformance switch over to `AccessLevelMode` accordingly.
1 parent 792a9ae commit 9ff6286

8 files changed

Lines changed: 75 additions & 52 deletions

File tree

Package.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ let package = Package(
4343
targets: ["SwiftJavaConfigurationShared"]
4444
),
4545

46+
.library(
47+
name: "SwiftExtractConfigurationShared",
48+
targets: ["SwiftExtractConfigurationShared"]
49+
),
50+
4651
.library(
4752
name: "JavaUtil",
4853
targets: ["JavaUtil"]
@@ -276,7 +281,14 @@ let package = Package(
276281
),
277282

278283
.target(
279-
name: "SwiftJavaConfigurationShared"
284+
name: "SwiftJavaConfigurationShared",
285+
dependencies: [
286+
"SwiftExtractConfigurationShared"
287+
]
288+
),
289+
290+
.target(
291+
name: "SwiftExtractConfigurationShared"
280292
),
281293

282294
.target(
@@ -351,6 +363,7 @@ let package = Package(
351363
.product(name: "SwiftSyntax", package: "swift-syntax"),
352364
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
353365
.product(name: "Logging", package: "swift-log"),
366+
"SwiftExtractConfigurationShared",
354367
],
355368
path: "Sources/SwiftExtract",
356369
resources: [
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../Sources/SwiftExtractConfigurationShared

Sources/JExtractSwiftLib/Configuration+SwiftExtract.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ import SwiftJavaConfigurationShared
1818
/// Bridges swift-java's `Configuration` onto the language-neutral
1919
/// `SwiftExtractConfiguration` surface consumed by `SwiftExtract`.
2020
///
21-
/// Most members are satisfied directly by `Configuration`'s own properties; only
22-
/// the two enum-typed members need a mapping from swift-java's enums onto the
23-
/// neutral `AccessLevelMode` / `Logger.Level`.
21+
/// Most members are satisfied directly by `Configuration`'s own properties.
22+
/// `Configuration` shares `AccessLevelMode` with the analyzer (both pull it in
23+
/// from `SwiftExtractConfigurationShared`), so `swiftExtractAccessLevel` is a
24+
/// straight passthrough. Only `swiftExtractLogLevel` needs a mapping from
25+
/// swift-java's `LogLevel` onto the neutral `Logger.Level`.
2426
extension Configuration: SwiftExtractConfiguration {
2527
public var swiftExtractAccessLevel: AccessLevelMode {
26-
switch effectiveMinimumInputAccessLevelMode {
27-
case .public: .public
28-
case .package: .package
29-
case .internal: .internal
30-
}
28+
effectiveMinimumInputAccessLevelMode
3129
}
3230

3331
public var swiftExtractLogLevel: SwiftExtract.Logger.Level? {

Sources/SwiftExtract/SwiftExtractConfiguration.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
/// Minimum access level a declaration must have to be considered for extraction.
16-
///
17-
/// Language-neutral counterpart to a configuration's access-level setting. The
18-
/// concrete `Configuration` types in language layers (e.g. swift-java, or other
19-
/// language code generators) map their own enums onto this.
20-
public enum AccessLevelMode: String, Sendable {
21-
case `public`
22-
case `package`
23-
case `internal`
24-
}
15+
@_exported import SwiftExtractConfigurationShared
2516

2617
/// The configuration surface required by the language-neutral `SwiftExtract`
2718
/// analysis layer.
@@ -33,9 +24,14 @@ public enum AccessLevelMode: String, Sendable {
3324
/// (e.g. Java/JNI/FFM, or other language code generators) without pulling
3425
/// target-specific config types into `SwiftExtract`.
3526
///
36-
/// The two enum-typed members use `swiftExtract`-prefixed names so a conforming
37-
/// type can keep its own, differently-typed `logLevel` /
38-
/// `effectiveMinimumInputAccessLevelMode` members without a name collision.
27+
/// `AccessLevelMode` lives in the small `SwiftExtractConfigurationShared`
28+
/// target so language-specific configuration shared modules (e.g.
29+
/// `SwiftJavaConfigurationShared`) can use the same enum directly without
30+
/// taking a dependency on SwiftSyntax.
31+
///
32+
/// The enum-typed `swiftExtractLogLevel` member uses a `swiftExtract`-prefixed
33+
/// name so a conforming type can keep its own, differently-typed `logLevel`
34+
/// member without a name collision.
3935
public protocol SwiftExtractConfiguration {
4036
/// Name of the Swift module being analyzed.
4137
var swiftModule: String? { get }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024-2026 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// Minimum access level a declaration must have to be considered for extraction.
16+
///
17+
/// Lives in the small `SwiftExtractConfigurationShared` target so the analysis
18+
/// layer and language-specific configuration layers (e.g. swift-java's
19+
/// `SwiftJavaConfigurationShared`) can both depend on it without dragging
20+
/// SwiftSyntax into the latter.
21+
#if compiler(>=6.2)
22+
@nonexhaustive
23+
#endif
24+
public enum AccessLevelMode: String, Codable, Sendable {
25+
case `public`
26+
case `package`
27+
case `internal`
28+
}
29+
30+
extension AccessLevelMode {
31+
public static var `default`: Self {
32+
.public
33+
}
34+
}

Sources/SwiftJavaConfigurationShared/Configuration.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414

1515
import Foundation
1616

17+
// In a real module build this resolves to a separate target. In plugin builds
18+
// the file is inlined (via symlink) alongside `AccessLevelMode.swift`, so the
19+
// module isn't a discoverable import — guard with canImport.
20+
#if canImport(SwiftExtractConfigurationShared)
21+
@_exported import SwiftExtractConfigurationShared
22+
#endif
23+
1724
////////////////////////////////////////////////////////////////////////////////
1825
// This file is only supposed to be edited in `Shared/` and must be symlinked //
1926
// from everywhere else! We cannot share dependencies with or between plugins //
@@ -62,8 +69,8 @@ public struct Configuration: Codable {
6269
writeEmptyFiles ?? false
6370
}
6471

65-
public var minimumInputAccessLevelMode: JExtractMinimumAccessLevelMode?
66-
public var effectiveMinimumInputAccessLevelMode: JExtractMinimumAccessLevelMode {
72+
public var minimumInputAccessLevelMode: AccessLevelMode?
73+
public var effectiveMinimumInputAccessLevelMode: AccessLevelMode {
6774
minimumInputAccessLevelMode ?? .default
6875
}
6976

Sources/SwiftJavaConfigurationShared/JExtract/JExtractMinimumAccessLevelMode.swift

Lines changed: 0 additions & 26 deletions
This file was deleted.

Sources/SwiftJavaTool/Commands/JExtractCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ extension SwiftJava {
6666
var writeEmptyFiles: Bool?
6767

6868
@Option(help: "The lowest access level of Swift declarations that should be extracted, defaults to 'public'.")
69-
var minimumInputAccessLevelMode: JExtractMinimumAccessLevelMode?
69+
var minimumInputAccessLevelMode: AccessLevelMode?
7070

7171
@Option(
7272
help:
@@ -222,6 +222,6 @@ struct IllegalModeCombinationError: Error {
222222
}
223223

224224
extension JExtractGenerationMode: ExpressibleByArgument {}
225-
extension JExtractMinimumAccessLevelMode: ExpressibleByArgument {}
225+
extension AccessLevelMode: ExpressibleByArgument {}
226226
extension JExtractMemoryManagementMode: ExpressibleByArgument {}
227227
extension JExtractAsyncFuncMode: ExpressibleByArgument {}

0 commit comments

Comments
 (0)