Skip to content

Commit b2f9466

Browse files
committed
Eliminate SyntaxNode
`SyntaxNode` was introduced as a lighter `Syntax` when existentials were needed for creating `Syntax` instance. Now that `Syntax`/`SyntaxData` is just a struct that holds one class (`SyntaxBox`). So it's not that heavy. More importantly `SyntaxNode` was not safe to escape because it didn't retain the `SyntaxArena` of the `AbsoluteRawSyntax` `SyntaxNode` was only used in `SyntaxCursor` for `IncrementalParseLookup`. Use `SyntaxData` instead.
1 parent 1901568 commit b2f9466

File tree

6 files changed

+33
-1745
lines changed

6 files changed

+33
-1745
lines changed

Sources/SwiftSyntax/IncrementalParseTransition.swift

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ public protocol IncrementalParseReusedNodeDelegate {
2525
/// - range: The source region of the currently parsed source.
2626
/// - previousNode: The node from the previous tree that is associated with
2727
/// the skipped source region.
28-
func parserReusedNode(range: ByteSourceRange, previousNode: SyntaxNode)
28+
func parserReusedNode(range: ByteSourceRange, previousNode: Syntax)
2929
}
3030

3131
/// An implementation of `IncrementalParseReusedNodeDelegate` that just collects
3232
/// the range and re-used node into an array.
3333
public final class IncrementalParseReusedNodeCollector:
3434
IncrementalParseReusedNodeDelegate {
35-
public var rangeAndNodes: [(ByteSourceRange, SyntaxNode)] = []
35+
public var rangeAndNodes: [(ByteSourceRange, Syntax)] = []
3636

3737
public init() {}
3838

39-
public func parserReusedNode(range: ByteSourceRange, previousNode: SyntaxNode) {
39+
public func parserReusedNode(range: ByteSourceRange, previousNode: Syntax) {
4040
rangeAndNodes.append((range, previousNode))
4141
}
4242
}
@@ -226,7 +226,7 @@ public struct IncrementalParseLookup {
226226

227227
public init(transition: IncrementalParseTransition) {
228228
self.transition = transition
229-
self.cursor = .init(root: transition.previousTree.data.absoluteRaw)
229+
self.cursor = .init(root: transition.previousTree.data)
230230
}
231231

232232
fileprivate var edits: ConcurrentEdits {
@@ -246,11 +246,11 @@ public struct IncrementalParseLookup {
246246
/// - Parameters:
247247
/// - offset: The byte offset of the source string that is currently parsed.
248248
/// - kind: The `CSyntaxKind` that the parser expects at this position.
249-
/// - Returns: A `SyntaxNode` node from the previous parse invocation,
249+
/// - Returns: A `Syntax` node from the previous parse invocation,
250250
/// representing the contents of this region, if it is still valid
251251
/// to re-use. `nil` otherwise.
252252
@_spi(RawSyntax)
253-
public mutating func lookUp(_ newOffset: Int, kind: SyntaxKind) -> SyntaxNode? {
253+
public mutating func lookUp(_ newOffset: Int, kind: SyntaxKind) -> Syntax? {
254254
guard let prevOffset = translateToPreEditOffset(newOffset) else {
255255
return nil
256256
}
@@ -266,7 +266,7 @@ public struct IncrementalParseLookup {
266266

267267
mutating fileprivate func cursorLookup(
268268
prevPosition: AbsolutePosition, kind: SyntaxKind
269-
) -> SyntaxNode? {
269+
) -> Syntax? {
270270
guard !cursor.finished else { return nil }
271271

272272
while true {
@@ -348,60 +348,52 @@ public struct IncrementalParseLookup {
348348
/// Functions as an iterator that walks the tree looking for nodes with a
349349
/// certain position.
350350
fileprivate struct SyntaxCursor {
351-
var parents: [AbsoluteRawSyntax]
352-
var node: AbsoluteRawSyntax
351+
var node: SyntaxData
353352
var finished: Bool
354353
let viewMode = SyntaxTreeViewMode.sourceAccurate
355354

356-
init(root: AbsoluteRawSyntax) {
355+
init(root: SyntaxData) {
357356
self.node = root
358-
self.parents = []
359357
self.finished = false
360358
}
361359

362-
var asSyntaxNode: SyntaxNode {
363-
return SyntaxNode(node: node, parents: ArraySlice(parents))
360+
var asSyntaxNode: Syntax {
361+
return Syntax(node)
364362
}
365363

366364
/// Returns the next sibling node or the parent's sibling node if this is
367365
/// the last child. The cursor state is unmodified.
368-
/// - Returns: False if it run out of nodes to walk to.
369-
var nextSibling: AbsoluteRawSyntax? {
370-
var parents = ArraySlice(self.parents)
366+
/// - Returns: `nil` if it run out of nodes to walk to.
367+
var nextSibling: SyntaxData? {
371368
var node = self.node
372-
while !parents.isEmpty {
373-
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
374-
return sibling
369+
while let parent = node.parent?.data {
370+
if let sibling = node.absoluteRaw.nextSibling(parent: parent.absoluteRaw, viewMode: viewMode) {
371+
return SyntaxData(sibling, parent: Syntax(parent))
375372
}
376-
node = parents.removeLast()
373+
node = parent
377374
}
378-
379375
return nil
380376
}
381377

382378
/// Moves to the first child of the current node.
383379
/// - Returns: False if the node has no children.
384380
mutating func advanceToFirstChild() -> Bool {
385-
guard let child = node.firstChild(viewMode: viewMode) else { return false }
386-
parents.append(node)
387-
node = child
381+
guard let child = node.absoluteRaw.firstChild(viewMode: viewMode) else { return false }
382+
node = SyntaxData(child, parent: Syntax(node))
388383
return true
389384
}
390385

391386
/// Moves to the next sibling node or the parent's sibling node if this is
392387
/// the last child.
393388
/// - Returns: False if it run out of nodes to walk to.
394389
mutating func advanceToNextSibling() -> Bool {
395-
while !parents.isEmpty {
396-
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
397-
node = sibling
398-
return true
399-
}
400-
node = parents.removeLast()
390+
guard let next = nextSibling else {
391+
finished = true
392+
return false
401393
}
402394

403-
finished = true
404-
return false
395+
self.node = next
396+
return true
405397
}
406398

407399
/// Moves to the next node in the tree with the provided `position`.

Sources/SwiftSyntax/Misc.swift.gyb

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
NODE_MAP = create_node_map()
66
# Ignore the following admonition it applies to the resulting .swift file only
77
}%
8-
//// Automatically Generated From SyntaxNodes.swift.gyb.
8+
//// Automatically Generated From Misc.swift.gyb.
99
//// Do Not Edit Directly!
1010
//===---------- Misc.swift - Miscelaneous SwiftSyntax definitions ---------===//
1111
//
@@ -19,24 +19,6 @@
1919
//
2020
//===----------------------------------------------------------------------===//
2121

22-
extension SyntaxNode {
23-
public var isUnknown: Bool { return raw.kind.isUnknown }
24-
public var asUnknown: UnknownSyntax? {
25-
guard isUnknown else { return nil }
26-
return UnknownSyntax(asSyntaxData)
27-
}
28-
% for node in SYNTAX_NODES:
29-
% if not node.is_base():
30-
31-
public var is${node.syntax_kind}: Bool { return raw.kind == .${node.swift_syntax_kind} }
32-
public var as${node.syntax_kind}: ${node.name}? {
33-
guard is${node.syntax_kind} else { return nil }
34-
return ${node.name}(asSyntaxData)
35-
}
36-
% end
37-
% end
38-
}
39-
4022
extension Syntax {
4123
/// Syntax nodes always conform to SyntaxProtocol. This API is just added
4224
/// for consistency.

Sources/SwiftSyntax/Syntax.swift

Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -658,109 +658,6 @@ extension ReversedTokenSequence: CustomReflectable {
658658
}
659659
}
660660

661-
/// Represents a node from the syntax tree.
662-
///
663-
/// This is a more efficient representation than `Syntax` because it avoids casts
664-
/// to `Syntax` for representing the parent hierarchy.
665-
/// It provides generic information, like the node's position, range, and
666-
/// a unique `id`, while still allowing getting the associated `Syntax`
667-
/// object if necessary.
668-
///
669-
/// `SyntaxParser` uses `SyntaxNode` to efficiently report which syntax nodes
670-
/// got re-used during incremental re-parsing.
671-
public struct SyntaxNode {
672-
let absoluteRaw: AbsoluteRawSyntax
673-
let parents: ArraySlice<AbsoluteRawSyntax>
674-
675-
internal init(node: AbsoluteRawSyntax, parents: ArraySlice<AbsoluteRawSyntax>) {
676-
self.absoluteRaw = node
677-
self.parents = parents
678-
}
679-
680-
var raw: RawSyntax {
681-
return absoluteRaw.raw
682-
}
683-
684-
@_spi(RawSyntax)
685-
public func withUnsafeRawSyntax<R>(_ body: (RawSyntax) throws -> R) rethrows -> R {
686-
return try body(raw)
687-
}
688-
689-
/// Converts this node to a `SyntaxData` object.
690-
///
691-
/// This operation results in wrapping all of the node's parents into
692-
/// `SyntaxData` objects. There's a cost associated with it that should be
693-
/// taken into account before used inside performance critical code.
694-
internal var asSyntaxData: SyntaxData {
695-
if let parent = parent {
696-
return SyntaxData(absoluteRaw, parent: parent.asSyntax)
697-
} else {
698-
return SyntaxData.forRoot(absoluteRaw.raw)
699-
}
700-
}
701-
702-
/// Converts this node to a `Syntax` object.
703-
///
704-
/// This operation results in wrapping this node and all of its parents into
705-
/// `Syntax` objects. There's a cost associated with it that should be taken
706-
/// into account before used inside performance critical code.
707-
public var asSyntax: Syntax {
708-
return Syntax(self.asSyntaxData)
709-
}
710-
711-
/// The parent of this syntax node, or `nil` if this node is the root.
712-
public var parent: SyntaxNode? {
713-
guard !parents.isEmpty else { return nil }
714-
return SyntaxNode(node: parents.last!, parents: parents.dropLast())
715-
}
716-
717-
/// The absolute position of the starting point of this node.
718-
public var position: AbsolutePosition {
719-
return absoluteRaw.position
720-
}
721-
722-
/// The end position of this node, including its trivia.
723-
public var endPosition: AbsolutePosition {
724-
return absoluteRaw.endPosition
725-
}
726-
727-
/// The textual byte length of this node including leading and trailing trivia.
728-
public var byteSize: Int {
729-
return totalLength.utf8Length
730-
}
731-
732-
/// The byte source range of this node including leading and trailing trivia.
733-
public var byteRange: ByteSourceRange {
734-
return ByteSourceRange(offset: position.utf8Offset, length: byteSize)
735-
}
736-
737-
/// The length of this node including all of its trivia.
738-
public var totalLength: SourceLength {
739-
return raw.totalLength
740-
}
741-
}
742-
743-
extension SyntaxNode: Identifiable {
744-
/// Returns a value representing the unique identity of the node.
745-
public var id: SyntaxIdentifier {
746-
return absoluteRaw.info.nodeId
747-
}
748-
}
749-
750-
extension SyntaxNode: CustomStringConvertible, TextOutputStreamable {
751-
/// A source-accurate description of this node.
752-
public var description: String {
753-
return raw.description
754-
}
755-
756-
/// Prints the raw value of this node to the provided stream.
757-
/// - Parameter stream: The stream to which to print the raw tree.
758-
public func write<Target>(to target: inout Target)
759-
where Target: TextOutputStream {
760-
raw.write(to: &target)
761-
}
762-
}
763-
764661
/// Expose `recursiveDescription` on raw nodes for debugging purposes.
765662
extension RawSyntaxNodeProtocol {
766663
/// Print this raw syntax node including all of its children.
@@ -769,3 +666,6 @@ extension RawSyntaxNodeProtocol {
769666
return Syntax(raw: raw).recursiveDescription
770667
}
771668
}
669+
670+
@available(*, unavailable, message: "use 'Syntax' instead")
671+
public struct SyntaxNode {}

0 commit comments

Comments
 (0)