@@ -72,8 +72,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
72
72
"""
73
73
/// Rewrite `node`, keeping its parent unless `detach` is `true`.
74
74
public func rewrite(_ node: some SyntaxProtocol, detach: Bool = false) -> Syntax {
75
- var rewritten = Syntax(node)
76
- self.dispatchVisit(&rewritten)
75
+ let rewritten = self.visitImpl(Syntax(node))
77
76
if detach {
78
77
return rewritten
79
78
}
@@ -87,11 +86,20 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
87
86
88
87
DeclSyntax (
89
88
"""
90
- /// Visit a ``TokenSyntax`` .
91
- /// - Parameter token : the token that is being visited
89
+ /// Visit any Syntax node .
90
+ /// - Parameter node : the node that is being visited
92
91
/// - Returns: the rewritten node
93
- open func visit(_ token: TokenSyntax) -> TokenSyntax {
94
- return token
92
+ @available(*, deprecated, renamed: " rewrite(_:detach:) " )
93
+ public func visit(_ node: Syntax) -> Syntax {
94
+ return visitImpl(node)
95
+ }
96
+ """
97
+ )
98
+
99
+ DeclSyntax (
100
+ """
101
+ public func visit<T: SyntaxChildChoices>(_ node: T) -> T {
102
+ visitImpl(Syntax(node)).cast(T.self)
95
103
}
96
104
"""
97
105
)
@@ -133,24 +141,11 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
133
141
134
142
DeclSyntax (
135
143
"""
136
- /// Visit any Syntax node .
137
- /// - Parameter node : the node that is being visited
144
+ /// Visit a ``TokenSyntax`` .
145
+ /// - Parameter token : the token that is being visited
138
146
/// - Returns: the rewritten node
139
- @available(*, deprecated, renamed: " rewrite(_:detach:) " )
140
- public func visit(_ node: Syntax) -> Syntax {
141
- var rewritten = node
142
- dispatchVisit(&rewritten)
143
- return rewritten
144
- }
145
- """
146
- )
147
-
148
- DeclSyntax (
149
- """
150
- public func visit<T: SyntaxChildChoices>(_ node: T) -> T {
151
- var rewritten = Syntax(node)
152
- dispatchVisit(&rewritten)
153
- return rewritten.cast(T.self)
147
+ open func visit(_ token: TokenSyntax) -> TokenSyntax {
148
+ return token
154
149
}
155
150
"""
156
151
)
@@ -164,7 +159,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
164
159
/// - Returns: the rewritten node
165
160
\( node. apiAttributes ( ) ) \
166
161
open func visit(_ node: \( node. kind. syntaxType) ) -> \( node. kind. syntaxType) {
167
- return visitChildren(node._syntaxNode).cast( \( node. kind. syntaxType) .self )
162
+ return \( node. kind. syntaxType) (unsafeCasting: visitChildren(node._syntaxNode) )
168
163
}
169
164
"""
170
165
)
@@ -176,7 +171,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
176
171
/// - Returns: the rewritten node
177
172
\( node. apiAttributes ( ) ) \
178
173
open func visit(_ node: \( node. kind. syntaxType) ) -> \( node. baseType. syntaxBaseName) {
179
- return \( node. baseType. syntaxBaseName) (visitChildren(node._syntaxNode).cast( \( node. kind. syntaxType) .self ))
174
+ return \( node. baseType. syntaxBaseName) ( \( node. kind. syntaxType) (unsafeCasting: visitChildren(node._syntaxNode) ))
180
175
}
181
176
"""
182
177
)
@@ -193,32 +188,35 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
193
188
/// - Returns: the rewritten node
194
189
\( baseNode. apiAttributes ( ) ) \
195
190
public func visit(_ node: \( baseKind. syntaxType) ) -> \( baseKind. syntaxType) {
196
- var node: Syntax = Syntax(node)
197
- dispatchVisit(&node)
198
- return node.cast( \( baseKind. syntaxType) .self)
191
+ visitImpl(Syntax(node)).cast( \( baseKind. syntaxType) .self)
199
192
}
200
193
"""
201
194
)
202
195
}
203
196
197
+ // NOTE: '@inline(never)' because perf tests showed the best results.
198
+ // It keeps 'dispatchVisit(_:)' function small, and make all 'case' bodies exactly the same pattern.
199
+ // Which enables some optimizations.
204
200
DeclSyntax (
205
201
"""
206
- /// Interpret `node` as a node of type `nodeType`, visit it, calling
207
- /// the `visit` to transform the node.
208
- @inline(__always)
209
- private func visitImpl<NodeType: SyntaxProtocol>(
210
- _ node: inout Syntax,
211
- _ nodeType: NodeType.Type,
212
- _ visit: (NodeType) -> some SyntaxProtocol
213
- ) {
214
- let origNode = node
215
- visitPre(origNode)
216
- node = visitAny(origNode) ?? Syntax(visit(origNode.cast(NodeType.self)))
217
- visitPost(origNode)
202
+ @inline(never)
203
+ private func visitTokenSyntaxImpl(_ node: Syntax) -> Syntax {
204
+ Syntax(visit(TokenSyntax(unsafeCasting: node)))
218
205
}
219
206
"""
220
207
)
221
208
209
+ for node in NON_BASE_SYNTAX_NODES {
210
+ DeclSyntax (
211
+ """
212
+ @inline(never)
213
+ private func visit \( node. kind. syntaxType) Impl(_ node: Syntax) -> Syntax {
214
+ Syntax(visit( \( node. kind. syntaxType) (unsafeCasting: node)))
215
+ }
216
+ """
217
+ )
218
+ }
219
+
222
220
try IfConfigDeclSyntax (
223
221
leadingTrivia:
224
222
"""
@@ -255,26 +253,26 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
255
253
/// that determines the correct visitation function will be popped of the
256
254
/// stack before the function is being called, making the switch's stack
257
255
/// space transient instead of having it linger in the call stack.
258
- private func visitationFunc(for node: Syntax) -> ((inout Syntax) -> Void)
256
+ private func visitationFunc(for node: Syntax) -> (Syntax) -> Syntax
259
257
"""
260
258
) {
261
259
try SwitchExprSyntax ( " switch node.raw.kind " ) {
262
260
SwitchCaseSyntax ( " case .token: " ) {
263
- StmtSyntax ( " return { self.visitImpl(&$0, TokenSyntax.self, self.visit) } " )
261
+ StmtSyntax ( " return self.visitTokenSyntaxImpl(_:) " )
264
262
}
265
263
266
264
for node in NON_BASE_SYNTAX_NODES {
267
265
SwitchCaseSyntax ( " case . \( node. enumCaseCallName) : " ) {
268
- StmtSyntax ( " return { self.visitImpl(&$0, \( node. kind. syntaxType) .self, self.visit) } " )
266
+ StmtSyntax ( " return self.visit \( node. kind. syntaxType) Impl(_:) " )
269
267
}
270
268
}
271
269
}
272
270
}
273
271
274
272
DeclSyntax (
275
273
"""
276
- private func dispatchVisit(_ node: inout Syntax) {
277
- visitationFunc(for: node)(& node)
274
+ private func dispatchVisit(_ node: Syntax) -> Syntax {
275
+ visitationFunc(for: node)(node)
278
276
}
279
277
"""
280
278
)
@@ -285,15 +283,15 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
285
283
poundKeyword: . poundElseToken( ) ,
286
284
elements: . statements(
287
285
CodeBlockItemListSyntax {
288
- try ! FunctionDeclSyntax ( " private func dispatchVisit(_ node: inout Syntax) " ) {
286
+ try ! FunctionDeclSyntax ( " private func dispatchVisit(_ node: Syntax) -> Syntax " ) {
289
287
try SwitchExprSyntax ( " switch node.raw.kind " ) {
290
288
SwitchCaseSyntax ( " case .token: " ) {
291
- StmtSyntax ( " return visitImpl(& node, TokenSyntax.self, visit ) " )
289
+ StmtSyntax ( " return visitTokenSyntaxImpl( node) " )
292
290
}
293
291
294
292
for node in NON_BASE_SYNTAX_NODES {
295
293
SwitchCaseSyntax ( " case . \( node. enumCaseCallName) : " ) {
296
- StmtSyntax ( " return visitImpl(&node, \( node. kind. syntaxType) .self, visit )" )
294
+ StmtSyntax ( " return visit \( node. kind. syntaxType) Impl(node )" )
297
295
}
298
296
}
299
297
}
@@ -304,6 +302,16 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
304
302
}
305
303
)
306
304
305
+ DeclSyntax (
306
+ """
307
+ private func visitImpl(_ node: Syntax) -> Syntax {
308
+ visitPre(node)
309
+ defer { visitPost(node) }
310
+ return visitAny(node) ?? dispatchVisit(node)
311
+ }
312
+ """
313
+ )
314
+
307
315
DeclSyntax (
308
316
"""
309
317
private func visitChildren(_ node: Syntax) -> Syntax {
@@ -325,9 +333,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
325
333
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
326
334
327
335
// Build the Syntax node to rewrite
328
- var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
329
-
330
- dispatchVisit(&childNode)
336
+ var childNode = visitImpl(nodeFactory.create(parent: node, raw: child, absoluteInfo: info))
331
337
if childNode.raw.id != child.id {
332
338
// The node was rewritten, let's handle it
333
339
0 commit comments