Skip to content

Commit e6bb9cf

Browse files
committed
Add a new visitor: SyntaxTransformVisitor.
This visitor returns a transformed Syntax node rather than exposing a pre-post visitor.
1 parent c4dc91e commit e6bb9cf

File tree

4 files changed

+4678
-1
lines changed

4 files changed

+4678
-1
lines changed

Sources/SwiftSyntax/SyntaxData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class SyntaxBox: CustomStringConvertible,
213213
///
214214
/// SyntaxData is an implementation detail, and should not be exposed to clients
215215
/// of SwiftSyntax.
216-
struct SyntaxData {
216+
public struct SyntaxData {
217217
private enum ParentOrArena {
218218
// For non-root nodes.
219219
case parent(SyntaxBox)

Sources/SwiftSyntax/SyntaxVisitor.swift.gyb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,75 @@ open class SyntaxVisitor {
141141
}
142142
}
143143
}
144+
145+
public protocol SyntaxTransformVisitor {
146+
associatedtype ResultType = Void
147+
148+
func visit(_ token: TokenSyntax) -> ResultType?
149+
func visit(_ node: UnknownSyntax) -> ResultType?
150+
151+
% for node in SYNTAX_NODES:
152+
% if is_visitable(node):
153+
/// Visiting `${node.name}` specifically.
154+
/// - Parameter node: the node we are visiting.
155+
/// - Returns: nil by default.
156+
func visit(_ node: ${node.name}) -> ResultType?
157+
% end
158+
% end
159+
}
160+
161+
extension SyntaxTransformVisitor {
162+
public func visit(_ token: TokenSyntax) -> ResultType? { nil }
163+
public func visit(_ node: UnknownSyntax) -> ResultType? { nil }
164+
165+
% for node in SYNTAX_NODES:
166+
% if is_visitable(node):
167+
/// Visiting `${node.name}` specifically.
168+
/// - Parameter node: the node we are visiting.
169+
/// - Returns: nil by default.
170+
public func visit(_ node: ${node.name}) -> ResultType? {
171+
% if node.is_base():
172+
// Avoid calling into visitChildren if possible.
173+
if !node.raw.layoutView!.children.isEmpty {
174+
return visitChildren(node).first
175+
}
176+
return nil
177+
% else:
178+
// Avoid calling into visitChildren if possible.
179+
if !node.raw.layoutView!.children.isEmpty {
180+
return visitChildren(node).first
181+
}
182+
return nil
183+
% end
184+
}
185+
% end
186+
% end
187+
188+
public func visit(_ data: SyntaxData) -> ResultType? {
189+
switch data.raw.kind {
190+
case .token:
191+
let node = TokenSyntax(data)
192+
return visit(node)
193+
case .unknown:
194+
let node = UnknownSyntax(data)
195+
return visit(node)
196+
// The implementation of every generated case goes into its own function. This
197+
// circumvents an issue where the compiler allocates stack space for every
198+
// case statement next to each other in debug builds, causing it to allocate
199+
// ~50KB per call to this function. rdar://55929175
200+
% for node in NON_BASE_SYNTAX_NODES:
201+
case .${node.swift_syntax_kind}:
202+
let node = ${node.name}(data)
203+
return visit(node)
204+
% end
205+
}
206+
}
207+
208+
public func visitChildren<SyntaxType: SyntaxProtocol>(_ node: SyntaxType) -> [ResultType] {
209+
let syntaxNode = Syntax(node)
210+
return NonNilRawSyntaxChildren(syntaxNode, viewMode: .sourceAccurate).compactMap { childRaw in
211+
let childData = SyntaxData(childRaw, parent: syntaxNode)
212+
return visit(childData)
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)