Skip to content

Commit 903ac50

Browse files
authored
Merge pull request swiftlang#158 from ahoppen/syntax-visitor-class
Model SyntaxVisitor as a class
2 parents 6aa112e + bc5fef3 commit 903ac50

File tree

8 files changed

+146
-191
lines changed

8 files changed

+146
-191
lines changed

Sources/SwiftSyntax/SyntaxNodes.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ extension Syntax {
5353
public struct UnknownSyntax: SyntaxProtocol {
5454
public let _syntaxNode: Syntax
5555

56-
/// Convert the given `Syntax` node to a `UnknownSyntax` if possible. Return
56+
/// Convert the given `Syntax` node to an `UnknownSyntax` if possible. Return
5757
/// `nil` if the conversion is not possible.
5858
public init?(_ syntax: Syntax) {
5959
guard syntax.raw.kind == .unknown else { return nil }

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 108 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ open class SyntaxRewriter {
4747
return Syntax(token)
4848
}
4949

50-
/// Visit a `UnknownSyntax`.
50+
/// Visit an `UnknownSyntax`.
5151
/// - Parameter node: the node that is being visited
5252
/// - Returns: the rewritten node
5353
open func visit(_ node: UnknownSyntax) -> Syntax {
@@ -106,7 +106,7 @@ open class SyntaxRewriter {
106106

107107
% end
108108

109-
final func visit(_ data: SyntaxData) -> Syntax {
109+
private func visit(_ data: SyntaxData) -> Syntax {
110110
switch data.raw.kind {
111111
case .token:
112112
let node = TokenSyntax(data)
@@ -133,8 +133,9 @@ open class SyntaxRewriter {
133133
}
134134
}
135135

136-
final func visitChildren<SyntaxType: SyntaxProtocol>(_ node: SyntaxType)
137-
-> SyntaxType {
136+
private func visitChildren<SyntaxType: SyntaxProtocol>(
137+
_ node: SyntaxType
138+
) -> SyntaxType {
138139
// Walk over all children of this node and rewrite them. Don't store any
139140
// rewritten nodes until the first non-`nil` value is encountered. When this
140141
// happens, retrieve all previous syntax nodes from the parent node to
@@ -222,71 +223,122 @@ public enum SyntaxVisitorContinueKind {
222223
case skipChildren
223224
}
224225

225-
public protocol SyntaxVisitor {
226+
open class SyntaxVisitor {
227+
public init() {}
228+
229+
/// Walk all nodes of the given syntax tree, calling the corresponding `visit`
230+
/// function for every node that is being visited.
231+
public func walk<SyntaxType: SyntaxProtocol>(_ node: SyntaxType) {
232+
visit(node.data)
233+
}
234+
226235
% for node in SYNTAX_NODES:
227236
% if is_visitable(node):
228237
/// Visiting `${node.name}` specifically.
229238
/// - Parameter node: the node we are visiting.
230239
/// - Returns: how should we continue visiting.
231-
mutating func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind
240+
open func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
241+
return .visitChildren
242+
}
232243

233244
/// The function called after visiting `${node.name}` and its descendents.
234245
/// - node: the node we just finished visiting.
235-
mutating func visitPost(_ node: ${node.name})
246+
open func visitPost(_ node: ${node.name}) {}
236247
% end
237248
% end
238249

239250
/// Visiting `TokenSyntax` specifically.
240251
/// - Parameter node: the node we are visiting.
241252
/// - Returns: how should we continue visiting.
242-
mutating func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind
253+
open func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
254+
return .visitChildren
255+
}
243256

244257
/// The function called after visiting the node and its descendents.
245258
/// - node: the node we just finished visiting.
246-
mutating func visitPost(_ node: TokenSyntax)
259+
open func visitPost(_ node: TokenSyntax) {}
247260

248261
/// Visiting `UnknownSyntax` specifically.
249262
/// - Parameter node: the node we are visiting.
250263
/// - Returns: how should we continue visiting.
251-
mutating func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind
264+
open func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
265+
return .visitChildren
266+
}
252267

253268
/// The function called after visiting the node and its descendents.
254269
/// - node: the node we just finished visiting.
255-
mutating func visitPost(_ node: UnknownSyntax)
256-
}
270+
open func visitPost(_ node: UnknownSyntax) {}
257271

258-
public extension SyntaxVisitor {
259272
% for node in SYNTAX_NODES:
260-
% if is_visitable(node):
261-
mutating func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
262-
return .visitChildren
263-
}
264-
mutating func visitPost(_ node: ${node.name}) {}
273+
/// Implementation detail of doVisit(_:_:). Do not call directly.
274+
private func visitImpl${node.name}(_ data: SyntaxData) {
275+
% if node.is_base():
276+
let node = Unknown${node.name}(data)
277+
let needsChildren = (visit(node) == .visitChildren)
278+
// Avoid calling into visitChildren if possible.
279+
if needsChildren && node.raw.numberOfChildren > 0 {
280+
visitChildren(node)
281+
}
282+
visitPost(node)
283+
% else:
284+
let node = ${node.name}(data)
285+
let needsChildren = (visit(node) == .visitChildren)
286+
// Avoid calling into visitChildren if possible.
287+
if needsChildren && node.raw.numberOfChildren > 0 {
288+
visitChildren(node)
289+
}
290+
visitPost(node)
265291
% end
292+
}
293+
266294
% end
267295

268-
mutating func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
269-
return .visitChildren
296+
private func visit(_ data: SyntaxData) {
297+
switch data.raw.kind {
298+
case .token:
299+
let node = TokenSyntax(data)
300+
_ = visit(node)
301+
// No children to visit.
302+
visitPost(node)
303+
case .unknown:
304+
let node = UnknownSyntax(data)
305+
let needsChildren = (visit(node) == .visitChildren)
306+
// Avoid calling into visitChildren if possible.
307+
if needsChildren && node.raw.numberOfChildren > 0 {
308+
visitChildren(node)
309+
}
310+
visitPost(node)
311+
// The implementation of every generated case goes into its own function. This
312+
// circumvents an issue where the compiler allocates stack space for every
313+
// case statement next to each other in debug builds, causing it to allocate
314+
// ~50KB per call to this function. rdar://55929175
315+
% for node in SYNTAX_NODES:
316+
case .${node.swift_syntax_kind}:
317+
visitImpl${node.name}(data)
318+
% end
319+
}
270320
}
271-
mutating func visitPost(_ node: TokenSyntax) {}
272321

273-
mutating func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
274-
return .visitChildren
322+
private func visitChildren<SyntaxType: SyntaxProtocol>(_ node: SyntaxType) {
323+
let syntaxNode = Syntax(node)
324+
let parentBox = SyntaxBox(syntaxNode)
325+
for childRaw in PresentRawSyntaxChildren(syntaxNode) {
326+
let childData = SyntaxData(childRaw, parentBox: parentBox)
327+
visit(childData)
328+
}
275329
}
276-
mutating func visitPost(_ node: UnknownSyntax) {}
277330
}
278331

279332
/// A `SyntaxVisitor` that can visit the nodes as generic `Syntax` values.
280333
///
281-
/// This is a separate protocol because this kind of visitation is slower than
282-
/// the type-specific visitation of `SyntaxVisitor`. Use `SyntaxAnyVisitor` if
283-
/// the `visitAny(_)` function would be useful to have, otherwise use
284-
/// `SyntaxVisitor`.
334+
/// This subclass of `SyntaxVisitor` is slower than the type-specific visitation
335+
/// of `SyntaxVisitor`. Use `SyntaxAnyVisitor` if the `visitAny(_)` function
336+
/// would be useful to have, otherwise inherit from `SyntaxVisitor`.
285337
///
286-
/// This works by introducing default implementations of the type-specific
287-
/// visit function that delegate to `visitAny(_)`. A conformant type that
288-
/// provides a custom type-specific visit function, should also call
289-
/// `visitAny(_)` in its implementation, if calling `visitAny` is needed:
338+
/// This works by overriding the type-specific visit function that delegate to
339+
/// `visitAny(_)`. A subclass that provides a custom type-specific visit
340+
/// function, should also call `visitAny(_)` in its implementation, if calling
341+
/// `visitAny` is needed:
290342
///
291343
/// struct MyVisitor: SyntaxAnyVisitor {
292344
/// func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
@@ -299,143 +351,46 @@ public extension SyntaxVisitor {
299351
/// visitAny(token)
300352
/// }
301353
///
302-
public protocol SyntaxAnyVisitor: SyntaxVisitor {
303-
mutating func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind
304-
mutating func visitAnyPost(_ node: Syntax)
305-
}
354+
open class SyntaxAnyVisitor: SyntaxVisitor {
355+
/// Visiting `UnknownSyntax` specifically.
356+
/// - Parameter node: the node we are visiting.
357+
/// - Returns: how should we continue visiting.
358+
open func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
359+
return .visitChildren
360+
}
306361

307-
public extension SyntaxAnyVisitor {
308-
mutating func visitAnyPost(_ node: Syntax) {}
362+
/// The function called after visiting the node and its descendents.
363+
/// - node: the node we just finished visiting.
364+
open func visitAnyPost(_ node: Syntax) {}
309365

310-
% for node in SYNTAX_NODES:
311-
% if is_visitable(node):
312-
mutating func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
313-
return visitAny(node._syntaxNode)
314-
}
315-
mutating func visitPost(_ node: ${node.name}) {
316-
return visitAnyPost(node._syntaxNode)
317-
}
318-
% end
319-
% end
366+
// MARK: Override type specific visit methods
320367

321-
mutating func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
368+
override open func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
322369
return visitAny(token._syntaxNode)
323370
}
324-
mutating func visitPost(_ node: TokenSyntax) {
325-
return visitAnyPost(node._syntaxNode)
371+
372+
override open func visitPost(_ node: TokenSyntax) {
373+
visitAnyPost(node._syntaxNode)
326374
}
327375

328-
mutating func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
376+
override open func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
329377
return visitAny(node._syntaxNode)
330378
}
331-
mutating func visitPost(_ node: UnknownSyntax) {
332-
return visitAnyPost(node._syntaxNode)
333-
}
334-
}
335-
336-
/// A class version of the `SyntaxVisitor` protocol. This is useful if you
337-
/// intend to have subclasses overriding specific methods of a common base
338-
/// `SyntaxVisitor` class.
339-
///
340-
/// It workarounds the issue of not being able to override the default
341-
/// implementations of the protocol (see https://bugs.swift.org/browse/SR-103).
342-
open class SyntaxVisitorBase: SyntaxVisitor {
343-
public init() {}
344379

380+
override open func visitPost(_ node: UnknownSyntax) {
381+
visitAnyPost(node._syntaxNode)
382+
}
383+
345384
% for node in SYNTAX_NODES:
346385
% if is_visitable(node):
347-
open func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
348-
return .visitChildren
349-
}
350-
open func visitPost(_ node: ${node.name}) {}
351-
% end
352-
% end
353-
354-
open func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
355-
return .visitChildren
356-
}
357-
open func visitPost(_ node: TokenSyntax) {}
358-
359-
open func visit(_ node: UnknownSyntax) -> SyntaxVisitorContinueKind {
360-
return .visitChildren
361-
}
362-
open func visitPost(_ node: UnknownSyntax) {}
363-
}
364-
365-
public extension Syntax {
366-
func walk<Visitor>(_ visitor: inout Visitor) where Visitor : SyntaxVisitor {
367-
guard isPresent else { return }
368-
return doVisit(data, &visitor)
386+
override open func visit(_ node: ${node.name}) -> SyntaxVisitorContinueKind {
387+
return visitAny(node._syntaxNode)
369388
}
370-
}
371389

372-
public extension SyntaxProtocol {
373-
func walk<Visitor>(_ visitor: inout Visitor) where Visitor : SyntaxVisitor {
374-
return _syntaxNode.walk(&visitor)
390+
override open func visitPost(_ node: ${node.name}) {
391+
visitAnyPost(node._syntaxNode)
375392
}
376-
}
377-
378-
% for node in SYNTAX_NODES:
379-
/// Implementation detail of doVisit(_:_:). Do not call directly.
380-
private func _doVisitImpl${node.name}<Visitor>(
381-
_ data: SyntaxData, _ visitor: inout Visitor
382-
) where Visitor : SyntaxVisitor {
383-
% if node.is_base():
384-
let node = Unknown${node.name}(data)
385-
let needsChildren = (visitor.visit(node) == .visitChildren)
386-
// Avoid calling into visitChildren if possible.
387-
if needsChildren && node.raw.numberOfChildren > 0 {
388-
visitChildren(node, &visitor)
389-
}
390-
visitor.visitPost(node)
391-
% else:
392-
let node = ${node.name}(data)
393-
let needsChildren = (visitor.visit(node) == .visitChildren)
394-
// Avoid calling into visitChildren if possible.
395-
if needsChildren && node.raw.numberOfChildren > 0 {
396-
visitChildren(node, &visitor)
397-
}
398-
visitor.visitPost(node)
399393
% end
400-
}
401-
402394
% end
403395

404-
fileprivate func doVisit<Visitor>(
405-
_ data: SyntaxData, _ visitor: inout Visitor
406-
) where Visitor : SyntaxVisitor {
407-
switch data.raw.kind {
408-
case .token:
409-
let node = TokenSyntax(data)
410-
_ = visitor.visit(node)
411-
// No children to visit.
412-
visitor.visitPost(node)
413-
case .unknown:
414-
let node = UnknownSyntax(data)
415-
let needsChildren = (visitor.visit(node) == .visitChildren)
416-
// Avoid calling into visitChildren if possible.
417-
if needsChildren && node.raw.numberOfChildren > 0 {
418-
visitChildren(node, &visitor)
419-
}
420-
visitor.visitPost(node)
421-
// The implementation of every generated case goes into its own function. This
422-
// circumvents an issue where the compiler allocates stack space for every
423-
// case statement next to each other in debug builds, causing it to allocate
424-
// ~50KB per call to this function. rdar://55929175
425-
% for node in SYNTAX_NODES:
426-
case .${node.swift_syntax_kind}:
427-
_doVisitImpl${node.name}(data, &visitor)
428-
% end
429-
}
430-
}
431-
432-
fileprivate func visitChildren<SyntaxType: SyntaxProtocol, Visitor>(
433-
_ syntax: SyntaxType, _ visitor: inout Visitor
434-
) where Visitor : SyntaxVisitor {
435-
let syntaxNode = Syntax(syntax)
436-
let parentBox = SyntaxBox(syntaxNode)
437-
for childRaw in PresentRawSyntaxChildren(syntaxNode) {
438-
let childData = SyntaxData(childRaw, parentBox: parentBox)
439-
doVisit(childData, &visitor)
440-
}
441396
}

Sources/SwiftSyntax/SyntaxVerifier.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,26 @@ public enum SyntaxVerifierError: Error, CustomStringConvertible {
2626
}
2727

2828
/// Verifier to check that there are no unknown syntax nodes in the tree.
29-
public struct SyntaxVerifier: SyntaxAnyVisitor {
29+
public class SyntaxVerifier: SyntaxAnyVisitor {
3030

3131
var unknownNodes: [Syntax] = []
3232

33-
public mutating func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
33+
public override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
3434
if node.isUnknown {
3535
unknownNodes.append(node)
3636
}
3737
return .visitChildren
3838
}
3939

40-
private mutating func verify(_ node: Syntax) throws {
41-
node.walk(&self)
40+
private func verify(_ node: Syntax) throws {
41+
self.walk(node)
4242
if let unknownNode = unknownNodes.first {
4343
throw SyntaxVerifierError.unknownSyntaxFound(node: unknownNode)
4444
}
4545
}
4646

4747
public static func verify(_ node: Syntax) throws {
48-
var verifier = SyntaxVerifier()
48+
let verifier = SyntaxVerifier()
4949
try verifier.verify(node)
5050
}
5151
}

0 commit comments

Comments
 (0)