From 37654b4bcc8398a96913932ac61629e2aa900d80 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 20 Jul 2026 13:49:55 +0000 Subject: [PATCH] unified: Port import rules to swift-syntax Retarget imports to the swift-syntax AST, output unchanged. swift-syntax represents the dotted path as a list of `importPathComponent`s, folded into a `name_expr`/`member_access_expr` chain via `member_chain`. A single rule handles both forms via an optional `importKindSpecifier`: a scoped import (`import struct Foo.Bar`) has one and binds the last path component as a `name_pattern`; a plain import (`import Foundation`) has none and uses a `bulk_importing_pattern`. Leading attributes (`@_exported`) and access modifiers (`public`) become `modifier`s. The tree-sitter multi-part `identifier` rule is dropped: swift-syntax qualified names are already `memberAccessExpr` chains, and import paths are `importPathComponent`s. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../extractor/src/languages/swift/swift.rs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index dfddfa9c66a..148a2df2218 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -911,35 +911,37 @@ fn translation_rules() -> Vec> { // `forceUnwrapExpr` node (the tree-sitter path used the generic postfix // operator rule instead). rule!((forceUnwrapExpr expression: @e) => (unary_expr operator: (postfix_operator "!") operand: {e})), - // A multi-part identifier (for example `Foo.Bar.Baz`) is translated to - // a member_access_expr chain with a name_expr base. + // ---- Imports ---- + // An import declaration. The dotted path (a list of + // `importPathComponent`s) becomes a `name_expr`/`member_access_expr` + // chain (via `member_chain`). A scoped import (`import struct Foo.Bar`) + // has an `importKindSpecifier` and binds the last path component as a + // `name_pattern`; a plain import (`import Foundation`) has none and uses + // a `bulk_importing_pattern` spanning the whole declaration. Any leading + // attributes (`@_exported`) and access modifiers (`public`) become + // `modifier`s. rule!( - (identifier part: _+ @parts) + (importDecl + attributes: _* @attrs + modifiers: _* @mods + importKindSpecifier: _? @@kind + path: (importPathComponent name: @@parts)*) => - expr { member_chain(&mut ctx, parts) } - ), - // Scoped import declaration (for example `import struct Foo.Bar`): - // flatten the identifier parts into a member_access_expr and bind the - // final segment as a name_pattern. - rule!( - (import_declaration scoped_import_kind: @kind name: (identifier part: _+ @parts) @name modifiers: (modifiers)? @mods) - => - (import_declaration - pattern: (name_pattern identifier: (identifier #{parts.last().unwrap()})) - imported_expr: {name} - modifier: (modifier #{kind}) - modifier: {mods}) - ), - // Non-scoped import declaration (for example `import Foundation`): - // flatten the identifier parts into a member_access_expr and use a - // bulk_importing_pattern. - rule!( - (import_declaration name: @name modifiers: (modifiers)? @mods) - => - (import_declaration - pattern: (bulk_importing_pattern) - imported_expr: {name} - modifier: {mods}) + import_declaration { + let pattern = match kind { + Some(_) => { + let last = *parts.last().ok_or("import has no path")?; + tree!((name_pattern identifier: (identifier #{last}))) + } + None => tree!((bulk_importing_pattern)), + }; + tree!((import_declaration + modifier: {kind.map(|k| tree!((modifier #{k})))} + modifier: {attrs} + modifier: {mods} + pattern: {pattern} + imported_expr: {member_chain(&mut ctx, parts)})) + } ), // ---- Types and classes ---- // Self expression → name_expr