Skip to content

[WIP] Work towards SwiftExtract, a reusable Swift analysis module#774

Draft
ktoso wants to merge 13 commits into
swiftlang:mainfrom
ktoso:wip-swift-extract
Draft

[WIP] Work towards SwiftExtract, a reusable Swift analysis module#774
ktoso wants to merge 13 commits into
swiftlang:mainfrom
ktoso:wip-swift-extract

Conversation

@ktoso
Copy link
Copy Markdown
Collaborator

@ktoso ktoso commented May 27, 2026

🚧 🚧 🚧 Early WIP, not worth reviewing yet 🚧 🚧 🚧

This will take a while to finish up but this introduces a new core foundational module called SwiftExtract that allows for analysis of swift code into an analysis result that then source generators -- such as swift-java can use.

This is to prepare reuse from other language generators, with a strong well maintianed analysis core that swift-java has spearheaded here.

This is a massive "move stuff around" so reviewing everyting might be hard but the long term benefit will be massive.

@ktoso ktoso changed the title Separate out SwiftExcract, a reusable Swift analysis module [WIP] Introduce SwiftExcract, a reusable Swift analysis module May 27, 2026
@ktoso ktoso force-pushed the wip-swift-extract branch from b5fb44d to a434174 Compare May 27, 2026 07:11
@ktoso ktoso changed the title [WIP] Introduce SwiftExcract, a reusable Swift analysis module [WIP] Introduce SwiftExtract, a reusable Swift analysis module May 27, 2026
@ktoso ktoso changed the title [WIP] Introduce SwiftExtract, a reusable Swift analysis module [WIP] Work towards SwiftExtract - a reusable Swift analysis module May 27, 2026
@ktoso ktoso changed the title [WIP] Work towards SwiftExtract - a reusable Swift analysis module [WIP] Work towards SwiftExtract, a reusable Swift analysis module May 27, 2026
@ktoso ktoso force-pushed the wip-swift-extract branch 2 times, most recently from 10143bc to c4ebf47 Compare June 1, 2026 23:48
This will take a while to finish up but this introduces a new core
foundational module called `SwiftExtract` that allows for analysis of
swift code into an analysis result that then source generators -- such
as swift-java can use.

This is to prepare reuse from other language generators, with a strong
well maintianed analysis core that swift-java has spearheaded here.

This is a massive "move stuff around" so reviewing everyting might be
hard but the long term benefit will be massive.
@ktoso ktoso force-pushed the wip-swift-extract branch 2 times, most recently from ac9071e to e23ae2c Compare June 3, 2026 13:00
ktoso added 12 commits June 4, 2026 08:22
Introduce SwiftExtractConfiguration protocol (neutral AccessLevelMode /
Logger.Level) so SwiftExtract no longer depends on SwiftJavaConfigurationShared;
Configuration conforms via JExtractSwiftLib/Configuration+SwiftExtract.swift.
Gate operator extraction behind config.extractsOperators (default off -> Java
unchanged). Expose ExtractedNominalType.declAttributes / declGroupSyntax.
All SwiftExtractTests + JExtractSwiftTests pass.
…zer extraction

Two opt-in, language-neutral knobs on `SwiftExtractConfiguration` (both default to
the prior behavior, so the Java path is unchanged):

- `availableImportModules: Set<String>` — module names treated as importable when
  resolving `#if canImport(<module>)`. The analyzer wraps its build configuration
  in an `ImportOverlayBuildConfiguration` so declarations guarded behind
  `#if canImport(MyModule)` are extracted (e.g. another language code generator
  may declare its runtime module importable here, even when the static build
  config doesn't otherwise know about it).

- `extractsGenericTypeInitializers: Bool` — extract initializers of generic
  nominal types even when not specialized. swift-java skips these by default (an
  open generic isn't directly constructible); other language code generators
  that specialize generics in a post-analysis pass need the base type's
  initializers available to clone onto the specialization.
The Foundation/FoundationEssentials known-module source files declared
Data, Date, and UUID, but URL was missing — so any user code using URL
(e.g. `func getHost(url: URL)`) failed to import with an unresolved-type
warning, dropping the function silently. Other consumers of the
language-neutral analyzer rely on URL for bridging tests; add it
alongside the other Foundation built-ins, declaring just the failable
`init(string:)` and `absoluteString` property the language-neutral
analyzer needs to resolve uses.
…Modules

`DefaultSwiftExtractConfiguration` exposed `extractsOperators` and
`availableImportModules` as stored properties with init parameters but
omitted `extractsGenericTypeInitializers`, so callers using the default
config could not opt into it programmatically and silently fell back to
the protocol-extension default of `false`. Add it as a stored property
and init parameter alongside the other two.

Also add four targeted tests in `AnalysisResultTests` exercising the
non-default paths of the two analysis-shaping knobs:

- `unspecializedGenericInitializersAreSkippedByDefault` and
  `extractsGenericTypeInitializersKeepsBaseInitializers` confirm the
  base `Tank<Fish>` flips between 0 and 2 initializers as the knob
  toggles.

- `canImportGuardedDeclsAreSkippedWhenModuleNotAvailable` and
  `availableImportModulesActivatesCanImportClause` confirm
  `#if canImport(MadeUpModule)`-guarded types are extracted only when
  the module is listed in `availableImportModules`.
The `Resources/dummy.json` placeholder exists only so SwiftPM emits a
`Bundle.module` for the SwiftExtract target — the real
`static-build-config.json` is generated at build time by
`_StaticBuildConfigPlugin`. Note that on the `.process("Resources")`
line so a future reader doesn't try to delete the empty-looking file.
… on tests

Three small review-driven cleanups:

- `SwiftAnalyzer` doc: drop the parenthetical example in the lead-in and
  fix "language-neutral" to "output is language-neutral"; drop the
  "useful for tests / no code generation" sentence on the static
  `analyze` convenience.

- `SwiftExtractConfiguration`: remove the protocol-extension defaults for
  `extractsOperators` and `extractsGenericTypeInitializers`. Both are
  semantic decisions about what the analysis layer should do for a given
  language target; making conformers state their position keeps a new
  language code generator from silently inheriting the Java-specific
  defaults. `Configuration` (swift-java) now declares both as `false`
  explicitly.

- `SwiftExtractTests`: drop the `.swiftLanguageMode(.v5)` override; the
  target compiles and runs cleanly under the package's default Swift 6
  mode.
…hared

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant