From 01d455f997095f6b67865f2360a4108052895551 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 14 Oct 2026 10:31:34 +0200 Subject: [PATCH 1/2] Unified: Add corpus coverage for qualified Swift generic types Record the current extraction of generic arguments on qualified and nested types before fixing their omission. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d0033cd3-ca30-4469-969f-36c4cf463e71 --- .../corpus/swift/types/qualified-type.output | 264 +++++++++++++++++- .../corpus/swift/types/qualified-type.swift | 11 + 2 files changed, 273 insertions(+), 2 deletions(-) diff --git a/unified/extractor/tests/corpus/swift/types/qualified-type.output b/unified/extractor/tests/corpus/swift/types/qualified-type.output index 3eada1c5f429..2c138e0fb523 100644 --- a/unified/extractor/tests/corpus/swift/types/qualified-type.output +++ b/unified/extractor/tests/corpus/swift/types/qualified-type.output @@ -2,11 +2,22 @@ struct Outer { struct Inner { struct Deep {} } + + struct Generic { + struct Deep {} + struct Plain {} + } } let value: Outer.Inner let nested: Outer.Inner.Deep +func qualifiedTypes( + _ value: Outer.Generic, + _ nested: Outer.Generic.Deep, + _ plain: Outer.Generic.Plain +) {} + --- sourceFile @@ -46,6 +57,62 @@ sourceFile structKeyword: struct modifiers: structKeyword: struct + memberBlockItem + decl: + structDecl + attributes: + name: identifier "Generic" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + attributes: + name: identifier "T" + leftAngle: < + rightAngle: > + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + memberBlockItem + decl: + structDecl + attributes: + name: identifier "Deep" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + attributes: + name: identifier "U" + trailingComma: , + genericParameter + attributes: + name: identifier "V" + leftAngle: < + rightAngle: > + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + modifiers: + structKeyword: struct + memberBlockItem + decl: + structDecl + attributes: + name: identifier "Plain" + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + modifiers: + structKeyword: struct + modifiers: + structKeyword: struct modifiers: structKeyword: struct codeBlockItem @@ -94,14 +161,139 @@ sourceFile name: identifier "Outer" period: . period: . + codeBlockItem + item: + functionDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + name: identifier "qualifiedTypes" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + attributes: + name: identifier "Metadata" + trailingComma: , + genericParameter + attributes: + name: identifier "ErrorComponentType" + leftAngle: < + rightAngle: > + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + functionParameter + colon: : + attributes: + modifiers: + trailingComma: , + type: + memberType + name: identifier "Generic" + baseType: + identifierType + name: identifier "Outer" + period: . + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + argument: + identifierType + name: identifier "Metadata" + leftAngle: < + rightAngle: > + firstName: _ + secondName: identifier "value" + functionParameter + colon: : + attributes: + modifiers: + trailingComma: , + type: + memberType + name: identifier "Deep" + baseType: + memberType + name: identifier "Generic" + baseType: + identifierType + name: identifier "Outer" + period: . + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + argument: + identifierType + name: identifier "Metadata" + leftAngle: < + rightAngle: > + period: . + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + trailingComma: , + argument: + identifierType + name: identifier "ErrorComponentType" + genericArgument + argument: + arrayType + leftSquare: [ + rightSquare: ] + element: + identifierType + name: identifier "Metadata" + leftAngle: < + rightAngle: > + firstName: _ + secondName: identifier "nested" + functionParameter + colon: : + attributes: + modifiers: + type: + memberType + name: identifier "Plain" + baseType: + memberType + name: identifier "Generic" + baseType: + identifierType + name: identifier "Outer" + period: . + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + argument: + identifierType + name: identifier "Metadata" + leftAngle: < + rightAngle: > + period: . + firstName: _ + secondName: identifier "plain" + funcKeyword: func --- top_level source="⟨body⟩" body: - block source="⟨stmt⟩\n\n⟨stmt⟩\n⟨stmt⟩" + block source="⟨stmt⟩\n\n⟨stmt⟩\n⟨stmt⟩\n\n⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n\n ⟨member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "Outer" source="Outer" member: @@ -121,6 +313,38 @@ top_level source="⟨body⟩" modifier: modifier "generated" source="" name_node: identifier "init" source="" body: block source="" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩> {\n ⟨member⟩\n ⟨member⟩\n }" + modifier: modifier "struct" source="struct" + name_node: identifier "Generic" source="Generic" + type_parameter: + type_parameter source="⟨name_node⟩" + name_node: identifier "T" source="T" + member: + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩, ⟨type_parameter⟩> {}" + modifier: modifier "struct" source="struct" + name_node: identifier "Deep" source="Deep" + type_parameter: + type_parameter source="⟨name_node⟩" + name_node: identifier "U" source="U" + type_parameter source="⟨name_node⟩" + name_node: identifier "V" source="V" + member: + constructor_declaration source="" + modifier: modifier "generated" source="" + name_node: identifier "init" source="" + body: block source="" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}" + modifier: modifier "struct" source="struct" + name_node: identifier "Plain" source="Plain" + member: + constructor_declaration source="" + modifier: modifier "generated" source="" + name_node: identifier "init" source="" + body: block source="" + constructor_declaration source="" + modifier: modifier "generated" source="" + name_node: identifier "init" source="" + body: block source="" constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" @@ -142,3 +366,39 @@ top_level source="⟨body⟩" base: identifier "Outer" source="Outer" member_name_node: identifier "Inner" source="Inner" member_name_node: identifier "Deep" source="Deep" + function_declaration source="func ⟨name_node⟩<⟨type_parameter⟩, ⟨type_parameter⟩>(\n ⟨parameter⟩,\n ⟨parameter⟩,\n ⟨parameter⟩\n) ⟨body⟩" + name_node: identifier "qualifiedTypes" source="qualifiedTypes" + type_parameter: + type_parameter source="⟨name_node⟩" + name_node: identifier "Metadata" source="Metadata" + type_parameter source="⟨name_node⟩" + name_node: identifier "ErrorComponentType" source="ErrorComponentType" + parameter: + parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "_" source="_" + type: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + pattern: identifier "value" source="value" + parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "_" source="_" + type: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + member_name_node: identifier "Deep" source="Deep" + pattern: identifier "nested" source="nested" + parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "_" source="_" + type: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + member_name_node: identifier "Plain" source="Plain" + pattern: identifier "plain" source="plain" + body: block "{}" source="{}" diff --git a/unified/extractor/tests/corpus/swift/types/qualified-type.swift b/unified/extractor/tests/corpus/swift/types/qualified-type.swift index 9c5fbae3cf7b..626a5374830a 100644 --- a/unified/extractor/tests/corpus/swift/types/qualified-type.swift +++ b/unified/extractor/tests/corpus/swift/types/qualified-type.swift @@ -2,7 +2,18 @@ struct Outer { struct Inner { struct Deep {} } + + struct Generic { + struct Deep {} + struct Plain {} + } } let value: Outer.Inner let nested: Outer.Inner.Deep + +func qualifiedTypes( + _ value: Outer.Generic, + _ nested: Outer.Generic.Deep, + _ plain: Outer.Generic.Plain +) {} From 6d39f761060cb7c7eb0e9bc7a3984f929b2b24dc Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 14 Oct 2026 10:32:55 +0200 Subject: [PATCH 2/2] Unified: Preserve generic arguments on qualified Swift types Wrap generic member types in generic_type_expr so qualified and nested type arguments remain visible to name binding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d0033cd3-ca30-4469-969f-36c4cf463e71 --- .../extractor/src/languages/swift/swift.rs | 12 +++++- .../corpus/swift/types/qualified-type.output | 38 +++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 2319beb62ca1..4bae882cf74a 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1186,7 +1186,17 @@ fn translation_rules() -> Vec> { rule!((identifierType name: @@n) => (identifier #{n})), // A qualified type (`Outer.Inner`, `NSString.CompareOptions`). swift-syntax // nests these as `memberType` nodes; preserve the nesting as ordinary - // member access. + // member access, wrapped in a generic type when it has type arguments. + rule!( + (memberType + baseType: @base + name: @@name + genericArgumentClause: (genericArgumentClause arguments: (genericArgument argument: @args)*)) + => + (generic_type_expr + base: (member_access_expr base: {base} member_name_node: (identifier #{name})) + type_argument: {args}) + ), rule!( (memberType baseType: @base name: @@name) => diff --git a/unified/extractor/tests/corpus/swift/types/qualified-type.output b/unified/extractor/tests/corpus/swift/types/qualified-type.output index 2c138e0fb523..2d81f4dcbb94 100644 --- a/unified/extractor/tests/corpus/swift/types/qualified-type.output +++ b/unified/extractor/tests/corpus/swift/types/qualified-type.output @@ -377,28 +377,44 @@ top_level source="⟨body⟩" parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" external_name_node: identifier "_" source="_" type: - member_access_expr source="⟨base⟩.⟨member_name_node⟩" - base: identifier "Outer" source="Outer" - member_name_node: identifier "Generic" source="Generic" + generic_type_expr source="⟨base⟩<⟨type_argument⟩>" + base: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + type_argument: identifier "Metadata" source="Metadata" pattern: identifier "value" source="value" parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" external_name_node: identifier "_" source="_" type: - member_access_expr source="⟨base⟩.⟨member_name_node⟩" + generic_type_expr source="⟨base⟩<⟨type_argument⟩, ⟨type_argument⟩>" base: - member_access_expr source="⟨base⟩.⟨member_name_node⟩" - base: identifier "Outer" source="Outer" - member_name_node: identifier "Generic" source="Generic" - member_name_node: identifier "Deep" source="Deep" + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: + generic_type_expr source="⟨base⟩<⟨type_argument⟩>" + base: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + type_argument: identifier "Metadata" source="Metadata" + member_name_node: identifier "Deep" source="Deep" + type_argument: + identifier "ErrorComponentType" source="ErrorComponentType" + generic_type_expr source="[⟨type_argument⟩]" + base: identifier "Array" source="" + type_argument: identifier "Metadata" source="Metadata" pattern: identifier "nested" source="nested" parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" external_name_node: identifier "_" source="_" type: member_access_expr source="⟨base⟩.⟨member_name_node⟩" base: - member_access_expr source="⟨base⟩.⟨member_name_node⟩" - base: identifier "Outer" source="Outer" - member_name_node: identifier "Generic" source="Generic" + generic_type_expr source="⟨base⟩<⟨type_argument⟩>" + base: + member_access_expr source="⟨base⟩.⟨member_name_node⟩" + base: identifier "Outer" source="Outer" + member_name_node: identifier "Generic" source="Generic" + type_argument: identifier "Metadata" source="Metadata" member_name_node: identifier "Plain" source="Plain" pattern: identifier "plain" source="plain" body: block "{}" source="{}"