swiftlang / swift-syntax

A set of Swift libraries for parsing, inspecting, generating, and transforming Swift source code.
Apache License 2.0
3.24k stars 413 forks source link

[SwiftParser] Improve ergonomics for initializers of `RawXXXSyntax` #2805

Closed AppAppWorks closed 2 months ago

AppAppWorks commented 2 months ago

This PR aims to improve developer ergonomics when using the designated initializers of raw syntax nodes. Simply put, it's through achieving feature parity with the non-raw side.

Prefer generic types over type erasure types

Currently the designated initializer of RawXXXSyntax always uses type-erased types instead of generic types as parameter types, leading to much clunkiness, e.g.

public struct RawPatternExprSyntax: RawExprSyntaxNodeProtocol {
  public init(
    _ unexpectedBeforePattern: RawUnexpectedNodesSyntax? = nil, 
    pattern: RawPatternSyntax, // type erasure
    _ unexpectedAfterPattern: RawUnexpectedNodesSyntax? = nil, 
    arena: __shared SyntaxArena
  )
}

let pattern = RawIdentifierPatternSyntax(
  identifier: identifier,
  arena: self.arena
)
return RawExprSyntax(RawPatternExprSyntax(pattern: RawPatternSyntax(pattern), arena: self.arena))

The initializer body, however, only invokes pattern.raw, therefore we could just replace pattern: RawPatternSyntax with pattern: some RawPatternSyntaxNodeProtocol transparently,

public struct RawPatternExprSyntax: RawExprSyntaxNodeProtocol {
  public init(
    _ unexpectedBeforePattern: RawUnexpectedNodesSyntax? = nil, 
    pattern: some RawPatternSyntaxNodeProtocol, // generic type
    _ unexpectedAfterPattern: RawUnexpectedNodesSyntax? = nil, 
    arena: __shared SyntaxArena
  )
}

let pattern = RawIdentifierPatternSyntax(
  identifier: identifier,
  arena: self.arena
)
// clunky type-erasure removed
return RawExprSyntax(RawPatternExprSyntax(pattern: pattern, arena: self.arena))

Actually it's what PatternExprSyntax.init has been doing,

public init(
  leadingTrivia: Trivia? = nil,
  _ unexpectedBeforePattern: UnexpectedNodesSyntax? = nil,
  pattern: some PatternSyntaxProtocol,
  _ unexpectedAfterPattern: UnexpectedNodesSyntax? = nil,
  trailingTrivia: Trivia? = nil
)

Generate convenience generic initializers for choices enum

e.g.

public struct RawCodeBlockItemSyntax: RawSyntaxNodeProtocol {
  public enum Item: RawSyntaxNodeProtocol {
    case `decl`(RawDeclSyntax)
    case `stmt`(RawStmtSyntax)
    case `expr`(RawExprSyntax)

    public init(_ other: some RawDeclSyntaxNodeProtocol) {
      self = .decl(RawDeclSyntax(other))
    }

    public init(_ other: some RawStmtSyntaxNodeProtocol) {
      self = .stmt(RawStmtSyntax(other))
    }

    public init(_ other: some RawExprSyntaxNodeProtocol) {
      self = .expr(RawExprSyntax(other))
    }

So call sites might get rid of type erasure as follows,

// requires type erasure
.expr(RawExprSyntax(RawMissingExprSyntax(arena: self.arena)))

// supports generic type 
.init(RawMissingExprSyntax(arena: self.arena))

Remove type erasure in call sites

This PR has removed unnecessary uses of type-erased types in many call sites thanks to the new initializers.

AppAppWorks commented 2 months ago

Could you change those to the actual type names or stick with the enum case name if we don’t actually need to go through the initializer?

I've reverted most changes back to their original states, however for the convenience initializers I've implemented another idea, e.g.

/// convenience init for `decl`
public init(decl: some RawDeclSyntaxNodeProtocol) {
  self = .decl(RawDeclSyntax(decl))
}

The call sites should be more readable now?

item: .init(expr: RawMissingExprSyntax(arena: self.arena)),
ahoppen commented 2 months ago

@swift-ci Please test

ahoppen commented 2 months ago

@swift-ci Please test

ahoppen commented 2 months ago

@swift-ci Please test Windows