From 790d4f11be262adc32c64c53c448aa5138d07f74 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:27 +0200 Subject: [PATCH] unified/swift: add closure and capture mappings --- .../extractor/src/languages/swift/swift.rs | 64 +++++++++++ .../extractor/tests/corpus/swift/closures.txt | 105 +++++++++++++++++- 2 files changed, 164 insertions(+), 5 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index db04f56ffab..c31b2ca882b 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -386,6 +386,70 @@ fn translation_rules() -> Vec { rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), rule!((control_transfer_statement kind: "continue") => (continue_expr)), rule!((control_transfer_statement kind: (throw_keyword) result: @val) => (throw_expr value: {val})), + // ---- Closures ---- + // Lambda literal with optional type header (parameters + optional return type). + // The return_type capture is optional, so this rule covers both cases. + rule!( + (lambda_literal + attribute: _* @attrs + captures: (capture_list item: _* @captures)? + type: (lambda_function_type + params: (lambda_function_type_parameters parameter: _* @params) + return_type: _? @ret)? + statement: _* @body) + => + (function_expr + modifier: {..attrs} + capture_declaration: {..captures} + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body})) + ), + // capture_list_item with ownership modifier (e.g. [weak self], [unowned x]) + rule!( + (capture_list_item ownership: _? @ownership name: @name value: _? @val) + => + (variable_declaration + modifier: {..ownership} + pattern: (name_pattern identifier: (identifier #{name})) + value: {..val}) + ), + // Lambda parameter with type and optional external name + rule!( + (lambda_parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (lambda_parameter name: @name) + => + (parameter pattern: (name_pattern identifier: (identifier #{name}))) + ), + // Call expression with trailing closure (no value_arguments) + rule!( + (call_expression function: @func suffix: (call_suffix lambda: (lambda_literal) @closure)) + => + (call_expr + callee: {func} + argument: (argument value: {closure})) + ), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 3bfe46ff411..2d18062bb2c 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -57,7 +57,24 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in x * 2 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "x" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int" === Closure with shorthand parameters @@ -97,7 +114,19 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ $0 + $1 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "$0" + right: + name_expr + identifier: identifier "$1" === Trailing closure @@ -131,7 +160,27 @@ source_file top_level body: block - stmt: unsupported_node "xs.map { $0 * 2 }" + stmt: + call_expr + argument: + argument + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "$0" + right: int_literal "2" + callee: + member_access_expr + base: + name_expr + identifier: identifier "xs" + member: identifier "map" === Closure with capture list @@ -188,7 +237,22 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ [weak self] in self?.doThing() }" + value: + function_expr + body: + block + stmt: + call_expr + callee: + member_access_expr + base: unsupported_node "self?" + member: identifier "doThing" + capture_declaration: + variable_declaration + modifier: unsupported_node "weak" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got unsupported_node + pattern: + name_pattern + identifier: identifier "self" === Multi-statement closure @@ -269,4 +333,35 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" + value: + function_expr + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "1" + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "y" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int"