From b1681b055ebe6bb414d35c23b4e3a84f8d765c99 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Thu, 8 Oct 2026 13:00:15 +0200 Subject: [PATCH 1/5] Unified: Add additional Swift tests --- .../nested-enum-case-pattern.output | 80 ++++++++++++++++++- .../nested-enum-case-pattern.swift | 2 + 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output index 95d0a5585dcc..be2538d9926a 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output +++ b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output @@ -3,6 +3,8 @@ case let .received(.some(value), timestamp): print(value, timestamp) case Type.some(let value): print(value) +case values?.map { $0 * 2 }: + break default: break } @@ -138,6 +140,53 @@ sourceFile calledExpression: declReferenceExpr baseName: identifier "print" + switchCase + label: + switchCaseLabel + colon: : + caseKeyword: case + caseItems: + switchCaseItem + pattern: + expressionPattern + expression: + functionCallExpr + arguments: + additionalTrailingClosures: + calledExpression: + memberAccessExpr + period: . + declName: + declReferenceExpr + baseName: identifier "map" + base: + optionalChainingExpr + expression: + declReferenceExpr + baseName: identifier "values" + questionMark: ? + trailingClosure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + infixOperatorExpr + operator: + binaryOperatorExpr + operator: binaryOperator "*" + leftOperand: + declReferenceExpr + baseName: dollarIdentifier "$0" + rightOperand: + integerLiteralExpr + literal: integerLiteral "2" + statements: + codeBlockItem + item: + breakStmt + breakKeyword: break switchCase label: switchDefaultLabel @@ -159,7 +208,7 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - switch_expr source="switch ⟨value⟩ {\n⟨case⟩\n⟨case⟩\n⟨case⟩\n}" + switch_expr source="switch ⟨value⟩ {\n⟨case⟩\n⟨case⟩\n⟨case⟩\n⟨case⟩\n}" value: identifier "event" source="event" case: switch_case source="case ⟨pattern⟩:\n ⟨body⟩" @@ -216,6 +265,35 @@ top_level source="⟨body⟩" argument: argument source="⟨value⟩" value: identifier "value" source="value" + switch_case source="case ⟨pattern⟩:\n ⟨body⟩" + pattern: + call_expr source="⟨callee⟩ ⟨argument⟩" + callee: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: + call_expr source="⟨argument⟩?" + callee: + member_access_expr source="" + base: identifier "Optional" source="" + member_name_node: identifier "some" source="" + argument: + argument source="⟨value⟩" + value: identifier "values" source="values" + member_name_node: identifier "map" source="map" + argument: + argument source="⟨value⟩" + value: + function_expr source="{ ⟨body⟩ }" + body: + block source="⟨stmt⟩" + stmt: + binary_expr source="⟨left⟩ ⟨operator⟩ ⟨right⟩" + left: identifier "$0" source="$0" + operator: infix_operator "*" source="*" + right: int_literal "2" source="2" + body: + block source="⟨stmt⟩" + stmt: break_expr "break" source="break" switch_case source="default:\n ⟨body⟩" body: block source="⟨stmt⟩" diff --git a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.swift b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.swift index ed1a6b738a4b..be136fec4a6c 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.swift +++ b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.swift @@ -3,6 +3,8 @@ case let .received(.some(value), timestamp): print(value, timestamp) case Type.some(let value): print(value) +case values?.map { $0 * 2 }: + break default: break } From aeca3cfbd93d3dd119517c29a30cc93e69aba1fe Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Thu, 8 Oct 2026 13:07:19 +0200 Subject: [PATCH 2/5] Unified: Translate callee in non-pattern context also for calls with trailing closure Previously the callee was handled differently depending on whether the call had a trailing closure. This merges the two rules for `functionCallExpr` into one to erase the difference. --- .../extractor/src/languages/swift/swift.rs | 20 ++++++++++--------- .../nested-enum-case-pattern.output | 12 ++--------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index f80d5bacdf7e..555cb8e5d5d6 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -704,15 +704,13 @@ fn translation_rules() -> Vec> { ), // A function/method call (`foo(1, 2)`). `calledExpression` is the callee // and `arguments` is an (elided) list of `labeledExpr`, each translated - // to an `argument` below. A trailing closure (`xs.map { … }`) becomes a - // final unlabelled argument; that variant is matched first. + // to an `argument` below. A trailing closure (`xs.map { … }`) becomes + // a final unlabelled argument. rule!( - (functionCallExpr calledExpression: @callee arguments: _* @args trailingClosure: @tc) - => - (call_expr callee: {callee} argument: {args} argument: (argument value: {tc})) - ), - rule!( - (functionCallExpr calledExpression: @@rawCallee arguments: _* @args) + (functionCallExpr + calledExpression: @@rawCallee + arguments: _* @args + trailingClosure: _? @trailing) => expr { // Always translate the callee in non-pattern context. @@ -720,7 +718,11 @@ fn translation_rules() -> Vec> { ctx.in_pattern = false; ctx.translate(rawCallee) })?; - tree!((call_expr callee: {callee} argument: {args})) + tree!((call_expr + callee: {callee} + argument: {args} + argument: {trailing.map(|trailing| tree!((argument value: {trailing})))} + )) } ), // A call or enum-case pattern argument. diff --git a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output index be2538d9926a..ea814c00d53b 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output +++ b/unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output @@ -269,16 +269,8 @@ top_level source="⟨body⟩" pattern: call_expr source="⟨callee⟩ ⟨argument⟩" callee: - member_access_expr source="⟨base⟩.⟨member_name_node⟩" - base: - call_expr source="⟨argument⟩?" - callee: - member_access_expr source="" - base: identifier "Optional" source="" - member_name_node: identifier "some" source="" - argument: - argument source="⟨value⟩" - value: identifier "values" source="values" + member_access_expr source="⟨base⟩?.⟨member_name_node⟩" + base: identifier "values" source="values" member_name_node: identifier "map" source="map" argument: argument source="⟨value⟩" From 781d2b9bff294b4e798db584c9050c772e9a4830 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Thu, 8 Oct 2026 13:44:02 +0200 Subject: [PATCH 3/5] Unified: Add Swift tests with additional trailing closure --- .../swift/expressions/subscript-call.output | 111 ++++++++++++++++++ .../swift/expressions/subscript-call.swift | 9 ++ .../swift/functions/function-call.output | 80 ++++++++++++- .../swift/functions/function-call.swift | 8 ++ 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 unified/extractor/tests/corpus/swift/expressions/subscript-call.output create mode 100644 unified/extractor/tests/corpus/swift/expressions/subscript-call.swift diff --git a/unified/extractor/tests/corpus/swift/expressions/subscript-call.output b/unified/extractor/tests/corpus/swift/expressions/subscript-call.output new file mode 100644 index 000000000000..81809f8e4ee8 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/expressions/subscript-call.output @@ -0,0 +1,111 @@ +handlers[0] + +handlers[0] { + first() +} completion: { + second() +} failure: { + third() +} + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + subscriptCallExpr + leftSquare: [ + rightSquare: ] + arguments: + labeledExpr + expression: + integerLiteralExpr + literal: integerLiteral "0" + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "handlers" + codeBlockItem + item: + subscriptCallExpr + leftSquare: [ + rightSquare: ] + arguments: + labeledExpr + expression: + integerLiteralExpr + literal: integerLiteral "0" + additionalTrailingClosures: + multipleTrailingClosureElement + colon: : + label: identifier "completion" + closure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "second" + multipleTrailingClosureElement + colon: : + label: identifier "failure" + closure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "third" + calledExpression: + declReferenceExpr + baseName: identifier "handlers" + trailingClosure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "first" + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩\n\n⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩[⟨argument⟩]" + callee: identifier "handlers" source="handlers" + argument: + argument source="⟨value⟩" + value: int_literal "0" source="0" + call_expr source="⟨callee⟩[⟨argument⟩] {\n first()\n} completion: {\n second()\n} failure: {\n third()\n}" + callee: identifier "handlers" source="handlers" + argument: + argument source="⟨value⟩" + value: int_literal "0" source="0" diff --git a/unified/extractor/tests/corpus/swift/expressions/subscript-call.swift b/unified/extractor/tests/corpus/swift/expressions/subscript-call.swift new file mode 100644 index 000000000000..a0e4b51700bd --- /dev/null +++ b/unified/extractor/tests/corpus/swift/expressions/subscript-call.swift @@ -0,0 +1,9 @@ +handlers[0] + +handlers[0] { + first() +} completion: { + second() +} failure: { + third() +} diff --git a/unified/extractor/tests/corpus/swift/functions/function-call.output b/unified/extractor/tests/corpus/swift/functions/function-call.output index 19f1d7cf988f..b6d177316382 100644 --- a/unified/extractor/tests/corpus/swift/functions/function-call.output +++ b/unified/extractor/tests/corpus/swift/functions/function-call.output @@ -1,5 +1,13 @@ foo(1, 2) +handle { + first() +} completion: { + second() +} failure: { + third() +} + --- sourceFile @@ -24,12 +32,71 @@ sourceFile calledExpression: declReferenceExpr baseName: identifier "foo" + codeBlockItem + item: + functionCallExpr + arguments: + additionalTrailingClosures: + multipleTrailingClosureElement + colon: : + label: identifier "completion" + closure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "second" + multipleTrailingClosureElement + colon: : + label: identifier "failure" + closure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "third" + calledExpression: + declReferenceExpr + baseName: identifier "handle" + trailingClosure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "first" --- top_level source="⟨body⟩" body: - block source="⟨stmt⟩" + block source="⟨stmt⟩\n\n⟨stmt⟩" stmt: call_expr source="⟨callee⟩(⟨argument⟩, ⟨argument⟩)" callee: identifier "foo" source="foo" @@ -38,3 +105,14 @@ top_level source="⟨body⟩" value: int_literal "1" source="1" argument source="⟨value⟩" value: int_literal "2" source="2" + call_expr source="⟨callee⟩ ⟨argument⟩ completion: {\n second()\n} failure: {\n third()\n}" + callee: identifier "handle" source="handle" + argument: + argument source="⟨value⟩" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "first" source="first" diff --git a/unified/extractor/tests/corpus/swift/functions/function-call.swift b/unified/extractor/tests/corpus/swift/functions/function-call.swift index 22594bf8c7da..d3fc9dd70c9c 100644 --- a/unified/extractor/tests/corpus/swift/functions/function-call.swift +++ b/unified/extractor/tests/corpus/swift/functions/function-call.swift @@ -1 +1,9 @@ foo(1, 2) + +handle { + first() +} completion: { + second() +} failure: { + third() +} From 463d8a715c1a714f6451773dae614e40c05100d7 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Thu, 8 Oct 2026 13:47:20 +0200 Subject: [PATCH 4/5] Unified: Handle `additionalTrailingClosures` for more expression kinds --- .../extractor/src/languages/swift/swift.rs | 25 +++++++++++++---- .../swift/expressions/subscript-call.output | 28 ++++++++++++++++++- .../swift/functions/function-call.output | 20 ++++++++++++- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 555cb8e5d5d6..2319beb62ca1 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -671,7 +671,8 @@ fn translation_rules() -> Vec> { calledExpression: (arrayExpr elements: (arrayElement expression: (genericSpecializationExpr) @element)) @@array arguments: _* @args - trailingClosure: @tc) + trailingClosure: @tc + additionalTrailingClosures: _* @additional) => call_expr { let callee = tree_at!( @@ -683,7 +684,8 @@ fn translation_rules() -> Vec> { tree!((call_expr callee: {callee} argument: {args} - argument: (argument value: {tc}))) + argument: (argument value: {tc}) + argument: {additional})) } ), rule!( @@ -705,12 +707,14 @@ fn translation_rules() -> Vec> { // A function/method call (`foo(1, 2)`). `calledExpression` is the callee // and `arguments` is an (elided) list of `labeledExpr`, each translated // to an `argument` below. A trailing closure (`xs.map { … }`) becomes - // a final unlabelled argument. + // a final unlabelled argument, followed by any additional labeled + // trailing closures. rule!( (functionCallExpr calledExpression: @@rawCallee arguments: _* @args - trailingClosure: _? @trailing) + trailingClosure: _? @trailing + additionalTrailingClosures: _* @additional) => expr { // Always translate the callee in non-pattern context. @@ -722,6 +726,7 @@ fn translation_rules() -> Vec> { callee: {callee} argument: {args} argument: {trailing.map(|trailing| tree!((argument value: {trailing})))} + argument: {additional} )) } ), @@ -1021,9 +1026,17 @@ fn translation_rules() -> Vec> { // subscripts their own shape needs only a `subscript_expr` node in // ast_types.yml and a remap here. rule!( - (subscriptCallExpr calledExpression: @callee arguments: _* @args) + (subscriptCallExpr + calledExpression: @callee + arguments: _* @args + trailingClosure: _? @trailing + additionalTrailingClosures: _* @additional) => - (call_expr callee: {callee} argument: {args}) + (call_expr + callee: {callee} + argument: {args} + argument: {trailing.map(|trailing| tree!((argument value: {trailing})))} + argument: {additional}) ), // ---- Optionals and errors ---- // Optional chaining — unwrap the marker diff --git a/unified/extractor/tests/corpus/swift/expressions/subscript-call.output b/unified/extractor/tests/corpus/swift/expressions/subscript-call.output index 81809f8e4ee8..138caa29eb62 100644 --- a/unified/extractor/tests/corpus/swift/expressions/subscript-call.output +++ b/unified/extractor/tests/corpus/swift/expressions/subscript-call.output @@ -104,8 +104,34 @@ top_level source="⟨body⟩" argument: argument source="⟨value⟩" value: int_literal "0" source="0" - call_expr source="⟨callee⟩[⟨argument⟩] {\n first()\n} completion: {\n second()\n} failure: {\n third()\n}" + call_expr source="⟨callee⟩[⟨argument⟩] ⟨argument⟩ ⟨argument⟩ ⟨argument⟩" callee: identifier "handlers" source="handlers" argument: argument source="⟨value⟩" value: int_literal "0" source="0" + argument source="⟨value⟩" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "first" source="first" + argument source="⟨name_node⟩: ⟨value⟩" + name_node: identifier "completion" source="completion" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "second" source="second" + argument source="⟨name_node⟩: ⟨value⟩" + name_node: identifier "failure" source="failure" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "third" source="third" diff --git a/unified/extractor/tests/corpus/swift/functions/function-call.output b/unified/extractor/tests/corpus/swift/functions/function-call.output index b6d177316382..79fc9e7f52e5 100644 --- a/unified/extractor/tests/corpus/swift/functions/function-call.output +++ b/unified/extractor/tests/corpus/swift/functions/function-call.output @@ -105,7 +105,7 @@ top_level source="⟨body⟩" value: int_literal "1" source="1" argument source="⟨value⟩" value: int_literal "2" source="2" - call_expr source="⟨callee⟩ ⟨argument⟩ completion: {\n second()\n} failure: {\n third()\n}" + call_expr source="⟨callee⟩ ⟨argument⟩ ⟨argument⟩ ⟨argument⟩" callee: identifier "handle" source="handle" argument: argument source="⟨value⟩" @@ -116,3 +116,21 @@ top_level source="⟨body⟩" stmt: call_expr source="⟨callee⟩()" callee: identifier "first" source="first" + argument source="⟨name_node⟩: ⟨value⟩" + name_node: identifier "completion" source="completion" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "second" source="second" + argument source="⟨name_node⟩: ⟨value⟩" + name_node: identifier "failure" source="failure" + value: + function_expr source="{\n ⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "third" source="third" From a5d5a4fae57c863ebce30df7912b8f616bb12116 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Thu, 8 Oct 2026 14:51:15 +0200 Subject: [PATCH 5/5] Unified: Add additional trailing closure in array type constructor test --- .../expressions/array-type-constructor.output | 31 ++++++++++++++++++- .../expressions/array-type-constructor.swift | 2 ++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.output b/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.output index c0970babe165..28c66609b41b 100644 --- a/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.output +++ b/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.output @@ -1,6 +1,8 @@ let values = [Result]() let initialized = [Result](unsafeUninitializedCapacity: 1) { _, count in count = 0 +} completion: { + done() } --- @@ -71,6 +73,24 @@ sourceFile integerLiteralExpr literal: integerLiteral "1" additionalTrailingClosures: + multipleTrailingClosureElement + colon: : + label: identifier "completion" + closure: + closureExpr + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "done" calledExpression: arrayExpr elements: @@ -144,7 +164,7 @@ top_level source="⟨body⟩" modifier: modifier "let" source="let" pattern: identifier "initialized" source="initialized" value: - call_expr source="⟨callee⟩(⟨argument⟩) ⟨argument⟩" + call_expr source="⟨callee⟩(⟨argument⟩) ⟨argument⟩ ⟨argument⟩" callee: generic_type_expr source="[⟨type_argument⟩]" base: identifier "Array" source="" @@ -171,3 +191,12 @@ top_level source="⟨body⟩" left: identifier "count" source="count" operator: infix_operator "=" source="=" right: int_literal "0" source="0" + argument source="⟨name_node⟩: ⟨value⟩" + name_node: identifier "completion" source="completion" + value: + function_expr source="{\n\t⟨body⟩\n}" + body: + block source="⟨stmt⟩" + stmt: + call_expr source="⟨callee⟩()" + callee: identifier "done" source="done" diff --git a/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.swift b/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.swift index bd25a9cbb4ce..a4dce57c797e 100644 --- a/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.swift +++ b/unified/extractor/tests/corpus/swift/expressions/array-type-constructor.swift @@ -1,4 +1,6 @@ let values = [Result]() let initialized = [Result](unsafeUninitializedCapacity: 1) { _, count in count = 0 +} completion: { + done() }