unified: Emit base types from inheritance clauses

Map a nominal type's inheritance clause (`class C: Base, Proto`, and
likewise for enum/struct/protocol/extension) to `base_type` children on
the `class_like_declaration`, one per inherited type. The tree-sitter
path dropped these (no corpus target had a `base_type`) and the mapping
matched that for parity; swift-syntax exposes the clause cleanly.

Adds a focused `class-with-multiple-base-types` corpus case and updates
`class-inheritance` to witness the restored base types.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Taus
2026-07-23 13:43:13 +00:00
parent 08024d8f4a
commit 80c4b443f6
4 changed files with 97 additions and 11 deletions

View File

@@ -1028,50 +1028,73 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
// (swift-syntax represents `#selector`/`#keyPath` and other macro
// expansions uniformly as a `macroExpansionExpr`).
rule!((macroExpansionExpr) => (unsupported_node)),
// PARITY(tree-sitter): a nominal type's `inheritanceClause` (`: Base,
// Proto`) is not emitted as a `base_type` — the tree-sitter path drops
// it (no corpus target has a `base_type`). swift-syntax exposes it
// cleanly, so emitting `base_type` is a correctness improvement to make
// once tree-sitter is retired. Each declaration keyword gets its own
// rule; the bodies are identical but for the keyword.
// A nominal type's `inheritanceClause` (`: Base, Proto`) becomes a list
// of `base_type`s, one per inherited type. The tree-sitter path dropped
// it (no corpus target had a `base_type`) and the mapping matched that
// for parity; swift-syntax exposes it cleanly. Each declaration keyword
// gets its own rule; the bodies are identical but for the keyword.
// Class declaration with body containing members
rule!(
(classDecl classKeyword: @kind modifiers: _* @mods name: @name memberBlock: (memberBlock members: _* @members))
(classDecl
classKeyword: @kind
modifiers: _* @mods
name: @name
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// Enum class declaration: same as a regular class but with an enum body.
rule!(
(enumDecl enumKeyword: @kind modifiers: _* @mods name: @name memberBlock: (memberBlock members: _* @members))
(enumDecl
enumKeyword: @kind
modifiers: _* @mods
name: @name
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// A `struct` declaration.
rule!(
(structDecl structKeyword: @kind modifiers: _* @mods name: @name memberBlock: (memberBlock members: _* @members))
(structDecl
structKeyword: @kind
modifiers: _* @mods
name: @name
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// Protocol declaration
rule!(
(protocolDecl protocolKeyword: @kind modifiers: _* @mods name: @name memberBlock: (memberBlock members: _* @members))
(protocolDecl
protocolKeyword: @kind
modifiers: _* @mods
name: @name
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// An `extension Foo { … }` is likewise a `class_like_declaration`, named
@@ -1080,12 +1103,18 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
// a `memberType`) name the declaration just like simple ones, matching the
// old tree-sitter `user_type` behaviour.
rule!(
(extensionDecl extensionKeyword: @kind modifiers: _* @mods extendedType: @@name memberBlock: (memberBlock members: _* @members))
(extensionDecl
extensionKeyword: @kind
modifiers: _* @mods
extendedType: @@name
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// A member of a type declaration unwraps to the contained declaration.

View File

@@ -35,3 +35,8 @@ top_level
class_like_declaration
modifier: modifier "class"
name: identifier "Dog"
base_type:
base_type
type:
named_type_expr
name: identifier "Animal"

View File

@@ -0,0 +1,51 @@
class Button: Control, Drawable {}
---
sourceFile
endOfFileToken: endOfFile
statements:
codeBlockItem
item:
classDecl
attributes:
name: identifier "Button"
inheritanceClause:
inheritanceClause
colon: :
inheritedTypes:
inheritedType
trailingComma: ,
type:
identifierType
name: identifier "Control"
inheritedType
type:
identifierType
name: identifier "Drawable"
memberBlock:
memberBlock
leftBrace: {
rightBrace: }
members:
modifiers:
classKeyword: class
---
top_level
body:
block
stmt:
class_like_declaration
modifier: modifier "class"
name: identifier "Button"
base_type:
base_type
type:
named_type_expr
name: identifier "Control"
base_type
type:
named_type_expr
name: identifier "Drawable"

View File

@@ -0,0 +1 @@
class Button: Control, Drawable {}