From 276c8d40e505c8ef287373734d02e1118f39dbe1 Mon Sep 17 00:00:00 2001 From: Roope Virtanen Date: Thu, 14 Nov 2024 13:24:56 +0200 Subject: [PATCH 1/3] Add `AsyncSyntaxRewriter` --- .../GenerateSwiftSyntax.swift | 9 + .../SwiftSyntaxDoccIndexTemplate.md | 1 + .../swiftsyntax/SyntaxRewriterFile.swift | 634 +-- Sources/SwiftSyntax/CMakeLists.txt | 1 + .../generated/SwiftSyntax.md | 1 + .../generated/AsyncSyntaxRewriter.swift | 3970 +++++++++++++++++ .../generated/SyntaxRewriter.swift | 7 +- 7 files changed, 4323 insertions(+), 300 deletions(-) create mode 100644 Sources/SwiftSyntax/generated/AsyncSyntaxRewriter.swift diff --git a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift index 4889bf1d639..2c6044ac055 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift @@ -129,6 +129,15 @@ struct GenerateSwiftSyntax: AsyncParsableCommand { GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxEnum.swift"], syntaxEnumFile), GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxKind.swift"], syntaxKindFile), GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxRewriter.swift"], syntaxRewriterFile), + GeneratedFileSpec( + swiftSyntaxGeneratedDir + ["AsyncSyntaxRewriter.swift"], + makeSyntaxRewriterFile( + className: "AsyncSyntaxRewriter", + functionEffectSpecifiers: "async", + functionCallOperators: "await", + spiName: "MacroExpansion" + ) + ), GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTraits.swift"], syntaxTraitsFile), GeneratedFileSpec( swiftSyntaxGeneratedDir + ["SyntaxVisitor.swift"], diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md index c100fb2eb1c..dc56bb2577f 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md @@ -48,6 +48,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift index 3761db83607..e81fb9cfb0f 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxRewriterFile.swift @@ -15,356 +15,392 @@ import SwiftSyntaxBuilder import SyntaxSupport import Utils -let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) { - try! ClassDeclSyntax( - """ - // - // This file defines the SyntaxRewriter, a class that performs a standard walk - // and tree-rebuilding pattern. - // - // Subclassers of this class can override the walking behavior for any syntax - // node and transform nodes however they like. - // - //===----------------------------------------------------------------------===// - - open class SyntaxRewriter - """ - ) { - DeclSyntax("public let viewMode: SyntaxTreeViewMode") - DeclSyntax( - """ - /// The arena in which the parents of rewritten nodes should be allocated. - /// - /// The `SyntaxRewriter` subclass is responsible for generating the rewritten nodes. To incorporate them into the - /// tree, all of the rewritten node's parents also need to be re-created. This is the arena in which those - /// intermediate nodes should be allocated. - private let arena: SyntaxArena? - """ - ) +let syntaxRewriterFile = makeSyntaxRewriterFile() + +func makeSyntaxRewriterFile( + className: String = "SyntaxRewriter", + functionEffectSpecifiers: String? = nil, + functionCallOperators: String? = nil, + spiName: String? = nil +) -> SourceFileSyntax { + let functionEffectSpecifiers = + if let functionEffectSpecifiers { + " ".appending(functionEffectSpecifiers) + } else { + String() + } + let functionCallOperators = functionCallOperators?.appending(" ") ?? String() + let spiAttribute = + if let spiName { + "\n@_spi(\(spiName))" + } else { + String() + } - DeclSyntax( + return SourceFileSyntax(leadingTrivia: copyrightHeader) { + try! ClassDeclSyntax( """ - /// 'Syntax' object factory recycling 'Syntax.Info' instances. - private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() + // + // This file defines the \(raw: className), a class that performs a standard walk + // and tree-rebuilding pattern. + // + // Subclassers of this class can override the walking behavior for any syntax + // node and transform nodes however they like. + // + //===----------------------------------------------------------------------===// + \(raw: spiAttribute) + open class \(raw: className) """ - ) + ) { + DeclSyntax("public let viewMode: SyntaxTreeViewMode") + DeclSyntax( + """ + /// The arena in which the parents of rewritten nodes should be allocated. + /// + /// The `\(raw: className)` subclass is responsible for generating the rewritten nodes. To incorporate them into the + /// tree, all of the rewritten node's parents also need to be re-created. This is the arena in which those + /// intermediate nodes should be allocated. + private let arena: SyntaxArena? + """ + ) - DeclSyntax( - """ - public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) { - self.viewMode = viewMode - self.arena = nil - } - """ - ) + DeclSyntax( + """ + /// 'Syntax' object factory recycling 'Syntax.Info' instances. + private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() + """ + ) - DeclSyntax( - """ - @_spi(RawSyntax) - public init(viewMode: SyntaxTreeViewMode = .sourceAccurate, arena: SyntaxArena? = nil) { - self.viewMode = viewMode - self.arena = arena - } - """ - ) + DeclSyntax( + """ + public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) { + self.viewMode = viewMode + self.arena = nil + } + """ + ) - DeclSyntax( - """ - /// Rewrite `node`, keeping its parent unless `detach` is `true`. - public func rewrite(_ node: some SyntaxProtocol, detach: Bool = false) -> Syntax { - var rewritten = Syntax(node) - self.dispatchVisit(&rewritten) - if detach { - return rewritten + DeclSyntax( + """ + @_spi(RawSyntax) + public init(viewMode: SyntaxTreeViewMode = .sourceAccurate, arena: SyntaxArena? = nil) { + self.viewMode = viewMode + self.arena = arena } + """ + ) + + DeclSyntax( + """ + /// Rewrite `node`, keeping its parent unless `detach` is `true`. + public func rewrite(_ node: some SyntaxProtocol, detach: Bool = false)\(raw: functionEffectSpecifiers) -> Syntax { + var rewritten = Syntax(node) + \(raw: functionCallOperators)self.dispatchVisit(&rewritten) + if detach { + return rewritten + } - return withExtendedLifetime(rewritten) { - return Syntax(node).replacingSelf(rewritten.raw, rawNodeArena: rewritten.raw.arenaReference.retained, allocationArena: SyntaxArena()) + return withExtendedLifetime(rewritten) { + return Syntax(node).replacingSelf(rewritten.raw, rawNodeArena: rewritten.raw.arenaReference.retained, allocationArena: SyntaxArena()) + } } - } - """ - ) + """ + ) - DeclSyntax( - """ - /// Visit a ``TokenSyntax``. - /// - Parameter token: the token that is being visited - /// - Returns: the rewritten node - open func visit(_ token: TokenSyntax) -> TokenSyntax { - return token - } - """ - ) + DeclSyntax( + """ + /// Visit a ``TokenSyntax``. + /// - Parameter token: the token that is being visited + /// - Returns: the rewritten node + open func visit(_ token: TokenSyntax)\(raw: functionEffectSpecifiers) -> TokenSyntax { + return token + } + """ + ) - DeclSyntax( - """ - /// The function called before visiting the node and its descendants. - /// - node: the node we are about to visit. - open func visitPre(_ node: Syntax) {} - """ - ) + DeclSyntax( + """ + /// The function called before visiting the node and its descendants. + /// - node: the node we are about to visit. + open func visitPre(_ node: Syntax)\(raw: functionEffectSpecifiers) {} + """ + ) - DeclSyntax( - """ - /// Override point to choose custom visitation dispatch instead of the - /// specialized `visit(_:)` methods. Use this instead of those methods if - /// you intend to dynamically dispatch rewriting behavior. - /// - note: If this method returns a non-nil result, the subsequent - /// `visitAny(_:)` methods and the specialized `visit(_:)` - /// methods will not be called for this node and the - /// visited node will be replaced by the returned node in the - /// rewritten tree. - /// You can call the ``SyntaxRewriter/rewrite(_:detach:)`` - /// method recursively when returning a non-nil result - /// if you want to visit the node's children anyway. - open func visitAny(_ node: Syntax) -> Syntax? { - return nil - } - """ - ) + DeclSyntax( + """ + /// Override point to choose custom visitation dispatch instead of the + /// specialized `visit(_:)` methods. Use this instead of those methods if + /// you intend to dynamically dispatch rewriting behavior. + /// - note: If this method returns a non-nil result, the subsequent + /// `visitAny(_:)` methods and the specialized `visit(_:)` + /// methods will not be called for this node and the + /// visited node will be replaced by the returned node in the + /// rewritten tree. + /// You can call the ``\(raw: className)/rewrite(_:detach:)`` + /// method recursively when returning a non-nil result + /// if you want to visit the node's children anyway. + open func visitAny(_ node: Syntax)\(raw: functionEffectSpecifiers) -> Syntax? { + return nil + } + """ + ) - DeclSyntax( - """ - /// The function called after visiting the node and its descendants. - /// - node: the node we just finished visiting. - open func visitPost(_ node: Syntax) {} - """ - ) + DeclSyntax( + """ + /// The function called after visiting the node and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: Syntax)\(raw: functionEffectSpecifiers) {} + """ + ) - DeclSyntax( - """ - /// Visit any Syntax node. - /// - Parameter node: the node that is being visited - /// - Returns: the rewritten node - @available(*, deprecated, renamed: "rewrite(_:detach:)") - public func visit(_ node: Syntax) -> Syntax { - var rewritten = node - dispatchVisit(&rewritten) - return rewritten - } - """ - ) + DeclSyntax( + """ + /// Visit any Syntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + @available(*, deprecated, renamed: "rewrite(_:detach:)") + public func visit(_ node: Syntax)\(raw: functionEffectSpecifiers) -> Syntax { + var rewritten = node + \(raw: functionCallOperators)dispatchVisit(&rewritten) + return rewritten + } + """ + ) - DeclSyntax( - """ - public func visit(_ node: T) -> T { - var rewritten = Syntax(node) - dispatchVisit(&rewritten) - return rewritten.cast(T.self) + DeclSyntax( + """ + public func visit(_ node: T)\(raw: functionEffectSpecifiers) -> T { + var rewritten = Syntax(node) + \(raw: functionCallOperators)dispatchVisit(&rewritten) + return rewritten.cast(T.self) + } + """ + ) + + for node in SYNTAX_NODES where !node.kind.isBase { + if (node.base == .syntax || node.base == .syntaxCollection) && node.kind != .missing { + DeclSyntax( + """ + /// Visit a \(raw: node.kind.doccLink). + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + \(node.apiAttributes())\ + open func visit(_ node: \(node.kind.syntaxType))\(raw: functionEffectSpecifiers) -> \(node.kind.syntaxType) { + return \(raw: functionCallOperators)visitChildren(node._syntaxNode).cast(\(node.kind.syntaxType).self) + } + """ + ) + } else { + DeclSyntax( + """ + /// Visit a \(raw: node.kind.doccLink). + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + \(node.apiAttributes())\ + open func visit(_ node: \(node.kind.syntaxType))\(raw: functionEffectSpecifiers) -> \(node.baseType.syntaxBaseName) { + return \(node.baseType.syntaxBaseName)(\(raw: functionCallOperators)visitChildren(node._syntaxNode).cast(\(node.kind.syntaxType).self)) + } + """ + ) + } } - """ - ) - for node in SYNTAX_NODES where !node.kind.isBase { - if (node.base == .syntax || node.base == .syntaxCollection) && node.kind != .missing { + for baseNode in SYNTAX_NODES + where baseNode.kind.isBase && baseNode.kind != .syntax && baseNode.kind != .syntaxCollection { + let baseKind = baseNode.kind DeclSyntax( """ - /// Visit a \(raw: node.kind.doccLink). + /// Visit any \(baseKind.syntaxType) node. /// - Parameter node: the node that is being visited /// - Returns: the rewritten node - \(node.apiAttributes())\ - open func visit(_ node: \(node.kind.syntaxType)) -> \(node.kind.syntaxType) { - return visitChildren(node._syntaxNode).cast(\(node.kind.syntaxType).self) - } - """ - ) - } else { - DeclSyntax( - """ - /// Visit a \(raw: node.kind.doccLink). - /// - Parameter node: the node that is being visited - /// - Returns: the rewritten node - \(node.apiAttributes())\ - open func visit(_ node: \(node.kind.syntaxType)) -> \(node.baseType.syntaxBaseName) { - return \(node.baseType.syntaxBaseName)(visitChildren(node._syntaxNode).cast(\(node.kind.syntaxType).self)) + \(baseNode.apiAttributes())\ + public func visit(_ node: \(baseKind.syntaxType))\(raw: functionEffectSpecifiers) -> \(baseKind.syntaxType) { + var node: Syntax = Syntax(node) + \(raw: functionCallOperators)dispatchVisit(&node) + return node.cast(\(baseKind.syntaxType).self) } """ ) } - } - for baseNode in SYNTAX_NODES - where baseNode.kind.isBase && baseNode.kind != .syntax && baseNode.kind != .syntaxCollection { - let baseKind = baseNode.kind DeclSyntax( """ - /// Visit any \(baseKind.syntaxType) node. - /// - Parameter node: the node that is being visited - /// - Returns: the rewritten node - \(baseNode.apiAttributes())\ - public func visit(_ node: \(baseKind.syntaxType)) -> \(baseKind.syntaxType) { - var node: Syntax = Syntax(node) - dispatchVisit(&node) - return node.cast(\(baseKind.syntaxType).self) + /// Interpret `node` as a node of type `nodeType`, visit it, calling + /// the `visit` to transform the node. + @inline(__always) + private func visitImpl( + _ node: inout Syntax, + _ nodeType: NodeType.Type, + _ visit: (NodeType)\(raw: functionEffectSpecifiers) -> some SyntaxProtocol + )\(raw: functionEffectSpecifiers) { + let origNode = node + \(raw: functionCallOperators)visitPre(origNode) + node = + if let newNode = \(raw: functionCallOperators)visitAny(origNode) { + newNode + } else { + Syntax(\(raw: functionCallOperators)visit(origNode.cast(NodeType.self))) + } + \(raw: functionCallOperators)visitPost(origNode) } """ ) - } - DeclSyntax( - """ - /// Interpret `node` as a node of type `nodeType`, visit it, calling - /// the `visit` to transform the node. - @inline(__always) - private func visitImpl( - _ node: inout Syntax, - _ nodeType: NodeType.Type, - _ visit: (NodeType) -> some SyntaxProtocol - ) { - let origNode = node - visitPre(origNode) - node = visitAny(origNode) ?? Syntax(visit(origNode.cast(NodeType.self))) - visitPost(origNode) - } - """ - ) - - try IfConfigDeclSyntax( - leadingTrivia: - """ - // SwiftSyntax requires a lot of stack space in debug builds for syntax tree - // rewriting. In scenarios with reduced stack space (in particular dispatch - // queues), this easily results in a stack overflow. To work around this issue, - // use a less performant but also less stack-hungry version of SwiftSyntax's - // SyntaxRewriter in debug builds. - - """, - clauses: IfConfigClauseListSyntax { - IfConfigClauseSyntax( - poundKeyword: .poundIfToken(), - condition: ExprSyntax("DEBUG"), - elements: .statements( - try CodeBlockItemListSyntax { - try FunctionDeclSyntax( - """ - /// Implementation detail of visit(_:). Do not call directly. - /// - /// Returns the function that shall be called to visit a specific syntax node. - /// - /// To determine the correct specific visitation function for a syntax node, - /// we need to switch through a huge switch statement that covers all syntax - /// types. In debug builds, the cases of this switch statement do not share - /// stack space (rdar://55929175). Because of this, the switch statement - /// requires about 15KB of stack space. In scenarios with reduced - /// stack size (in particular dispatch queues), this often results in a stack - /// overflow during syntax tree rewriting. - /// - /// To circumvent this problem, make calling the specific visitation function - /// a two-step process: First determine the function to call in this function - /// and return a reference to it, then call it. This way, the stack frame - /// that determines the correct visitation function will be popped of the - /// stack before the function is being called, making the switch's stack - /// space transient instead of having it linger in the call stack. - private func visitationFunc(for node: Syntax) -> ((inout Syntax) -> Void) - """ - ) { - try SwitchExprSyntax("switch node.raw.kind") { - SwitchCaseSyntax("case .token:") { - StmtSyntax("return { self.visitImpl(&$0, TokenSyntax.self, self.visit) }") - } + try IfConfigDeclSyntax( + leadingTrivia: + """ + // SwiftSyntax requires a lot of stack space in debug builds for syntax tree + // rewriting. In scenarios with reduced stack space (in particular dispatch + // queues), this easily results in a stack overflow. To work around this issue, + // use a less performant but also less stack-hungry version of SwiftSyntax's + // \(className) in debug builds. + + """, + clauses: IfConfigClauseListSyntax { + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("DEBUG"), + elements: .statements( + try CodeBlockItemListSyntax { + try FunctionDeclSyntax( + """ + /// Implementation detail of visit(_:). Do not call directly. + /// + /// Returns the function that shall be called to visit a specific syntax node. + /// + /// To determine the correct specific visitation function for a syntax node, + /// we need to switch through a huge switch statement that covers all syntax + /// types. In debug builds, the cases of this switch statement do not share + /// stack space (rdar://55929175). Because of this, the switch statement + /// requires about 15KB of stack space. In scenarios with reduced + /// stack size (in particular dispatch queues), this often results in a stack + /// overflow during syntax tree rewriting. + /// + /// To circumvent this problem, make calling the specific visitation function + /// a two-step process: First determine the function to call in this function + /// and return a reference to it, then call it. This way, the stack frame + /// that determines the correct visitation function will be popped of the + /// stack before the function is being called, making the switch's stack + /// space transient instead of having it linger in the call stack. + private func visitationFunc(for node: Syntax)\(raw: functionEffectSpecifiers) -> ((inout Syntax)\(raw: functionEffectSpecifiers) -> Void) + """ + ) { + try SwitchExprSyntax("switch node.raw.kind") { + SwitchCaseSyntax("case .token:") { + StmtSyntax( + "return { \(raw: functionCallOperators)self.visitImpl(&$0, TokenSyntax.self, self.visit) }" + ) + } - for node in NON_BASE_SYNTAX_NODES { - SwitchCaseSyntax("case .\(node.enumCaseCallName):") { - StmtSyntax("return { self.visitImpl(&$0, \(node.kind.syntaxType).self, self.visit) }") + for node in NON_BASE_SYNTAX_NODES { + SwitchCaseSyntax("case .\(node.enumCaseCallName):") { + StmtSyntax( + "return { \(raw: functionCallOperators)self.visitImpl(&$0, \(node.kind.syntaxType).self, self.visit) }" + ) + } } } } - } - DeclSyntax( - """ - private func dispatchVisit(_ node: inout Syntax) { - visitationFunc(for: node)(&node) - } - """ - ) - } - ) - ) - IfConfigClauseSyntax( - poundKeyword: .poundElseToken(), - elements: .statements( - CodeBlockItemListSyntax { - try! FunctionDeclSyntax("private func dispatchVisit(_ node: inout Syntax)") { - try SwitchExprSyntax("switch node.raw.kind") { - SwitchCaseSyntax("case .token:") { - StmtSyntax("return visitImpl(&node, TokenSyntax.self, visit)") + DeclSyntax( + """ + private func dispatchVisit(_ node: inout Syntax)\(raw: functionEffectSpecifiers) { + \(raw: functionCallOperators)visitationFunc(for: node)(&node) } + """ + ) + } + ) + ) + IfConfigClauseSyntax( + poundKeyword: .poundElseToken(), + elements: .statements( + CodeBlockItemListSyntax { + try! FunctionDeclSyntax( + "private func dispatchVisit(_ node: inout Syntax)\(raw: functionEffectSpecifiers)" + ) { + try SwitchExprSyntax("switch node.raw.kind") { + SwitchCaseSyntax("case .token:") { + StmtSyntax("return \(raw: functionCallOperators)visitImpl(&node, TokenSyntax.self, visit)") + } - for node in NON_BASE_SYNTAX_NODES { - SwitchCaseSyntax("case .\(node.enumCaseCallName):") { - StmtSyntax("return visitImpl(&node, \(node.kind.syntaxType).self, visit)") + for node in NON_BASE_SYNTAX_NODES { + SwitchCaseSyntax("case .\(node.enumCaseCallName):") { + StmtSyntax( + "return \(raw: functionCallOperators)visitImpl(&node, \(node.kind.syntaxType).self, visit)" + ) + } } } } } - } + ) ) - ) - } - ) + } + ) - DeclSyntax( - """ - private func visitChildren(_ node: Syntax) -> Syntax { - // Walk over all children of this node and rewrite them. Don't store any - // rewritten nodes until the first non-`nil` value is encountered. When this - // happens, retrieve all previous syntax nodes from the parent node to - // initialize the new layout. Once we know that we have to rewrite the - // layout, we need to collect all further children, regardless of whether - // they are rewritten or not. - - // newLayout is nil until the first child node is rewritten and rewritten - // nodes are being collected. - var newLayout: UnsafeMutableBufferPointer = .init(start: nil, count: 0) - - // Keep 'SyntaxArena' of rewritten nodes alive until they are wrapped - // with 'Syntax' - var rewrittens: ContiguousArray = [] - - for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { - - // Build the Syntax node to rewrite - var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info) - - dispatchVisit(&childNode) - if childNode.raw.id != child.id { - // The node was rewritten, let's handle it - - if newLayout.baseAddress == nil { - // We have not yet collected any previous rewritten nodes. Initialize - // the new layout with the previous nodes of the parent. - newLayout = .allocate(capacity: node.raw.layoutView!.children.count) - _ = newLayout.initialize(fromContentsOf: node.raw.layoutView!.children) + DeclSyntax( + """ + private func visitChildren(_ node: Syntax)\(raw: functionEffectSpecifiers) -> Syntax { + // Walk over all children of this node and rewrite them. Don't store any + // rewritten nodes until the first non-`nil` value is encountered. When this + // happens, retrieve all previous syntax nodes from the parent node to + // initialize the new layout. Once we know that we have to rewrite the + // layout, we need to collect all further children, regardless of whether + // they are rewritten or not. + + // newLayout is nil until the first child node is rewritten and rewritten + // nodes are being collected. + var newLayout: UnsafeMutableBufferPointer = .init(start: nil, count: 0) + + // Keep 'SyntaxArena' of rewritten nodes alive until they are wrapped + // with 'Syntax' + var rewrittens: ContiguousArray = [] + + for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { + + // Build the Syntax node to rewrite + var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info) + + \(raw: functionCallOperators)dispatchVisit(&childNode) + if childNode.raw.id != child.id { + // The node was rewritten, let's handle it + + if newLayout.baseAddress == nil { + // We have not yet collected any previous rewritten nodes. Initialize + // the new layout with the previous nodes of the parent. + newLayout = .allocate(capacity: node.raw.layoutView!.children.count) + _ = newLayout.initialize(fromContentsOf: node.raw.layoutView!.children) + } + + // Update the rewritten child. + newLayout[Int(info.indexInParent)] = childNode.raw + // Retain the syntax arena of the new node until it's wrapped with Syntax node. + rewrittens.append(childNode.raw.arenaReference.retained) } - // Update the rewritten child. - newLayout[Int(info.indexInParent)] = childNode.raw - // Retain the syntax arena of the new node until it's wrapped with Syntax node. - rewrittens.append(childNode.raw.arenaReference.retained) + // Recycle 'childNode.info' + nodeFactory.dispose(&childNode) } - // Recycle 'childNode.info' - nodeFactory.dispose(&childNode) - } + if newLayout.baseAddress != nil { + // A child node was rewritten. Build the updated node. - if newLayout.baseAddress != nil { - // A child node was rewritten. Build the updated node. - - let arena = self.arena ?? SyntaxArena() - let newRaw = node.raw.layoutView!.replacingLayout(with: newLayout, arena: arena) - newLayout.deinitialize() - newLayout.deallocate() - // 'withExtendedLifetime' to keep 'SyntaxArena's of them alive until here. - return withExtendedLifetime(rewrittens) { - Syntax(raw: newRaw, rawNodeArena: arena) + let arena = self.arena ?? SyntaxArena() + let newRaw = node.raw.layoutView!.replacingLayout(with: newLayout, arena: arena) + newLayout.deinitialize() + newLayout.deallocate() + // 'withExtendedLifetime' to keep 'SyntaxArena's of them alive until here. + return withExtendedLifetime(rewrittens) { + Syntax(raw: newRaw, rawNodeArena: arena) + } + } else { + // No child node was rewritten. So no need to change this node as well. + return node } - } else { - // No child node was rewritten. So no need to change this node as well. - return node } - } - """ - ) + """ + ) + } } } diff --git a/Sources/SwiftSyntax/CMakeLists.txt b/Sources/SwiftSyntax/CMakeLists.txt index dd77da30625..a252968d509 100644 --- a/Sources/SwiftSyntax/CMakeLists.txt +++ b/Sources/SwiftSyntax/CMakeLists.txt @@ -58,6 +58,7 @@ add_swift_syntax_library(SwiftSyntax generated/raw/RawSyntaxNodesTUVWXYZ.swift generated/raw/RawSyntaxValidation.swift + generated/AsyncSyntaxRewriter.swift generated/ChildNameForKeyPath.swift generated/Keyword.swift generated/RenamedChildrenCompatibility.swift diff --git a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md index 10a71a658da..a38ef649a0d 100644 --- a/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md +++ b/Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md @@ -48,6 +48,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code. - - - +- - - - diff --git a/Sources/SwiftSyntax/generated/AsyncSyntaxRewriter.swift b/Sources/SwiftSyntax/generated/AsyncSyntaxRewriter.swift new file mode 100644 index 00000000000..fdfc9cf6ab1 --- /dev/null +++ b/Sources/SwiftSyntax/generated/AsyncSyntaxRewriter.swift @@ -0,0 +1,3970 @@ +//// Automatically generated by generate-swift-syntax +//// Do not edit directly! +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// +// This file defines the AsyncSyntaxRewriter, a class that performs a standard walk +// and tree-rebuilding pattern. +// +// Subclassers of this class can override the walking behavior for any syntax +// node and transform nodes however they like. +// +//===----------------------------------------------------------------------===// + +@_spi(MacroExpansion) +open class AsyncSyntaxRewriter { + public let viewMode: SyntaxTreeViewMode + + /// The arena in which the parents of rewritten nodes should be allocated. + /// + /// The `AsyncSyntaxRewriter` subclass is responsible for generating the rewritten nodes. To incorporate them into the + /// tree, all of the rewritten node's parents also need to be re-created. This is the arena in which those + /// intermediate nodes should be allocated. + private let arena: SyntaxArena? + + /// 'Syntax' object factory recycling 'Syntax.Info' instances. + private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory() + + public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) { + self.viewMode = viewMode + self.arena = nil + } + + @_spi(RawSyntax) + public init(viewMode: SyntaxTreeViewMode = .sourceAccurate, arena: SyntaxArena? = nil) { + self.viewMode = viewMode + self.arena = arena + } + + /// Rewrite `node`, keeping its parent unless `detach` is `true`. + public func rewrite(_ node: some SyntaxProtocol, detach: Bool = false) async -> Syntax { + var rewritten = Syntax(node) + await self.dispatchVisit(&rewritten) + if detach { + return rewritten + } + + return withExtendedLifetime(rewritten) { + return Syntax(node).replacingSelf(rewritten.raw, rawNodeArena: rewritten.raw.arenaReference.retained, allocationArena: SyntaxArena()) + } + } + + /// Visit a ``TokenSyntax``. + /// - Parameter token: the token that is being visited + /// - Returns: the rewritten node + open func visit(_ token: TokenSyntax) async -> TokenSyntax { + return token + } + + /// The function called before visiting the node and its descendants. + /// - node: the node we are about to visit. + open func visitPre(_ node: Syntax) async { + } + + /// Override point to choose custom visitation dispatch instead of the + /// specialized `visit(_:)` methods. Use this instead of those methods if + /// you intend to dynamically dispatch rewriting behavior. + /// - note: If this method returns a non-nil result, the subsequent + /// `visitAny(_:)` methods and the specialized `visit(_:)` + /// methods will not be called for this node and the + /// visited node will be replaced by the returned node in the + /// rewritten tree. + /// You can call the ``AsyncSyntaxRewriter/rewrite(_:detach:)`` + /// method recursively when returning a non-nil result + /// if you want to visit the node's children anyway. + open func visitAny(_ node: Syntax) async -> Syntax? { + return nil + } + + /// The function called after visiting the node and its descendants. + /// - node: the node we just finished visiting. + open func visitPost(_ node: Syntax) async { + } + + /// Visit any Syntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + @available(*, deprecated, renamed: "rewrite(_:detach:)") + public func visit(_ node: Syntax) async -> Syntax { + var rewritten = node + await dispatchVisit(&rewritten) + return rewritten + } + + public func visit(_ node: T) async -> T { + var rewritten = Syntax(node) + await dispatchVisit(&rewritten) + return rewritten.cast(T.self) + } + + /// Visit a ``AccessorBlockSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AccessorBlockSyntax) async -> AccessorBlockSyntax { + return await visitChildren(node._syntaxNode).cast(AccessorBlockSyntax.self) + } + + /// Visit a ``AccessorDeclListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AccessorDeclListSyntax) async -> AccessorDeclListSyntax { + return await visitChildren(node._syntaxNode).cast(AccessorDeclListSyntax.self) + } + + /// Visit a ``AccessorDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AccessorDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(AccessorDeclSyntax.self)) + } + + /// Visit a ``AccessorEffectSpecifiersSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AccessorEffectSpecifiersSyntax) async -> AccessorEffectSpecifiersSyntax { + return await visitChildren(node._syntaxNode).cast(AccessorEffectSpecifiersSyntax.self) + } + + /// Visit a ``AccessorParametersSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AccessorParametersSyntax) async -> AccessorParametersSyntax { + return await visitChildren(node._syntaxNode).cast(AccessorParametersSyntax.self) + } + + /// Visit a ``ActorDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ActorDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(ActorDeclSyntax.self)) + } + + /// Visit a ``ArrayElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ArrayElementListSyntax) async -> ArrayElementListSyntax { + return await visitChildren(node._syntaxNode).cast(ArrayElementListSyntax.self) + } + + /// Visit a ``ArrayElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ArrayElementSyntax) async -> ArrayElementSyntax { + return await visitChildren(node._syntaxNode).cast(ArrayElementSyntax.self) + } + + /// Visit a ``ArrayExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ArrayExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(ArrayExprSyntax.self)) + } + + /// Visit a ``ArrayTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ArrayTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(ArrayTypeSyntax.self)) + } + + /// Visit a ``ArrowExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ArrowExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(ArrowExprSyntax.self)) + } + + /// Visit a ``AsExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AsExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(AsExprSyntax.self)) + } + + /// Visit a ``AssignmentExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AssignmentExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(AssignmentExprSyntax.self)) + } + + /// Visit a ``AssociatedTypeDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AssociatedTypeDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(AssociatedTypeDeclSyntax.self)) + } + + /// Visit a ``AttributeListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AttributeListSyntax) async -> AttributeListSyntax { + return await visitChildren(node._syntaxNode).cast(AttributeListSyntax.self) + } + + /// Visit a ``AttributeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AttributeSyntax) async -> AttributeSyntax { + return await visitChildren(node._syntaxNode).cast(AttributeSyntax.self) + } + + /// Visit a ``AttributedTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AttributedTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(AttributedTypeSyntax.self)) + } + + /// Visit a ``AvailabilityArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AvailabilityArgumentListSyntax) async -> AvailabilityArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(AvailabilityArgumentListSyntax.self) + } + + /// Visit a ``AvailabilityArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AvailabilityArgumentSyntax) async -> AvailabilityArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(AvailabilityArgumentSyntax.self) + } + + /// Visit a ``AvailabilityConditionSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AvailabilityConditionSyntax) async -> AvailabilityConditionSyntax { + return await visitChildren(node._syntaxNode).cast(AvailabilityConditionSyntax.self) + } + + /// Visit a ``AvailabilityLabeledArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AvailabilityLabeledArgumentSyntax) async -> AvailabilityLabeledArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(AvailabilityLabeledArgumentSyntax.self) + } + + /// Visit a ``AwaitExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: AwaitExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(AwaitExprSyntax.self)) + } + + /// Visit a ``BackDeployedAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: BackDeployedAttributeArgumentsSyntax) async -> BackDeployedAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(BackDeployedAttributeArgumentsSyntax.self) + } + + /// Visit a ``BinaryOperatorExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: BinaryOperatorExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(BinaryOperatorExprSyntax.self)) + } + + /// Visit a ``BooleanLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: BooleanLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(BooleanLiteralExprSyntax.self)) + } + + /// Visit a ``BorrowExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: BorrowExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(BorrowExprSyntax.self)) + } + + /// Visit a ``BreakStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: BreakStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(BreakStmtSyntax.self)) + } + + /// Visit a `_CanImportExprSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: _CanImportExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(_CanImportExprSyntax.self)) + } + + /// Visit a `_CanImportVersionInfoSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: _CanImportVersionInfoSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(_CanImportVersionInfoSyntax.self)) + } + + /// Visit a ``CatchClauseListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CatchClauseListSyntax) async -> CatchClauseListSyntax { + return await visitChildren(node._syntaxNode).cast(CatchClauseListSyntax.self) + } + + /// Visit a ``CatchClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CatchClauseSyntax) async -> CatchClauseSyntax { + return await visitChildren(node._syntaxNode).cast(CatchClauseSyntax.self) + } + + /// Visit a ``CatchItemListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CatchItemListSyntax) async -> CatchItemListSyntax { + return await visitChildren(node._syntaxNode).cast(CatchItemListSyntax.self) + } + + /// Visit a ``CatchItemSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CatchItemSyntax) async -> CatchItemSyntax { + return await visitChildren(node._syntaxNode).cast(CatchItemSyntax.self) + } + + /// Visit a ``ClassDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClassDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(ClassDeclSyntax.self)) + } + + /// Visit a ``ClassRestrictionTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClassRestrictionTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(ClassRestrictionTypeSyntax.self)) + } + + /// Visit a ``ClosureCaptureClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureCaptureClauseSyntax) async -> ClosureCaptureClauseSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureCaptureClauseSyntax.self) + } + + /// Visit a ``ClosureCaptureListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureCaptureListSyntax) async -> ClosureCaptureListSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureCaptureListSyntax.self) + } + + /// Visit a ``ClosureCaptureSpecifierSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureCaptureSpecifierSyntax) async -> ClosureCaptureSpecifierSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureCaptureSpecifierSyntax.self) + } + + /// Visit a ``ClosureCaptureSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureCaptureSyntax) async -> ClosureCaptureSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureCaptureSyntax.self) + } + + /// Visit a ``ClosureExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(ClosureExprSyntax.self)) + } + + /// Visit a ``ClosureParameterClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureParameterClauseSyntax) async -> ClosureParameterClauseSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureParameterClauseSyntax.self) + } + + /// Visit a ``ClosureParameterListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureParameterListSyntax) async -> ClosureParameterListSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureParameterListSyntax.self) + } + + /// Visit a ``ClosureParameterSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureParameterSyntax) async -> ClosureParameterSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureParameterSyntax.self) + } + + /// Visit a ``ClosureShorthandParameterListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureShorthandParameterListSyntax) async -> ClosureShorthandParameterListSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureShorthandParameterListSyntax.self) + } + + /// Visit a ``ClosureShorthandParameterSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureShorthandParameterSyntax) async -> ClosureShorthandParameterSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureShorthandParameterSyntax.self) + } + + /// Visit a ``ClosureSignatureSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ClosureSignatureSyntax) async -> ClosureSignatureSyntax { + return await visitChildren(node._syntaxNode).cast(ClosureSignatureSyntax.self) + } + + /// Visit a ``CodeBlockItemListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CodeBlockItemListSyntax) async -> CodeBlockItemListSyntax { + return await visitChildren(node._syntaxNode).cast(CodeBlockItemListSyntax.self) + } + + /// Visit a ``CodeBlockItemSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CodeBlockItemSyntax) async -> CodeBlockItemSyntax { + return await visitChildren(node._syntaxNode).cast(CodeBlockItemSyntax.self) + } + + /// Visit a ``CodeBlockSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CodeBlockSyntax) async -> CodeBlockSyntax { + return await visitChildren(node._syntaxNode).cast(CodeBlockSyntax.self) + } + + /// Visit a ``CompositionTypeElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CompositionTypeElementListSyntax) async -> CompositionTypeElementListSyntax { + return await visitChildren(node._syntaxNode).cast(CompositionTypeElementListSyntax.self) + } + + /// Visit a ``CompositionTypeElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CompositionTypeElementSyntax) async -> CompositionTypeElementSyntax { + return await visitChildren(node._syntaxNode).cast(CompositionTypeElementSyntax.self) + } + + /// Visit a ``CompositionTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CompositionTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(CompositionTypeSyntax.self)) + } + + /// Visit a ``ConditionElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConditionElementListSyntax) async -> ConditionElementListSyntax { + return await visitChildren(node._syntaxNode).cast(ConditionElementListSyntax.self) + } + + /// Visit a ``ConditionElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConditionElementSyntax) async -> ConditionElementSyntax { + return await visitChildren(node._syntaxNode).cast(ConditionElementSyntax.self) + } + + /// Visit a ``ConformanceRequirementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConformanceRequirementSyntax) async -> ConformanceRequirementSyntax { + return await visitChildren(node._syntaxNode).cast(ConformanceRequirementSyntax.self) + } + + /// Visit a ``ConsumeExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConsumeExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(ConsumeExprSyntax.self)) + } + + /// Visit a ``ContinueStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ContinueStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ContinueStmtSyntax.self)) + } + + /// Visit a ``ConventionAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConventionAttributeArgumentsSyntax) async -> ConventionAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(ConventionAttributeArgumentsSyntax.self) + } + + /// Visit a ``ConventionWitnessMethodAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ConventionWitnessMethodAttributeArgumentsSyntax) async -> ConventionWitnessMethodAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(ConventionWitnessMethodAttributeArgumentsSyntax.self) + } + + /// Visit a ``CopyExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: CopyExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(CopyExprSyntax.self)) + } + + /// Visit a ``DeclModifierDetailSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclModifierDetailSyntax) async -> DeclModifierDetailSyntax { + return await visitChildren(node._syntaxNode).cast(DeclModifierDetailSyntax.self) + } + + /// Visit a ``DeclModifierListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclModifierListSyntax) async -> DeclModifierListSyntax { + return await visitChildren(node._syntaxNode).cast(DeclModifierListSyntax.self) + } + + /// Visit a ``DeclModifierSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclModifierSyntax) async -> DeclModifierSyntax { + return await visitChildren(node._syntaxNode).cast(DeclModifierSyntax.self) + } + + /// Visit a ``DeclNameArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclNameArgumentListSyntax) async -> DeclNameArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(DeclNameArgumentListSyntax.self) + } + + /// Visit a ``DeclNameArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclNameArgumentSyntax) async -> DeclNameArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(DeclNameArgumentSyntax.self) + } + + /// Visit a ``DeclNameArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclNameArgumentsSyntax) async -> DeclNameArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(DeclNameArgumentsSyntax.self) + } + + /// Visit a ``DeclReferenceExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeclReferenceExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(DeclReferenceExprSyntax.self)) + } + + /// Visit a ``DeferStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeferStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(DeferStmtSyntax.self)) + } + + /// Visit a ``DeinitializerDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeinitializerDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(DeinitializerDeclSyntax.self)) + } + + /// Visit a ``DeinitializerEffectSpecifiersSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DeinitializerEffectSpecifiersSyntax) async -> DeinitializerEffectSpecifiersSyntax { + return await visitChildren(node._syntaxNode).cast(DeinitializerEffectSpecifiersSyntax.self) + } + + /// Visit a ``DerivativeAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DerivativeAttributeArgumentsSyntax) async -> DerivativeAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(DerivativeAttributeArgumentsSyntax.self) + } + + /// Visit a ``DesignatedTypeListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DesignatedTypeListSyntax) async -> DesignatedTypeListSyntax { + return await visitChildren(node._syntaxNode).cast(DesignatedTypeListSyntax.self) + } + + /// Visit a ``DesignatedTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DesignatedTypeSyntax) async -> DesignatedTypeSyntax { + return await visitChildren(node._syntaxNode).cast(DesignatedTypeSyntax.self) + } + + /// Visit a ``DictionaryElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DictionaryElementListSyntax) async -> DictionaryElementListSyntax { + return await visitChildren(node._syntaxNode).cast(DictionaryElementListSyntax.self) + } + + /// Visit a ``DictionaryElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DictionaryElementSyntax) async -> DictionaryElementSyntax { + return await visitChildren(node._syntaxNode).cast(DictionaryElementSyntax.self) + } + + /// Visit a ``DictionaryExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DictionaryExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(DictionaryExprSyntax.self)) + } + + /// Visit a ``DictionaryTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DictionaryTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(DictionaryTypeSyntax.self)) + } + + /// Visit a ``DifferentiabilityArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DifferentiabilityArgumentListSyntax) async -> DifferentiabilityArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(DifferentiabilityArgumentListSyntax.self) + } + + /// Visit a ``DifferentiabilityArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DifferentiabilityArgumentSyntax) async -> DifferentiabilityArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(DifferentiabilityArgumentSyntax.self) + } + + /// Visit a ``DifferentiabilityArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DifferentiabilityArgumentsSyntax) async -> DifferentiabilityArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(DifferentiabilityArgumentsSyntax.self) + } + + /// Visit a ``DifferentiabilityWithRespectToArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DifferentiabilityWithRespectToArgumentSyntax) async -> DifferentiabilityWithRespectToArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(DifferentiabilityWithRespectToArgumentSyntax.self) + } + + /// Visit a ``DifferentiableAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DifferentiableAttributeArgumentsSyntax) async -> DifferentiableAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(DifferentiableAttributeArgumentsSyntax.self) + } + + /// Visit a ``DiscardAssignmentExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DiscardAssignmentExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(DiscardAssignmentExprSyntax.self)) + } + + /// Visit a ``DiscardStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DiscardStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(DiscardStmtSyntax.self)) + } + + /// Visit a `DoExprSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: DoExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(DoExprSyntax.self)) + } + + /// Visit a ``DoStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DoStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(DoStmtSyntax.self)) + } + + /// Visit a ``DocumentationAttributeArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DocumentationAttributeArgumentListSyntax) async -> DocumentationAttributeArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(DocumentationAttributeArgumentListSyntax.self) + } + + /// Visit a ``DocumentationAttributeArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DocumentationAttributeArgumentSyntax) async -> DocumentationAttributeArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(DocumentationAttributeArgumentSyntax.self) + } + + /// Visit a ``DynamicReplacementAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: DynamicReplacementAttributeArgumentsSyntax) async -> DynamicReplacementAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(DynamicReplacementAttributeArgumentsSyntax.self) + } + + /// Visit a ``EditorPlaceholderDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EditorPlaceholderDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(EditorPlaceholderDeclSyntax.self)) + } + + /// Visit a ``EditorPlaceholderExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EditorPlaceholderExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(EditorPlaceholderExprSyntax.self)) + } + + /// Visit a ``EffectsAttributeArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EffectsAttributeArgumentListSyntax) async -> EffectsAttributeArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(EffectsAttributeArgumentListSyntax.self) + } + + /// Visit a ``EnumCaseDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(EnumCaseDeclSyntax.self)) + } + + /// Visit a ``EnumCaseElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseElementListSyntax) async -> EnumCaseElementListSyntax { + return await visitChildren(node._syntaxNode).cast(EnumCaseElementListSyntax.self) + } + + /// Visit a ``EnumCaseElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseElementSyntax) async -> EnumCaseElementSyntax { + return await visitChildren(node._syntaxNode).cast(EnumCaseElementSyntax.self) + } + + /// Visit a ``EnumCaseParameterClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseParameterClauseSyntax) async -> EnumCaseParameterClauseSyntax { + return await visitChildren(node._syntaxNode).cast(EnumCaseParameterClauseSyntax.self) + } + + /// Visit a ``EnumCaseParameterListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseParameterListSyntax) async -> EnumCaseParameterListSyntax { + return await visitChildren(node._syntaxNode).cast(EnumCaseParameterListSyntax.self) + } + + /// Visit a ``EnumCaseParameterSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumCaseParameterSyntax) async -> EnumCaseParameterSyntax { + return await visitChildren(node._syntaxNode).cast(EnumCaseParameterSyntax.self) + } + + /// Visit a ``EnumDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: EnumDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(EnumDeclSyntax.self)) + } + + /// Visit a ``ExposeAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExposeAttributeArgumentsSyntax) async -> ExposeAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(ExposeAttributeArgumentsSyntax.self) + } + + /// Visit a ``ExprListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExprListSyntax) async -> ExprListSyntax { + return await visitChildren(node._syntaxNode).cast(ExprListSyntax.self) + } + + /// Visit a ``ExpressionPatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExpressionPatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(ExpressionPatternSyntax.self)) + } + + /// Visit a ``ExpressionSegmentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExpressionSegmentSyntax) async -> ExpressionSegmentSyntax { + return await visitChildren(node._syntaxNode).cast(ExpressionSegmentSyntax.self) + } + + /// Visit a ``ExpressionStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExpressionStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ExpressionStmtSyntax.self)) + } + + /// Visit a ``ExtensionDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ExtensionDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(ExtensionDeclSyntax.self)) + } + + /// Visit a ``FallThroughStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FallThroughStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(FallThroughStmtSyntax.self)) + } + + /// Visit a ``FloatLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FloatLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(FloatLiteralExprSyntax.self)) + } + + /// Visit a ``ForStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ForStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ForStmtSyntax.self)) + } + + /// Visit a ``ForceUnwrapExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ForceUnwrapExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(ForceUnwrapExprSyntax.self)) + } + + /// Visit a ``FunctionCallExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionCallExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(FunctionCallExprSyntax.self)) + } + + /// Visit a ``FunctionDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(FunctionDeclSyntax.self)) + } + + /// Visit a ``FunctionEffectSpecifiersSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionEffectSpecifiersSyntax) async -> FunctionEffectSpecifiersSyntax { + return await visitChildren(node._syntaxNode).cast(FunctionEffectSpecifiersSyntax.self) + } + + /// Visit a ``FunctionParameterClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionParameterClauseSyntax) async -> FunctionParameterClauseSyntax { + return await visitChildren(node._syntaxNode).cast(FunctionParameterClauseSyntax.self) + } + + /// Visit a ``FunctionParameterListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionParameterListSyntax) async -> FunctionParameterListSyntax { + return await visitChildren(node._syntaxNode).cast(FunctionParameterListSyntax.self) + } + + /// Visit a ``FunctionParameterSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionParameterSyntax) async -> FunctionParameterSyntax { + return await visitChildren(node._syntaxNode).cast(FunctionParameterSyntax.self) + } + + /// Visit a ``FunctionSignatureSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionSignatureSyntax) async -> FunctionSignatureSyntax { + return await visitChildren(node._syntaxNode).cast(FunctionSignatureSyntax.self) + } + + /// Visit a ``FunctionTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: FunctionTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(FunctionTypeSyntax.self)) + } + + /// Visit a ``GenericArgumentClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericArgumentClauseSyntax) async -> GenericArgumentClauseSyntax { + return await visitChildren(node._syntaxNode).cast(GenericArgumentClauseSyntax.self) + } + + /// Visit a ``GenericArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericArgumentListSyntax) async -> GenericArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(GenericArgumentListSyntax.self) + } + + /// Visit a ``GenericArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericArgumentSyntax) async -> GenericArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(GenericArgumentSyntax.self) + } + + /// Visit a ``GenericParameterClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericParameterClauseSyntax) async -> GenericParameterClauseSyntax { + return await visitChildren(node._syntaxNode).cast(GenericParameterClauseSyntax.self) + } + + /// Visit a ``GenericParameterListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericParameterListSyntax) async -> GenericParameterListSyntax { + return await visitChildren(node._syntaxNode).cast(GenericParameterListSyntax.self) + } + + /// Visit a ``GenericParameterSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericParameterSyntax) async -> GenericParameterSyntax { + return await visitChildren(node._syntaxNode).cast(GenericParameterSyntax.self) + } + + /// Visit a ``GenericRequirementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericRequirementListSyntax) async -> GenericRequirementListSyntax { + return await visitChildren(node._syntaxNode).cast(GenericRequirementListSyntax.self) + } + + /// Visit a ``GenericRequirementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericRequirementSyntax) async -> GenericRequirementSyntax { + return await visitChildren(node._syntaxNode).cast(GenericRequirementSyntax.self) + } + + /// Visit a ``GenericSpecializationExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericSpecializationExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(GenericSpecializationExprSyntax.self)) + } + + /// Visit a ``GenericWhereClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GenericWhereClauseSyntax) async -> GenericWhereClauseSyntax { + return await visitChildren(node._syntaxNode).cast(GenericWhereClauseSyntax.self) + } + + /// Visit a ``GuardStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: GuardStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(GuardStmtSyntax.self)) + } + + /// Visit a ``IdentifierPatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IdentifierPatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(IdentifierPatternSyntax.self)) + } + + /// Visit a ``IdentifierTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IdentifierTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(IdentifierTypeSyntax.self)) + } + + /// Visit a ``IfConfigClauseListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IfConfigClauseListSyntax) async -> IfConfigClauseListSyntax { + return await visitChildren(node._syntaxNode).cast(IfConfigClauseListSyntax.self) + } + + /// Visit a ``IfConfigClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IfConfigClauseSyntax) async -> IfConfigClauseSyntax { + return await visitChildren(node._syntaxNode).cast(IfConfigClauseSyntax.self) + } + + /// Visit a ``IfConfigDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IfConfigDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(IfConfigDeclSyntax.self)) + } + + /// Visit a ``IfExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IfExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(IfExprSyntax.self)) + } + + /// Visit a ``ImplementsAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ImplementsAttributeArgumentsSyntax) async -> ImplementsAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(ImplementsAttributeArgumentsSyntax.self) + } + + /// Visit a ``ImplicitlyUnwrappedOptionalTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ImplicitlyUnwrappedOptionalTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(ImplicitlyUnwrappedOptionalTypeSyntax.self)) + } + + /// Visit a ``ImportDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ImportDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(ImportDeclSyntax.self)) + } + + /// Visit a ``ImportPathComponentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ImportPathComponentListSyntax) async -> ImportPathComponentListSyntax { + return await visitChildren(node._syntaxNode).cast(ImportPathComponentListSyntax.self) + } + + /// Visit a ``ImportPathComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ImportPathComponentSyntax) async -> ImportPathComponentSyntax { + return await visitChildren(node._syntaxNode).cast(ImportPathComponentSyntax.self) + } + + /// Visit a ``InOutExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InOutExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(InOutExprSyntax.self)) + } + + /// Visit a ``InfixOperatorExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InfixOperatorExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(InfixOperatorExprSyntax.self)) + } + + /// Visit a ``InheritanceClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InheritanceClauseSyntax) async -> InheritanceClauseSyntax { + return await visitChildren(node._syntaxNode).cast(InheritanceClauseSyntax.self) + } + + /// Visit a ``InheritedTypeListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InheritedTypeListSyntax) async -> InheritedTypeListSyntax { + return await visitChildren(node._syntaxNode).cast(InheritedTypeListSyntax.self) + } + + /// Visit a ``InheritedTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InheritedTypeSyntax) async -> InheritedTypeSyntax { + return await visitChildren(node._syntaxNode).cast(InheritedTypeSyntax.self) + } + + /// Visit a ``InitializerClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InitializerClauseSyntax) async -> InitializerClauseSyntax { + return await visitChildren(node._syntaxNode).cast(InitializerClauseSyntax.self) + } + + /// Visit a ``InitializerDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: InitializerDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(InitializerDeclSyntax.self)) + } + + /// Visit a ``IntegerLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IntegerLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(IntegerLiteralExprSyntax.self)) + } + + /// Visit a ``IsExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IsExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(IsExprSyntax.self)) + } + + /// Visit a ``IsTypePatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: IsTypePatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(IsTypePatternSyntax.self)) + } + + /// Visit a ``KeyPathComponentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathComponentListSyntax) async -> KeyPathComponentListSyntax { + return await visitChildren(node._syntaxNode).cast(KeyPathComponentListSyntax.self) + } + + /// Visit a ``KeyPathComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathComponentSyntax) async -> KeyPathComponentSyntax { + return await visitChildren(node._syntaxNode).cast(KeyPathComponentSyntax.self) + } + + /// Visit a ``KeyPathExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(KeyPathExprSyntax.self)) + } + + /// Visit a ``KeyPathOptionalComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathOptionalComponentSyntax) async -> KeyPathOptionalComponentSyntax { + return await visitChildren(node._syntaxNode).cast(KeyPathOptionalComponentSyntax.self) + } + + /// Visit a ``KeyPathPropertyComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathPropertyComponentSyntax) async -> KeyPathPropertyComponentSyntax { + return await visitChildren(node._syntaxNode).cast(KeyPathPropertyComponentSyntax.self) + } + + /// Visit a ``KeyPathSubscriptComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: KeyPathSubscriptComponentSyntax) async -> KeyPathSubscriptComponentSyntax { + return await visitChildren(node._syntaxNode).cast(KeyPathSubscriptComponentSyntax.self) + } + + /// Visit a ``LabeledExprListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: LabeledExprListSyntax) async -> LabeledExprListSyntax { + return await visitChildren(node._syntaxNode).cast(LabeledExprListSyntax.self) + } + + /// Visit a ``LabeledExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: LabeledExprSyntax) async -> LabeledExprSyntax { + return await visitChildren(node._syntaxNode).cast(LabeledExprSyntax.self) + } + + /// Visit a ``LabeledSpecializeArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: LabeledSpecializeArgumentSyntax) async -> LabeledSpecializeArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(LabeledSpecializeArgumentSyntax.self) + } + + /// Visit a ``LabeledStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: LabeledStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(LabeledStmtSyntax.self)) + } + + /// Visit a ``LayoutRequirementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: LayoutRequirementSyntax) async -> LayoutRequirementSyntax { + return await visitChildren(node._syntaxNode).cast(LayoutRequirementSyntax.self) + } + + /// Visit a `LifetimeSpecifierArgumentListSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: LifetimeSpecifierArgumentListSyntax) async -> LifetimeSpecifierArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(LifetimeSpecifierArgumentListSyntax.self) + } + + /// Visit a `LifetimeSpecifierArgumentSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: LifetimeSpecifierArgumentSyntax) async -> LifetimeSpecifierArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(LifetimeSpecifierArgumentSyntax.self) + } + + /// Visit a `LifetimeTypeSpecifierSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: LifetimeTypeSpecifierSyntax) async -> LifetimeTypeSpecifierSyntax { + return await visitChildren(node._syntaxNode).cast(LifetimeTypeSpecifierSyntax.self) + } + + /// Visit a ``MacroDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MacroDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(MacroDeclSyntax.self)) + } + + /// Visit a ``MacroExpansionDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MacroExpansionDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(MacroExpansionDeclSyntax.self)) + } + + /// Visit a ``MacroExpansionExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MacroExpansionExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(MacroExpansionExprSyntax.self)) + } + + /// Visit a ``MatchingPatternConditionSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MatchingPatternConditionSyntax) async -> MatchingPatternConditionSyntax { + return await visitChildren(node._syntaxNode).cast(MatchingPatternConditionSyntax.self) + } + + /// Visit a ``MemberAccessExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MemberAccessExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(MemberAccessExprSyntax.self)) + } + + /// Visit a ``MemberBlockItemListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MemberBlockItemListSyntax) async -> MemberBlockItemListSyntax { + return await visitChildren(node._syntaxNode).cast(MemberBlockItemListSyntax.self) + } + + /// Visit a ``MemberBlockItemSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MemberBlockItemSyntax) async -> MemberBlockItemSyntax { + return await visitChildren(node._syntaxNode).cast(MemberBlockItemSyntax.self) + } + + /// Visit a ``MemberBlockSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MemberBlockSyntax) async -> MemberBlockSyntax { + return await visitChildren(node._syntaxNode).cast(MemberBlockSyntax.self) + } + + /// Visit a ``MemberTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MemberTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(MemberTypeSyntax.self)) + } + + /// Visit a ``MetatypeTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MetatypeTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(MetatypeTypeSyntax.self)) + } + + /// Visit a ``MissingDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(MissingDeclSyntax.self)) + } + + /// Visit a ``MissingExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(MissingExprSyntax.self)) + } + + /// Visit a ``MissingPatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingPatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(MissingPatternSyntax.self)) + } + + /// Visit a ``MissingStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(MissingStmtSyntax.self)) + } + + /// Visit a ``MissingSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingSyntax) async -> Syntax { + return Syntax(await visitChildren(node._syntaxNode).cast(MissingSyntax.self)) + } + + /// Visit a ``MissingTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MissingTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(MissingTypeSyntax.self)) + } + + /// Visit a ``MultipleTrailingClosureElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MultipleTrailingClosureElementListSyntax) async -> MultipleTrailingClosureElementListSyntax { + return await visitChildren(node._syntaxNode).cast(MultipleTrailingClosureElementListSyntax.self) + } + + /// Visit a ``MultipleTrailingClosureElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: MultipleTrailingClosureElementSyntax) async -> MultipleTrailingClosureElementSyntax { + return await visitChildren(node._syntaxNode).cast(MultipleTrailingClosureElementSyntax.self) + } + + /// Visit a ``NamedOpaqueReturnTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: NamedOpaqueReturnTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(NamedOpaqueReturnTypeSyntax.self)) + } + + /// Visit a ``NilLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: NilLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(NilLiteralExprSyntax.self)) + } + + /// Visit a ``ObjCSelectorPieceListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ObjCSelectorPieceListSyntax) async -> ObjCSelectorPieceListSyntax { + return await visitChildren(node._syntaxNode).cast(ObjCSelectorPieceListSyntax.self) + } + + /// Visit a ``ObjCSelectorPieceSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ObjCSelectorPieceSyntax) async -> ObjCSelectorPieceSyntax { + return await visitChildren(node._syntaxNode).cast(ObjCSelectorPieceSyntax.self) + } + + /// Visit a ``OpaqueReturnTypeOfAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OpaqueReturnTypeOfAttributeArgumentsSyntax) async -> OpaqueReturnTypeOfAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(OpaqueReturnTypeOfAttributeArgumentsSyntax.self) + } + + /// Visit a ``OperatorDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OperatorDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(OperatorDeclSyntax.self)) + } + + /// Visit a ``OperatorPrecedenceAndTypesSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OperatorPrecedenceAndTypesSyntax) async -> OperatorPrecedenceAndTypesSyntax { + return await visitChildren(node._syntaxNode).cast(OperatorPrecedenceAndTypesSyntax.self) + } + + /// Visit a ``OptionalBindingConditionSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OptionalBindingConditionSyntax) async -> OptionalBindingConditionSyntax { + return await visitChildren(node._syntaxNode).cast(OptionalBindingConditionSyntax.self) + } + + /// Visit a ``OptionalChainingExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OptionalChainingExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(OptionalChainingExprSyntax.self)) + } + + /// Visit a ``OptionalTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OptionalTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(OptionalTypeSyntax.self)) + } + + /// Visit a ``OriginallyDefinedInAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: OriginallyDefinedInAttributeArgumentsSyntax) async -> OriginallyDefinedInAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(OriginallyDefinedInAttributeArgumentsSyntax.self) + } + + /// Visit a ``PackElementExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PackElementExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PackElementExprSyntax.self)) + } + + /// Visit a ``PackElementTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PackElementTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(PackElementTypeSyntax.self)) + } + + /// Visit a ``PackExpansionExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PackExpansionExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PackExpansionExprSyntax.self)) + } + + /// Visit a ``PackExpansionTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PackExpansionTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(PackExpansionTypeSyntax.self)) + } + + /// Visit a ``PatternBindingListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PatternBindingListSyntax) async -> PatternBindingListSyntax { + return await visitChildren(node._syntaxNode).cast(PatternBindingListSyntax.self) + } + + /// Visit a ``PatternBindingSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PatternBindingSyntax) async -> PatternBindingSyntax { + return await visitChildren(node._syntaxNode).cast(PatternBindingSyntax.self) + } + + /// Visit a ``PatternExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PatternExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PatternExprSyntax.self)) + } + + /// Visit a ``PlatformVersionItemListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PlatformVersionItemListSyntax) async -> PlatformVersionItemListSyntax { + return await visitChildren(node._syntaxNode).cast(PlatformVersionItemListSyntax.self) + } + + /// Visit a ``PlatformVersionItemSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PlatformVersionItemSyntax) async -> PlatformVersionItemSyntax { + return await visitChildren(node._syntaxNode).cast(PlatformVersionItemSyntax.self) + } + + /// Visit a ``PlatformVersionSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PlatformVersionSyntax) async -> PlatformVersionSyntax { + return await visitChildren(node._syntaxNode).cast(PlatformVersionSyntax.self) + } + + /// Visit a ``PostfixIfConfigExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PostfixIfConfigExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PostfixIfConfigExprSyntax.self)) + } + + /// Visit a ``PostfixOperatorExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PostfixOperatorExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PostfixOperatorExprSyntax.self)) + } + + /// Visit a ``PoundSourceLocationArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PoundSourceLocationArgumentsSyntax) async -> PoundSourceLocationArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(PoundSourceLocationArgumentsSyntax.self) + } + + /// Visit a ``PoundSourceLocationSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PoundSourceLocationSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(PoundSourceLocationSyntax.self)) + } + + /// Visit a ``PrecedenceGroupAssignmentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupAssignmentSyntax) async -> PrecedenceGroupAssignmentSyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupAssignmentSyntax.self) + } + + /// Visit a ``PrecedenceGroupAssociativitySyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupAssociativitySyntax) async -> PrecedenceGroupAssociativitySyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupAssociativitySyntax.self) + } + + /// Visit a ``PrecedenceGroupAttributeListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupAttributeListSyntax) async -> PrecedenceGroupAttributeListSyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupAttributeListSyntax.self) + } + + /// Visit a ``PrecedenceGroupDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(PrecedenceGroupDeclSyntax.self)) + } + + /// Visit a ``PrecedenceGroupNameListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupNameListSyntax) async -> PrecedenceGroupNameListSyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupNameListSyntax.self) + } + + /// Visit a ``PrecedenceGroupNameSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupNameSyntax) async -> PrecedenceGroupNameSyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupNameSyntax.self) + } + + /// Visit a ``PrecedenceGroupRelationSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrecedenceGroupRelationSyntax) async -> PrecedenceGroupRelationSyntax { + return await visitChildren(node._syntaxNode).cast(PrecedenceGroupRelationSyntax.self) + } + + /// Visit a ``PrefixOperatorExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrefixOperatorExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(PrefixOperatorExprSyntax.self)) + } + + /// Visit a ``PrimaryAssociatedTypeClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrimaryAssociatedTypeClauseSyntax) async -> PrimaryAssociatedTypeClauseSyntax { + return await visitChildren(node._syntaxNode).cast(PrimaryAssociatedTypeClauseSyntax.self) + } + + /// Visit a ``PrimaryAssociatedTypeListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrimaryAssociatedTypeListSyntax) async -> PrimaryAssociatedTypeListSyntax { + return await visitChildren(node._syntaxNode).cast(PrimaryAssociatedTypeListSyntax.self) + } + + /// Visit a ``PrimaryAssociatedTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: PrimaryAssociatedTypeSyntax) async -> PrimaryAssociatedTypeSyntax { + return await visitChildren(node._syntaxNode).cast(PrimaryAssociatedTypeSyntax.self) + } + + /// Visit a ``ProtocolDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ProtocolDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(ProtocolDeclSyntax.self)) + } + + /// Visit a ``RegexLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: RegexLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(RegexLiteralExprSyntax.self)) + } + + /// Visit a ``RepeatStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: RepeatStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(RepeatStmtSyntax.self)) + } + + /// Visit a ``ReturnClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ReturnClauseSyntax) async -> ReturnClauseSyntax { + return await visitChildren(node._syntaxNode).cast(ReturnClauseSyntax.self) + } + + /// Visit a ``ReturnStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ReturnStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ReturnStmtSyntax.self)) + } + + /// Visit a ``SameTypeRequirementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SameTypeRequirementSyntax) async -> SameTypeRequirementSyntax { + return await visitChildren(node._syntaxNode).cast(SameTypeRequirementSyntax.self) + } + + /// Visit a ``SequenceExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SequenceExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(SequenceExprSyntax.self)) + } + + /// Visit a ``SimpleStringLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SimpleStringLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(SimpleStringLiteralExprSyntax.self)) + } + + /// Visit a ``SimpleStringLiteralSegmentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SimpleStringLiteralSegmentListSyntax) async -> SimpleStringLiteralSegmentListSyntax { + return await visitChildren(node._syntaxNode).cast(SimpleStringLiteralSegmentListSyntax.self) + } + + /// Visit a ``SimpleTypeSpecifierSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SimpleTypeSpecifierSyntax) async -> SimpleTypeSpecifierSyntax { + return await visitChildren(node._syntaxNode).cast(SimpleTypeSpecifierSyntax.self) + } + + /// Visit a ``SomeOrAnyTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SomeOrAnyTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(SomeOrAnyTypeSyntax.self)) + } + + /// Visit a ``SourceFileSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SourceFileSyntax) async -> SourceFileSyntax { + return await visitChildren(node._syntaxNode).cast(SourceFileSyntax.self) + } + + /// Visit a ``SpecializeAttributeArgumentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SpecializeAttributeArgumentListSyntax) async -> SpecializeAttributeArgumentListSyntax { + return await visitChildren(node._syntaxNode).cast(SpecializeAttributeArgumentListSyntax.self) + } + + /// Visit a ``SpecializeAvailabilityArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SpecializeAvailabilityArgumentSyntax) async -> SpecializeAvailabilityArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(SpecializeAvailabilityArgumentSyntax.self) + } + + /// Visit a ``SpecializeTargetFunctionArgumentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SpecializeTargetFunctionArgumentSyntax) async -> SpecializeTargetFunctionArgumentSyntax { + return await visitChildren(node._syntaxNode).cast(SpecializeTargetFunctionArgumentSyntax.self) + } + + /// Visit a ``StringLiteralExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: StringLiteralExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(StringLiteralExprSyntax.self)) + } + + /// Visit a ``StringLiteralSegmentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: StringLiteralSegmentListSyntax) async -> StringLiteralSegmentListSyntax { + return await visitChildren(node._syntaxNode).cast(StringLiteralSegmentListSyntax.self) + } + + /// Visit a ``StringSegmentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: StringSegmentSyntax) async -> StringSegmentSyntax { + return await visitChildren(node._syntaxNode).cast(StringSegmentSyntax.self) + } + + /// Visit a ``StructDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: StructDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(StructDeclSyntax.self)) + } + + /// Visit a ``SubscriptCallExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SubscriptCallExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(SubscriptCallExprSyntax.self)) + } + + /// Visit a ``SubscriptDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SubscriptDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(SubscriptDeclSyntax.self)) + } + + /// Visit a ``SuperExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SuperExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(SuperExprSyntax.self)) + } + + /// Visit a ``SuppressedTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SuppressedTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(SuppressedTypeSyntax.self)) + } + + /// Visit a ``SwitchCaseItemListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchCaseItemListSyntax) async -> SwitchCaseItemListSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchCaseItemListSyntax.self) + } + + /// Visit a ``SwitchCaseItemSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchCaseItemSyntax) async -> SwitchCaseItemSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchCaseItemSyntax.self) + } + + /// Visit a ``SwitchCaseLabelSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchCaseLabelSyntax) async -> SwitchCaseLabelSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchCaseLabelSyntax.self) + } + + /// Visit a ``SwitchCaseListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchCaseListSyntax) async -> SwitchCaseListSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchCaseListSyntax.self) + } + + /// Visit a ``SwitchCaseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchCaseSyntax) async -> SwitchCaseSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchCaseSyntax.self) + } + + /// Visit a ``SwitchDefaultLabelSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchDefaultLabelSyntax) async -> SwitchDefaultLabelSyntax { + return await visitChildren(node._syntaxNode).cast(SwitchDefaultLabelSyntax.self) + } + + /// Visit a ``SwitchExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: SwitchExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(SwitchExprSyntax.self)) + } + + /// Visit a ``TernaryExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TernaryExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(TernaryExprSyntax.self)) + } + + /// Visit a `ThenStmtSyntax`. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + #if compiler(>=5.8) + @_spi(ExperimentalLanguageFeatures) + #endif + open func visit(_ node: ThenStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ThenStmtSyntax.self)) + } + + /// Visit a ``ThrowStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ThrowStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(ThrowStmtSyntax.self)) + } + + /// Visit a ``ThrowsClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ThrowsClauseSyntax) async -> ThrowsClauseSyntax { + return await visitChildren(node._syntaxNode).cast(ThrowsClauseSyntax.self) + } + + /// Visit a ``TryExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TryExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(TryExprSyntax.self)) + } + + /// Visit a ``TupleExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TupleExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(TupleExprSyntax.self)) + } + + /// Visit a ``TuplePatternElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TuplePatternElementListSyntax) async -> TuplePatternElementListSyntax { + return await visitChildren(node._syntaxNode).cast(TuplePatternElementListSyntax.self) + } + + /// Visit a ``TuplePatternElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TuplePatternElementSyntax) async -> TuplePatternElementSyntax { + return await visitChildren(node._syntaxNode).cast(TuplePatternElementSyntax.self) + } + + /// Visit a ``TuplePatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TuplePatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(TuplePatternSyntax.self)) + } + + /// Visit a ``TupleTypeElementListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TupleTypeElementListSyntax) async -> TupleTypeElementListSyntax { + return await visitChildren(node._syntaxNode).cast(TupleTypeElementListSyntax.self) + } + + /// Visit a ``TupleTypeElementSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TupleTypeElementSyntax) async -> TupleTypeElementSyntax { + return await visitChildren(node._syntaxNode).cast(TupleTypeElementSyntax.self) + } + + /// Visit a ``TupleTypeSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TupleTypeSyntax) async -> TypeSyntax { + return TypeSyntax(await visitChildren(node._syntaxNode).cast(TupleTypeSyntax.self)) + } + + /// Visit a ``TypeAliasDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeAliasDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(TypeAliasDeclSyntax.self)) + } + + /// Visit a ``TypeAnnotationSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeAnnotationSyntax) async -> TypeAnnotationSyntax { + return await visitChildren(node._syntaxNode).cast(TypeAnnotationSyntax.self) + } + + /// Visit a ``TypeEffectSpecifiersSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeEffectSpecifiersSyntax) async -> TypeEffectSpecifiersSyntax { + return await visitChildren(node._syntaxNode).cast(TypeEffectSpecifiersSyntax.self) + } + + /// Visit a ``TypeExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(TypeExprSyntax.self)) + } + + /// Visit a ``TypeInitializerClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeInitializerClauseSyntax) async -> TypeInitializerClauseSyntax { + return await visitChildren(node._syntaxNode).cast(TypeInitializerClauseSyntax.self) + } + + /// Visit a ``TypeSpecifierListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: TypeSpecifierListSyntax) async -> TypeSpecifierListSyntax { + return await visitChildren(node._syntaxNode).cast(TypeSpecifierListSyntax.self) + } + + /// Visit a ``UnavailableFromAsyncAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnavailableFromAsyncAttributeArgumentsSyntax) async -> UnavailableFromAsyncAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(UnavailableFromAsyncAttributeArgumentsSyntax.self) + } + + /// Visit a ``UnderscorePrivateAttributeArgumentsSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnderscorePrivateAttributeArgumentsSyntax) async -> UnderscorePrivateAttributeArgumentsSyntax { + return await visitChildren(node._syntaxNode).cast(UnderscorePrivateAttributeArgumentsSyntax.self) + } + + /// Visit a ``UnexpectedNodesSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnexpectedNodesSyntax) async -> UnexpectedNodesSyntax { + return await visitChildren(node._syntaxNode).cast(UnexpectedNodesSyntax.self) + } + + /// Visit a ``UnresolvedAsExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnresolvedAsExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(UnresolvedAsExprSyntax.self)) + } + + /// Visit a ``UnresolvedIsExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnresolvedIsExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(UnresolvedIsExprSyntax.self)) + } + + /// Visit a ``UnresolvedTernaryExprSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: UnresolvedTernaryExprSyntax) async -> ExprSyntax { + return ExprSyntax(await visitChildren(node._syntaxNode).cast(UnresolvedTernaryExprSyntax.self)) + } + + /// Visit a ``ValueBindingPatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: ValueBindingPatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(ValueBindingPatternSyntax.self)) + } + + /// Visit a ``VariableDeclSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: VariableDeclSyntax) async -> DeclSyntax { + return DeclSyntax(await visitChildren(node._syntaxNode).cast(VariableDeclSyntax.self)) + } + + /// Visit a ``VersionComponentListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: VersionComponentListSyntax) async -> VersionComponentListSyntax { + return await visitChildren(node._syntaxNode).cast(VersionComponentListSyntax.self) + } + + /// Visit a ``VersionComponentSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: VersionComponentSyntax) async -> VersionComponentSyntax { + return await visitChildren(node._syntaxNode).cast(VersionComponentSyntax.self) + } + + /// Visit a ``VersionTupleSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: VersionTupleSyntax) async -> VersionTupleSyntax { + return await visitChildren(node._syntaxNode).cast(VersionTupleSyntax.self) + } + + /// Visit a ``WhereClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: WhereClauseSyntax) async -> WhereClauseSyntax { + return await visitChildren(node._syntaxNode).cast(WhereClauseSyntax.self) + } + + /// Visit a ``WhileStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: WhileStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(WhileStmtSyntax.self)) + } + + /// Visit a ``WildcardPatternSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: WildcardPatternSyntax) async -> PatternSyntax { + return PatternSyntax(await visitChildren(node._syntaxNode).cast(WildcardPatternSyntax.self)) + } + + /// Visit a ``YieldStmtSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: YieldStmtSyntax) async -> StmtSyntax { + return StmtSyntax(await visitChildren(node._syntaxNode).cast(YieldStmtSyntax.self)) + } + + /// Visit a ``YieldedExpressionListSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: YieldedExpressionListSyntax) async -> YieldedExpressionListSyntax { + return await visitChildren(node._syntaxNode).cast(YieldedExpressionListSyntax.self) + } + + /// Visit a ``YieldedExpressionSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: YieldedExpressionSyntax) async -> YieldedExpressionSyntax { + return await visitChildren(node._syntaxNode).cast(YieldedExpressionSyntax.self) + } + + /// Visit a ``YieldedExpressionsClauseSyntax``. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + open func visit(_ node: YieldedExpressionsClauseSyntax) async -> YieldedExpressionsClauseSyntax { + return await visitChildren(node._syntaxNode).cast(YieldedExpressionsClauseSyntax.self) + } + + /// Visit any DeclSyntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + public func visit(_ node: DeclSyntax) async -> DeclSyntax { + var node: Syntax = Syntax(node) + await dispatchVisit(&node) + return node.cast(DeclSyntax.self) + } + + /// Visit any ExprSyntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + public func visit(_ node: ExprSyntax) async -> ExprSyntax { + var node: Syntax = Syntax(node) + await dispatchVisit(&node) + return node.cast(ExprSyntax.self) + } + + /// Visit any PatternSyntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + public func visit(_ node: PatternSyntax) async -> PatternSyntax { + var node: Syntax = Syntax(node) + await dispatchVisit(&node) + return node.cast(PatternSyntax.self) + } + + /// Visit any StmtSyntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + public func visit(_ node: StmtSyntax) async -> StmtSyntax { + var node: Syntax = Syntax(node) + await dispatchVisit(&node) + return node.cast(StmtSyntax.self) + } + + /// Visit any TypeSyntax node. + /// - Parameter node: the node that is being visited + /// - Returns: the rewritten node + public func visit(_ node: TypeSyntax) async -> TypeSyntax { + var node: Syntax = Syntax(node) + await dispatchVisit(&node) + return node.cast(TypeSyntax.self) + } + + /// Interpret `node` as a node of type `nodeType`, visit it, calling + /// the `visit` to transform the node. + @inline(__always) + private func visitImpl( + _ node: inout Syntax, + _ nodeType: NodeType.Type, + _ visit: (NodeType) async -> some SyntaxProtocol + ) async { + let origNode = node + await visitPre(origNode) + node = + if let newNode = await visitAny(origNode) { + newNode + } else { + Syntax(await visit(origNode.cast(NodeType.self))) + } + await visitPost(origNode) + } + + // SwiftSyntax requires a lot of stack space in debug builds for syntax tree + // rewriting. In scenarios with reduced stack space (in particular dispatch + // queues), this easily results in a stack overflow. To work around this issue, + // use a less performant but also less stack-hungry version of SwiftSyntax's + // AsyncSyntaxRewriter in debug builds. + #if DEBUG + /// Implementation detail of visit(_:). Do not call directly. + /// + /// Returns the function that shall be called to visit a specific syntax node. + /// + /// To determine the correct specific visitation function for a syntax node, + /// we need to switch through a huge switch statement that covers all syntax + /// types. In debug builds, the cases of this switch statement do not share + /// stack space (rdar://55929175). Because of this, the switch statement + /// requires about 15KB of stack space. In scenarios with reduced + /// stack size (in particular dispatch queues), this often results in a stack + /// overflow during syntax tree rewriting. + /// + /// To circumvent this problem, make calling the specific visitation function + /// a two-step process: First determine the function to call in this function + /// and return a reference to it, then call it. This way, the stack frame + /// that determines the correct visitation function will be popped of the + /// stack before the function is being called, making the switch's stack + /// space transient instead of having it linger in the call stack. + private func visitationFunc(for node: Syntax) async -> ((inout Syntax) async -> Void) { + switch node.raw.kind { + case .token: + return { + await self.visitImpl(&$0, TokenSyntax.self, self.visit) + } + case .accessorBlock: + return { + await self.visitImpl(&$0, AccessorBlockSyntax.self, self.visit) + } + case .accessorDeclList: + return { + await self.visitImpl(&$0, AccessorDeclListSyntax.self, self.visit) + } + case .accessorDecl: + return { + await self.visitImpl(&$0, AccessorDeclSyntax.self, self.visit) + } + case .accessorEffectSpecifiers: + return { + await self.visitImpl(&$0, AccessorEffectSpecifiersSyntax.self, self.visit) + } + case .accessorParameters: + return { + await self.visitImpl(&$0, AccessorParametersSyntax.self, self.visit) + } + case .actorDecl: + return { + await self.visitImpl(&$0, ActorDeclSyntax.self, self.visit) + } + case .arrayElementList: + return { + await self.visitImpl(&$0, ArrayElementListSyntax.self, self.visit) + } + case .arrayElement: + return { + await self.visitImpl(&$0, ArrayElementSyntax.self, self.visit) + } + case .arrayExpr: + return { + await self.visitImpl(&$0, ArrayExprSyntax.self, self.visit) + } + case .arrayType: + return { + await self.visitImpl(&$0, ArrayTypeSyntax.self, self.visit) + } + case .arrowExpr: + return { + await self.visitImpl(&$0, ArrowExprSyntax.self, self.visit) + } + case .asExpr: + return { + await self.visitImpl(&$0, AsExprSyntax.self, self.visit) + } + case .assignmentExpr: + return { + await self.visitImpl(&$0, AssignmentExprSyntax.self, self.visit) + } + case .associatedTypeDecl: + return { + await self.visitImpl(&$0, AssociatedTypeDeclSyntax.self, self.visit) + } + case .attributeList: + return { + await self.visitImpl(&$0, AttributeListSyntax.self, self.visit) + } + case .attribute: + return { + await self.visitImpl(&$0, AttributeSyntax.self, self.visit) + } + case .attributedType: + return { + await self.visitImpl(&$0, AttributedTypeSyntax.self, self.visit) + } + case .availabilityArgumentList: + return { + await self.visitImpl(&$0, AvailabilityArgumentListSyntax.self, self.visit) + } + case .availabilityArgument: + return { + await self.visitImpl(&$0, AvailabilityArgumentSyntax.self, self.visit) + } + case .availabilityCondition: + return { + await self.visitImpl(&$0, AvailabilityConditionSyntax.self, self.visit) + } + case .availabilityLabeledArgument: + return { + await self.visitImpl(&$0, AvailabilityLabeledArgumentSyntax.self, self.visit) + } + case .awaitExpr: + return { + await self.visitImpl(&$0, AwaitExprSyntax.self, self.visit) + } + case .backDeployedAttributeArguments: + return { + await self.visitImpl(&$0, BackDeployedAttributeArgumentsSyntax.self, self.visit) + } + case .binaryOperatorExpr: + return { + await self.visitImpl(&$0, BinaryOperatorExprSyntax.self, self.visit) + } + case .booleanLiteralExpr: + return { + await self.visitImpl(&$0, BooleanLiteralExprSyntax.self, self.visit) + } + case .borrowExpr: + return { + await self.visitImpl(&$0, BorrowExprSyntax.self, self.visit) + } + case .breakStmt: + return { + await self.visitImpl(&$0, BreakStmtSyntax.self, self.visit) + } + case ._canImportExpr: + return { + await self.visitImpl(&$0, _CanImportExprSyntax.self, self.visit) + } + case ._canImportVersionInfo: + return { + await self.visitImpl(&$0, _CanImportVersionInfoSyntax.self, self.visit) + } + case .catchClauseList: + return { + await self.visitImpl(&$0, CatchClauseListSyntax.self, self.visit) + } + case .catchClause: + return { + await self.visitImpl(&$0, CatchClauseSyntax.self, self.visit) + } + case .catchItemList: + return { + await self.visitImpl(&$0, CatchItemListSyntax.self, self.visit) + } + case .catchItem: + return { + await self.visitImpl(&$0, CatchItemSyntax.self, self.visit) + } + case .classDecl: + return { + await self.visitImpl(&$0, ClassDeclSyntax.self, self.visit) + } + case .classRestrictionType: + return { + await self.visitImpl(&$0, ClassRestrictionTypeSyntax.self, self.visit) + } + case .closureCaptureClause: + return { + await self.visitImpl(&$0, ClosureCaptureClauseSyntax.self, self.visit) + } + case .closureCaptureList: + return { + await self.visitImpl(&$0, ClosureCaptureListSyntax.self, self.visit) + } + case .closureCaptureSpecifier: + return { + await self.visitImpl(&$0, ClosureCaptureSpecifierSyntax.self, self.visit) + } + case .closureCapture: + return { + await self.visitImpl(&$0, ClosureCaptureSyntax.self, self.visit) + } + case .closureExpr: + return { + await self.visitImpl(&$0, ClosureExprSyntax.self, self.visit) + } + case .closureParameterClause: + return { + await self.visitImpl(&$0, ClosureParameterClauseSyntax.self, self.visit) + } + case .closureParameterList: + return { + await self.visitImpl(&$0, ClosureParameterListSyntax.self, self.visit) + } + case .closureParameter: + return { + await self.visitImpl(&$0, ClosureParameterSyntax.self, self.visit) + } + case .closureShorthandParameterList: + return { + await self.visitImpl(&$0, ClosureShorthandParameterListSyntax.self, self.visit) + } + case .closureShorthandParameter: + return { + await self.visitImpl(&$0, ClosureShorthandParameterSyntax.self, self.visit) + } + case .closureSignature: + return { + await self.visitImpl(&$0, ClosureSignatureSyntax.self, self.visit) + } + case .codeBlockItemList: + return { + await self.visitImpl(&$0, CodeBlockItemListSyntax.self, self.visit) + } + case .codeBlockItem: + return { + await self.visitImpl(&$0, CodeBlockItemSyntax.self, self.visit) + } + case .codeBlock: + return { + await self.visitImpl(&$0, CodeBlockSyntax.self, self.visit) + } + case .compositionTypeElementList: + return { + await self.visitImpl(&$0, CompositionTypeElementListSyntax.self, self.visit) + } + case .compositionTypeElement: + return { + await self.visitImpl(&$0, CompositionTypeElementSyntax.self, self.visit) + } + case .compositionType: + return { + await self.visitImpl(&$0, CompositionTypeSyntax.self, self.visit) + } + case .conditionElementList: + return { + await self.visitImpl(&$0, ConditionElementListSyntax.self, self.visit) + } + case .conditionElement: + return { + await self.visitImpl(&$0, ConditionElementSyntax.self, self.visit) + } + case .conformanceRequirement: + return { + await self.visitImpl(&$0, ConformanceRequirementSyntax.self, self.visit) + } + case .consumeExpr: + return { + await self.visitImpl(&$0, ConsumeExprSyntax.self, self.visit) + } + case .continueStmt: + return { + await self.visitImpl(&$0, ContinueStmtSyntax.self, self.visit) + } + case .conventionAttributeArguments: + return { + await self.visitImpl(&$0, ConventionAttributeArgumentsSyntax.self, self.visit) + } + case .conventionWitnessMethodAttributeArguments: + return { + await self.visitImpl(&$0, ConventionWitnessMethodAttributeArgumentsSyntax.self, self.visit) + } + case .copyExpr: + return { + await self.visitImpl(&$0, CopyExprSyntax.self, self.visit) + } + case .declModifierDetail: + return { + await self.visitImpl(&$0, DeclModifierDetailSyntax.self, self.visit) + } + case .declModifierList: + return { + await self.visitImpl(&$0, DeclModifierListSyntax.self, self.visit) + } + case .declModifier: + return { + await self.visitImpl(&$0, DeclModifierSyntax.self, self.visit) + } + case .declNameArgumentList: + return { + await self.visitImpl(&$0, DeclNameArgumentListSyntax.self, self.visit) + } + case .declNameArgument: + return { + await self.visitImpl(&$0, DeclNameArgumentSyntax.self, self.visit) + } + case .declNameArguments: + return { + await self.visitImpl(&$0, DeclNameArgumentsSyntax.self, self.visit) + } + case .declReferenceExpr: + return { + await self.visitImpl(&$0, DeclReferenceExprSyntax.self, self.visit) + } + case .deferStmt: + return { + await self.visitImpl(&$0, DeferStmtSyntax.self, self.visit) + } + case .deinitializerDecl: + return { + await self.visitImpl(&$0, DeinitializerDeclSyntax.self, self.visit) + } + case .deinitializerEffectSpecifiers: + return { + await self.visitImpl(&$0, DeinitializerEffectSpecifiersSyntax.self, self.visit) + } + case .derivativeAttributeArguments: + return { + await self.visitImpl(&$0, DerivativeAttributeArgumentsSyntax.self, self.visit) + } + case .designatedTypeList: + return { + await self.visitImpl(&$0, DesignatedTypeListSyntax.self, self.visit) + } + case .designatedType: + return { + await self.visitImpl(&$0, DesignatedTypeSyntax.self, self.visit) + } + case .dictionaryElementList: + return { + await self.visitImpl(&$0, DictionaryElementListSyntax.self, self.visit) + } + case .dictionaryElement: + return { + await self.visitImpl(&$0, DictionaryElementSyntax.self, self.visit) + } + case .dictionaryExpr: + return { + await self.visitImpl(&$0, DictionaryExprSyntax.self, self.visit) + } + case .dictionaryType: + return { + await self.visitImpl(&$0, DictionaryTypeSyntax.self, self.visit) + } + case .differentiabilityArgumentList: + return { + await self.visitImpl(&$0, DifferentiabilityArgumentListSyntax.self, self.visit) + } + case .differentiabilityArgument: + return { + await self.visitImpl(&$0, DifferentiabilityArgumentSyntax.self, self.visit) + } + case .differentiabilityArguments: + return { + await self.visitImpl(&$0, DifferentiabilityArgumentsSyntax.self, self.visit) + } + case .differentiabilityWithRespectToArgument: + return { + await self.visitImpl(&$0, DifferentiabilityWithRespectToArgumentSyntax.self, self.visit) + } + case .differentiableAttributeArguments: + return { + await self.visitImpl(&$0, DifferentiableAttributeArgumentsSyntax.self, self.visit) + } + case .discardAssignmentExpr: + return { + await self.visitImpl(&$0, DiscardAssignmentExprSyntax.self, self.visit) + } + case .discardStmt: + return { + await self.visitImpl(&$0, DiscardStmtSyntax.self, self.visit) + } + case .doExpr: + return { + await self.visitImpl(&$0, DoExprSyntax.self, self.visit) + } + case .doStmt: + return { + await self.visitImpl(&$0, DoStmtSyntax.self, self.visit) + } + case .documentationAttributeArgumentList: + return { + await self.visitImpl(&$0, DocumentationAttributeArgumentListSyntax.self, self.visit) + } + case .documentationAttributeArgument: + return { + await self.visitImpl(&$0, DocumentationAttributeArgumentSyntax.self, self.visit) + } + case .dynamicReplacementAttributeArguments: + return { + await self.visitImpl(&$0, DynamicReplacementAttributeArgumentsSyntax.self, self.visit) + } + case .editorPlaceholderDecl: + return { + await self.visitImpl(&$0, EditorPlaceholderDeclSyntax.self, self.visit) + } + case .editorPlaceholderExpr: + return { + await self.visitImpl(&$0, EditorPlaceholderExprSyntax.self, self.visit) + } + case .effectsAttributeArgumentList: + return { + await self.visitImpl(&$0, EffectsAttributeArgumentListSyntax.self, self.visit) + } + case .enumCaseDecl: + return { + await self.visitImpl(&$0, EnumCaseDeclSyntax.self, self.visit) + } + case .enumCaseElementList: + return { + await self.visitImpl(&$0, EnumCaseElementListSyntax.self, self.visit) + } + case .enumCaseElement: + return { + await self.visitImpl(&$0, EnumCaseElementSyntax.self, self.visit) + } + case .enumCaseParameterClause: + return { + await self.visitImpl(&$0, EnumCaseParameterClauseSyntax.self, self.visit) + } + case .enumCaseParameterList: + return { + await self.visitImpl(&$0, EnumCaseParameterListSyntax.self, self.visit) + } + case .enumCaseParameter: + return { + await self.visitImpl(&$0, EnumCaseParameterSyntax.self, self.visit) + } + case .enumDecl: + return { + await self.visitImpl(&$0, EnumDeclSyntax.self, self.visit) + } + case .exposeAttributeArguments: + return { + await self.visitImpl(&$0, ExposeAttributeArgumentsSyntax.self, self.visit) + } + case .exprList: + return { + await self.visitImpl(&$0, ExprListSyntax.self, self.visit) + } + case .expressionPattern: + return { + await self.visitImpl(&$0, ExpressionPatternSyntax.self, self.visit) + } + case .expressionSegment: + return { + await self.visitImpl(&$0, ExpressionSegmentSyntax.self, self.visit) + } + case .expressionStmt: + return { + await self.visitImpl(&$0, ExpressionStmtSyntax.self, self.visit) + } + case .extensionDecl: + return { + await self.visitImpl(&$0, ExtensionDeclSyntax.self, self.visit) + } + case .fallThroughStmt: + return { + await self.visitImpl(&$0, FallThroughStmtSyntax.self, self.visit) + } + case .floatLiteralExpr: + return { + await self.visitImpl(&$0, FloatLiteralExprSyntax.self, self.visit) + } + case .forStmt: + return { + await self.visitImpl(&$0, ForStmtSyntax.self, self.visit) + } + case .forceUnwrapExpr: + return { + await self.visitImpl(&$0, ForceUnwrapExprSyntax.self, self.visit) + } + case .functionCallExpr: + return { + await self.visitImpl(&$0, FunctionCallExprSyntax.self, self.visit) + } + case .functionDecl: + return { + await self.visitImpl(&$0, FunctionDeclSyntax.self, self.visit) + } + case .functionEffectSpecifiers: + return { + await self.visitImpl(&$0, FunctionEffectSpecifiersSyntax.self, self.visit) + } + case .functionParameterClause: + return { + await self.visitImpl(&$0, FunctionParameterClauseSyntax.self, self.visit) + } + case .functionParameterList: + return { + await self.visitImpl(&$0, FunctionParameterListSyntax.self, self.visit) + } + case .functionParameter: + return { + await self.visitImpl(&$0, FunctionParameterSyntax.self, self.visit) + } + case .functionSignature: + return { + await self.visitImpl(&$0, FunctionSignatureSyntax.self, self.visit) + } + case .functionType: + return { + await self.visitImpl(&$0, FunctionTypeSyntax.self, self.visit) + } + case .genericArgumentClause: + return { + await self.visitImpl(&$0, GenericArgumentClauseSyntax.self, self.visit) + } + case .genericArgumentList: + return { + await self.visitImpl(&$0, GenericArgumentListSyntax.self, self.visit) + } + case .genericArgument: + return { + await self.visitImpl(&$0, GenericArgumentSyntax.self, self.visit) + } + case .genericParameterClause: + return { + await self.visitImpl(&$0, GenericParameterClauseSyntax.self, self.visit) + } + case .genericParameterList: + return { + await self.visitImpl(&$0, GenericParameterListSyntax.self, self.visit) + } + case .genericParameter: + return { + await self.visitImpl(&$0, GenericParameterSyntax.self, self.visit) + } + case .genericRequirementList: + return { + await self.visitImpl(&$0, GenericRequirementListSyntax.self, self.visit) + } + case .genericRequirement: + return { + await self.visitImpl(&$0, GenericRequirementSyntax.self, self.visit) + } + case .genericSpecializationExpr: + return { + await self.visitImpl(&$0, GenericSpecializationExprSyntax.self, self.visit) + } + case .genericWhereClause: + return { + await self.visitImpl(&$0, GenericWhereClauseSyntax.self, self.visit) + } + case .guardStmt: + return { + await self.visitImpl(&$0, GuardStmtSyntax.self, self.visit) + } + case .identifierPattern: + return { + await self.visitImpl(&$0, IdentifierPatternSyntax.self, self.visit) + } + case .identifierType: + return { + await self.visitImpl(&$0, IdentifierTypeSyntax.self, self.visit) + } + case .ifConfigClauseList: + return { + await self.visitImpl(&$0, IfConfigClauseListSyntax.self, self.visit) + } + case .ifConfigClause: + return { + await self.visitImpl(&$0, IfConfigClauseSyntax.self, self.visit) + } + case .ifConfigDecl: + return { + await self.visitImpl(&$0, IfConfigDeclSyntax.self, self.visit) + } + case .ifExpr: + return { + await self.visitImpl(&$0, IfExprSyntax.self, self.visit) + } + case .implementsAttributeArguments: + return { + await self.visitImpl(&$0, ImplementsAttributeArgumentsSyntax.self, self.visit) + } + case .implicitlyUnwrappedOptionalType: + return { + await self.visitImpl(&$0, ImplicitlyUnwrappedOptionalTypeSyntax.self, self.visit) + } + case .importDecl: + return { + await self.visitImpl(&$0, ImportDeclSyntax.self, self.visit) + } + case .importPathComponentList: + return { + await self.visitImpl(&$0, ImportPathComponentListSyntax.self, self.visit) + } + case .importPathComponent: + return { + await self.visitImpl(&$0, ImportPathComponentSyntax.self, self.visit) + } + case .inOutExpr: + return { + await self.visitImpl(&$0, InOutExprSyntax.self, self.visit) + } + case .infixOperatorExpr: + return { + await self.visitImpl(&$0, InfixOperatorExprSyntax.self, self.visit) + } + case .inheritanceClause: + return { + await self.visitImpl(&$0, InheritanceClauseSyntax.self, self.visit) + } + case .inheritedTypeList: + return { + await self.visitImpl(&$0, InheritedTypeListSyntax.self, self.visit) + } + case .inheritedType: + return { + await self.visitImpl(&$0, InheritedTypeSyntax.self, self.visit) + } + case .initializerClause: + return { + await self.visitImpl(&$0, InitializerClauseSyntax.self, self.visit) + } + case .initializerDecl: + return { + await self.visitImpl(&$0, InitializerDeclSyntax.self, self.visit) + } + case .integerLiteralExpr: + return { + await self.visitImpl(&$0, IntegerLiteralExprSyntax.self, self.visit) + } + case .isExpr: + return { + await self.visitImpl(&$0, IsExprSyntax.self, self.visit) + } + case .isTypePattern: + return { + await self.visitImpl(&$0, IsTypePatternSyntax.self, self.visit) + } + case .keyPathComponentList: + return { + await self.visitImpl(&$0, KeyPathComponentListSyntax.self, self.visit) + } + case .keyPathComponent: + return { + await self.visitImpl(&$0, KeyPathComponentSyntax.self, self.visit) + } + case .keyPathExpr: + return { + await self.visitImpl(&$0, KeyPathExprSyntax.self, self.visit) + } + case .keyPathOptionalComponent: + return { + await self.visitImpl(&$0, KeyPathOptionalComponentSyntax.self, self.visit) + } + case .keyPathPropertyComponent: + return { + await self.visitImpl(&$0, KeyPathPropertyComponentSyntax.self, self.visit) + } + case .keyPathSubscriptComponent: + return { + await self.visitImpl(&$0, KeyPathSubscriptComponentSyntax.self, self.visit) + } + case .labeledExprList: + return { + await self.visitImpl(&$0, LabeledExprListSyntax.self, self.visit) + } + case .labeledExpr: + return { + await self.visitImpl(&$0, LabeledExprSyntax.self, self.visit) + } + case .labeledSpecializeArgument: + return { + await self.visitImpl(&$0, LabeledSpecializeArgumentSyntax.self, self.visit) + } + case .labeledStmt: + return { + await self.visitImpl(&$0, LabeledStmtSyntax.self, self.visit) + } + case .layoutRequirement: + return { + await self.visitImpl(&$0, LayoutRequirementSyntax.self, self.visit) + } + case .lifetimeSpecifierArgumentList: + return { + await self.visitImpl(&$0, LifetimeSpecifierArgumentListSyntax.self, self.visit) + } + case .lifetimeSpecifierArgument: + return { + await self.visitImpl(&$0, LifetimeSpecifierArgumentSyntax.self, self.visit) + } + case .lifetimeTypeSpecifier: + return { + await self.visitImpl(&$0, LifetimeTypeSpecifierSyntax.self, self.visit) + } + case .macroDecl: + return { + await self.visitImpl(&$0, MacroDeclSyntax.self, self.visit) + } + case .macroExpansionDecl: + return { + await self.visitImpl(&$0, MacroExpansionDeclSyntax.self, self.visit) + } + case .macroExpansionExpr: + return { + await self.visitImpl(&$0, MacroExpansionExprSyntax.self, self.visit) + } + case .matchingPatternCondition: + return { + await self.visitImpl(&$0, MatchingPatternConditionSyntax.self, self.visit) + } + case .memberAccessExpr: + return { + await self.visitImpl(&$0, MemberAccessExprSyntax.self, self.visit) + } + case .memberBlockItemList: + return { + await self.visitImpl(&$0, MemberBlockItemListSyntax.self, self.visit) + } + case .memberBlockItem: + return { + await self.visitImpl(&$0, MemberBlockItemSyntax.self, self.visit) + } + case .memberBlock: + return { + await self.visitImpl(&$0, MemberBlockSyntax.self, self.visit) + } + case .memberType: + return { + await self.visitImpl(&$0, MemberTypeSyntax.self, self.visit) + } + case .metatypeType: + return { + await self.visitImpl(&$0, MetatypeTypeSyntax.self, self.visit) + } + case .missingDecl: + return { + await self.visitImpl(&$0, MissingDeclSyntax.self, self.visit) + } + case .missingExpr: + return { + await self.visitImpl(&$0, MissingExprSyntax.self, self.visit) + } + case .missingPattern: + return { + await self.visitImpl(&$0, MissingPatternSyntax.self, self.visit) + } + case .missingStmt: + return { + await self.visitImpl(&$0, MissingStmtSyntax.self, self.visit) + } + case .missing: + return { + await self.visitImpl(&$0, MissingSyntax.self, self.visit) + } + case .missingType: + return { + await self.visitImpl(&$0, MissingTypeSyntax.self, self.visit) + } + case .multipleTrailingClosureElementList: + return { + await self.visitImpl(&$0, MultipleTrailingClosureElementListSyntax.self, self.visit) + } + case .multipleTrailingClosureElement: + return { + await self.visitImpl(&$0, MultipleTrailingClosureElementSyntax.self, self.visit) + } + case .namedOpaqueReturnType: + return { + await self.visitImpl(&$0, NamedOpaqueReturnTypeSyntax.self, self.visit) + } + case .nilLiteralExpr: + return { + await self.visitImpl(&$0, NilLiteralExprSyntax.self, self.visit) + } + case .objCSelectorPieceList: + return { + await self.visitImpl(&$0, ObjCSelectorPieceListSyntax.self, self.visit) + } + case .objCSelectorPiece: + return { + await self.visitImpl(&$0, ObjCSelectorPieceSyntax.self, self.visit) + } + case .opaqueReturnTypeOfAttributeArguments: + return { + await self.visitImpl(&$0, OpaqueReturnTypeOfAttributeArgumentsSyntax.self, self.visit) + } + case .operatorDecl: + return { + await self.visitImpl(&$0, OperatorDeclSyntax.self, self.visit) + } + case .operatorPrecedenceAndTypes: + return { + await self.visitImpl(&$0, OperatorPrecedenceAndTypesSyntax.self, self.visit) + } + case .optionalBindingCondition: + return { + await self.visitImpl(&$0, OptionalBindingConditionSyntax.self, self.visit) + } + case .optionalChainingExpr: + return { + await self.visitImpl(&$0, OptionalChainingExprSyntax.self, self.visit) + } + case .optionalType: + return { + await self.visitImpl(&$0, OptionalTypeSyntax.self, self.visit) + } + case .originallyDefinedInAttributeArguments: + return { + await self.visitImpl(&$0, OriginallyDefinedInAttributeArgumentsSyntax.self, self.visit) + } + case .packElementExpr: + return { + await self.visitImpl(&$0, PackElementExprSyntax.self, self.visit) + } + case .packElementType: + return { + await self.visitImpl(&$0, PackElementTypeSyntax.self, self.visit) + } + case .packExpansionExpr: + return { + await self.visitImpl(&$0, PackExpansionExprSyntax.self, self.visit) + } + case .packExpansionType: + return { + await self.visitImpl(&$0, PackExpansionTypeSyntax.self, self.visit) + } + case .patternBindingList: + return { + await self.visitImpl(&$0, PatternBindingListSyntax.self, self.visit) + } + case .patternBinding: + return { + await self.visitImpl(&$0, PatternBindingSyntax.self, self.visit) + } + case .patternExpr: + return { + await self.visitImpl(&$0, PatternExprSyntax.self, self.visit) + } + case .platformVersionItemList: + return { + await self.visitImpl(&$0, PlatformVersionItemListSyntax.self, self.visit) + } + case .platformVersionItem: + return { + await self.visitImpl(&$0, PlatformVersionItemSyntax.self, self.visit) + } + case .platformVersion: + return { + await self.visitImpl(&$0, PlatformVersionSyntax.self, self.visit) + } + case .postfixIfConfigExpr: + return { + await self.visitImpl(&$0, PostfixIfConfigExprSyntax.self, self.visit) + } + case .postfixOperatorExpr: + return { + await self.visitImpl(&$0, PostfixOperatorExprSyntax.self, self.visit) + } + case .poundSourceLocationArguments: + return { + await self.visitImpl(&$0, PoundSourceLocationArgumentsSyntax.self, self.visit) + } + case .poundSourceLocation: + return { + await self.visitImpl(&$0, PoundSourceLocationSyntax.self, self.visit) + } + case .precedenceGroupAssignment: + return { + await self.visitImpl(&$0, PrecedenceGroupAssignmentSyntax.self, self.visit) + } + case .precedenceGroupAssociativity: + return { + await self.visitImpl(&$0, PrecedenceGroupAssociativitySyntax.self, self.visit) + } + case .precedenceGroupAttributeList: + return { + await self.visitImpl(&$0, PrecedenceGroupAttributeListSyntax.self, self.visit) + } + case .precedenceGroupDecl: + return { + await self.visitImpl(&$0, PrecedenceGroupDeclSyntax.self, self.visit) + } + case .precedenceGroupNameList: + return { + await self.visitImpl(&$0, PrecedenceGroupNameListSyntax.self, self.visit) + } + case .precedenceGroupName: + return { + await self.visitImpl(&$0, PrecedenceGroupNameSyntax.self, self.visit) + } + case .precedenceGroupRelation: + return { + await self.visitImpl(&$0, PrecedenceGroupRelationSyntax.self, self.visit) + } + case .prefixOperatorExpr: + return { + await self.visitImpl(&$0, PrefixOperatorExprSyntax.self, self.visit) + } + case .primaryAssociatedTypeClause: + return { + await self.visitImpl(&$0, PrimaryAssociatedTypeClauseSyntax.self, self.visit) + } + case .primaryAssociatedTypeList: + return { + await self.visitImpl(&$0, PrimaryAssociatedTypeListSyntax.self, self.visit) + } + case .primaryAssociatedType: + return { + await self.visitImpl(&$0, PrimaryAssociatedTypeSyntax.self, self.visit) + } + case .protocolDecl: + return { + await self.visitImpl(&$0, ProtocolDeclSyntax.self, self.visit) + } + case .regexLiteralExpr: + return { + await self.visitImpl(&$0, RegexLiteralExprSyntax.self, self.visit) + } + case .repeatStmt: + return { + await self.visitImpl(&$0, RepeatStmtSyntax.self, self.visit) + } + case .returnClause: + return { + await self.visitImpl(&$0, ReturnClauseSyntax.self, self.visit) + } + case .returnStmt: + return { + await self.visitImpl(&$0, ReturnStmtSyntax.self, self.visit) + } + case .sameTypeRequirement: + return { + await self.visitImpl(&$0, SameTypeRequirementSyntax.self, self.visit) + } + case .sequenceExpr: + return { + await self.visitImpl(&$0, SequenceExprSyntax.self, self.visit) + } + case .simpleStringLiteralExpr: + return { + await self.visitImpl(&$0, SimpleStringLiteralExprSyntax.self, self.visit) + } + case .simpleStringLiteralSegmentList: + return { + await self.visitImpl(&$0, SimpleStringLiteralSegmentListSyntax.self, self.visit) + } + case .simpleTypeSpecifier: + return { + await self.visitImpl(&$0, SimpleTypeSpecifierSyntax.self, self.visit) + } + case .someOrAnyType: + return { + await self.visitImpl(&$0, SomeOrAnyTypeSyntax.self, self.visit) + } + case .sourceFile: + return { + await self.visitImpl(&$0, SourceFileSyntax.self, self.visit) + } + case .specializeAttributeArgumentList: + return { + await self.visitImpl(&$0, SpecializeAttributeArgumentListSyntax.self, self.visit) + } + case .specializeAvailabilityArgument: + return { + await self.visitImpl(&$0, SpecializeAvailabilityArgumentSyntax.self, self.visit) + } + case .specializeTargetFunctionArgument: + return { + await self.visitImpl(&$0, SpecializeTargetFunctionArgumentSyntax.self, self.visit) + } + case .stringLiteralExpr: + return { + await self.visitImpl(&$0, StringLiteralExprSyntax.self, self.visit) + } + case .stringLiteralSegmentList: + return { + await self.visitImpl(&$0, StringLiteralSegmentListSyntax.self, self.visit) + } + case .stringSegment: + return { + await self.visitImpl(&$0, StringSegmentSyntax.self, self.visit) + } + case .structDecl: + return { + await self.visitImpl(&$0, StructDeclSyntax.self, self.visit) + } + case .subscriptCallExpr: + return { + await self.visitImpl(&$0, SubscriptCallExprSyntax.self, self.visit) + } + case .subscriptDecl: + return { + await self.visitImpl(&$0, SubscriptDeclSyntax.self, self.visit) + } + case .superExpr: + return { + await self.visitImpl(&$0, SuperExprSyntax.self, self.visit) + } + case .suppressedType: + return { + await self.visitImpl(&$0, SuppressedTypeSyntax.self, self.visit) + } + case .switchCaseItemList: + return { + await self.visitImpl(&$0, SwitchCaseItemListSyntax.self, self.visit) + } + case .switchCaseItem: + return { + await self.visitImpl(&$0, SwitchCaseItemSyntax.self, self.visit) + } + case .switchCaseLabel: + return { + await self.visitImpl(&$0, SwitchCaseLabelSyntax.self, self.visit) + } + case .switchCaseList: + return { + await self.visitImpl(&$0, SwitchCaseListSyntax.self, self.visit) + } + case .switchCase: + return { + await self.visitImpl(&$0, SwitchCaseSyntax.self, self.visit) + } + case .switchDefaultLabel: + return { + await self.visitImpl(&$0, SwitchDefaultLabelSyntax.self, self.visit) + } + case .switchExpr: + return { + await self.visitImpl(&$0, SwitchExprSyntax.self, self.visit) + } + case .ternaryExpr: + return { + await self.visitImpl(&$0, TernaryExprSyntax.self, self.visit) + } + case .thenStmt: + return { + await self.visitImpl(&$0, ThenStmtSyntax.self, self.visit) + } + case .throwStmt: + return { + await self.visitImpl(&$0, ThrowStmtSyntax.self, self.visit) + } + case .throwsClause: + return { + await self.visitImpl(&$0, ThrowsClauseSyntax.self, self.visit) + } + case .tryExpr: + return { + await self.visitImpl(&$0, TryExprSyntax.self, self.visit) + } + case .tupleExpr: + return { + await self.visitImpl(&$0, TupleExprSyntax.self, self.visit) + } + case .tuplePatternElementList: + return { + await self.visitImpl(&$0, TuplePatternElementListSyntax.self, self.visit) + } + case .tuplePatternElement: + return { + await self.visitImpl(&$0, TuplePatternElementSyntax.self, self.visit) + } + case .tuplePattern: + return { + await self.visitImpl(&$0, TuplePatternSyntax.self, self.visit) + } + case .tupleTypeElementList: + return { + await self.visitImpl(&$0, TupleTypeElementListSyntax.self, self.visit) + } + case .tupleTypeElement: + return { + await self.visitImpl(&$0, TupleTypeElementSyntax.self, self.visit) + } + case .tupleType: + return { + await self.visitImpl(&$0, TupleTypeSyntax.self, self.visit) + } + case .typeAliasDecl: + return { + await self.visitImpl(&$0, TypeAliasDeclSyntax.self, self.visit) + } + case .typeAnnotation: + return { + await self.visitImpl(&$0, TypeAnnotationSyntax.self, self.visit) + } + case .typeEffectSpecifiers: + return { + await self.visitImpl(&$0, TypeEffectSpecifiersSyntax.self, self.visit) + } + case .typeExpr: + return { + await self.visitImpl(&$0, TypeExprSyntax.self, self.visit) + } + case .typeInitializerClause: + return { + await self.visitImpl(&$0, TypeInitializerClauseSyntax.self, self.visit) + } + case .typeSpecifierList: + return { + await self.visitImpl(&$0, TypeSpecifierListSyntax.self, self.visit) + } + case .unavailableFromAsyncAttributeArguments: + return { + await self.visitImpl(&$0, UnavailableFromAsyncAttributeArgumentsSyntax.self, self.visit) + } + case .underscorePrivateAttributeArguments: + return { + await self.visitImpl(&$0, UnderscorePrivateAttributeArgumentsSyntax.self, self.visit) + } + case .unexpectedNodes: + return { + await self.visitImpl(&$0, UnexpectedNodesSyntax.self, self.visit) + } + case .unresolvedAsExpr: + return { + await self.visitImpl(&$0, UnresolvedAsExprSyntax.self, self.visit) + } + case .unresolvedIsExpr: + return { + await self.visitImpl(&$0, UnresolvedIsExprSyntax.self, self.visit) + } + case .unresolvedTernaryExpr: + return { + await self.visitImpl(&$0, UnresolvedTernaryExprSyntax.self, self.visit) + } + case .valueBindingPattern: + return { + await self.visitImpl(&$0, ValueBindingPatternSyntax.self, self.visit) + } + case .variableDecl: + return { + await self.visitImpl(&$0, VariableDeclSyntax.self, self.visit) + } + case .versionComponentList: + return { + await self.visitImpl(&$0, VersionComponentListSyntax.self, self.visit) + } + case .versionComponent: + return { + await self.visitImpl(&$0, VersionComponentSyntax.self, self.visit) + } + case .versionTuple: + return { + await self.visitImpl(&$0, VersionTupleSyntax.self, self.visit) + } + case .whereClause: + return { + await self.visitImpl(&$0, WhereClauseSyntax.self, self.visit) + } + case .whileStmt: + return { + await self.visitImpl(&$0, WhileStmtSyntax.self, self.visit) + } + case .wildcardPattern: + return { + await self.visitImpl(&$0, WildcardPatternSyntax.self, self.visit) + } + case .yieldStmt: + return { + await self.visitImpl(&$0, YieldStmtSyntax.self, self.visit) + } + case .yieldedExpressionList: + return { + await self.visitImpl(&$0, YieldedExpressionListSyntax.self, self.visit) + } + case .yieldedExpression: + return { + await self.visitImpl(&$0, YieldedExpressionSyntax.self, self.visit) + } + case .yieldedExpressionsClause: + return { + await self.visitImpl(&$0, YieldedExpressionsClauseSyntax.self, self.visit) + } + } + } + private func dispatchVisit(_ node: inout Syntax) async { + await visitationFunc(for: node)(&node) + } + #else + private func dispatchVisit(_ node: inout Syntax) async { + switch node.raw.kind { + case .token: + return await visitImpl(&node, TokenSyntax.self, visit) + case .accessorBlock: + return await visitImpl(&node, AccessorBlockSyntax.self, visit) + case .accessorDeclList: + return await visitImpl(&node, AccessorDeclListSyntax.self, visit) + case .accessorDecl: + return await visitImpl(&node, AccessorDeclSyntax.self, visit) + case .accessorEffectSpecifiers: + return await visitImpl(&node, AccessorEffectSpecifiersSyntax.self, visit) + case .accessorParameters: + return await visitImpl(&node, AccessorParametersSyntax.self, visit) + case .actorDecl: + return await visitImpl(&node, ActorDeclSyntax.self, visit) + case .arrayElementList: + return await visitImpl(&node, ArrayElementListSyntax.self, visit) + case .arrayElement: + return await visitImpl(&node, ArrayElementSyntax.self, visit) + case .arrayExpr: + return await visitImpl(&node, ArrayExprSyntax.self, visit) + case .arrayType: + return await visitImpl(&node, ArrayTypeSyntax.self, visit) + case .arrowExpr: + return await visitImpl(&node, ArrowExprSyntax.self, visit) + case .asExpr: + return await visitImpl(&node, AsExprSyntax.self, visit) + case .assignmentExpr: + return await visitImpl(&node, AssignmentExprSyntax.self, visit) + case .associatedTypeDecl: + return await visitImpl(&node, AssociatedTypeDeclSyntax.self, visit) + case .attributeList: + return await visitImpl(&node, AttributeListSyntax.self, visit) + case .attribute: + return await visitImpl(&node, AttributeSyntax.self, visit) + case .attributedType: + return await visitImpl(&node, AttributedTypeSyntax.self, visit) + case .availabilityArgumentList: + return await visitImpl(&node, AvailabilityArgumentListSyntax.self, visit) + case .availabilityArgument: + return await visitImpl(&node, AvailabilityArgumentSyntax.self, visit) + case .availabilityCondition: + return await visitImpl(&node, AvailabilityConditionSyntax.self, visit) + case .availabilityLabeledArgument: + return await visitImpl(&node, AvailabilityLabeledArgumentSyntax.self, visit) + case .awaitExpr: + return await visitImpl(&node, AwaitExprSyntax.self, visit) + case .backDeployedAttributeArguments: + return await visitImpl(&node, BackDeployedAttributeArgumentsSyntax.self, visit) + case .binaryOperatorExpr: + return await visitImpl(&node, BinaryOperatorExprSyntax.self, visit) + case .booleanLiteralExpr: + return await visitImpl(&node, BooleanLiteralExprSyntax.self, visit) + case .borrowExpr: + return await visitImpl(&node, BorrowExprSyntax.self, visit) + case .breakStmt: + return await visitImpl(&node, BreakStmtSyntax.self, visit) + case ._canImportExpr: + return await visitImpl(&node, _CanImportExprSyntax.self, visit) + case ._canImportVersionInfo: + return await visitImpl(&node, _CanImportVersionInfoSyntax.self, visit) + case .catchClauseList: + return await visitImpl(&node, CatchClauseListSyntax.self, visit) + case .catchClause: + return await visitImpl(&node, CatchClauseSyntax.self, visit) + case .catchItemList: + return await visitImpl(&node, CatchItemListSyntax.self, visit) + case .catchItem: + return await visitImpl(&node, CatchItemSyntax.self, visit) + case .classDecl: + return await visitImpl(&node, ClassDeclSyntax.self, visit) + case .classRestrictionType: + return await visitImpl(&node, ClassRestrictionTypeSyntax.self, visit) + case .closureCaptureClause: + return await visitImpl(&node, ClosureCaptureClauseSyntax.self, visit) + case .closureCaptureList: + return await visitImpl(&node, ClosureCaptureListSyntax.self, visit) + case .closureCaptureSpecifier: + return await visitImpl(&node, ClosureCaptureSpecifierSyntax.self, visit) + case .closureCapture: + return await visitImpl(&node, ClosureCaptureSyntax.self, visit) + case .closureExpr: + return await visitImpl(&node, ClosureExprSyntax.self, visit) + case .closureParameterClause: + return await visitImpl(&node, ClosureParameterClauseSyntax.self, visit) + case .closureParameterList: + return await visitImpl(&node, ClosureParameterListSyntax.self, visit) + case .closureParameter: + return await visitImpl(&node, ClosureParameterSyntax.self, visit) + case .closureShorthandParameterList: + return await visitImpl(&node, ClosureShorthandParameterListSyntax.self, visit) + case .closureShorthandParameter: + return await visitImpl(&node, ClosureShorthandParameterSyntax.self, visit) + case .closureSignature: + return await visitImpl(&node, ClosureSignatureSyntax.self, visit) + case .codeBlockItemList: + return await visitImpl(&node, CodeBlockItemListSyntax.self, visit) + case .codeBlockItem: + return await visitImpl(&node, CodeBlockItemSyntax.self, visit) + case .codeBlock: + return await visitImpl(&node, CodeBlockSyntax.self, visit) + case .compositionTypeElementList: + return await visitImpl(&node, CompositionTypeElementListSyntax.self, visit) + case .compositionTypeElement: + return await visitImpl(&node, CompositionTypeElementSyntax.self, visit) + case .compositionType: + return await visitImpl(&node, CompositionTypeSyntax.self, visit) + case .conditionElementList: + return await visitImpl(&node, ConditionElementListSyntax.self, visit) + case .conditionElement: + return await visitImpl(&node, ConditionElementSyntax.self, visit) + case .conformanceRequirement: + return await visitImpl(&node, ConformanceRequirementSyntax.self, visit) + case .consumeExpr: + return await visitImpl(&node, ConsumeExprSyntax.self, visit) + case .continueStmt: + return await visitImpl(&node, ContinueStmtSyntax.self, visit) + case .conventionAttributeArguments: + return await visitImpl(&node, ConventionAttributeArgumentsSyntax.self, visit) + case .conventionWitnessMethodAttributeArguments: + return await visitImpl(&node, ConventionWitnessMethodAttributeArgumentsSyntax.self, visit) + case .copyExpr: + return await visitImpl(&node, CopyExprSyntax.self, visit) + case .declModifierDetail: + return await visitImpl(&node, DeclModifierDetailSyntax.self, visit) + case .declModifierList: + return await visitImpl(&node, DeclModifierListSyntax.self, visit) + case .declModifier: + return await visitImpl(&node, DeclModifierSyntax.self, visit) + case .declNameArgumentList: + return await visitImpl(&node, DeclNameArgumentListSyntax.self, visit) + case .declNameArgument: + return await visitImpl(&node, DeclNameArgumentSyntax.self, visit) + case .declNameArguments: + return await visitImpl(&node, DeclNameArgumentsSyntax.self, visit) + case .declReferenceExpr: + return await visitImpl(&node, DeclReferenceExprSyntax.self, visit) + case .deferStmt: + return await visitImpl(&node, DeferStmtSyntax.self, visit) + case .deinitializerDecl: + return await visitImpl(&node, DeinitializerDeclSyntax.self, visit) + case .deinitializerEffectSpecifiers: + return await visitImpl(&node, DeinitializerEffectSpecifiersSyntax.self, visit) + case .derivativeAttributeArguments: + return await visitImpl(&node, DerivativeAttributeArgumentsSyntax.self, visit) + case .designatedTypeList: + return await visitImpl(&node, DesignatedTypeListSyntax.self, visit) + case .designatedType: + return await visitImpl(&node, DesignatedTypeSyntax.self, visit) + case .dictionaryElementList: + return await visitImpl(&node, DictionaryElementListSyntax.self, visit) + case .dictionaryElement: + return await visitImpl(&node, DictionaryElementSyntax.self, visit) + case .dictionaryExpr: + return await visitImpl(&node, DictionaryExprSyntax.self, visit) + case .dictionaryType: + return await visitImpl(&node, DictionaryTypeSyntax.self, visit) + case .differentiabilityArgumentList: + return await visitImpl(&node, DifferentiabilityArgumentListSyntax.self, visit) + case .differentiabilityArgument: + return await visitImpl(&node, DifferentiabilityArgumentSyntax.self, visit) + case .differentiabilityArguments: + return await visitImpl(&node, DifferentiabilityArgumentsSyntax.self, visit) + case .differentiabilityWithRespectToArgument: + return await visitImpl(&node, DifferentiabilityWithRespectToArgumentSyntax.self, visit) + case .differentiableAttributeArguments: + return await visitImpl(&node, DifferentiableAttributeArgumentsSyntax.self, visit) + case .discardAssignmentExpr: + return await visitImpl(&node, DiscardAssignmentExprSyntax.self, visit) + case .discardStmt: + return await visitImpl(&node, DiscardStmtSyntax.self, visit) + case .doExpr: + return await visitImpl(&node, DoExprSyntax.self, visit) + case .doStmt: + return await visitImpl(&node, DoStmtSyntax.self, visit) + case .documentationAttributeArgumentList: + return await visitImpl(&node, DocumentationAttributeArgumentListSyntax.self, visit) + case .documentationAttributeArgument: + return await visitImpl(&node, DocumentationAttributeArgumentSyntax.self, visit) + case .dynamicReplacementAttributeArguments: + return await visitImpl(&node, DynamicReplacementAttributeArgumentsSyntax.self, visit) + case .editorPlaceholderDecl: + return await visitImpl(&node, EditorPlaceholderDeclSyntax.self, visit) + case .editorPlaceholderExpr: + return await visitImpl(&node, EditorPlaceholderExprSyntax.self, visit) + case .effectsAttributeArgumentList: + return await visitImpl(&node, EffectsAttributeArgumentListSyntax.self, visit) + case .enumCaseDecl: + return await visitImpl(&node, EnumCaseDeclSyntax.self, visit) + case .enumCaseElementList: + return await visitImpl(&node, EnumCaseElementListSyntax.self, visit) + case .enumCaseElement: + return await visitImpl(&node, EnumCaseElementSyntax.self, visit) + case .enumCaseParameterClause: + return await visitImpl(&node, EnumCaseParameterClauseSyntax.self, visit) + case .enumCaseParameterList: + return await visitImpl(&node, EnumCaseParameterListSyntax.self, visit) + case .enumCaseParameter: + return await visitImpl(&node, EnumCaseParameterSyntax.self, visit) + case .enumDecl: + return await visitImpl(&node, EnumDeclSyntax.self, visit) + case .exposeAttributeArguments: + return await visitImpl(&node, ExposeAttributeArgumentsSyntax.self, visit) + case .exprList: + return await visitImpl(&node, ExprListSyntax.self, visit) + case .expressionPattern: + return await visitImpl(&node, ExpressionPatternSyntax.self, visit) + case .expressionSegment: + return await visitImpl(&node, ExpressionSegmentSyntax.self, visit) + case .expressionStmt: + return await visitImpl(&node, ExpressionStmtSyntax.self, visit) + case .extensionDecl: + return await visitImpl(&node, ExtensionDeclSyntax.self, visit) + case .fallThroughStmt: + return await visitImpl(&node, FallThroughStmtSyntax.self, visit) + case .floatLiteralExpr: + return await visitImpl(&node, FloatLiteralExprSyntax.self, visit) + case .forStmt: + return await visitImpl(&node, ForStmtSyntax.self, visit) + case .forceUnwrapExpr: + return await visitImpl(&node, ForceUnwrapExprSyntax.self, visit) + case .functionCallExpr: + return await visitImpl(&node, FunctionCallExprSyntax.self, visit) + case .functionDecl: + return await visitImpl(&node, FunctionDeclSyntax.self, visit) + case .functionEffectSpecifiers: + return await visitImpl(&node, FunctionEffectSpecifiersSyntax.self, visit) + case .functionParameterClause: + return await visitImpl(&node, FunctionParameterClauseSyntax.self, visit) + case .functionParameterList: + return await visitImpl(&node, FunctionParameterListSyntax.self, visit) + case .functionParameter: + return await visitImpl(&node, FunctionParameterSyntax.self, visit) + case .functionSignature: + return await visitImpl(&node, FunctionSignatureSyntax.self, visit) + case .functionType: + return await visitImpl(&node, FunctionTypeSyntax.self, visit) + case .genericArgumentClause: + return await visitImpl(&node, GenericArgumentClauseSyntax.self, visit) + case .genericArgumentList: + return await visitImpl(&node, GenericArgumentListSyntax.self, visit) + case .genericArgument: + return await visitImpl(&node, GenericArgumentSyntax.self, visit) + case .genericParameterClause: + return await visitImpl(&node, GenericParameterClauseSyntax.self, visit) + case .genericParameterList: + return await visitImpl(&node, GenericParameterListSyntax.self, visit) + case .genericParameter: + return await visitImpl(&node, GenericParameterSyntax.self, visit) + case .genericRequirementList: + return await visitImpl(&node, GenericRequirementListSyntax.self, visit) + case .genericRequirement: + return await visitImpl(&node, GenericRequirementSyntax.self, visit) + case .genericSpecializationExpr: + return await visitImpl(&node, GenericSpecializationExprSyntax.self, visit) + case .genericWhereClause: + return await visitImpl(&node, GenericWhereClauseSyntax.self, visit) + case .guardStmt: + return await visitImpl(&node, GuardStmtSyntax.self, visit) + case .identifierPattern: + return await visitImpl(&node, IdentifierPatternSyntax.self, visit) + case .identifierType: + return await visitImpl(&node, IdentifierTypeSyntax.self, visit) + case .ifConfigClauseList: + return await visitImpl(&node, IfConfigClauseListSyntax.self, visit) + case .ifConfigClause: + return await visitImpl(&node, IfConfigClauseSyntax.self, visit) + case .ifConfigDecl: + return await visitImpl(&node, IfConfigDeclSyntax.self, visit) + case .ifExpr: + return await visitImpl(&node, IfExprSyntax.self, visit) + case .implementsAttributeArguments: + return await visitImpl(&node, ImplementsAttributeArgumentsSyntax.self, visit) + case .implicitlyUnwrappedOptionalType: + return await visitImpl(&node, ImplicitlyUnwrappedOptionalTypeSyntax.self, visit) + case .importDecl: + return await visitImpl(&node, ImportDeclSyntax.self, visit) + case .importPathComponentList: + return await visitImpl(&node, ImportPathComponentListSyntax.self, visit) + case .importPathComponent: + return await visitImpl(&node, ImportPathComponentSyntax.self, visit) + case .inOutExpr: + return await visitImpl(&node, InOutExprSyntax.self, visit) + case .infixOperatorExpr: + return await visitImpl(&node, InfixOperatorExprSyntax.self, visit) + case .inheritanceClause: + return await visitImpl(&node, InheritanceClauseSyntax.self, visit) + case .inheritedTypeList: + return await visitImpl(&node, InheritedTypeListSyntax.self, visit) + case .inheritedType: + return await visitImpl(&node, InheritedTypeSyntax.self, visit) + case .initializerClause: + return await visitImpl(&node, InitializerClauseSyntax.self, visit) + case .initializerDecl: + return await visitImpl(&node, InitializerDeclSyntax.self, visit) + case .integerLiteralExpr: + return await visitImpl(&node, IntegerLiteralExprSyntax.self, visit) + case .isExpr: + return await visitImpl(&node, IsExprSyntax.self, visit) + case .isTypePattern: + return await visitImpl(&node, IsTypePatternSyntax.self, visit) + case .keyPathComponentList: + return await visitImpl(&node, KeyPathComponentListSyntax.self, visit) + case .keyPathComponent: + return await visitImpl(&node, KeyPathComponentSyntax.self, visit) + case .keyPathExpr: + return await visitImpl(&node, KeyPathExprSyntax.self, visit) + case .keyPathOptionalComponent: + return await visitImpl(&node, KeyPathOptionalComponentSyntax.self, visit) + case .keyPathPropertyComponent: + return await visitImpl(&node, KeyPathPropertyComponentSyntax.self, visit) + case .keyPathSubscriptComponent: + return await visitImpl(&node, KeyPathSubscriptComponentSyntax.self, visit) + case .labeledExprList: + return await visitImpl(&node, LabeledExprListSyntax.self, visit) + case .labeledExpr: + return await visitImpl(&node, LabeledExprSyntax.self, visit) + case .labeledSpecializeArgument: + return await visitImpl(&node, LabeledSpecializeArgumentSyntax.self, visit) + case .labeledStmt: + return await visitImpl(&node, LabeledStmtSyntax.self, visit) + case .layoutRequirement: + return await visitImpl(&node, LayoutRequirementSyntax.self, visit) + case .lifetimeSpecifierArgumentList: + return await visitImpl(&node, LifetimeSpecifierArgumentListSyntax.self, visit) + case .lifetimeSpecifierArgument: + return await visitImpl(&node, LifetimeSpecifierArgumentSyntax.self, visit) + case .lifetimeTypeSpecifier: + return await visitImpl(&node, LifetimeTypeSpecifierSyntax.self, visit) + case .macroDecl: + return await visitImpl(&node, MacroDeclSyntax.self, visit) + case .macroExpansionDecl: + return await visitImpl(&node, MacroExpansionDeclSyntax.self, visit) + case .macroExpansionExpr: + return await visitImpl(&node, MacroExpansionExprSyntax.self, visit) + case .matchingPatternCondition: + return await visitImpl(&node, MatchingPatternConditionSyntax.self, visit) + case .memberAccessExpr: + return await visitImpl(&node, MemberAccessExprSyntax.self, visit) + case .memberBlockItemList: + return await visitImpl(&node, MemberBlockItemListSyntax.self, visit) + case .memberBlockItem: + return await visitImpl(&node, MemberBlockItemSyntax.self, visit) + case .memberBlock: + return await visitImpl(&node, MemberBlockSyntax.self, visit) + case .memberType: + return await visitImpl(&node, MemberTypeSyntax.self, visit) + case .metatypeType: + return await visitImpl(&node, MetatypeTypeSyntax.self, visit) + case .missingDecl: + return await visitImpl(&node, MissingDeclSyntax.self, visit) + case .missingExpr: + return await visitImpl(&node, MissingExprSyntax.self, visit) + case .missingPattern: + return await visitImpl(&node, MissingPatternSyntax.self, visit) + case .missingStmt: + return await visitImpl(&node, MissingStmtSyntax.self, visit) + case .missing: + return await visitImpl(&node, MissingSyntax.self, visit) + case .missingType: + return await visitImpl(&node, MissingTypeSyntax.self, visit) + case .multipleTrailingClosureElementList: + return await visitImpl(&node, MultipleTrailingClosureElementListSyntax.self, visit) + case .multipleTrailingClosureElement: + return await visitImpl(&node, MultipleTrailingClosureElementSyntax.self, visit) + case .namedOpaqueReturnType: + return await visitImpl(&node, NamedOpaqueReturnTypeSyntax.self, visit) + case .nilLiteralExpr: + return await visitImpl(&node, NilLiteralExprSyntax.self, visit) + case .objCSelectorPieceList: + return await visitImpl(&node, ObjCSelectorPieceListSyntax.self, visit) + case .objCSelectorPiece: + return await visitImpl(&node, ObjCSelectorPieceSyntax.self, visit) + case .opaqueReturnTypeOfAttributeArguments: + return await visitImpl(&node, OpaqueReturnTypeOfAttributeArgumentsSyntax.self, visit) + case .operatorDecl: + return await visitImpl(&node, OperatorDeclSyntax.self, visit) + case .operatorPrecedenceAndTypes: + return await visitImpl(&node, OperatorPrecedenceAndTypesSyntax.self, visit) + case .optionalBindingCondition: + return await visitImpl(&node, OptionalBindingConditionSyntax.self, visit) + case .optionalChainingExpr: + return await visitImpl(&node, OptionalChainingExprSyntax.self, visit) + case .optionalType: + return await visitImpl(&node, OptionalTypeSyntax.self, visit) + case .originallyDefinedInAttributeArguments: + return await visitImpl(&node, OriginallyDefinedInAttributeArgumentsSyntax.self, visit) + case .packElementExpr: + return await visitImpl(&node, PackElementExprSyntax.self, visit) + case .packElementType: + return await visitImpl(&node, PackElementTypeSyntax.self, visit) + case .packExpansionExpr: + return await visitImpl(&node, PackExpansionExprSyntax.self, visit) + case .packExpansionType: + return await visitImpl(&node, PackExpansionTypeSyntax.self, visit) + case .patternBindingList: + return await visitImpl(&node, PatternBindingListSyntax.self, visit) + case .patternBinding: + return await visitImpl(&node, PatternBindingSyntax.self, visit) + case .patternExpr: + return await visitImpl(&node, PatternExprSyntax.self, visit) + case .platformVersionItemList: + return await visitImpl(&node, PlatformVersionItemListSyntax.self, visit) + case .platformVersionItem: + return await visitImpl(&node, PlatformVersionItemSyntax.self, visit) + case .platformVersion: + return await visitImpl(&node, PlatformVersionSyntax.self, visit) + case .postfixIfConfigExpr: + return await visitImpl(&node, PostfixIfConfigExprSyntax.self, visit) + case .postfixOperatorExpr: + return await visitImpl(&node, PostfixOperatorExprSyntax.self, visit) + case .poundSourceLocationArguments: + return await visitImpl(&node, PoundSourceLocationArgumentsSyntax.self, visit) + case .poundSourceLocation: + return await visitImpl(&node, PoundSourceLocationSyntax.self, visit) + case .precedenceGroupAssignment: + return await visitImpl(&node, PrecedenceGroupAssignmentSyntax.self, visit) + case .precedenceGroupAssociativity: + return await visitImpl(&node, PrecedenceGroupAssociativitySyntax.self, visit) + case .precedenceGroupAttributeList: + return await visitImpl(&node, PrecedenceGroupAttributeListSyntax.self, visit) + case .precedenceGroupDecl: + return await visitImpl(&node, PrecedenceGroupDeclSyntax.self, visit) + case .precedenceGroupNameList: + return await visitImpl(&node, PrecedenceGroupNameListSyntax.self, visit) + case .precedenceGroupName: + return await visitImpl(&node, PrecedenceGroupNameSyntax.self, visit) + case .precedenceGroupRelation: + return await visitImpl(&node, PrecedenceGroupRelationSyntax.self, visit) + case .prefixOperatorExpr: + return await visitImpl(&node, PrefixOperatorExprSyntax.self, visit) + case .primaryAssociatedTypeClause: + return await visitImpl(&node, PrimaryAssociatedTypeClauseSyntax.self, visit) + case .primaryAssociatedTypeList: + return await visitImpl(&node, PrimaryAssociatedTypeListSyntax.self, visit) + case .primaryAssociatedType: + return await visitImpl(&node, PrimaryAssociatedTypeSyntax.self, visit) + case .protocolDecl: + return await visitImpl(&node, ProtocolDeclSyntax.self, visit) + case .regexLiteralExpr: + return await visitImpl(&node, RegexLiteralExprSyntax.self, visit) + case .repeatStmt: + return await visitImpl(&node, RepeatStmtSyntax.self, visit) + case .returnClause: + return await visitImpl(&node, ReturnClauseSyntax.self, visit) + case .returnStmt: + return await visitImpl(&node, ReturnStmtSyntax.self, visit) + case .sameTypeRequirement: + return await visitImpl(&node, SameTypeRequirementSyntax.self, visit) + case .sequenceExpr: + return await visitImpl(&node, SequenceExprSyntax.self, visit) + case .simpleStringLiteralExpr: + return await visitImpl(&node, SimpleStringLiteralExprSyntax.self, visit) + case .simpleStringLiteralSegmentList: + return await visitImpl(&node, SimpleStringLiteralSegmentListSyntax.self, visit) + case .simpleTypeSpecifier: + return await visitImpl(&node, SimpleTypeSpecifierSyntax.self, visit) + case .someOrAnyType: + return await visitImpl(&node, SomeOrAnyTypeSyntax.self, visit) + case .sourceFile: + return await visitImpl(&node, SourceFileSyntax.self, visit) + case .specializeAttributeArgumentList: + return await visitImpl(&node, SpecializeAttributeArgumentListSyntax.self, visit) + case .specializeAvailabilityArgument: + return await visitImpl(&node, SpecializeAvailabilityArgumentSyntax.self, visit) + case .specializeTargetFunctionArgument: + return await visitImpl(&node, SpecializeTargetFunctionArgumentSyntax.self, visit) + case .stringLiteralExpr: + return await visitImpl(&node, StringLiteralExprSyntax.self, visit) + case .stringLiteralSegmentList: + return await visitImpl(&node, StringLiteralSegmentListSyntax.self, visit) + case .stringSegment: + return await visitImpl(&node, StringSegmentSyntax.self, visit) + case .structDecl: + return await visitImpl(&node, StructDeclSyntax.self, visit) + case .subscriptCallExpr: + return await visitImpl(&node, SubscriptCallExprSyntax.self, visit) + case .subscriptDecl: + return await visitImpl(&node, SubscriptDeclSyntax.self, visit) + case .superExpr: + return await visitImpl(&node, SuperExprSyntax.self, visit) + case .suppressedType: + return await visitImpl(&node, SuppressedTypeSyntax.self, visit) + case .switchCaseItemList: + return await visitImpl(&node, SwitchCaseItemListSyntax.self, visit) + case .switchCaseItem: + return await visitImpl(&node, SwitchCaseItemSyntax.self, visit) + case .switchCaseLabel: + return await visitImpl(&node, SwitchCaseLabelSyntax.self, visit) + case .switchCaseList: + return await visitImpl(&node, SwitchCaseListSyntax.self, visit) + case .switchCase: + return await visitImpl(&node, SwitchCaseSyntax.self, visit) + case .switchDefaultLabel: + return await visitImpl(&node, SwitchDefaultLabelSyntax.self, visit) + case .switchExpr: + return await visitImpl(&node, SwitchExprSyntax.self, visit) + case .ternaryExpr: + return await visitImpl(&node, TernaryExprSyntax.self, visit) + case .thenStmt: + return await visitImpl(&node, ThenStmtSyntax.self, visit) + case .throwStmt: + return await visitImpl(&node, ThrowStmtSyntax.self, visit) + case .throwsClause: + return await visitImpl(&node, ThrowsClauseSyntax.self, visit) + case .tryExpr: + return await visitImpl(&node, TryExprSyntax.self, visit) + case .tupleExpr: + return await visitImpl(&node, TupleExprSyntax.self, visit) + case .tuplePatternElementList: + return await visitImpl(&node, TuplePatternElementListSyntax.self, visit) + case .tuplePatternElement: + return await visitImpl(&node, TuplePatternElementSyntax.self, visit) + case .tuplePattern: + return await visitImpl(&node, TuplePatternSyntax.self, visit) + case .tupleTypeElementList: + return await visitImpl(&node, TupleTypeElementListSyntax.self, visit) + case .tupleTypeElement: + return await visitImpl(&node, TupleTypeElementSyntax.self, visit) + case .tupleType: + return await visitImpl(&node, TupleTypeSyntax.self, visit) + case .typeAliasDecl: + return await visitImpl(&node, TypeAliasDeclSyntax.self, visit) + case .typeAnnotation: + return await visitImpl(&node, TypeAnnotationSyntax.self, visit) + case .typeEffectSpecifiers: + return await visitImpl(&node, TypeEffectSpecifiersSyntax.self, visit) + case .typeExpr: + return await visitImpl(&node, TypeExprSyntax.self, visit) + case .typeInitializerClause: + return await visitImpl(&node, TypeInitializerClauseSyntax.self, visit) + case .typeSpecifierList: + return await visitImpl(&node, TypeSpecifierListSyntax.self, visit) + case .unavailableFromAsyncAttributeArguments: + return await visitImpl(&node, UnavailableFromAsyncAttributeArgumentsSyntax.self, visit) + case .underscorePrivateAttributeArguments: + return await visitImpl(&node, UnderscorePrivateAttributeArgumentsSyntax.self, visit) + case .unexpectedNodes: + return await visitImpl(&node, UnexpectedNodesSyntax.self, visit) + case .unresolvedAsExpr: + return await visitImpl(&node, UnresolvedAsExprSyntax.self, visit) + case .unresolvedIsExpr: + return await visitImpl(&node, UnresolvedIsExprSyntax.self, visit) + case .unresolvedTernaryExpr: + return await visitImpl(&node, UnresolvedTernaryExprSyntax.self, visit) + case .valueBindingPattern: + return await visitImpl(&node, ValueBindingPatternSyntax.self, visit) + case .variableDecl: + return await visitImpl(&node, VariableDeclSyntax.self, visit) + case .versionComponentList: + return await visitImpl(&node, VersionComponentListSyntax.self, visit) + case .versionComponent: + return await visitImpl(&node, VersionComponentSyntax.self, visit) + case .versionTuple: + return await visitImpl(&node, VersionTupleSyntax.self, visit) + case .whereClause: + return await visitImpl(&node, WhereClauseSyntax.self, visit) + case .whileStmt: + return await visitImpl(&node, WhileStmtSyntax.self, visit) + case .wildcardPattern: + return await visitImpl(&node, WildcardPatternSyntax.self, visit) + case .yieldStmt: + return await visitImpl(&node, YieldStmtSyntax.self, visit) + case .yieldedExpressionList: + return await visitImpl(&node, YieldedExpressionListSyntax.self, visit) + case .yieldedExpression: + return await visitImpl(&node, YieldedExpressionSyntax.self, visit) + case .yieldedExpressionsClause: + return await visitImpl(&node, YieldedExpressionsClauseSyntax.self, visit) + } + } + #endif + + private func visitChildren(_ node: Syntax) async -> Syntax { + // Walk over all children of this node and rewrite them. Don't store any + // rewritten nodes until the first non-`nil` value is encountered. When this + // happens, retrieve all previous syntax nodes from the parent node to + // initialize the new layout. Once we know that we have to rewrite the + // layout, we need to collect all further children, regardless of whether + // they are rewritten or not. + + // newLayout is nil until the first child node is rewritten and rewritten + // nodes are being collected. + var newLayout: UnsafeMutableBufferPointer = .init(start: nil, count: 0) + + // Keep 'SyntaxArena' of rewritten nodes alive until they are wrapped + // with 'Syntax' + var rewrittens: ContiguousArray = [] + + for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) { + + // Build the Syntax node to rewrite + var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info) + + await dispatchVisit(&childNode) + if childNode.raw.id != child.id { + // The node was rewritten, let's handle it + + if newLayout.baseAddress == nil { + // We have not yet collected any previous rewritten nodes. Initialize + // the new layout with the previous nodes of the parent. + newLayout = .allocate(capacity: node.raw.layoutView!.children.count) + _ = newLayout.initialize(fromContentsOf: node.raw.layoutView!.children) + } + + // Update the rewritten child. + newLayout[Int(info.indexInParent)] = childNode.raw + // Retain the syntax arena of the new node until it's wrapped with Syntax node. + rewrittens.append(childNode.raw.arenaReference.retained) + } + + // Recycle 'childNode.info' + nodeFactory.dispose(&childNode) + } + + if newLayout.baseAddress != nil { + // A child node was rewritten. Build the updated node. + + let arena = self.arena ?? SyntaxArena() + let newRaw = node.raw.layoutView!.replacingLayout(with: newLayout, arena: arena) + newLayout.deinitialize() + newLayout.deallocate() + // 'withExtendedLifetime' to keep 'SyntaxArena's of them alive until here. + return withExtendedLifetime(rewrittens) { + Syntax(raw: newRaw, rawNodeArena: arena) + } + } else { + // No child node was rewritten. So no need to change this node as well. + return node + } + } +} diff --git a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift index ade68592503..0fdf60c0bd0 100644 --- a/Sources/SwiftSyntax/generated/SyntaxRewriter.swift +++ b/Sources/SwiftSyntax/generated/SyntaxRewriter.swift @@ -2157,7 +2157,12 @@ open class SyntaxRewriter { ) { let origNode = node visitPre(origNode) - node = visitAny(origNode) ?? Syntax(visit(origNode.cast(NodeType.self))) + node = + if let newNode = visitAny(origNode) { + newNode + } else { + Syntax(visit(origNode.cast(NodeType.self))) + } visitPost(origNode) } From 6f4cc1dfae6752c6738bfc20ef71a8eec9e71cd0 Mon Sep 17 00:00:00 2001 From: Roope Virtanen Date: Thu, 14 Nov 2024 13:33:33 +0200 Subject: [PATCH 2/3] Add async overloads of `expansion` functions to macro protocols --- .../MacroProtocols/AccessorMacro.swift | 19 ++++++ .../MacroProtocols/BodyMacro.swift | 21 +++++++ .../MacroProtocols/CodeItemMacro.swift | 16 +++++ .../MacroProtocols/DeclarationMacro.swift | 14 +++++ .../MacroProtocols/ExpressionMacro.swift | 16 +++++ .../MacroProtocols/ExtensionMacro.swift | 42 ++++++++++++++ .../MacroProtocols/MemberAttributeMacro.swift | 28 +++++++++ .../MacroProtocols/MemberMacro.swift | 58 +++++++++++++++++++ .../MacroProtocols/PeerMacro.swift | 22 +++++++ .../MacroProtocols/PreambleMacro.swift | 23 ++++++++ 10 files changed, 259 insertions(+) diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/AccessorMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/AccessorMacro.swift index dadab4eaf2e..0946e30d591 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/AccessorMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/AccessorMacro.swift @@ -24,4 +24,23 @@ public protocol AccessorMacro: AttachedMacro { providingAccessorsOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [AccessorDeclSyntax] + + /// Expand a macro that's expressed as a custom attribute attached to + /// the given declaration. The result is a set of accessors for the + /// declaration. + static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [AccessorDeclSyntax] +} + +extension AccessorMacro { + public static func expansion( + of node: AttributeSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [AccessorDeclSyntax] { + return try { try self.expansion(of: node, providingAccessorsOf: declaration, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/BodyMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/BodyMacro.swift index d5a53710fa4..c56deb771b6 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/BodyMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/BodyMacro.swift @@ -27,4 +27,25 @@ public protocol BodyMacro: AttachedMacro { providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, in context: some MacroExpansionContext ) throws -> [CodeBlockItemSyntax] + + /// Expand a macro described by the given custom attribute and + /// attached to the given declaration and evaluated within a + /// particular expansion context. + /// + /// The macro expansion can introduce a body for the given function. + static func expansion( + of node: AttributeSyntax, + providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] +} + +extension BodyMacro { + public static func expansion( + of node: AttributeSyntax, + providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] { + return try { try self.expansion(of: node, providingBodyFor: declaration, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/CodeItemMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/CodeItemMacro.swift index 00d2ce4b4d2..6d2cb1b1247 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/CodeItemMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/CodeItemMacro.swift @@ -22,4 +22,20 @@ public protocol CodeItemMacro: FreestandingMacro { of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) throws -> [CodeBlockItemSyntax] + + /// Expand a macro described by the given freestanding macro expansion + /// declaration within the given context to produce a set of declarations. + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] +} + +extension CodeItemMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] { + return try { try expansion(of: node, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/DeclarationMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/DeclarationMacro.swift index 4496328c2d5..033a634b4fb 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/DeclarationMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/DeclarationMacro.swift @@ -23,6 +23,13 @@ public protocol DeclarationMacro: FreestandingMacro { in context: some MacroExpansionContext ) throws -> [DeclSyntax] + /// Expand a macro described by the given freestanding macro expansion + /// declaration within the given context to produce a set of declarations. + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] + /// Whether to copy attributes on the expansion syntax to expanded declarations, /// 'true' by default. static var propagateFreestandingMacroAttributes: Bool { get } @@ -32,6 +39,13 @@ public protocol DeclarationMacro: FreestandingMacro { } extension DeclarationMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] { + return try { try self.expansion(of: node, in: context) }() + } + public static var propagateFreestandingMacroAttributes: Bool { true } public static var propagateFreestandingMacroModifiers: Bool { true } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/ExpressionMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/ExpressionMacro.swift index 8910e1fedc5..8a4822b53e8 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/ExpressionMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/ExpressionMacro.swift @@ -24,4 +24,20 @@ public protocol ExpressionMacro: FreestandingMacro { of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) throws -> ExprSyntax + + /// Expand a macro described by the given freestanding macro expansion + /// within the given context to produce a replacement expression. + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> ExprSyntax +} + +extension ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> ExprSyntax { + return try { try self.expansion(of: node, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift index 5fd5a241c2d..d7e1cdbfb90 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/ExtensionMacro.swift @@ -40,4 +40,46 @@ public protocol ExtensionMacro: AttachedMacro { conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [ExtensionDeclSyntax] + + /// Expand an attached extension macro to produce a set of extensions. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - type: The type to provide extensions of. + /// - protocols: The list of protocols to add conformances to. These will + /// always be protocols that `type` does not already state a conformance + /// to. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of extension declarations introduced by the macro, + /// which are always inserted at top-level scope. Each extension must extend + /// the `type` parameter. + static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) async throws -> [ExtensionDeclSyntax] +} + +extension ExtensionMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingExtensionsOf type: some TypeSyntaxProtocol, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) async throws -> [ExtensionDeclSyntax] { + return try { + try self.expansion( + of: node, + attachedTo: declaration, + providingExtensionsOf: type, + conformingTo: protocols, + in: context + ) + }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/MemberAttributeMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/MemberAttributeMacro.swift index 9538149ec3d..3dde0deee0f 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/MemberAttributeMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/MemberAttributeMacro.swift @@ -35,4 +35,32 @@ public protocol MemberAttributeMacro: AttachedMacro { providingAttributesFor member: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [AttributeSyntax] + + /// Expand an attached declaration macro to produce an attribute list for + /// a given member. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - member: The member declaration to attach the resulting attributes to. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of attributes to apply to the given member. + static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [AttributeSyntax] +} + +extension MemberAttributeMacro { + public static func expansion( + of node: AttributeSyntax, + attachedTo declaration: some DeclGroupSyntax, + providingAttributesFor member: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [AttributeSyntax] { + return try { try self.expansion(of: node, attachedTo: declaration, providingAttributesFor: member, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/MemberMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/MemberMacro.swift index 51bf598d3bb..df8c24505e8 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/MemberMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/MemberMacro.swift @@ -58,6 +58,47 @@ public protocol MemberMacro: AttachedMacro { conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [DeclSyntax] + + /// Expand an attached declaration macro to produce a set of members. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of member declarations introduced by this macro, which + /// are nested inside the `attachedTo` declaration. + /// + /// - Warning: This is the legacy `expansion` function of `MemberMacro` that is provided for backwards-compatiblity. + /// Use ``expansion(of:providingMembersOf:conformingTo:in:)-1sxoe`` instead. + static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] + + /// Expand an attached declaration macro to produce a set of members. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - conformingTo: The set of protocols that were declared + /// in the set of conformances for the macro and to which the declaration + /// does not explicitly conform. The member macro itself cannot declare + /// conformances to these protocols (only an extension macro can do that), + /// but can provide supporting declarations, such as a required + /// initializer or stored property, that cannot be written in an + /// extension. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of member declarations introduced by this macro, which + /// are nested inside the `attachedTo` declaration. + static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] } private struct UnimplementedExpansionMethodError: Error, CustomStringConvertible { @@ -80,6 +121,14 @@ extension MemberMacro { throw UnimplementedExpansionMethodError() } + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] { + return try { try self.expansion(of: node, providingMembersOf: declaration, in: context) }() + } + /// Default implementation that ignores the unhandled conformances. @available( *, @@ -94,4 +143,13 @@ extension MemberMacro { ) throws -> [DeclSyntax] { return try expansion(of: node, providingMembersOf: declaration, in: context) } + + public static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] { + return try { try self.expansion(of: node, providingMembersOf: declaration, conformingTo: protocols, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/PeerMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/PeerMacro.swift index ac9e6601bfe..ead8b0b1ad4 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/PeerMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/PeerMacro.swift @@ -26,4 +26,26 @@ public protocol PeerMacro: AttachedMacro { providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [DeclSyntax] + + /// Expand a macro described by the given custom attribute and + /// attached to the given declaration and evaluated within a + /// particular expansion context. + /// + /// The macro expansion can introduce "peer" declarations that sit alongside + /// the given declaration. + static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] +} + +extension PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] { + return try { try self.expansion(of: node, providingPeersOf: declaration, in: context) }() + } } diff --git a/Sources/SwiftSyntaxMacros/MacroProtocols/PreambleMacro.swift b/Sources/SwiftSyntaxMacros/MacroProtocols/PreambleMacro.swift index 973d7666246..2d4c48e7c7d 100644 --- a/Sources/SwiftSyntaxMacros/MacroProtocols/PreambleMacro.swift +++ b/Sources/SwiftSyntaxMacros/MacroProtocols/PreambleMacro.swift @@ -30,4 +30,27 @@ public protocol PreambleMacro: AttachedMacro { providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, in context: some MacroExpansionContext ) throws -> [CodeBlockItemSyntax] + + /// Expand a macro described by the given custom attribute and + /// attached to the given declaration and evaluated within a + /// particular expansion context. + /// + /// The macro expansion can introduce code items that form a preamble to + /// the body of the given function. The code items produced by this macro + /// expansion will be inserted at the beginning of the function body. + static func expansion( + of node: AttributeSyntax, + providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] +} + +extension PreambleMacro { + public static func expansion( + of node: AttributeSyntax, + providingPreambleFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] { + return try { try expansion(of: node, providingPreambleFor: declaration, in: context) }() + } } From d98d1fee0398a78cdc6dde6f931a4cf436f3f858 Mon Sep 17 00:00:00 2001 From: Roope Virtanen Date: Thu, 14 Nov 2024 14:45:00 +0200 Subject: [PATCH 3/3] Add support for async macro expansion --- .../GenerateSwiftSyntax.swift | 25 + .../MacroExpansionFile.swift | 635 ++++++ .../MacroSystemFile.swift | 1872 +++++++++++++++++ .../GenericMacroAssertionsFile.swift | 841 ++++++++ .../MacroAssertionsFile.swift | 177 ++ .../SwiftCompilerPlugin/CompilerPlugin.swift | 4 +- .../CompilerPluginMessageHandler.swift | 16 +- .../Macros.swift | 8 +- .../SwiftSyntaxMacroExpansion/CMakeLists.txt | 4 +- .../{ => generated}/MacroExpansion.swift | 370 +++- .../{ => generated}/MacroSystem.swift | 1240 ++++++++++- .../{ => generated}/Assertions.swift | 229 +- .../{ => generated}/Assertions.swift | 108 +- 13 files changed, 5447 insertions(+), 82 deletions(-) create mode 100644 CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroExpansionFile.swift create mode 100644 CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroSystemFile.swift create mode 100644 CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrosgenerictestsupport/GenericMacroAssertionsFile.swift create mode 100644 CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrostestsupport/MacroAssertionsFile.swift rename Sources/SwiftSyntaxMacroExpansion/{ => generated}/MacroExpansion.swift (58%) rename Sources/SwiftSyntaxMacroExpansion/{ => generated}/MacroSystem.swift (55%) rename Sources/SwiftSyntaxMacrosGenericTestSupport/{ => generated}/Assertions.swift (76%) rename Sources/SwiftSyntaxMacrosTestSupport/{ => generated}/Assertions.swift (54%) diff --git a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift index 2c6044ac055..04281e23998 100644 --- a/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift +++ b/CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift @@ -26,6 +26,9 @@ private let swiftParserGeneratedDir = ["SwiftParser", generatedDirName] private let swiftParserDiagnosticsGeneratedDir = ["SwiftParserDiagnostics", generatedDirName] private let swiftSyntaxGeneratedDir = ["SwiftSyntax", generatedDirName] private let swiftSyntaxBuilderGeneratedDir = ["SwiftSyntaxBuilder", generatedDirName] +private let swiftSyntaxMacroExpansionGeneratedDir = ["SwiftSyntaxMacroExpansion", generatedDirName] +private let swiftSyntaxMacrosGenericTestSupportGeneratedDir = ["SwiftSyntaxMacrosGenericTestSupport", generatedDirName] +private let swiftSyntaxMacrosTestSupportGeneratedDir = ["SwiftSyntaxMacrosTestSupport", generatedDirName] private let BASE_KIND_FILES: [SyntaxNodeKind: String] = [ .decl: "SyntaxDeclNodes.swift", .expr: "SyntaxExprNodes.swift", @@ -160,6 +163,28 @@ struct GenerateSwiftSyntax: AsyncParsableCommand { swiftSyntaxBuilderGeneratedDir + ["RenamedChildrenBuilderCompatibility.swift"], renamedChildrenBuilderCompatibilityFile ), + + // SwiftSyntaxMacroExpansion + GeneratedFileSpec( + swiftSyntaxMacroExpansionGeneratedDir + ["MacroExpansion.swift"], + macroExpansionFile + ), + GeneratedFileSpec( + swiftSyntaxMacroExpansionGeneratedDir + ["MacroSystem.swift"], + macroSystemFile + ), + + // SwiftSyntaxMacrosGenericTestSupport + GeneratedFileSpec( + swiftSyntaxMacrosGenericTestSupportGeneratedDir + ["Assertions.swift"], + genericMacroAssertionsFile + ), + + // SwiftSyntaxMacrosTestSupport + GeneratedFileSpec( + swiftSyntaxMacrosTestSupportGeneratedDir + ["Assertions.swift"], + macroAssertionsFile + ), ] // This split of letters produces files for the syntax nodes that have about equal size, which improves compile time diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroExpansionFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroExpansionFile.swift new file mode 100644 index 00000000000..9a0cfef0608 --- /dev/null +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroExpansionFile.swift @@ -0,0 +1,635 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SyntaxSupport +import Utils + +let macroExpansionFile = makeMacroExpansionFile() + +private func makeMacroExpansionFile() -> SourceFileSyntax { + return SourceFileSyntax(leadingTrivia: copyrightHeader) { + IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax { + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("swift(>=6)"), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftBasicFormat") + DeclSyntax("public import SwiftSyntax") + DeclSyntax("@_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) public import SwiftSyntaxMacros") + } + ) + ) + IfConfigClauseSyntax( + poundKeyword: .poundElseToken(), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftBasicFormat") + DeclSyntax("import SwiftSyntax") + DeclSyntax("@_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros") + } + ) + ) + } + ) + + try! EnumDeclSyntax("public enum MacroRole: Sendable") { + DeclSyntax("case expression") + DeclSyntax("case declaration") + DeclSyntax("case accessor") + DeclSyntax("case memberAttribute") + DeclSyntax("case member") + DeclSyntax("case peer") + DeclSyntax("case conformance") + DeclSyntax("case codeItem") + DeclSyntax("case `extension`") + DeclSyntax("@_spi(ExperimentalLanguageFeature) case preamble") + DeclSyntax("@_spi(ExperimentalLanguageFeature) case body") + } + + try! ExtensionDeclSyntax("extension MacroRole") { + DeclSyntax( + """ + var protocolName: String { + switch self { + case .expression: return "ExpressionMacro" + case .declaration: return "DeclarationMacro" + case .accessor: return "AccessorMacro" + case .memberAttribute: return "MemberAttributeMacro" + case .member: return "MemberMacro" + case .peer: return "PeerMacro" + case .conformance: return "ConformanceMacro" + case .codeItem: return "CodeItemMacro" + case .extension: return "ExtensionMacro" + case .preamble: return "PreambleMacro" + case .body: return "BodyMacro" + } + } + """ + ) + } + + try! EnumDeclSyntax( + """ + /// Simple diagnostic message + enum MacroExpansionError: Error, CustomStringConvertible + """ + ) { + DeclSyntax("case unmatchedMacroRole(Macro.Type, MacroRole)") + DeclSyntax("case parentDeclGroupNil") + DeclSyntax("case declarationNotDeclGroup") + DeclSyntax("case declarationNotIdentified") + DeclSyntax("case declarationHasNoBody") + DeclSyntax("case noExtendedTypeSyntax") + DeclSyntax("case noFreestandingMacroRoles(Macro.Type)") + DeclSyntax("case moreThanOneBodyMacro") + DeclSyntax("case preambleWithoutBody") + DeclSyntax("case recursiveExpansion(any Macro.Type)") + + DeclSyntax( + #""" + var description: String { + switch self { + case .unmatchedMacroRole(let type, let role): + return "macro implementation type '\(type)' doesn't conform to required protocol '\(role.protocolName)'" + + case .parentDeclGroupNil: + return "parent decl group is nil" + + case .declarationNotDeclGroup: + return "declaration is not a decl group syntax" + + case .declarationNotIdentified: + return "declaration is not a 'Identified' syntax" + + case .declarationHasNoBody: + return "declaration is not a type with an optional code block" + + case .noExtendedTypeSyntax: + return "no extended type for extension macro" + + case .noFreestandingMacroRoles(let type): + return "macro implementation type '\(type)' does not conform to any freestanding macro protocol" + + case .moreThanOneBodyMacro: + return "function can not have more than one body macro applied to it" + + case .preambleWithoutBody: + return "preamble macro cannot be applied to a function with no body" + + case .recursiveExpansion(let type): + return "recursive expansion of macro '\(type)'" + } + } + """# + ) + } + + makeExpandFreestandingMacroFunction(isAsync: false) + + DeclSyntax( + """ + /// Try to infer the freestanding macro role from the type definition itself. + /// + /// This is a workaround for older compilers with a newer plugin + public func inferFreestandingMacroRole(definition: Macro.Type) throws -> MacroRole { + switch definition { + case is ExpressionMacro.Type: return .expression + case is DeclarationMacro.Type: return .declaration + case is CodeItemMacro.Type: return .codeItem + + default: + throw MacroExpansionError.noFreestandingMacroRoles(definition) + } + } + """ + ) + + makeMacroExpansionFunctions(isAsync: false) + + try! ExtensionDeclSyntax("fileprivate extension SyntaxProtocol") { + DeclSyntax( + #""" + /// Perform a format if required and then trim any leading/trailing + /// whitespace. + func formattedExpansion(_ mode: FormatMode, indentationWidth: Trivia?) -> String { + switch mode { + case .auto: + return self.formatted(using: BasicFormat(indentationWidth: indentationWidth)) + .trimmedDescription(matching: \.isWhitespace) + case .disabled: + return Syntax(self).description + #if RESILIENT_LIBRARIES + @unknown default: + fatalError() + #endif + } + } + """# + ) + } + + try! ExtensionDeclSyntax("fileprivate extension DeclSyntax") { + DeclSyntax( + #""" + /// Returns this node with `attributes` and `modifiers` prepended to the + /// node’s attributes and modifiers, respectively. + /// + /// If the node can’t have attributes or modifiers, `attributes` or `modifiers` + /// are ignored and not applied. + func applying( + attributes: AttributeListSyntax?, + modifiers: DeclModifierListSyntax? + ) -> DeclSyntax { + func _combine(_ left: C, _ right: C?) -> C { + guard let right = right else { return left } + var elems: [C.Element] = [] + elems += left + elems += right + return C(elems) + } + var node = self + if let attributes = attributes, + let withAttrs = node.asProtocol(WithAttributesSyntax.self) + { + node = withAttrs.with( + \.attributes, + _combine(attributes, withAttrs.attributes) + ).cast(DeclSyntax.self) + } + if let modifiers = modifiers, + let withModifiers = node.asProtocol(WithModifiersSyntax.self) + { + node = withModifiers.with( + \.modifiers, + _combine(modifiers, withModifiers.modifiers) + ).cast(DeclSyntax.self) + } + return node + } + """# + ) + } + + DeclSyntax( + #""" + /// Join `expansions` + public func collapse( + expansions: [String], + for role: MacroRole, + attachedTo declarationNode: Node, + indentationWidth: Trivia? = nil + ) -> String { + if expansions.isEmpty { + return "" + } + + var expansions = expansions + var separator = "\n\n" + + // Wrap the expansions in a set of braces. + func wrapInBraces() { + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from + // the expansions to collapse. + expansions = expansions.map({ $0.indented(by: indentationWidth ?? .spaces(4)) }) + expansions[0] = "{\n" + expansions[0] + expansions[expansions.count - 1] += "\n}" + separator = "\n" + } + + switch role { + case .accessor: + let onDeclarationWithoutAccessor: Bool + if let varDecl = declarationNode.as(VariableDeclSyntax.self), + let binding = varDecl.bindings.first, + binding.accessorBlock == nil + { + onDeclarationWithoutAccessor = true + } else if let subscriptDecl = declarationNode.as(SubscriptDeclSyntax.self), + subscriptDecl.accessorBlock == nil + { + onDeclarationWithoutAccessor = true + } else { + onDeclarationWithoutAccessor = false + } + if onDeclarationWithoutAccessor { + wrapInBraces() + } + case .memberAttribute: + separator = " " + + case .body: + wrapInBraces() + + case .preamble: + // Only place a single newline between statements. + separator = "\n" + + default: + break + } + + // Join the expansions ensuring `separator` between them. + var collapsed = "" + for expansion in expansions { + if collapsed.isEmpty || expansion.hasPrefix(separator) { + collapsed.append(expansion) + } else { + collapsed.append(separator + expansion) + } + } + + return collapsed + } + """# + ) + + makeExpandFreestandingMacroFunction(isAsync: true) + makeMacroExpansionFunctions(isAsync: true) + } +} + +private func makeExpandFreestandingMacroFunction(isAsync: Bool) -> DeclSyntax { + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return DeclSyntax( + """ + /// Expand `@freestanding(XXX)` macros. + /// + /// - Parameters: + /// - definition: a type conforms to one of freestanding `Macro` protocol. + /// - macroRole: indicates which `Macro` protocol expansion should be performed + /// - node: macro expansion syntax node (e.g. `#macroName(argument)`). + /// - context: context of the expansion. + /// - indentationWidth: The indentation that should be added for each additional + /// nesting level + /// - Returns: expanded source text. Upon failure (i.e. `definition.expansion()` + /// throws) returns `nil`, and the diagnostics representing the `Error` are + /// guaranteed to be added to context. + public func expandFreestandingMacro( + definition: Macro.Type, + macroRole: MacroRole, + node: FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> String? { + do { + let expandedSyntax: Syntax + switch (macroRole, definition) { + case (.expression, let exprMacroDef as ExpressionMacro.Type): + expandedSyntax = try Syntax(\(raw: awaitOperator)exprMacroDef.expansion(of: node, in: context)) + + case (.declaration, let declMacroDef as DeclarationMacro.Type): + var rewritten = try \(raw: awaitOperator)declMacroDef.expansion(of: node, in: context) + // Copy attributes and modifiers to the generated decls. + if let expansionDecl = node.as(MacroExpansionDeclSyntax.self) { + // Strip any indentation from the attributes and modifiers that we are + // inheriting. The expanded macro should start at the leftmost column. + let attributes = + declMacroDef.propagateFreestandingMacroAttributes ? expansionDecl.attributes.withIndentationRemoved : nil + let modifiers = + declMacroDef.propagateFreestandingMacroModifiers ? expansionDecl.modifiers.withIndentationRemoved : nil + rewritten = rewritten.map { + $0.applying(attributes: attributes, modifiers: modifiers) + } + } + expandedSyntax = Syntax( + CodeBlockItemListSyntax( + rewritten.map { + CodeBlockItemSyntax(item: .decl($0)) + } + ) + ) + + case (.codeItem, let codeItemMacroDef as CodeItemMacro.Type): + let rewritten = try \(raw: awaitOperator)codeItemMacroDef.expansion(of: node, in: context) + expandedSyntax = Syntax(CodeBlockItemListSyntax(rewritten)) + + case (.accessor, _), (.memberAttribute, _), (.member, _), (.peer, _), (.conformance, _), (.extension, _), + (.expression, _), (.declaration, _), + (.codeItem, _), (.preamble, _), (.body, _): + throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) + } + return expandedSyntax.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } catch { + context.addDiagnostics(from: error, node: node) + return nil + } + } + """ + ) +} + +private func makeMacroExpansionFunctions(isAsync: Bool) -> CodeBlockItemListSyntax { + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return CodeBlockItemListSyntax() { + DeclSyntax( + """ + @available(*, deprecated, message: "pass a macro role, please!") + public func expandFreestandingMacro( + definition: Macro.Type, + node: FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + )\(raw: asyncSpecifier) -> String? { + do { + return \(raw: awaitOperator)expandFreestandingMacro( + definition: definition, + macroRole: try inferFreestandingMacroRole(definition: definition), + node: node, + in: context + ) + } catch { + context.addDiagnostics(from: error, node: node) + return nil + } + } + """ + ) + + DeclSyntax( + """ + /// Expand `@attached(XXX)` macros. + /// + /// - Parameters: + /// - definition: a type that conforms to one or more attached `Macro` protocols. + /// - macroRole: indicates which `Macro` protocol expansion should be performed + /// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`). + /// - declarationNode: target declaration syntax node to apply the expansion. + /// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent + /// context node of `declarationNode`. + /// - context: context of the expansion. + /// - indentationWidth: The indentation that should be added for each additional + /// nesting level + /// - Returns: A list of expanded source text. Upon failure (i.e. + /// `definition.expansion()` throws) returns `nil`, and the diagnostics + /// representing the `Error` are guaranteed to be added to context. + public func expandAttachedMacroWithoutCollapsing( + definition: Macro.Type, + macroRole: MacroRole, + attributeNode: AttributeSyntax, + declarationNode: DeclSyntax, + parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, + conformanceList: InheritedTypeListSyntax?, + in context: Context, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> [String]? { + do { + switch (definition, macroRole) { + case (let attachedMacro as AccessorMacro.Type, .accessor): + let accessors = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + providingAccessorsOf: declarationNode, + in: context + ) + return accessors.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute): + guard + let parentDeclGroup = parentDeclNode?.asProtocol(DeclGroupSyntax.self) + else { + // Compiler error: 'parentDecl' is mandatory for MemberAttributeMacro. + throw MacroExpansionError.parentDeclGroupNil + } + + let attributes = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + attachedTo: parentDeclGroup, + providingAttributesFor: declarationNode, + in: context + ) + + // Form a buffer containing an attribute list to return to the caller. + return attributes.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as MemberMacro.Type, .member): + guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) + else { + // Compiler error: declNode for member macro must be DeclGroupSyntax. + throw MacroExpansionError.declarationNotDeclGroup + } + + let members = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + providingMembersOf: declGroup, + conformingTo: conformanceList?.map(\\.type) ?? [], + in: context + ) + + // Form a buffer of member declarations to return to the caller. + return members.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as PeerMacro.Type, .peer): + let peers = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + providingPeersOf: declarationNode, + in: context + ) + + // Form a buffer of peer declarations to return to the caller. + return peers.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as ExtensionMacro.Type, .extension): + guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else { + // Compiler error: type mismatch. + throw MacroExpansionError.declarationNotDeclGroup + } + + let extensionOf: TypeSyntax + if let extendedType { + extensionOf = extendedType + } else if let identified = declarationNode.asProtocol(NamedDeclSyntax.self) { + // Fallback for old compilers with a new plugin, where + extensionOf = TypeSyntax(IdentifierTypeSyntax(name: identified.name)) + } else { + throw MacroExpansionError.noExtendedTypeSyntax + } + + let protocols = conformanceList?.map(\\.type) ?? [] + + let extensions = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + attachedTo: declGroup, + providingExtensionsOf: extensionOf, + conformingTo: protocols, + in: context + ) + + // Form a buffer of peer declarations to return to the caller. + return extensions.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as PreambleMacro.Type, .preamble): + guard + let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self) + as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax) + else { + // Compiler error: declaration must have a body. + throw MacroExpansionError.declarationHasNoBody + } + + let preamble = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + providingPreambleFor: declToPass, + in: context + ) + return preamble.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as BodyMacro.Type, .body): + guard + let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self) + as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax) + else { + // Compiler error: declaration must have a body. + throw MacroExpansionError.declarationHasNoBody + } + + let body = try \(raw: awaitOperator)attachedMacro.expansion( + of: attributeNode, + providingBodyFor: declToPass, + in: context + ) + return body.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + default: + throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) + } + } catch { + context.addDiagnostics(from: error, node: attributeNode) + return nil + } + } + """ + ) + + DeclSyntax( + """ + /// Expand `@attached(XXX)` macros. + /// + /// - Parameters: + /// - definition: a type that conforms to one or more attached `Macro` protocols. + /// - macroRole: indicates which `Macro` protocol expansion should be performed + /// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`). + /// - declarationNode: target declaration syntax node to apply the expansion. + /// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent + /// context node of `declarationNode`. + /// - context: context of the expansion. + /// - indentationWidth: The indentation that should be added for each additional + /// nesting level + /// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()` + /// throws) returns `nil`, and the diagnostics representing the `Error` are + /// guaranteed to be added to context. + public func expandAttachedMacro( + definition: Macro.Type, + macroRole: MacroRole, + attributeNode: AttributeSyntax, + declarationNode: DeclSyntax, + parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, + conformanceList: InheritedTypeListSyntax?, + in context: Context, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> String? { + let expandedSources = \(raw: awaitOperator)expandAttachedMacroWithoutCollapsing( + definition: definition, + macroRole: macroRole, + attributeNode: attributeNode, + declarationNode: declarationNode, + parentDeclNode: parentDeclNode, + extendedType: extendedType, + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + if let expandedSources { + // If formatting is disabled we don't want to add any indentation while collapsing + let collapseIndentationWidth: Trivia? + switch definition.formatMode { + case .auto: collapseIndentationWidth = indentationWidth + case .disabled: collapseIndentationWidth = [] + #if RESILIENT_LIBRARIES + @unknown default: fatalError() + #endif + } + return collapse( + expansions: expandedSources, + for: macroRole, + attachedTo: declarationNode, + indentationWidth: collapseIndentationWidth + ) + } + return nil + } + """ + ) + } +} diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroSystemFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroSystemFile.swift new file mode 100644 index 00000000000..3ef85e8dcbe --- /dev/null +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacroexpansion/MacroSystemFile.swift @@ -0,0 +1,1872 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SyntaxSupport +import Utils + +let macroSystemFile = makeMacroSystemFile() + +private func makeMacroSystemFile() -> SourceFileSyntax { + return SourceFileSyntax(leadingTrivia: copyrightHeader) { + IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax { + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("swift(>=6)"), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("internal import SwiftDiagnostics") + DeclSyntax("internal import SwiftOperators") + DeclSyntax("@_spi(MacroExpansion) internal import SwiftParser") + DeclSyntax("@_spi(MacroExpansion) public import SwiftSyntax") + DeclSyntax("internal import SwiftSyntaxBuilder") + DeclSyntax("@_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) public import SwiftSyntaxMacros") + } + ) + ) + IfConfigClauseSyntax( + poundKeyword: .poundElseToken(), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftDiagnostics") + DeclSyntax("import SwiftOperators") + DeclSyntax("@_spi(MacroExpansion) import SwiftParser") + DeclSyntax("@_spi(MacroExpansion) import SwiftSyntax") + DeclSyntax("import SwiftSyntaxBuilder") + DeclSyntax("@_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros") + } + ) + ) + } + ) + + makeMacroExpansionCodePart1(isAsync: false) + + DeclSyntax( + """ + /// Adds the appropriate indentation on expanded code even if it's multi line. + /// Makes sure original macro expression's trivia is maintained by adding it to expanded code. + private func adjustIndentationOfFreestandingMacro( + expandedCode: String, + node: some FreestandingMacroExpansionSyntax + ) -> String { + + if expandedCode.isEmpty { + return expandedCode.wrappingInTrivia(from: node) + } + + let indentationOfFirstLine = node.indentationOfFirstLine + let indentLength = indentationOfFirstLine.sourceLength.utf8Length + + // we are doing 3 step adjustment here + // step 1: add indentation to each line of expanded code + // step 2: remove indentation from first line of expaned code + // step 3: wrap the expanded code into macro expression's trivia. This trivia will contain appropriate existing + // indentation. Note that if macro expression occurs in middle of the line then there will be no indentation or extra space. + // Hence we are doing step 2 + + var indentedSource = expandedCode.indented(by: indentationOfFirstLine) + indentedSource.removeFirst(indentLength) + indentedSource = indentedSource.wrappingInTrivia(from: node) + + return indentedSource + } + """ + ) + + makeMacroExpansionCodePart2(isAsync: false) + + try! EnumDeclSyntax( + """ + // MARK: - MacroSystem + + /// Describes the kinds of errors that can occur within a macro system. + enum MacroSystemError: Error + """ + ) { + DeclSyntax( + """ + /// Indicates that a macro with the given name has already been defined. + case alreadyDefined(new: Macro.Type, existing: Macro.Type) + """ + ) + } + + try! StructDeclSyntax( + """ + /// A system of known macros that can be expanded syntactically + struct MacroSystem + """ + ) { + DeclSyntax("var macros: [String: MacroSpec] = [:]") + + DeclSyntax( + """ + /// Create an empty macro system. + init() {} + """ + ) + + DeclSyntax( + """ + /// Add a macro specification to the system. + /// + /// Throws an error if there is already a macro with this name. + mutating func add(_ macroSpec: MacroSpec, name: String) throws { + if let knownMacroSpec = macros[name] { + throw MacroSystemError.alreadyDefined(new: macroSpec.type, existing: knownMacroSpec.type) + } + + macros[name] = macroSpec + } + """ + ) + + DeclSyntax( + """ + /// Look for a macro specification with the given name. + func lookup(_ macroName: String) -> MacroSpec? { + return macros[macroName] + } + """ + ) + } + + try! ClassDeclSyntax( + """ + // MARK: - MacroApplication + + /// Removes attributes from a syntax tree while maintaining their surrounding trivia. + @_spi(Testing) + public class AttributeRemover: SyntaxRewriter + """ + ) { + DeclSyntax("let predicate: (AttributeSyntax) -> Bool") + + DeclSyntax("var triviaToAttachToNextToken: Trivia = Trivia()") + + DeclSyntax( + """ + /// Initializes an attribute remover with a given predicate to determine which attributes to remove. + /// + /// - Parameter predicate: A closure that determines whether a given `AttributeSyntax` should be removed. + /// If this closure returns `true` for an attribute, that attribute will be removed. + public init(removingWhere predicate: @escaping (AttributeSyntax) -> Bool) { + self.predicate = predicate + } + """ + ) + + DeclSyntax( + #""" + public override func visit(_ node: AttributeListSyntax) -> AttributeListSyntax { + var filteredAttributes: [AttributeListSyntax.Element] = [] + for case .attribute(let attribute) in node { + if self.predicate(attribute) { + var leadingTrivia = attribute.leadingTrivia + + // Don't leave behind an empty line when the attribute being removed is on its own line, + // based on the following conditions: + // - Leading trivia ends with a newline followed by arbitrary number of spaces or tabs + // - All leading trivia pieces after the last newline are just whitespace, ensuring + // there are no comments or other non-whitespace characters on the same line + // preceding the attribute. + // - There is no trailing trivia and the next token has leading trivia. + if let lastNewline = leadingTrivia.pieces.lastIndex(where: \.isNewline), + leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace), + attribute.trailingTrivia.isEmpty, + let nextToken = attribute.nextToken(viewMode: .sourceAccurate), + !nextToken.leadingTrivia.isEmpty + { + leadingTrivia = Trivia(pieces: leadingTrivia.pieces[.. TokenSyntax { + return prependAndClearAccumulatedTrivia(to: token) + } + """ + ) + + DeclSyntax( + #""" + /// Prepends the accumulated trivia to the given node's leading trivia. + /// + /// To preserve correct formatting after attribute removal, this function reassigns + /// significant trivia accumulated from removed attributes to the provided subsequent node. + /// Once attached, the accumulated trivia is cleared. + /// + /// - Parameter node: The syntax node receiving the accumulated trivia. + /// - Returns: The modified syntax node with the prepended trivia. + private func prependAndClearAccumulatedTrivia(to syntaxNode: T) -> T { + defer { triviaToAttachToNextToken = Trivia() } + return syntaxNode.with(\.leadingTrivia, triviaToAttachToNextToken + syntaxNode.leadingTrivia) + } + """# + ) + } + + try! ExtensionDeclSyntax("private extension Trivia") { + DeclSyntax( + """ + func trimmingPrefix( + while predicate: (TriviaPiece) -> Bool + ) -> Trivia { + Trivia(pieces: self.drop(while: predicate)) + } + """ + ) + + DeclSyntax( + """ + func trimmingSuffix( + while predicate: (TriviaPiece) -> Bool + ) -> Trivia { + Trivia( + pieces: self[...] + .reversed() + .drop(while: predicate) + .reversed() + ) + } + """ + ) + + DeclSyntax( + """ + var startsWithNewline: Bool { + self.first?.isNewline ?? false + } + """ + ) + } + + DeclSyntax(#"let diagnosticDomain: String = "SwiftSyntaxMacroExpansion""#) + + try! EnumDeclSyntax("private enum MacroApplicationError: DiagnosticMessage, Error") { + DeclSyntax("case accessorMacroOnVariableWithMultipleBindings") + DeclSyntax("case peerMacroOnVariableWithMultipleBindings") + DeclSyntax("case malformedAccessor") + + DeclSyntax( + #""" + var diagnosticID: MessageID { + return MessageID(domain: diagnosticDomain, id: "\(self)") + } + """# + ) + + DeclSyntax("var severity: DiagnosticSeverity { return .error }") + + DeclSyntax( + #""" + var message: String { + switch self { + case .accessorMacroOnVariableWithMultipleBindings: + return "accessor macro can only be applied to a single variable" + case .peerMacroOnVariableWithMultipleBindings: + return "peer macro can only be applied to a single variable" + case .malformedAccessor: + return """ + macro returned a malformed accessor. Accessors should start with an introducer like 'get' or 'set'. + """ + } + } + """# + ) + } + + makeMacroApplicationCode(isAsync: false) + + try! ExtensionDeclSyntax( + """ + // MARK: Syntax utilities + + private extension AccessorBlockSyntax + """ + ) { + DeclSyntax( + """ + /// Create a new `AccessorBlockSyntax` by adding the given accessors. + /// + /// If this `AccessorBlockSyntax` contains a single code block as a getter, + /// that code block will be converted to an explicit 'get' accessor. This + /// causes the body to be indented by `indentationWidth`. + func addingAccessors( + from newAccessors: AccessorDeclListSyntax, + indentationWidth: Trivia + ) -> AccessorBlockSyntax { + var result = self + switch self.accessors { + case .accessors(let accessors): + result.accessors = .accessors(accessors + Array(newAccessors)) + case .getter(let getter): + if newAccessors.isEmpty { + return self + } + // Convert the existing getter into an explicit accessor (with 'get') so + // we can add more accessors. + let baseIndentation = getter.indentationOfFirstLine + let getterAsAccessor = AccessorDeclSyntax( + accessorSpecifier: .keyword(.get, leadingTrivia: .newline + baseIndentation, trailingTrivia: .space), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: getter.indented(by: indentationWidth), + rightBrace: .rightBraceToken(leadingTrivia: .newline + baseIndentation) + ) + ) + result.accessors = .accessors(AccessorDeclListSyntax([getterAsAccessor] + Array(newAccessors))) + #if RESILIENT_LIBRARIES + @unknown default: + fatalError() + #endif + } + return result + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension String") { + DeclSyntax( + """ + /// Add any trivia from `node` before/after this string. + /// + /// We need to do this because we are replacing the entire macro expansion + /// declaration / expression with the expanded source but semantically, the + /// user should think about it as just replacing the `#...` expression without + /// any trivia. + func wrappingInTrivia(from node: some SyntaxProtocol) -> String { + return node.leadingTrivia.description + + self + + node.trailingTrivia.description + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension SyntaxProtocol") { + DeclSyntax( + """ + /// Detach the current node and inform the macro expansion context, + /// if it needs to know. + func detach(in context: MacroExpansionContext) -> Self { + if let basicContext = context as? BasicMacroExpansionContext { + return basicContext.detach(self) + } + + return self.detached + } + """ + ) + + DeclSyntax( + """ + /// Fold operators in this node using the given operator table, detach the + /// node and inform the macro expansion context, if it needs to know. + func detach( + in context: MacroExpansionContext, + foldingWith operatorTable: OperatorTable? + ) -> Syntax { + let folded: Syntax + if let operatorTable { + if let basicContext = context as? BasicMacroExpansionContext { + folded = basicContext.foldAllOperators(of: self, with: operatorTable) + } else { + folded = operatorTable.foldAll(self, errorHandler: { _ in /*ignore*/ }) + } + } else { + folded = Syntax(self) + } + return folded.detach(in: context) + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension FreestandingMacroExpansionSyntax") { + DeclSyntax( + """ + /// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type + /// `Self` since we know that operator folding doesn't change the type of any + /// `FreestandingMacroExpansionSyntax`. + func detach( + in context: MacroExpansionContext, + foldingWith operatorTable: OperatorTable? + ) -> Self { + return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self) + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension AttributeSyntax") { + DeclSyntax( + """ + /// Same as `SyntaxProtocol.detach(in:foldingWith:)` but returns a node of type + /// `Self` since we know that operator folding doesn't change the type of any + /// `AttributeSyntax`. + func detach( + in context: MacroExpansionContext, + foldingWith operatorTable: OperatorTable? + ) -> Self { + return (detach(in: context, foldingWith: operatorTable) as Syntax).cast(Self.self) + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension AccessorDeclSyntax") { + DeclSyntax( + """ + var isGetOrSet: Bool { + return accessorSpecifier.tokenKind == .keyword(.get) || accessorSpecifier.tokenKind == .keyword(.set) + } + """ + ) + } + + try! ExtensionDeclSyntax("private extension SyntaxProtocol") { + DeclSyntax( + """ + /// Retrieve the qualified type for the nearest extension or name decl. + /// + /// For example, for `struct Foo { struct Bar {} }`, calling this on the + /// inner struct (`Bar`) will return `Foo.Bar`. + var syntacticQualifiedTypeContext: TypeSyntax? { + if let ext = self.as(ExtensionDeclSyntax.self) { + // Don't handle nested 'extension' cases - they are invalid anyway. + return ext.extendedType.trimmed + } + + let base = self.parent?.syntacticQualifiedTypeContext + if let named = self.asProtocol(NamedDeclSyntax.self) { + if let base = base { + return TypeSyntax(MemberTypeSyntax(baseType: base, name: named.name.trimmed)) + } + return TypeSyntax(IdentifierTypeSyntax(name: named.name.trimmed)) + } + return base + } + """ + ) + } + + makeMacroExpansionCodePart1(isAsync: true) + makeMacroExpansionCodePart2(isAsync: true) + makeMacroApplicationCode(isAsync: true) + } +} + +private func makeMacroExpansionCodePart1(isAsync: Bool) -> CodeBlockItemListSyntax { + let namePrefix = if isAsync { "Async" } else { String() } + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return CodeBlockItemListSyntax() { + try! ExtensionDeclSyntax( + """ + // MARK: - Public entry function + + extension SyntaxProtocol + """ + ) { + DeclSyntax( + """ + /// Expand all uses of the given set of macros within this syntax node. + @available(*, deprecated, message: "Use contextGenerator form to produce a specific context for each expansion node") + public func expand( + macros: [String: Macro.Type], + in context: some MacroExpansionContext, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> Syntax { + return \(raw: awaitOperator)expand( + macros: macros, + contextGenerator: { _ in context }, + indentationWidth: indentationWidth + ) + } + """ + ) + + DeclSyntax( + """ + /// Expand all uses of the given set of macros within this syntax node. + /// - SeeAlso: ``expand(macroSpecs:contextGenerator:indentationWidth:)`` + /// to also specify the list of conformances passed to the macro expansion. + public func expand( + macros: [String: Macro.Type], + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> Syntax { + return \(raw: awaitOperator)expand( + macroSpecs: macros.mapValues { MacroSpec(type: $0) }, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + } + """ + ) + + DeclSyntax( + """ + /// Expand all uses of the given set of macros with specifications within this syntax node. + public func expand( + macroSpecs: [String: MacroSpec], + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? = nil + )\(raw: asyncSpecifier) -> Syntax { + // Build the macro system. + var system = MacroSystem() + for (macroName, macroSpec) in macroSpecs { + try! system.add(macroSpec, name: macroName) + } + + let applier = \(raw: namePrefix)MacroApplication( + macroSystem: system, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + + return \(raw: awaitOperator)applier.rewrite(self) + } + """ + ) + } + + DeclSyntax( + """ + // MARK: - Expand macros + + /// Expand the given freestanding macro and parse the resulting text into a + /// syntax tree. + private func expandFreestandingMemberDeclList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> MemberBlockItemListSyntax? { + guard + let expanded = try \(raw: awaitOperator)expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandFreestandingCodeItemList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> CodeBlockItemListSyntax? { + guard + let expanded = try \(raw: awaitOperator)expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandFreestandingExpr( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> ExprSyntax? { + guard + let expanded = \(raw: awaitOperator)expandFreestandingMacro( + definition: definition, + macroRole: .expression, + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\\(raw: indentedSource)" + } + """ + ) + } +} + +private func makeMacroExpansionCodePart2(isAsync: Bool) -> CodeBlockItemListSyntax { + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return CodeBlockItemListSyntax() { + DeclSyntax( + """ + private func expandMemberMacro( + definition: MemberMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + conformanceList: InheritedTypeListSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> MemberBlockItemListSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .member, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate new members from exisiting members by two newlines + let indentedSource = "\\n\\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandMemberAttributeMacro( + definition: MemberAttributeMacro.Type, + attributeNode: AttributeSyntax, + attachedTo declaration: DeclSyntax, + providingAttributeFor member: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> AttributeListSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .memberAttribute, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: member.detach(in: context), + parentDeclNode: declaration.detach(in: context), + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // The added attributes should be on their own line, so prepend them with a newline. + let indentedSource = "\\n" + expanded.indented(by: member.indentationOfFirstLine) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandPeerMacroMember( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> MemberBlockItemListSyntax? { + if let variable = attachedTo.as(VariableDeclSyntax.self), variable.bindings.count > 1 { + throw MacroApplicationError.peerMacroOnVariableWithMultipleBindings + } + + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\\n\\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandPeerMacroCodeItem( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> CodeBlockItemListSyntax? { + if let variable = attachedTo.as(VariableDeclSyntax.self), variable.bindings.count > 1 { + throw MacroApplicationError.peerMacroOnVariableWithMultipleBindings + } + + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\\n\\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + /// Expand an accessor macro of a declaration that does not have existing + /// accessors. + /// + /// See comment in `expandAccessors` for an explanation why we need this. + private func expandAccessorMacroWithoutExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> AccessorBlockSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ), + !expanded.isEmpty + else { + return nil + } + + // `expandAttachedMacro` adds the `{` and `}` to wrap the accessor block and + // then indents it. + // Remove any indentation from the first line using `drop(while:)` and then + // prepend a space to separate it from the variable declaration + let indentedSource = " " + expanded.indented(by: attachedTo.indentationOfFirstLine).drop(while: { $0.isWhitespace }) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + /// Expand an accessor macro of a declaration that already has an accessor. + /// + /// See comment in `expandAccessors` for an explanation why we need this. + private func expandAccessorMacroWithExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> AccessorDeclListSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the accessor from any existing accessors by an empty line + let indentedSource = "\\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandExtensionMacro( + definition: ExtensionMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + conformanceList: InheritedTypeListSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) throws -> CodeBlockItemListSyntax? { + guard attachedTo.isProtocol(DeclGroupSyntax.self) else { + return nil + } + + guard let extendedType = attachedTo.syntacticQualifiedTypeContext else { + return nil + } + + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .extension, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: extendedType.detach(in: context), + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the extension from other declarations in the source file by two newlines. + let indentedSource = "\\n\\n" + expanded + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + /// Expand a preamble macro into a list of code items. + private func expandPreambleMacro( + definition: PreambleMacro.Type, + attributeNode: AttributeSyntax, + attachedTo decl: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) -> CodeBlockItemListSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .preamble, + attributeNode: attributeNode.detach( + in: context, + foldingWith: .standardOperators + ), + declarationNode: DeclSyntax(decl.detach(in: context)), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return [] + } + + // Match the indentation of the statements if we can, and put newlines around + // the preamble to separate it from the rest of the body. + let indentation = decl.body?.statements.indentationOfFirstLine ?? (decl.indentationOfFirstLine + indentationWidth) + let indentedSource = "\\n" + expanded.indented(by: indentation) + "\\n" + return "\\(raw: indentedSource)" + } + """ + ) + + DeclSyntax( + """ + private func expandBodyMacro( + definition: BodyMacro.Type, + attributeNode: AttributeSyntax, + attachedTo decl: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia + )\(raw: asyncSpecifier) -> CodeBlockSyntax? { + guard + let expanded = \(raw: awaitOperator)expandAttachedMacro( + definition: definition, + macroRole: .body, + attributeNode: attributeNode.detach( + in: context, + foldingWith: .standardOperators + ), + declarationNode: DeclSyntax(decl.detach(in: context)), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ), + !expanded.isEmpty + else { + return nil + } + + // `expandAttachedMacro` adds the `{` and `}` to wrap the body and then + // indents it. + // Remove any indentation from the first line using `drop(while:)` and then + // prepend a space when it's being introduced on a declaration that has no + // body yet. + let leadingWhitespace = decl.body == nil ? " " : "" + let indentedSource = + leadingWhitespace + expanded.indented(by: decl.indentationOfFirstLine).drop(while: { $0.isWhitespace }) + return "\\(raw: indentedSource)" + } + """ + ) + } +} + +private func makeMacroApplicationCode(isAsync: Bool) -> CodeBlockItemListSyntax { + let namePrefix = if isAsync { "Async" } else { String() } + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return CodeBlockItemListSyntax() { + try! ClassDeclSyntax( + """ + // MARK: - \(raw: namePrefix)MacroApplication + + /// Syntax rewriter that evaluates any macros encountered along the way. + private class \(raw: namePrefix)MacroApplication: \(raw: namePrefix)SyntaxRewriter + """ + ) { + DeclSyntax("let macroSystem: MacroSystem") + + DeclSyntax("var contextGenerator: (Syntax) -> Context") + + DeclSyntax("var indentationWidth: Trivia") + + DeclSyntax( + """ + /// Nodes that we are currently handling in `visitAny` and that should be + /// visited using the node-specific handling function. + var skipVisitAnyHandling: Set = [] + """ + ) + + DeclSyntax( + """ + /// Store expanded extension while visiting member decls. This should be + /// added to top-level 'CodeBlockItemList'. + var extensions: [CodeBlockItemSyntax] = [] + """ + ) + + DeclSyntax( + """ + /// Stores the types of the freestanding macros that are currently expanding. + /// + /// As macros are expanded by DFS, `expandingFreestandingMacros` always represent the expansion path starting from + /// the root macro node to the last macro node currently expanding. + var expandingFreestandingMacros: [any Macro.Type] = [] + """ + ) + + DeclSyntax( + """ + init( + macroSystem: MacroSystem, + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? + ) { + self.macroSystem = macroSystem + self.contextGenerator = contextGenerator + + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from the + // source file in which we expand the macros. + self.indentationWidth = indentationWidth ?? .spaces(4) + super.init(viewMode: .sourceAccurate) + } + """ + ) + + DeclSyntax( + """ + override func visitAny(_ node: Syntax)\(raw: asyncSpecifier) -> Syntax? { + guard !skipVisitAnyHandling.contains(node) else { + return nil + } + + // Expand 'MacroExpansionExpr'. + // Note that 'MacroExpansionExpr'/'MacroExpansionExprDecl' at code item + // position are handled by 'visit(_:CodeBlockItemListSyntax)'. + // Only expression expansions inside other syntax nodes is handled here. + switch \(raw: awaitOperator)expandExpr(node: node) { + case .success(let expansion): + return \(raw: awaitOperator)expansion.withExpandedNode { expandedNode in + Syntax(\(raw: awaitOperator)visit(expandedNode)) + } + case .failure: + return Syntax(node) + case .notAMacro: + break + } + if var declSyntax = node.as(DeclSyntax.self), + let attributedNode = node.asProtocol(WithAttributesSyntax.self), + !attributedNode.attributes.isEmpty + { + // Apply body and preamble macros. + if let nodeWithBody = node.asProtocol(WithOptionalCodeBlockSyntax.self), + let declNodeWithBody = nodeWithBody as? any DeclSyntaxProtocol & WithOptionalCodeBlockSyntax + { + declSyntax = \(raw: awaitOperator)DeclSyntax(visitBodyAndPreambleMacros(declNodeWithBody)) + } + + // Visit the node, disabling the `visitAny` handling. + skipVisitAnyHandling.insert(Syntax(declSyntax)) + let visitedNode = \(raw: awaitOperator)self.visit(declSyntax) + skipVisitAnyHandling.remove(Syntax(declSyntax)) + + let attributesToRemove = self.macroAttributes(attachedTo: visitedNode).map(\\.attributeNode) + + return AttributeRemover(removingWhere: { attributesToRemove.contains($0) }).rewrite(visitedNode) + } + + return nil + } + """ + ) + + DeclSyntax( + """ + /// Visit for both the body and preamble macros. + func visitBodyAndPreambleMacros( + _ node: Node + )\(raw: asyncSpecifier) -> Node { + // Expand preamble macros into a set of code items. + let preamble = \(raw: awaitOperator)expandMacros( + attachedTo: DeclSyntax(node), + ofType: PreambleMacro.Type.self + ) { attributeNode, definition, _ in + \(raw: awaitOperator)expandPreambleMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + + // Expand body macro. + let expandedBodies = \(raw: awaitOperator)expandMacros( + attachedTo: DeclSyntax(node), + ofType: BodyMacro.Type.self + ) { attributeNode, definition, _ in + \(raw: awaitOperator)expandBodyMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ).map { [$0] } + } + + // Dig out the body. + let body: CodeBlockSyntax + switch expandedBodies.count { + case 0 where preamble.isEmpty: + // Nothing changes + return node + + case 0: + guard let existingBody = node.body else { + // Any leftover preamble statements have nowhere to go, complain and + // exit. + contextGenerator(Syntax(node)).addDiagnostics(from: MacroExpansionError.preambleWithoutBody, node: node) + + return node + } + + body = existingBody + + case 1: + body = expandedBodies[0] + + default: + contextGenerator(Syntax(node)).addDiagnostics(from: MacroExpansionError.moreThanOneBodyMacro, node: node) + body = expandedBodies[0] + } + + // If there's no preamble, swap in the new body. + if preamble.isEmpty { + return node.with(\\.body, body) + } + + return node.with(\\.body, body.with(\\.statements, preamble + body.statements)) + } + """ + ) + + DeclSyntax( + """ + override func visit(_ node: CodeBlockItemListSyntax)\(raw: asyncSpecifier) -> CodeBlockItemListSyntax { + var newItems: [CodeBlockItemSyntax] = [] + func addResult(_ node: CodeBlockItemSyntax)\(raw: asyncSpecifier) { + // Expand freestanding macro. + switch \(raw: awaitOperator)expandCodeBlockItem(node: node) { + case .success(let expansion): + \(raw: awaitOperator)expansion.withExpandedNode { expandedNode in + for item in expandedNode { + \(raw: awaitOperator)addResult(item) + } + } + return + case .failure: + // Expanding the macro threw an error. We don't have an expanded source. + // Retain the macro node as-is. + newItems.append(node) + case .notAMacro: + // Recurse on the child node + newItems.append(\(raw: awaitOperator)visit(node)) + } + + // Expand any peer macro on this item. + if case .decl(let decl) = node.item { + for peer in \(raw: awaitOperator)expandCodeBlockPeers(of: decl) { + \(raw: awaitOperator)addResult(peer) + } + extensions += \(raw: awaitOperator)expandExtensions(of: decl) + } + } + + for item in node { + \(raw: awaitOperator)addResult(item) + } + + // If this is the top-level code block item list, add the expanded extensions + // at its end. + if node.parent?.is(SourceFileSyntax.self) ?? false { + newItems += extensions + } + + return CodeBlockItemListSyntax(newItems) + } + """ + ) + + DeclSyntax( + """ + override func visit(_ node: MemberBlockSyntax)\(raw: asyncSpecifier) -> MemberBlockSyntax { + let parentDeclGroup = node + .parent? + .as(DeclSyntax.self) + var newItems: [MemberBlockItemSyntax] = [] + + func addResult(_ node: MemberBlockItemSyntax)\(raw: asyncSpecifier) { + // Expand freestanding macro. + switch \(raw: awaitOperator)expandMemberDecl(node: node) { + case .success(let expansion): + \(raw: awaitOperator)expansion.withExpandedNode { expandedNode in + for item in expandedNode { + \(raw: awaitOperator)addResult(item) + } + } + return + case .failure: + newItems.append(node) + case .notAMacro: + // Recurse on the child node. + newItems.append(\(raw: awaitOperator)visit(node)) + } + + // Expand any peer macro on this member. + for peer in \(raw: awaitOperator)expandMemberDeclPeers(of: node.decl) { + \(raw: awaitOperator)addResult(peer) + } + extensions += \(raw: awaitOperator)expandExtensions(of: node.decl) + } + + for var item in node.members { + // Expand member attribute members attached to the declaration context. + // Note that MemberAttribute macros are _not_ applied to generated members + if let parentDeclGroup, let decl = item.decl.asProtocol(WithAttributesSyntax.self) { + var attributes = \(raw: awaitOperator)expandAttributesFromMemberAttributeMacros( + of: item.decl, + parentDecl: parentDeclGroup + ) + + for i in 0.. DeclSyntax { + var node = \(raw: awaitOperator)super.visit(node).cast(VariableDeclSyntax.self) + + guard !macroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self).isEmpty else { + return DeclSyntax(node) + } + + guard node.bindings.count == 1, + var binding = node.bindings.first + else { + contextGenerator(Syntax(node)).addDiagnostics( + from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings, + node: node + ) + return DeclSyntax(node) + } + + var expansion = \(raw: awaitOperator)expandAccessors(of: node, existingAccessors: binding.accessorBlock) + + if expansion.accessors != binding.accessorBlock { + if binding.accessorBlock == nil { + // remove the trailing trivia of the variable declaration and move it + // to the trailing trivia of the left brace of the newly created accessor block + expansion.accessors?.leftBrace.trailingTrivia = binding.trailingTrivia + binding.trailingTrivia = [] + } + + if binding.initializer != nil, expansion.expandsGetSet { + // The accessor block will have a leading space, but there will already be a + // space between the variable and the to-be-removed initializer. Remove the + // leading trivia on the accessor block so we don't double up. + binding.accessorBlock = expansion.accessors?.with(\\.leadingTrivia, []) + binding.initializer = nil + } else { + binding.accessorBlock = expansion.accessors + } + + node.bindings = [binding] + } + + return DeclSyntax(node) + } + """ + ) + + DeclSyntax( + """ + override func visit(_ node: SubscriptDeclSyntax)\(raw: asyncSpecifier) -> DeclSyntax { + var node = \(raw: awaitOperator)super.visit(node).cast(SubscriptDeclSyntax.self) + node.accessorBlock = \(raw: awaitOperator)expandAccessors(of: node, existingAccessors: node.accessorBlock).accessors + return DeclSyntax(node) + } + """ + ) + } + + try! ExtensionDeclSyntax( + """ + // MARK: Attached macro expansions. + + extension \(raw: namePrefix)MacroApplication + """ + ) { + DeclSyntax( + """ + /// Get pairs of a macro attribute and the macro specification attached to `decl`. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax + ) -> [(attributeNode: AttributeSyntax, spec: MacroSpec)] { + guard let attributedNode = decl.asProtocol(WithAttributesSyntax.self) else { + return [] + } + + return attributedNode.attributes.compactMap { + guard case let .attribute(attribute) = $0, + let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, + let macroSpec = macroSystem.lookup(attributeName) + else { + return nil + } + + return (attribute, macroSpec) + } + } + """ + ) + + DeclSyntax( + """ + /// Get a list of the macro attribute, the macro definition and the conformance + /// protocols list attached to `decl` matching `ofType` macro type. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type + ) -> [(attributeNode: AttributeSyntax, definition: MacroType, conformanceList: InheritedTypeListSyntax)] { + return macroAttributes(attachedTo: decl) + .compactMap { (attributeNode: AttributeSyntax, spec: MacroSpec) in + if let macroType = spec.type as? MacroType { + return (attributeNode, macroType, spec.inheritedTypeList) + } else { + return nil + } + } + } + """ + ) + + DeclSyntax( + """ + /// Call `expandMacro` for every macro of type `ofType` attached to `decl` and + /// return the list of all expanded nodes. + private func expandMacros< + ExpandedNode: SyntaxProtocol, + ExpandedNodeCollection: Sequence, + MacroType + >( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type, + expandMacro: + ( + _ attributeNode: AttributeSyntax, + _ definition: MacroType, + _ conformanceList: InheritedTypeListSyntax + )\(raw: asyncSpecifier) throws -> ExpandedNodeCollection? + )\(raw: asyncSpecifier) -> [ExpandedNode] { + var result: [ExpandedNode] = [] + + for macroAttribute in macroAttributes(attachedTo: decl, ofType: ofType) { + do { + if let expanded = try \(raw: awaitOperator)expandMacro( + macroAttribute.attributeNode, + macroAttribute.definition, + macroAttribute.conformanceList + ) { + result += expanded + } + } catch { + contextGenerator(Syntax(decl)).addDiagnostics(from: error, node: macroAttribute.attributeNode) + } + } + return result + } + """ + ) + + DeclSyntax( + """ + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandCodeBlockPeers` returns them as `CodeBlockItemSyntax`. The + /// overload is chosen based on the context in which the peers are expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandMemberDeclPeers(of decl: DeclSyntax)\(raw: asyncSpecifier) -> [MemberBlockItemSyntax] { + return \(raw: awaitOperator)expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition, conformanceList in + return try \(raw: awaitOperator)expandPeerMacroMember( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandMemberDeclPeers` returns them as `MemberDeclListItemSyntax`. + /// The overload is chosen based on the context in which the peers are + /// expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandCodeBlockPeers(of decl: DeclSyntax)\(raw: asyncSpecifier) -> [CodeBlockItemSyntax] { + return \(raw: awaitOperator)expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition, conformanceList in + return try \(raw: awaitOperator)expandPeerMacroCodeItem( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand all 'extension' macros attached to `decl`. + /// + /// - Returns: The macro-synthesized extensions + private func expandExtensions(of decl: DeclSyntax)\(raw: asyncSpecifier) -> [CodeBlockItemSyntax] { + return \(raw: awaitOperator)expandMacros( + attachedTo: decl, + ofType: ExtensionMacro.Type.self + ) { attributeNode, definition, conformanceList in + return try \(raw: awaitOperator)expandExtensionMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + conformanceList: conformanceList, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand all 'member' macros attached to `decl`. + private func expandMembers(of decl: DeclSyntax)\(raw: asyncSpecifier) -> [MemberBlockItemSyntax] { + return \(raw: awaitOperator)expandMacros(attachedTo: decl, ofType: MemberMacro.Type.self) { attributeNode, definition, conformanceList in + return try \(raw: awaitOperator)expandMemberMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + conformanceList: conformanceList, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Return the attributes on `decl` that are synthesized from member attribute + /// macros on `parentDecl`. + /// + /// - Note: This only returns synthesized attributes from member attribute + /// macros, not the attributes that are attached to `decl` in the source code. + private func expandAttributesFromMemberAttributeMacros( + of decl: DeclSyntax, + parentDecl: DeclSyntax + )\(raw: asyncSpecifier) -> [AttributeListSyntax.Element] { + return \(raw: awaitOperator)expandMacros( + attachedTo: parentDecl, + ofType: MemberAttributeMacro.Type.self + ) { attributeNode, definition, conformanceList in + return try \(raw: awaitOperator)expandMemberAttributeMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: parentDecl, + providingAttributeFor: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand all 'accessor' macros attached to `storage`. + /// + /// - Returns: The final accessors block that includes both the existing + /// and expanded accessors, as well as whether any `get`/`set` were + /// expanded (in which case any initializer on `storage` should be + /// removed). + private func expandAccessors( + of storage: some DeclSyntaxProtocol, + existingAccessors: AccessorBlockSyntax? + )\(raw: asyncSpecifier) -> (accessors: AccessorBlockSyntax?, expandsGetSet: Bool) { + let accessorMacros = macroAttributes(attachedTo: DeclSyntax(storage), ofType: AccessorMacro.Type.self) + + var newAccessorsBlock = existingAccessors + var expandsGetSet = false + func checkExpansions(_ accessors: AccessorDeclListSyntax?) { + guard let accessors else { return } + expandsGetSet = expandsGetSet || accessors.contains(where: \\.isGetOrSet) + } + + for macro in accessorMacros { + do { + // Accessor macros get expanded differently depending on whether the + // variable already had an accessor. If not, '{' and '}' to wrap the + // accessor block are added. If an accessor block already exists, only + // the new accessors are returned. + // We need to parse the result from the macro invocation differently + // based on these cases. + if existingAccessors != nil { + if let newAccessors = try \(raw: awaitOperator)expandAccessorMacroWithExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: contextGenerator(Syntax(storage)), + indentationWidth: indentationWidth + ) { + checkExpansions(newAccessors) + + // If existingAccessors is not `nil`, then we also set + // `newAccessorBlock` above to a a non-nil value, so + // `newAccessorsBlock` also isn’t `nil`. + newAccessorsBlock = newAccessorsBlock!.addingAccessors( + from: newAccessors, + indentationWidth: self.indentationWidth + ) + } + } else if let newAccessors = try \(raw: awaitOperator)expandAccessorMacroWithoutExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: contextGenerator(Syntax(storage)), + indentationWidth: indentationWidth + ) { + guard case .accessors(let accessorList) = newAccessors.accessors else { + throw MacroApplicationError.malformedAccessor + } + + checkExpansions(accessorList) + + if let oldBlock = newAccessorsBlock { + newAccessorsBlock = oldBlock.addingAccessors( + from: accessorList, + indentationWidth: self.indentationWidth + ) + } else { + newAccessorsBlock = newAccessors + } + } + } catch { + contextGenerator(Syntax(storage)).addDiagnostics(from: error, node: macro.attributeNode) + } + } + return (newAccessorsBlock, expandsGetSet) + } + """ + ) + } + + try! ExtensionDeclSyntax( + """ + // MARK: Freestanding macro expansion + + extension \(raw: namePrefix)MacroApplication + """ + ) { + + try! StructDeclSyntax( + """ + /// Encapsulates an expanded node, the type of the macro from which the node was expanded, and the macro application, + /// such that recursive macro expansion can be consistently detected. + struct MacroExpansion + """ + ) { + DeclSyntax("private let expandedNode: ResultType") + + DeclSyntax("private let macro: any Macro.Type") + + DeclSyntax("private unowned let macroApplication: \(raw: namePrefix)MacroApplication") + + DeclSyntax( + """ + fileprivate init(expandedNode: ResultType, macro: any Macro.Type, macroApplication: \(raw: namePrefix)MacroApplication) { + self.expandedNode = expandedNode + self.macro = macro + self.macroApplication = macroApplication + } + """ + ) + + DeclSyntax( + """ + /// Invokes the given closure with the node resulting from a macro expansion. + /// + /// This method inserts a pair of push and pop operations immediately around the invocation of `body` to maintain + /// an exact stack of expanding freestanding macros to detect recursive macro expansion. Callers should perform any + /// further macro expansion on `expanded` only within the scope of `body`. + func withExpandedNode(_ body: (_ expandedNode: ResultType)\(raw: asyncSpecifier) throws -> T)\(raw: asyncSpecifier) rethrows -> T { + macroApplication.expandingFreestandingMacros.append(macro) + defer { + macroApplication.expandingFreestandingMacros.removeLast() + } + return try \(raw: awaitOperator)body(expandedNode) + } + """ + ) + } + + try! EnumDeclSyntax("enum MacroExpansionResult") { + DeclSyntax( + """ + /// Expansion of the macro succeeded. + case success(expansion: MacroExpansion) + """ + ) + + DeclSyntax( + """ + /// Macro system found the macro to expand but running the expansion threw + /// an error and thus no expansion result exists. + case failure + """ + ) + + DeclSyntax( + """ + /// The node that should be expanded was not a macro known to the macro system. + case notAMacro + """ + ) + } + + DeclSyntax( + """ + /// Expands the given freestanding macro node into a syntax node by invoking the given closure. + /// + /// Any error thrown by `expandMacro` and circular expansion error will be added to diagnostics. + /// + /// - Parameters: + /// - node: The freestanding macro node to be expanded. + /// - expandMacro: The closure that expands the given macro type and macro node into a syntax node. + /// + /// - Returns: + /// Returns `.notAMacro` if `node` is `nil` or `node.macroName` isn't registered with any macro type. + /// Returns `.failure` if `expandMacro` throws an error or returns `nil`, or recursive expansion is detected. + /// Returns `.success` otherwise. + private func expandFreestandingMacro( + _ node: (any FreestandingMacroExpansionSyntax)?, + expandMacro: (_ macro: any Macro.Type, _ node: any FreestandingMacroExpansionSyntax)\(raw: asyncSpecifier) throws -> ExpandedMacroType? + )\(raw: asyncSpecifier) -> MacroExpansionResult { + guard let node, + let macro = macroSystem.lookup(node.macroName.text)?.type + else { + return .notAMacro + } + + do { + guard !expandingFreestandingMacros.contains(where: { $0 == macro }) else { + // We may think of any ongoing macro expansion as a tree in which macro types being expanded are nodes. + // Any macro type being expanded more than once will create a cycle which the compiler as of now doesn't allow. + throw MacroExpansionError.recursiveExpansion(macro) + } + + if let expanded = try \(raw: awaitOperator)expandMacro(macro, node) { + return .success(expansion: MacroExpansion(expandedNode: expanded, macro: macro, macroApplication: self)) + } else { + return .failure + } + } catch { + contextGenerator(Syntax(node)).addDiagnostics(from: error, node: node) + return .failure + } + } + """ + ) + + DeclSyntax( + """ + /// Expand a freestanding macro expansion syntax in a code block item position. + /// + /// For example + /// ```swift + /// function test() { + /// #foo + /// } + /// ``` + func expandCodeBlockItem(node: CodeBlockItemSyntax)\(raw: asyncSpecifier) -> MacroExpansionResult { + return \(raw: awaitOperator)expandFreestandingMacro(node.item.asProtocol(FreestandingMacroExpansionSyntax.self)) { macro, node in + return try \(raw: awaitOperator)expandFreestandingCodeItemList( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand a freestanding macro expansion syntax in a member decl position. + /// + /// For exmple + /// ```swift + /// struct S { + /// #foo + /// } + /// ``` + func expandMemberDecl(node: MemberBlockItemSyntax)\(raw: asyncSpecifier) -> MacroExpansionResult { + return \(raw: awaitOperator)expandFreestandingMacro(node.decl.as(MacroExpansionDeclSyntax.self)) { macro, node in + return try \(raw: awaitOperator)expandFreestandingMemberDeclList( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + + DeclSyntax( + """ + /// Expand a freestanding macro expansion in a expression position inside + /// other products. + /// + /// For example + /// ```swift + /// let a = #foo + /// ``` + func expandExpr(node: Syntax)\(raw: asyncSpecifier) -> MacroExpansionResult { + return \(raw: awaitOperator)expandFreestandingMacro(node.as(MacroExpansionExprSyntax.self)) { macro, node in + return try \(raw: awaitOperator)expandFreestandingExpr( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } + """ + ) + } + } +} diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrosgenerictestsupport/GenericMacroAssertionsFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrosgenerictestsupport/GenericMacroAssertionsFile.swift new file mode 100644 index 00000000000..9b1ef545a09 --- /dev/null +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrosgenerictestsupport/GenericMacroAssertionsFile.swift @@ -0,0 +1,841 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SyntaxSupport +import Utils + +let genericMacroAssertionsFile = makeGenericMacroAssertionsFile() + +private func makeGenericMacroAssertionsFile() -> SourceFileSyntax { + return SourceFileSyntax(leadingTrivia: copyrightHeader) { + IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax { + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("swift(>=6.0)"), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftBasicFormat") + DeclSyntax("public import SwiftDiagnostics") + DeclSyntax("@_spi(FixItApplier) import SwiftIDEUtils") + DeclSyntax("import SwiftParser") + DeclSyntax("import SwiftParserDiagnostics") + DeclSyntax("public import SwiftSyntax") + DeclSyntax("public import SwiftSyntaxMacroExpansion") + DeclSyntax("private import SwiftSyntaxMacros") + DeclSyntax("private import _SwiftSyntaxGenericTestSupport") + } + ) + ) + IfConfigClauseSyntax( + poundKeyword: .poundElseToken(), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftBasicFormat") + DeclSyntax("import SwiftDiagnostics") + DeclSyntax("@_spi(FixItApplier) import SwiftIDEUtils") + DeclSyntax("import SwiftParser") + DeclSyntax("import SwiftParserDiagnostics") + DeclSyntax("import SwiftSyntax") + DeclSyntax("import SwiftSyntaxMacroExpansion") + DeclSyntax("import SwiftSyntaxMacros") + DeclSyntax("import _SwiftSyntaxGenericTestSupport") + } + ) + ) + } + ) + + try! StructDeclSyntax( + """ + /// Defines the location at which the a test failure should be anchored. This is typically the location where the + /// assertion function is called. + public struct TestFailureLocation + """ + ) { + DeclSyntax("@_spi(XCTestFailureLocation) public let staticFileID: StaticString") + DeclSyntax("public var fileID: String { staticFileID.description }") + + DeclSyntax("@_spi(XCTestFailureLocation) public let staticFilePath: StaticString") + DeclSyntax("public var filePath: String { staticFilePath.description }") + + DeclSyntax("@_spi(XCTestFailureLocation) public let unsignedLine: UInt") + DeclSyntax("public var line: Int { Int(unsignedLine) }") + + DeclSyntax("@_spi(XCTestFailureLocation) public let unsignedColumn: UInt") + DeclSyntax("public var column: Int { Int(unsignedColumn) }") + + DeclSyntax( + """ + public init( + fileID: StaticString, + filePath: StaticString, + line: UInt, + column: UInt + ) { + self.staticFileID = fileID + self.staticFilePath = filePath + self.unsignedLine = line + self.unsignedColumn = column + } + """ + ) + + DeclSyntax( + """ + fileprivate init(underlying: _SwiftSyntaxGenericTestSupport.TestFailureLocation) { + self.init( + fileID: underlying.fileID, + filePath: underlying.filePath, + line: underlying.line, + column: underlying.column + ) + } + """ + ) + + DeclSyntax( + """ + /// This type is intentionally different to `_SwiftSyntaxGenericTestSupport.TestFailureLocation` so we can + /// import `_SwiftSyntaxGenericTestSupport` privately and don't expose its internal types. + fileprivate var underlying: _SwiftSyntaxGenericTestSupport.TestFailureLocation { + _SwiftSyntaxGenericTestSupport.TestFailureLocation( + fileID: self.staticFileID, + filePath: self.staticFilePath, + line: self.unsignedLine, + column: self.unsignedColumn + ) + } + """ + ) + } + + try! StructDeclSyntax( + """ + /// Defines the details of a test failure, consisting of a message and the location at which the test failure should be + /// shown. + public struct TestFailureSpec + """ + ) { + DeclSyntax("public let message: String") + DeclSyntax("public let location: TestFailureLocation") + + DeclSyntax( + """ + public init(message: String, location: TestFailureLocation) { + self.message = message + self.location = location + } + """ + ) + + DeclSyntax( + """ + fileprivate init(underlying: _SwiftSyntaxGenericTestSupport.TestFailureSpec) { + self.init( + message: underlying.message, + location: TestFailureLocation(underlying: underlying.location) + ) + } + """ + ) + } + + try! StructDeclSyntax( + """ + // MARK: - Note + + /// Describes a diagnostic note that tests expect to be created by a macro expansion. + public struct NoteSpec + """ + ) { + DeclSyntax( + """ + /// The expected message of the note + public let message: String + """ + ) + + DeclSyntax( + """ + /// The line to which the note is expected to point + public let line: Int + """ + ) + + DeclSyntax( + """ + /// The column to which the note is expected to point + public let column: Int + """ + ) + + DeclSyntax( + """ + /// The file and line at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + internal let failureLocation: TestFailureLocation + """ + ) + + DeclSyntax( + """ + /// Creates a new ``NoteSpec`` that describes a note tests are expecting to be generated by a macro expansion. + /// + /// - Parameters: + /// - message: The expected message of the note + /// - line: The line to which the note is expected to point + /// - column: The column to which the note is expected to point + /// - originatorFile: The file at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + /// - originatorLine: The line at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + public init( + message: String, + line: Int, + column: Int, + originatorFileID: StaticString = #fileID, + originatorFile: StaticString = #filePath, + originatorLine: UInt = #line, + originatorColumn: UInt = #column + ) { + self.message = message + self.line = line + self.column = column + self.failureLocation = TestFailureLocation( + fileID: originatorFileID, + filePath: originatorFile, + line: originatorLine, + column: originatorColumn + ) + } + """ + ) + } + + DeclSyntax( + #""" + func assertNote( + _ note: Note, + in expansionContext: DiagnosticAssertionContext, + expected spec: NoteSpec, + failureHandler: (TestFailureSpec) -> Void + ) { + assertStringsEqualWithDiff( + note.message, + spec.message, + "message of note does not match", + location: spec.failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + let location = expansionContext.location(for: note.position, anchoredAt: note.node, fileName: "") + if location.line != spec.line { + failureHandler( + TestFailureSpec( + message: "line of note \(location.line) does not match expected line \(spec.line)", + location: spec.failureLocation + ) + ) + } + if location.column != spec.column { + failureHandler( + TestFailureSpec( + message: "column of note \(location.column) does not match expected column \(spec.column)", + location: spec.failureLocation + ) + ) + } + } + """# + ) + + try! StructDeclSyntax( + """ + // MARK: - Fix-It + + /// Describes a Fix-It that tests expect to be created by a macro expansion. + /// + /// Currently, it only compares the message of the Fix-It. In the future, it might + /// also compare the expected changes that should be performed by the Fix-It. + public struct FixItSpec + """ + ) { + DeclSyntax( + """ + /// The expected message of the Fix-It + public let message: String + """ + ) + + DeclSyntax( + """ + /// The file and line at which this ``FixItSpec`` was created, so that assertion failures can be reported at its location. + internal let failureLocation: TestFailureLocation + """ + ) + + DeclSyntax( + """ + /// Creates a new ``FixItSpec`` that describes a Fix-It tests are expecting to be generated by a macro expansion. + /// + /// - Parameters: + /// - message: The expected message of the note + /// - originatorFile: The file at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + /// - originatorLine: The line at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + public init( + message: String, + originatorFileID: StaticString = #fileID, + originatorFile: StaticString = #filePath, + originatorLine: UInt = #line, + originatorColumn: UInt = #column + ) { + self.message = message + self.failureLocation = TestFailureLocation( + fileID: originatorFileID, + filePath: originatorFile, + line: originatorLine, + column: originatorColumn + ) + } + """ + ) + } + + DeclSyntax( + """ + func assertFixIt( + _ fixIt: FixIt, + expected spec: FixItSpec, + failureHandler: (TestFailureSpec) -> Void + ) { + assertStringsEqualWithDiff( + fixIt.message.message, + spec.message, + "message of Fix-It does not match", + location: spec.failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + } + """ + ) + + try! StructDeclSyntax( + """ + // MARK: - Diagnostic + + /// Describes a diagnostic that tests expect to be created by a macro expansion. + public struct DiagnosticSpec + """ + ) { + DeclSyntax( + """ + /// If not `nil`, the ID, which the diagnostic is expected to have. + public let id: MessageID? + """ + ) + + DeclSyntax( + """ + /// The expected message of the diagnostic + public let message: String + """ + ) + + DeclSyntax( + """ + /// The line to which the diagnostic is expected to point + public let line: Int + """ + ) + + DeclSyntax( + """ + /// The column to which the diagnostic is expected to point + public let column: Int + """ + ) + + DeclSyntax( + """ + /// The expected severity of the diagnostic + public let severity: DiagnosticSeverity + """ + ) + + DeclSyntax( + """ + /// If not `nil`, the text fragments the diagnostic is expected to highlight + public let highlights: [String]? + """ + ) + + DeclSyntax( + """ + /// The notes that are expected to be attached to the diagnostic + public let notes: [NoteSpec] + """ + ) + + DeclSyntax( + """ + /// The messages of the Fix-Its the diagnostic is expected to produce + public let fixIts: [FixItSpec] + """ + ) + + DeclSyntax( + """ + /// The file and line at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + internal let failureLocation: TestFailureLocation + """ + ) + + DeclSyntax( + """ + /// Creates a new ``DiagnosticSpec`` that describes a diagnostic tests are expecting to be generated by a macro expansion. + /// + /// - Parameters: + /// - id: If not `nil`, the ID, which the diagnostic is expected to have. + /// - message: The expected message of the diagnostic + /// - line: The line to which the diagnostic is expected to point + /// - column: The column to which the diagnostic is expected to point + /// - severity: The expected severity of the diagnostic + /// - highlights: If not empty, the text fragments the diagnostic is expected to highlight + /// - notes: The notes that are expected to be attached to the diagnostic + /// - fixIts: The messages of the Fix-Its the diagnostic is expected to produce + /// - originatorFile: The file at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + /// - originatorLine: The line at which this ``NoteSpec`` was created, so that assertion failures can be reported at its location. + public init( + id: MessageID? = nil, + message: String, + line: Int, + column: Int, + severity: DiagnosticSeverity = .error, + highlights: [String]? = nil, + notes: [NoteSpec] = [], + fixIts: [FixItSpec] = [], + originatorFileID: StaticString = #fileID, + originatorFile: StaticString = #filePath, + originatorLine: UInt = #line, + originatorColumn: UInt = #column + ) { + self.id = id + self.message = message + self.line = line + self.column = column + self.severity = severity + self.highlights = highlights + self.notes = notes + self.fixIts = fixIts + self.failureLocation = TestFailureLocation( + fileID: originatorFileID, + filePath: originatorFile, + line: originatorLine, + column: originatorColumn + ) + } + """ + ) + } + + try! ExtensionDeclSyntax("extension DiagnosticSpec") { + DeclSyntax( + """ + @available(*, deprecated, message: "Use highlights instead") + public var highlight: String? { + guard let highlights else { + return nil + } + return highlights.joined(separator: " ") + } + """ + ) + + DeclSyntax( + """ + // swift-format-ignore + @available(*, deprecated, message: "Use init(id:message:line:column:severity:highlights:notes:fixIts:originatorFile:originatorLine:) instead") + @_disfavoredOverload + public init( + id: MessageID? = nil, + message: String, + line: Int, + column: Int, + severity: DiagnosticSeverity = .error, + highlight: String? = nil, + notes: [NoteSpec] = [], + fixIts: [FixItSpec] = [], + originatorFile: StaticString = #filePath, + originatorLine: UInt = #line + ) { + self.init( + id: id, + message: message, + line: line, + column: column, + severity: severity, + highlights: highlight.map { [$0] }, + notes: notes, + fixIts: fixIts + ) + } + """ + ) + + DeclSyntax( + """ + """ + ) + } + + try! EnumDeclSyntax( + """ + /// Describes the context in which we are asserting diagnostic correctness. + /// + /// This is used to map source locations. + public enum DiagnosticAssertionContext + """ + ) { + DeclSyntax("case macroExpansion(BasicMacroExpansionContext)") + DeclSyntax("case tree(any SyntaxProtocol)") + DeclSyntax( + """ + func location( + for position: AbsolutePosition, + anchoredAt node: Syntax, + fileName: String + ) -> SourceLocation { + switch self { + case .macroExpansion(let expansionContext): + return expansionContext.location( + for: position, + anchoredAt: node, + fileName: fileName + ) + + case .tree(let syntax): + return SourceLocationConverter(fileName: fileName, tree: syntax) + .location(for: position) + } + } + """ + ) + } + + DeclSyntax( + #""" + @_spi(Testing) + public func assertDiagnostic( + _ diag: Diagnostic, + in expansionContext: DiagnosticAssertionContext, + expected spec: DiagnosticSpec, + failureHandler: (TestFailureSpec) -> Void + ) { + if let id = spec.id, diag.diagnosticID != id { + failureHandler( + TestFailureSpec( + message: "diagnostic ID \(diag.diagnosticID) does not match expected id \(id)", + location: spec.failureLocation + ) + ) + } + assertStringsEqualWithDiff( + diag.message, + spec.message, + "message does not match", + location: spec.failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + let location = expansionContext.location(for: diag.position, anchoredAt: diag.node, fileName: "") + if location.line != spec.line { + failureHandler( + TestFailureSpec( + message: "line \(location.line) does not match expected line \(spec.line)", + location: spec.failureLocation + ) + ) + } + if location.column != spec.column { + failureHandler( + TestFailureSpec( + message: "column \(location.column) does not match expected column \(spec.column)", + location: spec.failureLocation + ) + ) + } + + if spec.severity != diag.diagMessage.severity { + failureHandler( + TestFailureSpec( + message: "severity \(diag.diagMessage.severity) does not match expected severity \(spec.severity)", + location: spec.failureLocation + ) + ) + } + + if let highlights = spec.highlights { + if diag.highlights.count != highlights.count { + failureHandler( + TestFailureSpec( + message: """ + Expected \(highlights.count) highlights but received \(diag.highlights.count): + \(diag.highlights.map(\.trimmedDescription).joined(separator: "\n")) + """, + location: spec.failureLocation + ) + ) + } else { + for (actual, expected) in zip(diag.highlights, highlights) { + assertStringsEqualWithDiff( + actual.trimmedDescription, + expected, + "highlight does not match", + location: spec.failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + } + } + } + if diag.notes.count != spec.notes.count { + failureHandler( + TestFailureSpec( + message: """ + Expected \(spec.notes.count) notes but received \(diag.notes.count): + \(diag.notes.map(\.debugDescription).joined(separator: "\n")) + """, + location: spec.failureLocation + ) + ) + } else { + for (note, expectedNote) in zip(diag.notes, spec.notes) { + assertNote(note, in: expansionContext, expected: expectedNote, failureHandler: failureHandler) + } + } + if diag.fixIts.count != spec.fixIts.count { + failureHandler( + TestFailureSpec( + message: """ + Expected \(spec.fixIts.count) Fix-Its but received \(diag.fixIts.count): + \(diag.fixIts.map(\.message.message).joined(separator: "\n")) + """, + location: spec.failureLocation + ) + ) + } else { + for (fixIt, expectedFixIt) in zip(diag.fixIts, spec.fixIts) { + assertFixIt(fixIt, expected: expectedFixIt, failureHandler: failureHandler) + } + } + } + """# + ) + + makeAssertMacroExpansionFunction(isAsync: false) + + try! ExtensionDeclSyntax("fileprivate extension FixIt.Change") { + DeclSyntax( + """ + /// Returns the edit for this change, translating positions from detached nodes + /// to the corresponding locations in the original source file based on + /// `expansionContext`. + /// + /// - SeeAlso: `FixIt.Change.edit` + func edit(in expansionContext: BasicMacroExpansionContext) -> SourceEdit { + switch self { + case .replace(let oldNode, let newNode): + let start = expansionContext.position(of: oldNode.position, anchoredAt: oldNode) + let end = expansionContext.position(of: oldNode.endPosition, anchoredAt: oldNode) + return SourceEdit( + range: start.. AbsolutePosition { + let location = self.location(for: position, anchoredAt: Syntax(node), fileName: "") + return AbsolutePosition(utf8Offset: location.offset) + } + """ + ) + } + + makeAssertMacroExpansionFunction(isAsync: true) + } +} + +private func makeAssertMacroExpansionFunction(isAsync: Bool) -> DeclSyntax { + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return DeclSyntax( + """ + /// Assert that expanding the given macros in the original source produces + /// the given expanded source code. + /// + /// - Parameters: + /// - originalSource: The original source code, which is expected to contain + /// macros in various places (e.g., `#stringify(x + y)`). + /// - expectedExpandedSource: The source code that we expect to see after + /// performing macro expansion on the original source. + /// - diagnostics: The diagnostics when expanding any macro + /// - macroSpecs: The macros that should be expanded, provided as a dictionary + /// mapping macro names (e.g., `"CodableMacro"`) to specification with macro type + /// (e.g., `CodableMacro.self`) and a list of conformances macro provides + /// (e.g., `["Decodable", "Encodable"]`). + /// - applyFixIts: If specified, filters the Fix-Its that are applied to generate `fixedSource` to only those whose message occurs in this array. If `nil`, all Fix-Its from the diagnostics are applied. + /// - fixedSource: If specified, asserts that the source code after applying Fix-Its matches this string. + /// - testModuleName: The name of the test module to use. + /// - testFileName: The name of the test file name to use. + /// - indentationWidth: The indentation width used in the expansion. + public func assertMacroExpansion( + _ originalSource: String, + expandedSource expectedExpandedSource: String, + diagnostics: [DiagnosticSpec] = [], + macroSpecs: [String: MacroSpec], + applyFixIts: [String]? = nil, + fixedSource expectedFixedSource: String? = nil, + testModuleName: String = "TestModule", + testFileName: String = "test.swift", + indentationWidth: Trivia = .spaces(4), + failureHandler: (TestFailureSpec) -> Void, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column + )\(raw: asyncSpecifier) { + let failureLocation = TestFailureLocation(fileID: fileID, filePath: filePath, line: line, column: column) + // Parse the original source file. + let origSourceFile = Parser.parse(source: originalSource) + + // Expand all macros in the source. + let context = BasicMacroExpansionContext( + sourceFiles: [origSourceFile: .init(moduleName: testModuleName, fullFilePath: testFileName)] + ) + + func contextGenerator(_ syntax: Syntax) -> BasicMacroExpansionContext { + return BasicMacroExpansionContext(sharingWith: context, lexicalContext: syntax.allMacroLexicalContexts()) + } + + let expandedSourceFile = \(raw: awaitOperator)origSourceFile.expand( + macroSpecs: macroSpecs, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + let diags = ParseDiagnosticsGenerator.diagnostics(for: expandedSourceFile) + if !diags.isEmpty { + failureHandler( + TestFailureSpec( + message: \"\"\" + Expanded source should not contain any syntax errors, but contains: + \\(DiagnosticsFormatter.annotatedSource(tree: expandedSourceFile, diags: diags)) + + Expanded syntax tree was: + \\(expandedSourceFile.debugDescription) + \"\"\", + location: failureLocation + ) + ) + } + + assertStringsEqualWithDiff( + expandedSourceFile.description.drop(while: \\.isNewline).droppingLast(while: \\.isNewline), + expectedExpandedSource.drop(while: \\.isNewline).droppingLast(while: \\.isNewline), + "Macro expansion did not produce the expected expanded source", + additionalInfo: \"\"\" + Actual expanded source: + \\(expandedSourceFile) + \"\"\", + location: failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + + if context.diagnostics.count != diagnostics.count { + failureHandler( + TestFailureSpec( + message: \"\"\" + Expected \\(diagnostics.count) diagnostics but received \\(context.diagnostics.count): + \\(context.diagnostics.map(\\.debugDescription).joined(separator: "\\n")) + \"\"\", + location: failureLocation + ) + ) + } else { + for (actualDiag, expectedDiag) in zip(context.diagnostics, diagnostics) { + assertDiagnostic( + actualDiag, + in: .macroExpansion(context), + expected: expectedDiag, + failureHandler: failureHandler + ) + } + } + + // Applying Fix-Its + if let expectedFixedSource = expectedFixedSource { + let messages = applyFixIts ?? context.diagnostics.compactMap { $0.fixIts.first?.message.message } + + let edits = + context.diagnostics + .flatMap(\\.fixIts) + .filter { messages.contains($0.message.message) } + .flatMap { $0.changes } + .map { $0.edit(in: context) } + + let fixedTree = FixItApplier.apply(edits: edits, to: origSourceFile) + let fixedTreeDescription = fixedTree.description + assertStringsEqualWithDiff( + fixedTreeDescription.trimmingTrailingWhitespace(), + expectedFixedSource.trimmingTrailingWhitespace(), + location: failureLocation.underlying, + failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + ) + } + } + """ + ) +} diff --git a/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrostestsupport/MacroAssertionsFile.swift b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrostestsupport/MacroAssertionsFile.swift new file mode 100644 index 00000000000..a08dcc897ef --- /dev/null +++ b/CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntaxmacrostestsupport/MacroAssertionsFile.swift @@ -0,0 +1,177 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftSyntax +import SwiftSyntaxBuilder +import SyntaxSupport +import Utils + +let macroAssertionsFile = makeMacroAssertionsFile() + +private func makeMacroAssertionsFile() -> SourceFileSyntax { + return SourceFileSyntax(leadingTrivia: copyrightHeader) { + IfConfigDeclSyntax( + clauses: IfConfigClauseListSyntax { + IfConfigClauseSyntax( + poundKeyword: .poundIfToken(), + condition: ExprSyntax("swift(>=6.0)"), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("public import SwiftSyntax") + DeclSyntax("public import SwiftSyntaxMacroExpansion") + DeclSyntax("public import SwiftSyntaxMacros") + DeclSyntax("@_spi(XCTestFailureLocation) public import SwiftSyntaxMacrosGenericTestSupport") + DeclSyntax("private import XCTest") + } + ) + ) + IfConfigClauseSyntax( + poundKeyword: .poundElseToken(), + elements: .statements( + CodeBlockItemListSyntax { + DeclSyntax("import SwiftSyntax") + DeclSyntax("import SwiftSyntaxMacroExpansion") + DeclSyntax("import SwiftSyntaxMacros") + DeclSyntax("@_spi(XCTestFailureLocation) import SwiftSyntaxMacrosGenericTestSupport") + DeclSyntax("import XCTest") + } + ) + ) + } + ) + + DeclSyntax( + """ + // Re-export the spec types from `SwiftSyntaxMacrosGenericTestSupport`. + public typealias NoteSpec = SwiftSyntaxMacrosGenericTestSupport.NoteSpec + """ + ) + DeclSyntax("public typealias FixItSpec = SwiftSyntaxMacrosGenericTestSupport.FixItSpec") + DeclSyntax("public typealias DiagnosticSpec = SwiftSyntaxMacrosGenericTestSupport.DiagnosticSpec") + + makeAssertMacroExpansionFunctions(isAsync: false) + makeAssertMacroExpansionFunctions(isAsync: true) + } +} + +private func makeAssertMacroExpansionFunctions(isAsync: Bool) -> CodeBlockItemListSyntax { + let asyncSpecifier = if isAsync { " async" } else { String() } + let awaitOperator = if isAsync { "await " } else { String() } + + return CodeBlockItemListSyntax() { + DeclSyntax( + """ + /// Assert that expanding the given macros in the original source produces + /// the given expanded source code. + /// + /// - Parameters: + /// - originalSource: The original source code, which is expected to contain + /// macros in various places (e.g., `#stringify(x + y)`). + /// - expectedExpandedSource: The source code that we expect to see after + /// performing macro expansion on the original source. + /// - diagnostics: The diagnostics when expanding any macro + /// - macros: The macros that should be expanded, provided as a dictionary + /// mapping macro names (e.g., `"stringify"`) to implementation types + /// (e.g., `StringifyMacro.self`). + /// - testModuleName: The name of the test module to use. + /// - testFileName: The name of the test file name to use. + /// - indentationWidth: The indentation width used in the expansion. + /// + /// - SeeAlso: ``assertMacroExpansion(_:expandedSource:diagnostics:macroSpecs:applyFixIts:fixedSource:testModuleName:testFileName:indentationWidth:file:line:)`` + /// to also specify the list of conformances passed to the macro expansion. + public func assertMacroExpansion( + _ originalSource: String, + expandedSource expectedExpandedSource: String, + diagnostics: [DiagnosticSpec] = [], + macros: [String: Macro.Type], + applyFixIts: [String]? = nil, + fixedSource expectedFixedSource: String? = nil, + testModuleName: String = "TestModule", + testFileName: String = "test.swift", + indentationWidth: Trivia = .spaces(4), + file: StaticString = #filePath, + line: UInt = #line + )\(raw: asyncSpecifier) { + let specs = macros.mapValues { MacroSpec(type: $0) } + \(raw: awaitOperator)assertMacroExpansion( + originalSource, + expandedSource: expectedExpandedSource, + diagnostics: diagnostics, + macroSpecs: specs, + applyFixIts: applyFixIts, + fixedSource: expectedFixedSource, + testModuleName: testModuleName, + testFileName: testFileName, + indentationWidth: indentationWidth, + file: file, + line: line + ) + } + """ + ) + + DeclSyntax( + """ + /// Assert that expanding the given macros in the original source produces + /// the given expanded source code. + /// + /// - Parameters: + /// - originalSource: The original source code, which is expected to contain + /// macros in various places (e.g., `#stringify(x + y)`). + /// - expectedExpandedSource: The source code that we expect to see after + /// performing macro expansion on the original source. + /// - diagnostics: The diagnostics when expanding any macro + /// - macroSpecs: The macros that should be expanded, provided as a dictionary + /// mapping macro names (e.g., `"CodableMacro"`) to specification with macro type + /// (e.g., `CodableMacro.self`) and a list of conformances macro provides + /// (e.g., `["Decodable", "Encodable"]`). + /// - applyFixIts: If specified, filters the Fix-Its that are applied to generate `fixedSource` to only those whose message occurs in this array. If `nil`, all Fix-Its from the diagnostics are applied. + /// - fixedSource: If specified, asserts that the source code after applying Fix-Its matches this string. + /// - testModuleName: The name of the test module to use. + /// - testFileName: The name of the test file name to use. + /// - indentationWidth: The indentation width used in the expansion. + public func assertMacroExpansion( + _ originalSource: String, + expandedSource expectedExpandedSource: String, + diagnostics: [DiagnosticSpec] = [], + macroSpecs: [String: MacroSpec], + applyFixIts: [String]? = nil, + fixedSource expectedFixedSource: String? = nil, + testModuleName: String = "TestModule", + testFileName: String = "test.swift", + indentationWidth: Trivia = .spaces(4), + file: StaticString = #filePath, + line: UInt = #line + )\(raw: asyncSpecifier) { + \(raw: awaitOperator)SwiftSyntaxMacrosGenericTestSupport.assertMacroExpansion( + originalSource, + expandedSource: expectedExpandedSource, + diagnostics: diagnostics, + macroSpecs: macroSpecs, + applyFixIts: applyFixIts, + fixedSource: expectedFixedSource, + testModuleName: testModuleName, + testFileName: testFileName, + indentationWidth: indentationWidth, + failureHandler: { + XCTFail($0.message, file: $0.location.staticFilePath, line: $0.location.unsignedLine) + }, + fileID: "", // Not used in the failure handler + filePath: file, + line: line, + column: 0 // Not used in the failure handler + ) + } + """ + ) + } +} diff --git a/Sources/SwiftCompilerPlugin/CompilerPlugin.swift b/Sources/SwiftCompilerPlugin/CompilerPlugin.swift index 3ced3a43de1..e029fc856ef 100644 --- a/Sources/SwiftCompilerPlugin/CompilerPlugin.swift +++ b/Sources/SwiftCompilerPlugin/CompilerPlugin.swift @@ -108,10 +108,10 @@ extension CompilerPlugin { /// Main entry point of the plugin — sets up a standard I/O communication /// channel with the plugin host and runs the main message loop. - public static func main() throws { + public static func main() async throws { let connection = try StandardIOMessageConnection() let provider = MacroProviderAdapter(plugin: Self()) let impl = CompilerPluginMessageListener(connection: connection, provider: provider) - impl.main() + await impl.main() } } diff --git a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift index 6c78e03e845..b1719ac3715 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift @@ -96,12 +96,12 @@ public class CompilerPluginMessageListener Bool { + private func handleNextMessage() async -> Bool { do { guard let message = try connection.waitForNextMessage(HostToPluginMessage.self) else { return false } - let result = handler.handleMessage(message) + let result = await handler.handleMessage(message) try connection.sendMessage(result) return true } catch { @@ -132,7 +132,7 @@ public class CompilerPluginMessageListener PluginToHostMessage + func handleMessage(_ message: HostToPluginMessage) async -> PluginToHostMessage } /// A `PluginMessageHandler` that uses a `PluginProvider`. @@ -154,7 +154,7 @@ public class PluginProviderMessageHandler: PluginMessa } /// Handles a single message received from the plugin host. - public func handleMessage(_ message: HostToPluginMessage) -> PluginToHostMessage { + public func handleMessage(_ message: HostToPluginMessage) async -> PluginToHostMessage { switch message { case .getCapability(let hostCapability): // Remember the peer capability if provided. @@ -176,7 +176,7 @@ public class PluginProviderMessageHandler: PluginMessa let expandingSyntax, let lexicalContext ): - return expandFreestandingMacro( + return await expandFreestandingMacro( macro: macro, macroRole: macroRole, discriminator: discriminator, @@ -195,7 +195,7 @@ public class PluginProviderMessageHandler: PluginMessa let conformanceListSyntax, let lexicalContext ): - return expandAttachedMacro( + return await expandAttachedMacro( macro: macro, macroRole: macroRole, discriminator: discriminator, diff --git a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift index 0e51be77df0..c2cba8862e9 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/Macros.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/Macros.swift @@ -55,7 +55,7 @@ extension PluginProviderMessageHandler { discriminator: String, expandingSyntax: PluginMessage.Syntax, lexicalContext: [PluginMessage.Syntax]? - ) -> PluginToHostMessage { + ) async -> PluginToHostMessage { let sourceManager = SourceManager(syntaxRegistry: syntaxRegistry) let syntax = sourceManager.add(expandingSyntax, foldingWith: .standardOperators) @@ -83,7 +83,7 @@ extension PluginProviderMessageHandler { macroRole = try inferFreestandingMacroRole(definition: macroDefinition) } - expandedSource = SwiftSyntaxMacroExpansion.expandFreestandingMacro( + expandedSource = await SwiftSyntaxMacroExpansion.expandFreestandingMacro( definition: macroDefinition, macroRole: macroRole, node: macroSyntax, @@ -119,7 +119,7 @@ extension PluginProviderMessageHandler { extendedTypeSyntax: PluginMessage.Syntax?, conformanceListSyntax: PluginMessage.Syntax?, lexicalContext: [PluginMessage.Syntax]? - ) -> PluginToHostMessage { + ) async -> PluginToHostMessage { let sourceManager = SourceManager(syntaxRegistry: syntaxRegistry) let attributeNode = sourceManager.add( attributeSyntax, @@ -152,7 +152,7 @@ extension PluginProviderMessageHandler { let macroDefinition = try resolveMacro(macro) let role = MacroRole(messageMacroRole: macroRole) - let expansions = SwiftSyntaxMacroExpansion.expandAttachedMacroWithoutCollapsing( + let expansions = await SwiftSyntaxMacroExpansion.expandAttachedMacroWithoutCollapsing( definition: macroDefinition, macroRole: role, attributeNode: attributeNode, diff --git a/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt b/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt index 892a2e30fe6..db141a98485 100644 --- a/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt +++ b/Sources/SwiftSyntaxMacroExpansion/CMakeLists.txt @@ -3,11 +3,11 @@ add_swift_syntax_library(SwiftSyntaxMacroExpansion FunctionParameterUtils.swift IndentationUtils.swift MacroArgument.swift - MacroExpansion.swift MacroExpansionDiagnosticMessages.swift MacroReplacement.swift MacroSpec.swift - MacroSystem.swift + generated/MacroExpansion.swift + generated/MacroSystem.swift ) target_link_swift_syntax_libraries(SwiftSyntaxMacroExpansion PUBLIC diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift b/Sources/SwiftSyntaxMacroExpansion/generated/MacroExpansion.swift similarity index 58% rename from Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift rename to Sources/SwiftSyntaxMacroExpansion/generated/MacroExpansion.swift index 6c1dbd49834..927fd29e9b4 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift +++ b/Sources/SwiftSyntaxMacroExpansion/generated/MacroExpansion.swift @@ -1,8 +1,10 @@ +//// Automatically generated by generate-swift-syntax +//// Do not edit directly! //===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -37,17 +39,28 @@ public enum MacroRole: Sendable { extension MacroRole { var protocolName: String { switch self { - case .expression: return "ExpressionMacro" - case .declaration: return "DeclarationMacro" - case .accessor: return "AccessorMacro" - case .memberAttribute: return "MemberAttributeMacro" - case .member: return "MemberMacro" - case .peer: return "PeerMacro" - case .conformance: return "ConformanceMacro" - case .codeItem: return "CodeItemMacro" - case .extension: return "ExtensionMacro" - case .preamble: return "PreambleMacro" - case .body: return "BodyMacro" + case .expression: + return "ExpressionMacro" + case .declaration: + return "DeclarationMacro" + case .accessor: + return "AccessorMacro" + case .memberAttribute: + return "MemberAttributeMacro" + case .member: + return "MemberMacro" + case .peer: + return "PeerMacro" + case .conformance: + return "ConformanceMacro" + case .codeItem: + return "CodeItemMacro" + case .extension: + return "ExtensionMacro" + case .preamble: + return "PreambleMacro" + case .body: + return "BodyMacro" } } } @@ -168,9 +181,12 @@ public func expandFreestandingMacro( /// This is a workaround for older compilers with a newer plugin public func inferFreestandingMacroRole(definition: Macro.Type) throws -> MacroRole { switch definition { - case is ExpressionMacro.Type: return .expression - case is DeclarationMacro.Type: return .declaration - case is CodeItemMacro.Type: return .codeItem + case is ExpressionMacro.Type: + return .expression + case is DeclarationMacro.Type: + return .declaration + case is CodeItemMacro.Type: + return .codeItem default: throw MacroExpansionError.noFreestandingMacroRoles(definition) @@ -402,10 +418,13 @@ public func expandAttachedMacro( // If formatting is disabled we don't want to add any indentation while collapsing let collapseIndentationWidth: Trivia? switch definition.formatMode { - case .auto: collapseIndentationWidth = indentationWidth - case .disabled: collapseIndentationWidth = [] + case .auto: + collapseIndentationWidth = indentationWidth + case .disabled: + collapseIndentationWidth = [] #if RESILIENT_LIBRARIES - @unknown default: fatalError() + @unknown default: + fatalError() #endif } return collapse( @@ -447,7 +466,9 @@ fileprivate extension DeclSyntax { modifiers: DeclModifierListSyntax? ) -> DeclSyntax { func _combine(_ left: C, _ right: C?) -> C { - guard let right = right else { return left } + guard let right = right else { + return left + } var elems: [C.Element] = [] elems += left elems += right @@ -493,7 +514,9 @@ public func collapse( // Default to 4 spaces if no indentation was passed. // In the future, we could consider inferring the indentation width from // the expansions to collapse. - expansions = expansions.map({ $0.indented(by: indentationWidth ?? .spaces(4)) }) + expansions = expansions.map({ + $0.indented(by: indentationWidth ?? .spaces(4)) + }) expansions[0] = "{\n" + expansions[0] expansions[expansions.count - 1] += "\n}" separator = "\n" @@ -543,3 +566,310 @@ public func collapse( return collapsed } + +/// Expand `@freestanding(XXX)` macros. +/// +/// - Parameters: +/// - definition: a type conforms to one of freestanding `Macro` protocol. +/// - macroRole: indicates which `Macro` protocol expansion should be performed +/// - node: macro expansion syntax node (e.g. `#macroName(argument)`). +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level +/// - Returns: expanded source text. Upon failure (i.e. `definition.expansion()` +/// throws) returns `nil`, and the diagnostics representing the `Error` are +/// guaranteed to be added to context. +public func expandFreestandingMacro( + definition: Macro.Type, + macroRole: MacroRole, + node: FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia? = nil +) async -> String? { + do { + let expandedSyntax: Syntax + switch (macroRole, definition) { + case (.expression, let exprMacroDef as ExpressionMacro.Type): + expandedSyntax = try Syntax(await exprMacroDef.expansion(of: node, in: context)) + + case (.declaration, let declMacroDef as DeclarationMacro.Type): + var rewritten = try await declMacroDef.expansion(of: node, in: context) + // Copy attributes and modifiers to the generated decls. + if let expansionDecl = node.as(MacroExpansionDeclSyntax.self) { + // Strip any indentation from the attributes and modifiers that we are + // inheriting. The expanded macro should start at the leftmost column. + let attributes = + declMacroDef.propagateFreestandingMacroAttributes ? expansionDecl.attributes.withIndentationRemoved : nil + let modifiers = + declMacroDef.propagateFreestandingMacroModifiers ? expansionDecl.modifiers.withIndentationRemoved : nil + rewritten = rewritten.map { + $0.applying(attributes: attributes, modifiers: modifiers) + } + } + expandedSyntax = Syntax( + CodeBlockItemListSyntax( + rewritten.map { + CodeBlockItemSyntax(item: .decl($0)) + } + ) + ) + + case (.codeItem, let codeItemMacroDef as CodeItemMacro.Type): + let rewritten = try await codeItemMacroDef.expansion(of: node, in: context) + expandedSyntax = Syntax(CodeBlockItemListSyntax(rewritten)) + + case (.accessor, _), (.memberAttribute, _), (.member, _), (.peer, _), (.conformance, _), (.extension, _), + (.expression, _), (.declaration, _), + (.codeItem, _), (.preamble, _), (.body, _): + throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) + } + return expandedSyntax.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } catch { + context.addDiagnostics(from: error, node: node) + return nil + } +} + +@available(*, deprecated, message: "pass a macro role, please!") +public func expandFreestandingMacro( + definition: Macro.Type, + node: FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext +) async -> String? { + do { + return await expandFreestandingMacro( + definition: definition, + macroRole: try inferFreestandingMacroRole(definition: definition), + node: node, + in: context + ) + } catch { + context.addDiagnostics(from: error, node: node) + return nil + } +} + +/// Expand `@attached(XXX)` macros. +/// +/// - Parameters: +/// - definition: a type that conforms to one or more attached `Macro` protocols. +/// - macroRole: indicates which `Macro` protocol expansion should be performed +/// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`). +/// - declarationNode: target declaration syntax node to apply the expansion. +/// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent +/// context node of `declarationNode`. +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level +/// - Returns: A list of expanded source text. Upon failure (i.e. +/// `definition.expansion()` throws) returns `nil`, and the diagnostics +/// representing the `Error` are guaranteed to be added to context. +public func expandAttachedMacroWithoutCollapsing( + definition: Macro.Type, + macroRole: MacroRole, + attributeNode: AttributeSyntax, + declarationNode: DeclSyntax, + parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, + conformanceList: InheritedTypeListSyntax?, + in context: Context, + indentationWidth: Trivia? = nil +) async -> [String]? { + do { + switch (definition, macroRole) { + case (let attachedMacro as AccessorMacro.Type, .accessor): + let accessors = try await attachedMacro.expansion( + of: attributeNode, + providingAccessorsOf: declarationNode, + in: context + ) + return accessors.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute): + guard + let parentDeclGroup = parentDeclNode?.asProtocol(DeclGroupSyntax.self) + else { + // Compiler error: 'parentDecl' is mandatory for MemberAttributeMacro. + throw MacroExpansionError.parentDeclGroupNil + } + + let attributes = try await attachedMacro.expansion( + of: attributeNode, + attachedTo: parentDeclGroup, + providingAttributesFor: declarationNode, + in: context + ) + + // Form a buffer containing an attribute list to return to the caller. + return attributes.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as MemberMacro.Type, .member): + guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) + else { + // Compiler error: declNode for member macro must be DeclGroupSyntax. + throw MacroExpansionError.declarationNotDeclGroup + } + + let members = try await attachedMacro.expansion( + of: attributeNode, + providingMembersOf: declGroup, + conformingTo: conformanceList?.map(\.type) ?? [], + in: context + ) + + // Form a buffer of member declarations to return to the caller. + return members.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as PeerMacro.Type, .peer): + let peers = try await attachedMacro.expansion( + of: attributeNode, + providingPeersOf: declarationNode, + in: context + ) + + // Form a buffer of peer declarations to return to the caller. + return peers.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as ExtensionMacro.Type, .extension): + guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else { + // Compiler error: type mismatch. + throw MacroExpansionError.declarationNotDeclGroup + } + + let extensionOf: TypeSyntax + if let extendedType { + extensionOf = extendedType + } else if let identified = declarationNode.asProtocol(NamedDeclSyntax.self) { + // Fallback for old compilers with a new plugin, where + extensionOf = TypeSyntax(IdentifierTypeSyntax(name: identified.name)) + } else { + throw MacroExpansionError.noExtendedTypeSyntax + } + + let protocols = conformanceList?.map(\.type) ?? [] + + let extensions = try await attachedMacro.expansion( + of: attributeNode, + attachedTo: declGroup, + providingExtensionsOf: extensionOf, + conformingTo: protocols, + in: context + ) + + // Form a buffer of peer declarations to return to the caller. + return extensions.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as PreambleMacro.Type, .preamble): + guard + let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self) + as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax) + else { + // Compiler error: declaration must have a body. + throw MacroExpansionError.declarationHasNoBody + } + + let preamble = try await attachedMacro.expansion( + of: attributeNode, + providingPreambleFor: declToPass, + in: context + ) + return preamble.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + case (let attachedMacro as BodyMacro.Type, .body): + guard + let declToPass = Syntax(declarationNode).asProtocol(SyntaxProtocol.self) + as? (DeclSyntaxProtocol & WithOptionalCodeBlockSyntax) + else { + // Compiler error: declaration must have a body. + throw MacroExpansionError.declarationHasNoBody + } + + let body = try await attachedMacro.expansion( + of: attributeNode, + providingBodyFor: declToPass, + in: context + ) + return body.map { + $0.formattedExpansion(definition.formatMode, indentationWidth: indentationWidth) + } + + default: + throw MacroExpansionError.unmatchedMacroRole(definition, macroRole) + } + } catch { + context.addDiagnostics(from: error, node: attributeNode) + return nil + } +} + +/// Expand `@attached(XXX)` macros. +/// +/// - Parameters: +/// - definition: a type that conforms to one or more attached `Macro` protocols. +/// - macroRole: indicates which `Macro` protocol expansion should be performed +/// - attributeNode: attribute syntax node (e.g. `@macroName(argument)`). +/// - declarationNode: target declaration syntax node to apply the expansion. +/// - parentDeclNode: Only used for `MacroRole.memberAttribute`. The parent +/// context node of `declarationNode`. +/// - context: context of the expansion. +/// - indentationWidth: The indentation that should be added for each additional +/// nesting level +/// - Returns: expanded source text. Upon failure (i.e. `defintion.expansion()` +/// throws) returns `nil`, and the diagnostics representing the `Error` are +/// guaranteed to be added to context. +public func expandAttachedMacro( + definition: Macro.Type, + macroRole: MacroRole, + attributeNode: AttributeSyntax, + declarationNode: DeclSyntax, + parentDeclNode: DeclSyntax?, + extendedType: TypeSyntax?, + conformanceList: InheritedTypeListSyntax?, + in context: Context, + indentationWidth: Trivia? = nil +) async -> String? { + let expandedSources = await expandAttachedMacroWithoutCollapsing( + definition: definition, + macroRole: macroRole, + attributeNode: attributeNode, + declarationNode: declarationNode, + parentDeclNode: parentDeclNode, + extendedType: extendedType, + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + if let expandedSources { + // If formatting is disabled we don't want to add any indentation while collapsing + let collapseIndentationWidth: Trivia? + switch definition.formatMode { + case .auto: + collapseIndentationWidth = indentationWidth + case .disabled: + collapseIndentationWidth = [] + #if RESILIENT_LIBRARIES + @unknown default: + fatalError() + #endif + } + return collapse( + expansions: expandedSources, + for: macroRole, + attachedTo: declarationNode, + indentationWidth: collapseIndentationWidth + ) + } + return nil +} diff --git a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift b/Sources/SwiftSyntaxMacroExpansion/generated/MacroSystem.swift similarity index 55% rename from Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift rename to Sources/SwiftSyntaxMacroExpansion/generated/MacroSystem.swift index a0c54cab223..b83e84947e6 100644 --- a/Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift +++ b/Sources/SwiftSyntaxMacroExpansion/generated/MacroSystem.swift @@ -1,3 +1,5 @@ +//// Automatically generated by generate-swift-syntax +//// Do not edit directly! //===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project @@ -14,14 +16,14 @@ internal import SwiftDiagnostics internal import SwiftOperators @_spi(MacroExpansion) internal import SwiftParser -public import SwiftSyntax +@_spi(MacroExpansion) public import SwiftSyntax internal import SwiftSyntaxBuilder @_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) public import SwiftSyntaxMacros #else import SwiftDiagnostics import SwiftOperators @_spi(MacroExpansion) import SwiftParser -import SwiftSyntax +@_spi(MacroExpansion) import SwiftSyntax import SwiftSyntaxBuilder @_spi(MacroExpansion) @_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros #endif @@ -38,7 +40,9 @@ extension SyntaxProtocol { ) -> Syntax { return expand( macros: macros, - contextGenerator: { _ in context }, + contextGenerator: { _ in + context + }, indentationWidth: indentationWidth ) } @@ -52,7 +56,9 @@ extension SyntaxProtocol { indentationWidth: Trivia? = nil ) -> Syntax { return expand( - macroSpecs: macros.mapValues { MacroSpec(type: $0) }, + macroSpecs: macros.mapValues { + MacroSpec(type: $0) + }, contextGenerator: contextGenerator, indentationWidth: indentationWidth ) @@ -335,7 +341,9 @@ private func expandAccessorMacroWithoutExistingAccessors( // then indents it. // Remove any indentation from the first line using `drop(while:)` and then // prepend a space to separate it from the variable declaration - let indentedSource = " " + expanded.indented(by: attachedTo.indentationOfFirstLine).drop(while: { $0.isWhitespace }) + let indentedSource = " " + expanded.indented(by: attachedTo.indentationOfFirstLine).drop(while: { + $0.isWhitespace + }) return "\(raw: indentedSource)" } @@ -475,7 +483,9 @@ private func expandBodyMacro( // body yet. let leadingWhitespace = decl.body == nil ? " " : "" let indentedSource = - leadingWhitespace + expanded.indented(by: decl.indentationOfFirstLine).drop(while: { $0.isWhitespace }) + leadingWhitespace + expanded.indented(by: decl.indentationOfFirstLine).drop(while: { + $0.isWhitespace + }) return "\(raw: indentedSource)" } @@ -492,7 +502,8 @@ struct MacroSystem { var macros: [String: MacroSpec] = [:] /// Create an empty macro system. - init() {} + init() { + } /// Add a macro specification to the system. /// @@ -600,7 +611,9 @@ public class AttributeRemover: SyntaxRewriter { /// - Parameter node: The syntax node receiving the accumulated trivia. /// - Returns: The modified syntax node with the prepended trivia. private func prependAndClearAccumulatedTrivia(to syntaxNode: T) -> T { - defer { triviaToAttachToNextToken = Trivia() } + defer { + triviaToAttachToNextToken = Trivia() + } return syntaxNode.with(\.leadingTrivia, triviaToAttachToNextToken + syntaxNode.leadingTrivia) } } @@ -616,7 +629,7 @@ private extension Trivia { while predicate: (TriviaPiece) -> Bool ) -> Trivia { Trivia( - pieces: self[...] + pieces: self [...] .reversed() .drop(while: predicate) .reversed() @@ -639,7 +652,9 @@ private enum MacroApplicationError: DiagnosticMessage, Error { return MessageID(domain: diagnosticDomain, id: "\(self)") } - var severity: DiagnosticSeverity { return .error } + var severity: DiagnosticSeverity { + return .error + } var message: String { switch self { @@ -655,11 +670,16 @@ private enum MacroApplicationError: DiagnosticMessage, Error { } } +// MARK: - MacroApplication + /// Syntax rewriter that evaluates any macros encountered along the way. private class MacroApplication: SyntaxRewriter { let macroSystem: MacroSystem + var contextGenerator: (Syntax) -> Context + var indentationWidth: Trivia + /// Nodes that we are currently handling in `visitAny` and that should be /// visited using the node-specific handling function. var skipVisitAnyHandling: Set = [] @@ -726,7 +746,9 @@ private class MacroApplication: SyntaxRewriter { let attributesToRemove = self.macroAttributes(attachedTo: visitedNode).map(\.attributeNode) - return AttributeRemover(removingWhere: { attributesToRemove.contains($0) }).rewrite(visitedNode) + return AttributeRemover(removingWhere: { + attributesToRemove.contains($0) + }).rewrite(visitedNode) } return nil @@ -761,7 +783,9 @@ private class MacroApplication: SyntaxRewriter { attachedTo: node, in: contextGenerator(Syntax(node)), indentationWidth: indentationWidth - ).map { [$0] } + ).map { + [$0] + } } // Dig out the body. @@ -875,13 +899,16 @@ private class MacroApplication: SyntaxRewriter { // Expand member attribute members attached to the declaration context. // Note that MemberAttribute macros are _not_ applied to generated members if let parentDeclGroup, let decl = item.decl.asProtocol(WithAttributesSyntax.self) { - var newAttributes = AttributeListSyntax( - expandAttributesFromMemberAttributeMacros( - of: item.decl, - parentDecl: parentDeclGroup - ) - .map { visit($0) } + var attributes = expandAttributesFromMemberAttributeMacros( + of: item.decl, + parentDecl: parentDeclGroup ) + + for i in 0 ..< attributes.count { + attributes[i] = visit(attributes[i]) + } + + var newAttributes = AttributeListSyntax(attributes) if !newAttributes.isEmpty { // Transfer the trailing trivia from the old attributes to the new attributes. // This way, we essentially insert the new attributes right after the last attribute in source @@ -911,7 +938,8 @@ private class MacroApplication: SyntaxRewriter { return node.rightBrace.leadingTrivia } - if node.rightBrace.leadingTrivia.contains(where: { $0.isNewline }) { + if node.rightBrace.leadingTrivia.contains(where: { $0.isNewline + }) { return node.rightBrace.leadingTrivia } @@ -1167,7 +1195,9 @@ extension MacroApplication { var newAccessorsBlock = existingAccessors var expandsGetSet = false func checkExpansions(_ accessors: AccessorDeclListSyntax?) { - guard let accessors else { return } + guard let accessors else { + return + } expandsGetSet = expandsGetSet || accessors.contains(where: \.isGetOrSet) } @@ -1234,7 +1264,9 @@ extension MacroApplication { /// such that recursive macro expansion can be consistently detected. struct MacroExpansion { private let expandedNode: ResultType + private let macro: any Macro.Type + private unowned let macroApplication: MacroApplication fileprivate init(expandedNode: ResultType, macro: any Macro.Type, macroApplication: MacroApplication) { @@ -1260,11 +1292,9 @@ extension MacroApplication { enum MacroExpansionResult { /// Expansion of the macro succeeded. case success(expansion: MacroExpansion) - /// Macro system found the macro to expand but running the expansion threw /// an error and thus no expansion result exists. case failure - /// The node that should be expanded was not a macro known to the macro system. case notAMacro } @@ -1292,7 +1322,8 @@ extension MacroApplication { } do { - guard !expandingFreestandingMacros.contains(where: { $0 == macro }) else { + guard !expandingFreestandingMacros.contains(where: { $0 == macro + }) else { // We may think of any ongoing macro expansion as a tree in which macro types being expanded are nodes. // Any macro type being expanded more than once will create a cycle which the compiler as of now doesn't allow. throw MacroExpansionError.recursiveExpansion(macro) @@ -1443,7 +1474,8 @@ private extension SyntaxProtocol { if let basicContext = context as? BasicMacroExpansionContext { folded = basicContext.foldAllOperators(of: self, with: operatorTable) } else { - folded = operatorTable.foldAll(self, errorHandler: { _ in /*ignore*/ }) + folded = operatorTable.foldAll(self, errorHandler: { _ in /*ignore*/ + }) } } else { folded = Syntax(self) @@ -1503,3 +1535,1163 @@ private extension SyntaxProtocol { return base } } + +// MARK: - Public entry function + +extension SyntaxProtocol { + /// Expand all uses of the given set of macros within this syntax node. + @available(*, deprecated, message: "Use contextGenerator form to produce a specific context for each expansion node") + public func expand( + macros: [String: Macro.Type], + in context: some MacroExpansionContext, + indentationWidth: Trivia? = nil + ) async -> Syntax { + return await expand( + macros: macros, + contextGenerator: { _ in + context + }, + indentationWidth: indentationWidth + ) + } + + /// Expand all uses of the given set of macros within this syntax node. + /// - SeeAlso: ``expand(macroSpecs:contextGenerator:indentationWidth:)`` + /// to also specify the list of conformances passed to the macro expansion. + public func expand( + macros: [String: Macro.Type], + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? = nil + ) async -> Syntax { + return await expand( + macroSpecs: macros.mapValues { + MacroSpec(type: $0) + }, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + } + + /// Expand all uses of the given set of macros with specifications within this syntax node. + public func expand( + macroSpecs: [String: MacroSpec], + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? = nil + ) async -> Syntax { + // Build the macro system. + var system = MacroSystem() + for (macroName, macroSpec) in macroSpecs { + try! system.add(macroSpec, name: macroName) + } + + let applier = AsyncMacroApplication( + macroSystem: system, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + + return await applier.rewrite(self) + } +} + +// MARK: - Expand macros + +/// Expand the given freestanding macro and parse the resulting text into a +/// syntax tree. +private func expandFreestandingMemberDeclList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> MemberBlockItemListSyntax? { + guard + let expanded = try await expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\(raw: indentedSource)" +} + +private func expandFreestandingCodeItemList( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> CodeBlockItemListSyntax? { + guard + let expanded = try await expandFreestandingMacro( + definition: definition, + macroRole: inferFreestandingMacroRole(definition: definition), + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\(raw: indentedSource)" +} + +private func expandFreestandingExpr( + definition: Macro.Type, + node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> ExprSyntax? { + guard + let expanded = await expandFreestandingMacro( + definition: definition, + macroRole: .expression, + node: node.detach(in: context, foldingWith: .standardOperators), + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + let indentedSource = adjustIndentationOfFreestandingMacro(expandedCode: expanded, node: node) + + return "\(raw: indentedSource)" +} + +private func expandMemberMacro( + definition: MemberMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + conformanceList: InheritedTypeListSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> MemberBlockItemListSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .member, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate new members from exisiting members by two newlines + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\(raw: indentedSource)" +} + +private func expandMemberAttributeMacro( + definition: MemberAttributeMacro.Type, + attributeNode: AttributeSyntax, + attachedTo declaration: DeclSyntax, + providingAttributeFor member: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> AttributeListSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .memberAttribute, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: member.detach(in: context), + parentDeclNode: declaration.detach(in: context), + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // The added attributes should be on their own line, so prepend them with a newline. + let indentedSource = "\n" + expanded.indented(by: member.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +private func expandPeerMacroMember( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> MemberBlockItemListSyntax? { + if let variable = attachedTo.as(VariableDeclSyntax.self), variable.bindings.count > 1 { + throw MacroApplicationError.peerMacroOnVariableWithMultipleBindings + } + + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +private func expandPeerMacroCodeItem( + definition: PeerMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> CodeBlockItemListSyntax? { + if let variable = attachedTo.as(VariableDeclSyntax.self), variable.bindings.count > 1 { + throw MacroApplicationError.peerMacroOnVariableWithMultipleBindings + } + + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .peer, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the peers and the declaration by two newlines. + let indentedSource = "\n\n" + expanded.indented(by: attachedTo.indentationOfFirstLine) + return "\(raw: indentedSource)" +} + +/// Expand an accessor macro of a declaration that does not have existing +/// accessors. +/// +/// See comment in `expandAccessors` for an explanation why we need this. +private func expandAccessorMacroWithoutExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> AccessorBlockSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ), + !expanded.isEmpty + else { + return nil + } + + // `expandAttachedMacro` adds the `{` and `}` to wrap the accessor block and + // then indents it. + // Remove any indentation from the first line using `drop(while:)` and then + // prepend a space to separate it from the variable declaration + let indentedSource = " " + expanded.indented(by: attachedTo.indentationOfFirstLine).drop(while: { + $0.isWhitespace + }) + return "\(raw: indentedSource)" +} + +/// Expand an accessor macro of a declaration that already has an accessor. +/// +/// See comment in `expandAccessors` for an explanation why we need this. +private func expandAccessorMacroWithExistingAccessors( + definition: AccessorMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> AccessorDeclListSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .accessor, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the accessor from any existing accessors by an empty line + let indentedSource = "\n" + expanded.indented(by: attachedTo.indentationOfFirstLine + indentationWidth) + return "\(raw: indentedSource)" +} + +private func expandExtensionMacro( + definition: ExtensionMacro.Type, + attributeNode: AttributeSyntax, + attachedTo: DeclSyntax, + conformanceList: InheritedTypeListSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async throws -> CodeBlockItemListSyntax? { + guard attachedTo.isProtocol(DeclGroupSyntax.self) else { + return nil + } + + guard let extendedType = attachedTo.syntacticQualifiedTypeContext else { + return nil + } + + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .extension, + attributeNode: attributeNode.detach(in: context, foldingWith: .standardOperators), + declarationNode: attachedTo.detach(in: context), + parentDeclNode: nil, + extendedType: extendedType.detach(in: context), + conformanceList: conformanceList, + in: context, + indentationWidth: indentationWidth + ) + else { + return nil + } + + // Separate the extension from other declarations in the source file by two newlines. + let indentedSource = "\n\n" + expanded + return "\(raw: indentedSource)" +} + +/// Expand a preamble macro into a list of code items. +private func expandPreambleMacro( + definition: PreambleMacro.Type, + attributeNode: AttributeSyntax, + attachedTo decl: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async -> CodeBlockItemListSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .preamble, + attributeNode: attributeNode.detach( + in: context, + foldingWith: .standardOperators + ), + declarationNode: DeclSyntax(decl.detach(in: context)), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ) + else { + return [] + } + + // Match the indentation of the statements if we can, and put newlines around + // the preamble to separate it from the rest of the body. + let indentation = decl.body?.statements.indentationOfFirstLine ?? (decl.indentationOfFirstLine + indentationWidth) + let indentedSource = "\n" + expanded.indented(by: indentation) + "\n" + return "\(raw: indentedSource)" +} + +private func expandBodyMacro( + definition: BodyMacro.Type, + attributeNode: AttributeSyntax, + attachedTo decl: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, + in context: some MacroExpansionContext, + indentationWidth: Trivia +) async -> CodeBlockSyntax? { + guard + let expanded = await expandAttachedMacro( + definition: definition, + macroRole: .body, + attributeNode: attributeNode.detach( + in: context, + foldingWith: .standardOperators + ), + declarationNode: DeclSyntax(decl.detach(in: context)), + parentDeclNode: nil, + extendedType: nil, + conformanceList: nil, + in: context, + indentationWidth: indentationWidth + ), + !expanded.isEmpty + else { + return nil + } + + // `expandAttachedMacro` adds the `{` and `}` to wrap the body and then + // indents it. + // Remove any indentation from the first line using `drop(while:)` and then + // prepend a space when it's being introduced on a declaration that has no + // body yet. + let leadingWhitespace = decl.body == nil ? " " : "" + let indentedSource = + leadingWhitespace + expanded.indented(by: decl.indentationOfFirstLine).drop(while: { + $0.isWhitespace + }) + return "\(raw: indentedSource)" +} + +// MARK: - AsyncMacroApplication + +/// Syntax rewriter that evaluates any macros encountered along the way. +private class AsyncMacroApplication: AsyncSyntaxRewriter { + let macroSystem: MacroSystem + + var contextGenerator: (Syntax) -> Context + + var indentationWidth: Trivia + + /// Nodes that we are currently handling in `visitAny` and that should be + /// visited using the node-specific handling function. + var skipVisitAnyHandling: Set = [] + + /// Store expanded extension while visiting member decls. This should be + /// added to top-level 'CodeBlockItemList'. + var extensions: [CodeBlockItemSyntax] = [] + + /// Stores the types of the freestanding macros that are currently expanding. + /// + /// As macros are expanded by DFS, `expandingFreestandingMacros` always represent the expansion path starting from + /// the root macro node to the last macro node currently expanding. + var expandingFreestandingMacros: [any Macro.Type] = [] + + init( + macroSystem: MacroSystem, + contextGenerator: @escaping (Syntax) -> Context, + indentationWidth: Trivia? + ) { + self.macroSystem = macroSystem + self.contextGenerator = contextGenerator + + // Default to 4 spaces if no indentation was passed. + // In the future, we could consider inferring the indentation width from the + // source file in which we expand the macros. + self.indentationWidth = indentationWidth ?? .spaces(4) + super.init(viewMode: .sourceAccurate) + } + + override func visitAny(_ node: Syntax) async -> Syntax? { + guard !skipVisitAnyHandling.contains(node) else { + return nil + } + + // Expand 'MacroExpansionExpr'. + // Note that 'MacroExpansionExpr'/'MacroExpansionExprDecl' at code item + // position are handled by 'visit(_:CodeBlockItemListSyntax)'. + // Only expression expansions inside other syntax nodes is handled here. + switch await expandExpr(node: node) { + case .success(let expansion): + return await expansion.withExpandedNode { expandedNode in + Syntax(await visit(expandedNode)) + } + case .failure: + return Syntax(node) + case .notAMacro: + break + } + if var declSyntax = node.as(DeclSyntax.self), + let attributedNode = node.asProtocol(WithAttributesSyntax.self), + !attributedNode.attributes.isEmpty + { + // Apply body and preamble macros. + if let nodeWithBody = node.asProtocol(WithOptionalCodeBlockSyntax.self), + let declNodeWithBody = nodeWithBody as? any DeclSyntaxProtocol & WithOptionalCodeBlockSyntax + { + declSyntax = await DeclSyntax(visitBodyAndPreambleMacros(declNodeWithBody)) + } + + // Visit the node, disabling the `visitAny` handling. + skipVisitAnyHandling.insert(Syntax(declSyntax)) + let visitedNode = await self.visit(declSyntax) + skipVisitAnyHandling.remove(Syntax(declSyntax)) + + let attributesToRemove = self.macroAttributes(attachedTo: visitedNode).map(\.attributeNode) + + return AttributeRemover(removingWhere: { + attributesToRemove.contains($0) + }).rewrite(visitedNode) + } + + return nil + } + + /// Visit for both the body and preamble macros. + func visitBodyAndPreambleMacros( + _ node: Node + ) async -> Node { + // Expand preamble macros into a set of code items. + let preamble = await expandMacros( + attachedTo: DeclSyntax(node), + ofType: PreambleMacro.Type.self + ) { attributeNode, definition, _ in + await expandPreambleMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + + // Expand body macro. + let expandedBodies = await expandMacros( + attachedTo: DeclSyntax(node), + ofType: BodyMacro.Type.self + ) { attributeNode, definition, _ in + await expandBodyMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ).map { + [$0] + } + } + + // Dig out the body. + let body: CodeBlockSyntax + switch expandedBodies.count { + case 0 where preamble.isEmpty: + // Nothing changes + return node + + case 0: + guard let existingBody = node.body else { + // Any leftover preamble statements have nowhere to go, complain and + // exit. + contextGenerator(Syntax(node)).addDiagnostics(from: MacroExpansionError.preambleWithoutBody, node: node) + + return node + } + + body = existingBody + + case 1: + body = expandedBodies[0] + + default: + contextGenerator(Syntax(node)).addDiagnostics(from: MacroExpansionError.moreThanOneBodyMacro, node: node) + body = expandedBodies[0] + } + + // If there's no preamble, swap in the new body. + if preamble.isEmpty { + return node.with(\.body, body) + } + + return node.with(\.body, body.with(\.statements, preamble + body.statements)) + } + + override func visit(_ node: CodeBlockItemListSyntax) async -> CodeBlockItemListSyntax { + var newItems: [CodeBlockItemSyntax] = [] + func addResult(_ node: CodeBlockItemSyntax) async { + // Expand freestanding macro. + switch await expandCodeBlockItem(node: node) { + case .success(let expansion): + await expansion.withExpandedNode { expandedNode in + for item in expandedNode { + await addResult(item) + } + } + return + case .failure: + // Expanding the macro threw an error. We don't have an expanded source. + // Retain the macro node as-is. + newItems.append(node) + case .notAMacro: + // Recurse on the child node + newItems.append(await visit(node)) + } + + // Expand any peer macro on this item. + if case .decl(let decl) = node.item { + for peer in await expandCodeBlockPeers(of: decl) { + await addResult(peer) + } + extensions += await expandExtensions(of: decl) + } + } + + for item in node { + await addResult(item) + } + + // If this is the top-level code block item list, add the expanded extensions + // at its end. + if node.parent?.is(SourceFileSyntax.self) ?? false { + newItems += extensions + } + + return CodeBlockItemListSyntax(newItems) + } + + override func visit(_ node: MemberBlockSyntax) async -> MemberBlockSyntax { + let parentDeclGroup = node + .parent? + .as(DeclSyntax.self) + var newItems: [MemberBlockItemSyntax] = [] + + func addResult(_ node: MemberBlockItemSyntax) async { + // Expand freestanding macro. + switch await expandMemberDecl(node: node) { + case .success(let expansion): + await expansion.withExpandedNode { expandedNode in + for item in expandedNode { + await addResult(item) + } + } + return + case .failure: + newItems.append(node) + case .notAMacro: + // Recurse on the child node. + newItems.append(await visit(node)) + } + + // Expand any peer macro on this member. + for peer in await expandMemberDeclPeers(of: node.decl) { + await addResult(peer) + } + extensions += await expandExtensions(of: node.decl) + } + + for var item in node.members { + // Expand member attribute members attached to the declaration context. + // Note that MemberAttribute macros are _not_ applied to generated members + if let parentDeclGroup, let decl = item.decl.asProtocol(WithAttributesSyntax.self) { + var attributes = await expandAttributesFromMemberAttributeMacros( + of: item.decl, + parentDecl: parentDeclGroup + ) + + for i in 0 ..< attributes.count { + attributes[i] = await visit(attributes[i]) + } + + var newAttributes = AttributeListSyntax(attributes) + if !newAttributes.isEmpty { + // Transfer the trailing trivia from the old attributes to the new attributes. + // This way, we essentially insert the new attributes right after the last attribute in source + // but before its trailing trivia, keeping the trivia that separates the attribute block + // from the variable itself. + newAttributes.trailingTrivia = newAttributes.trailingTrivia + decl.attributes.trailingTrivia + newAttributes.insert(contentsOf: decl.attributes.with(\.trailingTrivia, []), at: newAttributes.startIndex) + item.decl = decl.with(\.attributes, newAttributes).cast(DeclSyntax.self) + } + } + + // Recurse on the child node. + await addResult(item) + } + + // Expand any member macros of parent. + if let parentDeclGroup { + for member in await expandMembers(of: parentDeclGroup) { + await addResult(member) + } + } + + /// Returns an leading trivia for the member blocks closing brace. + /// It will add a leading newline, if there is none. + var leadingTriviaForClosingBrace: Trivia { + if newItems.isEmpty { + return node.rightBrace.leadingTrivia + } + + if node.rightBrace.leadingTrivia.contains(where: { $0.isNewline + }) { + return node.rightBrace.leadingTrivia + } + + if newItems.last?.trailingTrivia.pieces.last?.isNewline ?? false { + return node.rightBrace.leadingTrivia + } else { + return .newline + node.rightBrace.leadingTrivia + } + } + + return MemberBlockSyntax( + leftBrace: node.leftBrace, + members: MemberBlockItemListSyntax(newItems), + rightBrace: node.rightBrace.with(\.leadingTrivia, leadingTriviaForClosingBrace) + ) + } + + override func visit(_ node: VariableDeclSyntax) async -> DeclSyntax { + var node = await super.visit(node).cast(VariableDeclSyntax.self) + + guard !macroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self).isEmpty else { + return DeclSyntax(node) + } + + guard node.bindings.count == 1, + var binding = node.bindings.first + else { + contextGenerator(Syntax(node)).addDiagnostics( + from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings, + node: node + ) + return DeclSyntax(node) + } + + var expansion = await expandAccessors(of: node, existingAccessors: binding.accessorBlock) + + if expansion.accessors != binding.accessorBlock { + if binding.accessorBlock == nil { + // remove the trailing trivia of the variable declaration and move it + // to the trailing trivia of the left brace of the newly created accessor block + expansion.accessors?.leftBrace.trailingTrivia = binding.trailingTrivia + binding.trailingTrivia = [] + } + + if binding.initializer != nil, expansion.expandsGetSet { + // The accessor block will have a leading space, but there will already be a + // space between the variable and the to-be-removed initializer. Remove the + // leading trivia on the accessor block so we don't double up. + binding.accessorBlock = expansion.accessors?.with(\.leadingTrivia, []) + binding.initializer = nil + } else { + binding.accessorBlock = expansion.accessors + } + + node.bindings = [binding] + } + + return DeclSyntax(node) + } + + override func visit(_ node: SubscriptDeclSyntax) async -> DeclSyntax { + var node = await super.visit(node).cast(SubscriptDeclSyntax.self) + node.accessorBlock = await expandAccessors(of: node, existingAccessors: node.accessorBlock).accessors + return DeclSyntax(node) + } +} + +// MARK: Attached macro expansions. + +extension AsyncMacroApplication { + /// Get pairs of a macro attribute and the macro specification attached to `decl`. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax + ) -> [(attributeNode: AttributeSyntax, spec: MacroSpec)] { + guard let attributedNode = decl.asProtocol(WithAttributesSyntax.self) else { + return [] + } + + return attributedNode.attributes.compactMap { + guard case let .attribute(attribute) = $0, + let attributeName = attribute.attributeName.as(IdentifierTypeSyntax.self)?.name.text, + let macroSpec = macroSystem.lookup(attributeName) + else { + return nil + } + + return (attribute, macroSpec) + } + } + + /// Get a list of the macro attribute, the macro definition and the conformance + /// protocols list attached to `decl` matching `ofType` macro type. + /// + /// The macros must be registered in `macroSystem`. + private func macroAttributes( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type + ) -> [(attributeNode: AttributeSyntax, definition: MacroType, conformanceList: InheritedTypeListSyntax)] { + return macroAttributes(attachedTo: decl) + .compactMap { (attributeNode: AttributeSyntax, spec: MacroSpec) in + if let macroType = spec.type as? MacroType { + return (attributeNode, macroType, spec.inheritedTypeList) + } else { + return nil + } + } + } + + /// Call `expandMacro` for every macro of type `ofType` attached to `decl` and + /// return the list of all expanded nodes. + private func expandMacros< + ExpandedNode: SyntaxProtocol, + ExpandedNodeCollection: Sequence, + MacroType + >( + attachedTo decl: DeclSyntax, + ofType: MacroType.Type, + expandMacro: + ( + _ attributeNode: AttributeSyntax, + _ definition: MacroType, + _ conformanceList: InheritedTypeListSyntax + ) async throws -> ExpandedNodeCollection? + ) async -> [ExpandedNode] { + var result: [ExpandedNode] = [] + + for macroAttribute in macroAttributes(attachedTo: decl, ofType: ofType) { + do { + if let expanded = try await expandMacro( + macroAttribute.attributeNode, + macroAttribute.definition, + macroAttribute.conformanceList + ) { + result += expanded + } + } catch { + contextGenerator(Syntax(decl)).addDiagnostics(from: error, node: macroAttribute.attributeNode) + } + } + return result + } + + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandCodeBlockPeers` returns them as `CodeBlockItemSyntax`. The + /// overload is chosen based on the context in which the peers are expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandMemberDeclPeers(of decl: DeclSyntax) async -> [MemberBlockItemSyntax] { + return await expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition, conformanceList in + return try await expandPeerMacroMember( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand all the 'peer' macros attached to `decl`. + /// + /// - Note: This overload returns the list of peers as `MemberDeclListItemSyntax` + /// while `expandMemberDeclPeers` returns them as `MemberDeclListItemSyntax`. + /// The overload is chosen based on the context in which the peers are + /// expanded. + /// + /// - Returns: The macro-synthesized peers + private func expandCodeBlockPeers(of decl: DeclSyntax) async -> [CodeBlockItemSyntax] { + return await expandMacros(attachedTo: decl, ofType: PeerMacro.Type.self) { attributeNode, definition, conformanceList in + return try await expandPeerMacroCodeItem( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand all 'extension' macros attached to `decl`. + /// + /// - Returns: The macro-synthesized extensions + private func expandExtensions(of decl: DeclSyntax) async -> [CodeBlockItemSyntax] { + return await expandMacros( + attachedTo: decl, + ofType: ExtensionMacro.Type.self + ) { attributeNode, definition, conformanceList in + return try await expandExtensionMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + conformanceList: conformanceList, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand all 'member' macros attached to `decl`. + private func expandMembers(of decl: DeclSyntax) async -> [MemberBlockItemSyntax] { + return await expandMacros(attachedTo: decl, ofType: MemberMacro.Type.self) { attributeNode, definition, conformanceList in + return try await expandMemberMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: decl, + conformanceList: conformanceList, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + + /// Return the attributes on `decl` that are synthesized from member attribute + /// macros on `parentDecl`. + /// + /// - Note: This only returns synthesized attributes from member attribute + /// macros, not the attributes that are attached to `decl` in the source code. + private func expandAttributesFromMemberAttributeMacros( + of decl: DeclSyntax, + parentDecl: DeclSyntax + ) async -> [AttributeListSyntax.Element] { + return await expandMacros( + attachedTo: parentDecl, + ofType: MemberAttributeMacro.Type.self + ) { attributeNode, definition, conformanceList in + return try await expandMemberAttributeMacro( + definition: definition, + attributeNode: attributeNode, + attachedTo: parentDecl, + providingAttributeFor: decl, + in: contextGenerator(Syntax(decl)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand all 'accessor' macros attached to `storage`. + /// + /// - Returns: The final accessors block that includes both the existing + /// and expanded accessors, as well as whether any `get`/`set` were + /// expanded (in which case any initializer on `storage` should be + /// removed). + private func expandAccessors( + of storage: some DeclSyntaxProtocol, + existingAccessors: AccessorBlockSyntax? + ) async -> (accessors: AccessorBlockSyntax?, expandsGetSet: Bool) { + let accessorMacros = macroAttributes(attachedTo: DeclSyntax(storage), ofType: AccessorMacro.Type.self) + + var newAccessorsBlock = existingAccessors + var expandsGetSet = false + func checkExpansions(_ accessors: AccessorDeclListSyntax?) { + guard let accessors else { + return + } + expandsGetSet = expandsGetSet || accessors.contains(where: \.isGetOrSet) + } + + for macro in accessorMacros { + do { + // Accessor macros get expanded differently depending on whether the + // variable already had an accessor. If not, '{' and '}' to wrap the + // accessor block are added. If an accessor block already exists, only + // the new accessors are returned. + // We need to parse the result from the macro invocation differently + // based on these cases. + if existingAccessors != nil { + if let newAccessors = try await expandAccessorMacroWithExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: contextGenerator(Syntax(storage)), + indentationWidth: indentationWidth + ) { + checkExpansions(newAccessors) + + // If existingAccessors is not `nil`, then we also set + // `newAccessorBlock` above to a a non-nil value, so + // `newAccessorsBlock` also isn’t `nil`. + newAccessorsBlock = newAccessorsBlock!.addingAccessors( + from: newAccessors, + indentationWidth: self.indentationWidth + ) + } + } else if let newAccessors = try await expandAccessorMacroWithoutExistingAccessors( + definition: macro.definition, + attributeNode: macro.attributeNode, + attachedTo: DeclSyntax(storage), + in: contextGenerator(Syntax(storage)), + indentationWidth: indentationWidth + ) { + guard case .accessors(let accessorList) = newAccessors.accessors else { + throw MacroApplicationError.malformedAccessor + } + + checkExpansions(accessorList) + + if let oldBlock = newAccessorsBlock { + newAccessorsBlock = oldBlock.addingAccessors( + from: accessorList, + indentationWidth: self.indentationWidth + ) + } else { + newAccessorsBlock = newAccessors + } + } + } catch { + contextGenerator(Syntax(storage)).addDiagnostics(from: error, node: macro.attributeNode) + } + } + return (newAccessorsBlock, expandsGetSet) + } +} + +// MARK: Freestanding macro expansion + +extension AsyncMacroApplication { + /// Encapsulates an expanded node, the type of the macro from which the node was expanded, and the macro application, + /// such that recursive macro expansion can be consistently detected. + struct MacroExpansion { + private let expandedNode: ResultType + + private let macro: any Macro.Type + + private unowned let macroApplication: AsyncMacroApplication + + fileprivate init(expandedNode: ResultType, macro: any Macro.Type, macroApplication: AsyncMacroApplication) { + self.expandedNode = expandedNode + self.macro = macro + self.macroApplication = macroApplication + } + + /// Invokes the given closure with the node resulting from a macro expansion. + /// + /// This method inserts a pair of push and pop operations immediately around the invocation of `body` to maintain + /// an exact stack of expanding freestanding macros to detect recursive macro expansion. Callers should perform any + /// further macro expansion on `expanded` only within the scope of `body`. + func withExpandedNode(_ body: (_ expandedNode: ResultType) async throws -> T) async rethrows -> T { + macroApplication.expandingFreestandingMacros.append(macro) + defer { + macroApplication.expandingFreestandingMacros.removeLast() + } + return try await body(expandedNode) + } + } + + enum MacroExpansionResult { + /// Expansion of the macro succeeded. + case success(expansion: MacroExpansion) + /// Macro system found the macro to expand but running the expansion threw + /// an error and thus no expansion result exists. + case failure + /// The node that should be expanded was not a macro known to the macro system. + case notAMacro + } + + /// Expands the given freestanding macro node into a syntax node by invoking the given closure. + /// + /// Any error thrown by `expandMacro` and circular expansion error will be added to diagnostics. + /// + /// - Parameters: + /// - node: The freestanding macro node to be expanded. + /// - expandMacro: The closure that expands the given macro type and macro node into a syntax node. + /// + /// - Returns: + /// Returns `.notAMacro` if `node` is `nil` or `node.macroName` isn't registered with any macro type. + /// Returns `.failure` if `expandMacro` throws an error or returns `nil`, or recursive expansion is detected. + /// Returns `.success` otherwise. + private func expandFreestandingMacro( + _ node: (any FreestandingMacroExpansionSyntax)?, + expandMacro: (_ macro: any Macro.Type, _ node: any FreestandingMacroExpansionSyntax) async throws -> ExpandedMacroType? + ) async -> MacroExpansionResult { + guard let node, + let macro = macroSystem.lookup(node.macroName.text)?.type + else { + return .notAMacro + } + + do { + guard !expandingFreestandingMacros.contains(where: { $0 == macro + }) else { + // We may think of any ongoing macro expansion as a tree in which macro types being expanded are nodes. + // Any macro type being expanded more than once will create a cycle which the compiler as of now doesn't allow. + throw MacroExpansionError.recursiveExpansion(macro) + } + + if let expanded = try await expandMacro(macro, node) { + return .success(expansion: MacroExpansion(expandedNode: expanded, macro: macro, macroApplication: self)) + } else { + return .failure + } + } catch { + contextGenerator(Syntax(node)).addDiagnostics(from: error, node: node) + return .failure + } + } + + /// Expand a freestanding macro expansion syntax in a code block item position. + /// + /// For example + /// ```swift + /// function test() { + /// #foo + /// } + /// ``` + func expandCodeBlockItem(node: CodeBlockItemSyntax) async -> MacroExpansionResult { + return await expandFreestandingMacro(node.item.asProtocol(FreestandingMacroExpansionSyntax.self)) { macro, node in + return try await expandFreestandingCodeItemList( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand a freestanding macro expansion syntax in a member decl position. + /// + /// For exmple + /// ```swift + /// struct S { + /// #foo + /// } + /// ``` + func expandMemberDecl(node: MemberBlockItemSyntax) async -> MacroExpansionResult { + return await expandFreestandingMacro(node.decl.as(MacroExpansionDeclSyntax.self)) { macro, node in + return try await expandFreestandingMemberDeclList( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } + + /// Expand a freestanding macro expansion in a expression position inside + /// other products. + /// + /// For example + /// ```swift + /// let a = #foo + /// ``` + func expandExpr(node: Syntax) async -> MacroExpansionResult { + return await expandFreestandingMacro(node.as(MacroExpansionExprSyntax.self)) { macro, node in + return try await expandFreestandingExpr( + definition: macro, + node: node, + in: contextGenerator(Syntax(node)), + indentationWidth: indentationWidth + ) + } + } +} diff --git a/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift b/Sources/SwiftSyntaxMacrosGenericTestSupport/generated/Assertions.swift similarity index 76% rename from Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift rename to Sources/SwiftSyntaxMacrosGenericTestSupport/generated/Assertions.swift index e5cbe846cc2..bc764839efb 100644 --- a/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift +++ b/Sources/SwiftSyntaxMacrosGenericTestSupport/generated/Assertions.swift @@ -1,3 +1,5 @@ +//// Automatically generated by generate-swift-syntax +//// Do not edit directly! //===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project @@ -36,16 +38,28 @@ import _SwiftSyntaxGenericTestSupport /// assertion function is called. public struct TestFailureLocation { @_spi(XCTestFailureLocation) public let staticFileID: StaticString - public var fileID: String { staticFileID.description } + + public var fileID: String { + staticFileID.description + } @_spi(XCTestFailureLocation) public let staticFilePath: StaticString - public var filePath: String { staticFilePath.description } + + public var filePath: String { + staticFilePath.description + } @_spi(XCTestFailureLocation) public let unsignedLine: UInt - public var line: Int { Int(unsignedLine) } + + public var line: Int { + Int(unsignedLine) + } @_spi(XCTestFailureLocation) public let unsignedColumn: UInt - public var column: Int { Int(unsignedColumn) } + + public var column: Int { + Int(unsignedColumn) + } public init( fileID: StaticString, @@ -84,6 +98,7 @@ public struct TestFailureLocation { /// shown. public struct TestFailureSpec { public let message: String + public let location: TestFailureLocation public init(message: String, location: TestFailureLocation) { @@ -155,7 +170,9 @@ func assertNote( spec.message, "message of note does not match", location: spec.failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) let location = expansionContext.location(for: note.position, anchoredAt: note.node, fileName: "") if location.line != spec.line { @@ -222,7 +239,9 @@ func assertFixIt( spec.message, "message of Fix-It does not match", location: spec.failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) } @@ -331,7 +350,9 @@ extension DiagnosticSpec { line: line, column: column, severity: severity, - highlights: highlight.map { [$0] }, + highlights: highlight.map { + [$0] + }, notes: notes, fixIts: fixIts ) @@ -385,7 +406,9 @@ public func assertDiagnostic( spec.message, "message does not match", location: spec.failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) let location = expansionContext.location(for: diag.position, anchoredAt: diag.node, fileName: "") if location.line != spec.line { @@ -432,7 +455,9 @@ public func assertDiagnostic( expected, "highlight does not match", location: spec.failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) } } @@ -449,7 +474,12 @@ public func assertDiagnostic( ) } else { for (note, expectedNote) in zip(diag.notes, spec.notes) { - assertNote(note, in: expansionContext, expected: expectedNote, failureHandler: failureHandler) + assertNote( + note, + in: expansionContext, + expected: expectedNote, + failureHandler: failureHandler + ) } } if diag.fixIts.count != spec.fixIts.count { @@ -503,7 +533,12 @@ public func assertMacroExpansion( line: UInt = #line, column: UInt = #column ) { - let failureLocation = TestFailureLocation(fileID: fileID, filePath: filePath, line: line, column: column) + let failureLocation = TestFailureLocation( + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) // Parse the original source file. let origSourceFile = Parser.parse(source: originalSource) @@ -546,7 +581,9 @@ public func assertMacroExpansion( \(expandedSourceFile) """, location: failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) if context.diagnostics.count != diagnostics.count { @@ -572,14 +609,22 @@ public func assertMacroExpansion( // Applying Fix-Its if let expectedFixedSource = expectedFixedSource { - let messages = applyFixIts ?? context.diagnostics.compactMap { $0.fixIts.first?.message.message } + let messages = applyFixIts ?? context.diagnostics.compactMap { + $0.fixIts.first?.message.message + } let edits = context.diagnostics .flatMap(\.fixIts) - .filter { messages.contains($0.message.message) } - .flatMap { $0.changes } - .map { $0.edit(in: context) } + .filter { + messages.contains($0.message.message) + } + .flatMap { + $0.changes + } + .map { + $0.edit(in: context) + } let fixedTree = FixItApplier.apply(edits: edits, to: origSourceFile) let fixedTreeDescription = fixedTree.description @@ -587,7 +632,9 @@ public func assertMacroExpansion( fixedTreeDescription.trimmingTrailingWhitespace(), expectedFixedSource.trimmingTrailingWhitespace(), location: failureLocation.underlying, - failureHandler: { failureHandler(TestFailureSpec(underlying: $0)) } + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } ) } } @@ -604,7 +651,7 @@ fileprivate extension FixIt.Change { let start = expansionContext.position(of: oldNode.position, anchoredAt: oldNode) let end = expansionContext.position(of: oldNode.endPosition, anchoredAt: oldNode) return SourceEdit( - range: start.. Void, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column +) async { + let failureLocation = TestFailureLocation( + fileID: fileID, + filePath: filePath, + line: line, + column: column + ) + // Parse the original source file. + let origSourceFile = Parser.parse(source: originalSource) + + // Expand all macros in the source. + let context = BasicMacroExpansionContext( + sourceFiles: [origSourceFile: .init(moduleName: testModuleName, fullFilePath: testFileName)] + ) + + func contextGenerator(_ syntax: Syntax) -> BasicMacroExpansionContext { + return BasicMacroExpansionContext(sharingWith: context, lexicalContext: syntax.allMacroLexicalContexts()) + } + + let expandedSourceFile = await origSourceFile.expand( + macroSpecs: macroSpecs, + contextGenerator: contextGenerator, + indentationWidth: indentationWidth + ) + let diags = ParseDiagnosticsGenerator.diagnostics(for: expandedSourceFile) + if !diags.isEmpty { + failureHandler( + TestFailureSpec( + message: """ + Expanded source should not contain any syntax errors, but contains: + \(DiagnosticsFormatter.annotatedSource(tree: expandedSourceFile, diags: diags)) + + Expanded syntax tree was: + \(expandedSourceFile.debugDescription) + """, + location: failureLocation + ) + ) + } + + assertStringsEqualWithDiff( + expandedSourceFile.description.drop(while: \.isNewline).droppingLast(while: \.isNewline), + expectedExpandedSource.drop(while: \.isNewline).droppingLast(while: \.isNewline), + "Macro expansion did not produce the expected expanded source", + additionalInfo: """ + Actual expanded source: + \(expandedSourceFile) + """, + location: failureLocation.underlying, + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } + ) + + if context.diagnostics.count != diagnostics.count { + failureHandler( + TestFailureSpec( + message: """ + Expected \(diagnostics.count) diagnostics but received \(context.diagnostics.count): + \(context.diagnostics.map(\.debugDescription).joined(separator: "\n")) + """, + location: failureLocation + ) + ) + } else { + for (actualDiag, expectedDiag) in zip(context.diagnostics, diagnostics) { + assertDiagnostic( + actualDiag, + in: .macroExpansion(context), + expected: expectedDiag, + failureHandler: failureHandler + ) + } + } + + // Applying Fix-Its + if let expectedFixedSource = expectedFixedSource { + let messages = applyFixIts ?? context.diagnostics.compactMap { + $0.fixIts.first?.message.message + } + + let edits = + context.diagnostics + .flatMap(\.fixIts) + .filter { + messages.contains($0.message.message) + } + .flatMap { + $0.changes + } + .map { + $0.edit(in: context) + } + + let fixedTree = FixItApplier.apply(edits: edits, to: origSourceFile) + let fixedTreeDescription = fixedTree.description + assertStringsEqualWithDiff( + fixedTreeDescription.trimmingTrailingWhitespace(), + expectedFixedSource.trimmingTrailingWhitespace(), + location: failureLocation.underlying, + failureHandler: { + failureHandler(TestFailureSpec(underlying: $0)) + } + ) + } +} diff --git a/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift b/Sources/SwiftSyntaxMacrosTestSupport/generated/Assertions.swift similarity index 54% rename from Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift rename to Sources/SwiftSyntaxMacrosTestSupport/generated/Assertions.swift index 50296b267ab..4ca6a209b56 100644 --- a/Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift +++ b/Sources/SwiftSyntaxMacrosTestSupport/generated/Assertions.swift @@ -1,3 +1,5 @@ +//// Automatically generated by generate-swift-syntax +//// Do not edit directly! //===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project @@ -26,7 +28,9 @@ import XCTest // Re-export the spec types from `SwiftSyntaxMacrosGenericTestSupport`. public typealias NoteSpec = SwiftSyntaxMacrosGenericTestSupport.NoteSpec + public typealias FixItSpec = SwiftSyntaxMacrosGenericTestSupport.FixItSpec + public typealias DiagnosticSpec = SwiftSyntaxMacrosGenericTestSupport.DiagnosticSpec /// Assert that expanding the given macros in the original source produces @@ -60,7 +64,9 @@ public func assertMacroExpansion( file: StaticString = #filePath, line: UInt = #line ) { - let specs = macros.mapValues { MacroSpec(type: $0) } + let specs = macros.mapValues { + MacroSpec(type: $0) + } assertMacroExpansion( originalSource, expandedSource: expectedExpandedSource, @@ -126,3 +132,103 @@ public func assertMacroExpansion( column: 0 // Not used in the failure handler ) } + +/// Assert that expanding the given macros in the original source produces +/// the given expanded source code. +/// +/// - Parameters: +/// - originalSource: The original source code, which is expected to contain +/// macros in various places (e.g., `#stringify(x + y)`). +/// - expectedExpandedSource: The source code that we expect to see after +/// performing macro expansion on the original source. +/// - diagnostics: The diagnostics when expanding any macro +/// - macros: The macros that should be expanded, provided as a dictionary +/// mapping macro names (e.g., `"stringify"`) to implementation types +/// (e.g., `StringifyMacro.self`). +/// - testModuleName: The name of the test module to use. +/// - testFileName: The name of the test file name to use. +/// - indentationWidth: The indentation width used in the expansion. +/// +/// - SeeAlso: ``assertMacroExpansion(_:expandedSource:diagnostics:macroSpecs:applyFixIts:fixedSource:testModuleName:testFileName:indentationWidth:file:line:)`` +/// to also specify the list of conformances passed to the macro expansion. +public func assertMacroExpansion( + _ originalSource: String, + expandedSource expectedExpandedSource: String, + diagnostics: [DiagnosticSpec] = [], + macros: [String: Macro.Type], + applyFixIts: [String]? = nil, + fixedSource expectedFixedSource: String? = nil, + testModuleName: String = "TestModule", + testFileName: String = "test.swift", + indentationWidth: Trivia = .spaces(4), + file: StaticString = #filePath, + line: UInt = #line +) async { + let specs = macros.mapValues { + MacroSpec(type: $0) + } + await assertMacroExpansion( + originalSource, + expandedSource: expectedExpandedSource, + diagnostics: diagnostics, + macroSpecs: specs, + applyFixIts: applyFixIts, + fixedSource: expectedFixedSource, + testModuleName: testModuleName, + testFileName: testFileName, + indentationWidth: indentationWidth, + file: file, + line: line + ) +} + +/// Assert that expanding the given macros in the original source produces +/// the given expanded source code. +/// +/// - Parameters: +/// - originalSource: The original source code, which is expected to contain +/// macros in various places (e.g., `#stringify(x + y)`). +/// - expectedExpandedSource: The source code that we expect to see after +/// performing macro expansion on the original source. +/// - diagnostics: The diagnostics when expanding any macro +/// - macroSpecs: The macros that should be expanded, provided as a dictionary +/// mapping macro names (e.g., `"CodableMacro"`) to specification with macro type +/// (e.g., `CodableMacro.self`) and a list of conformances macro provides +/// (e.g., `["Decodable", "Encodable"]`). +/// - applyFixIts: If specified, filters the Fix-Its that are applied to generate `fixedSource` to only those whose message occurs in this array. If `nil`, all Fix-Its from the diagnostics are applied. +/// - fixedSource: If specified, asserts that the source code after applying Fix-Its matches this string. +/// - testModuleName: The name of the test module to use. +/// - testFileName: The name of the test file name to use. +/// - indentationWidth: The indentation width used in the expansion. +public func assertMacroExpansion( + _ originalSource: String, + expandedSource expectedExpandedSource: String, + diagnostics: [DiagnosticSpec] = [], + macroSpecs: [String: MacroSpec], + applyFixIts: [String]? = nil, + fixedSource expectedFixedSource: String? = nil, + testModuleName: String = "TestModule", + testFileName: String = "test.swift", + indentationWidth: Trivia = .spaces(4), + file: StaticString = #filePath, + line: UInt = #line +) async { + await SwiftSyntaxMacrosGenericTestSupport.assertMacroExpansion( + originalSource, + expandedSource: expectedExpandedSource, + diagnostics: diagnostics, + macroSpecs: macroSpecs, + applyFixIts: applyFixIts, + fixedSource: expectedFixedSource, + testModuleName: testModuleName, + testFileName: testFileName, + indentationWidth: indentationWidth, + failureHandler: { + XCTFail($0.message, file: $0.location.staticFilePath, line: $0.location.unsignedLine) + }, + fileID: "", // Not used in the failure handler + filePath: file, + line: line, + column: 0 // Not used in the failure handler + ) +}