Skip to content

Conversation

DougGregor
Copy link
Member

Make the main categories of syntax nodes (DeclSyntax, ExprSyntax, StmtSyntax, TypeSyntax, and PatternSyntax) conform to ExpressiblyByStringInterpolation. The string literal portions are source text, and one can interpolate both additional source text and syntax nodes into that source text. The result will be parsed by the parser to produce a node of the given kind. For example, we can produce a TypeSyntax like this:

let tupleSyntax: TypeSyntax = "(Int, name: String)"

And then use that in another production, for example to create a function declaration:

let fnSyntax: DeclSyntax =
   "func \(name)(String) async throws -> \(tupleSyntax)"

This approach is intended to simplify code generation, by allowing code to generate Swift syntax trees in the most natural way possible---by writing the Swift code that generates them.

Make the main categories of syntax nodes (`DeclSyntax`,
`ExprSyntax`, `StmtSyntax`, `TypeSyntax`, and `PatternSyntax`)
conform to `ExpressiblyByStringInterpolation`. The string literal
portions are source text, and one can interpolate both additional
source text and syntax nodes into that source text. The result
will be parsed by the parser to produce a node of the given
kind. For example, we can produce a `TypeSyntax` like this:

    let tupleSyntax: TypeSyntax = "(Int, name: String)"

And then use that in another production, for example to create a
function declaration:

    let fnSyntax: DeclSyntax =
       "func \(name)(String) async throws -> \(tupleSyntax)"

This approach is intended to simplify code generation, by
allowing code to generate Swift syntax trees in the most natural
way possible---by writing the Swift code that generates them.
@DougGregor DougGregor requested a review from ahoppen as a code owner September 12, 2022 04:05
@DougGregor
Copy link
Member Author

@swift-ci please test

@ktoso
Copy link
Contributor

ktoso commented Sep 12, 2022

I'm loving this a lot 🥳

Managed to get out of the source generation business a while ago, but it'll be back -- very happy we'll have this at our disposal then 👍

@DougGregor
Copy link
Member Author

Hmm, I think I should opt in SourceFileSyntax as well, because it's handy to generate whole source files like this.

@jpsim
Copy link
Contributor

jpsim commented Sep 12, 2022

Looks great. Here's how it simplifies some rewriting logic in SwiftLint:

Before

let functionCall = FunctionCallExprSyntax(
    calledExpression: ExprSyntax(
        MemberAccessExprSyntax(
            base: node.first!.withoutTrivia(),
            dot: .periodToken(),
            name: .identifier("toggle"),
            declNameArguments: nil
        )
    ),
    leftParen: .leftParenToken(), argumentList: .init([]), rightParen: .rightParenToken(),
    trailingClosure: nil, additionalTrailingClosures: nil
)

let newNode = node
    .replacing(childAt: 0, with: ExprSyntax(functionCall))
    .removingLast()
    .removingLast()
    .withLeadingTrivia(node.leadingTrivia ?? .zero)
    .withTrailingTrivia(node.trailingTrivia ?? .zero)

After

let newNode = node
    .replacing(childAt: 0, with: "\(node.first!.withoutTrivia()).toggle()")
    .removingLast()
    .removingLast()
    .withLeadingTrivia(node.leadingTrivia ?? .zero)
    .withTrailingTrivia(node.trailingTrivia ?? .zero)

This is for the toggle_bool rule that rewrites someFlag = !someFlag to someFlag.toggle().

@DougGregor
Copy link
Member Author

This is for the toggle_bool rule that rewrites someFlag = !someFlag to someFlag.toggle().

Beautiful!

@DougGregor DougGregor merged commit 3ce27c0 into swiftlang:main Sep 13, 2022
@DougGregor DougGregor deleted the syntax-string-interpolation branch September 13, 2022 04:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants