Skip to content

Commit a339c82

Browse files
[SwiftSyntax] Make Syntax nodes structs instead of classes (#14122)
* [Experiment] Make Syntax nodes structs instead of classes * [Experiment] Add Hashable conformance to concrete types * Fix pep8 violation * Remove AnySyntax, explicitly specialize SyntaxCollection nodes * Refine the comment for SyntaxCollection nodes
1 parent cb41882 commit a339c82

16 files changed

+350
-251
lines changed

test/SwiftSyntax/SyntaxChildren.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var SyntaxChildrenAPI = TestSuite("SyntaxChildrenAPI")
2828
SyntaxChildrenAPI.test("IterateWithAllPresent") {
2929
let returnStmt = SyntaxFactory.makeReturnStmt(
3030
returnKeyword: SyntaxFactory.makeReturnKeyword(),
31-
expression: SyntaxFactory.makeBlankExpr(),
31+
expression: SyntaxFactory.makeBlankUnknownExpr(),
3232
semicolon: SyntaxFactory.makeSemicolonToken())
3333

3434
var iterator = returnStmt.children.makeIterator()

test/SwiftSyntax/SyntaxFactory.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ SyntaxFactoryAPI.test("Generated") {
3434
}
3535
""")
3636

37-
let forType = SyntaxFactory.makeIdentifier("for",
37+
let forType = SyntaxFactory.makeIdentifier("for",
3838
leadingTrivia: .backticks(1),
3939
trailingTrivia: [
4040
.backticks(1), .spaces(1)
@@ -53,15 +53,16 @@ SyntaxFactoryAPI.test("Generated") {
5353
""")
5454

5555
expectNotEqual(structDecl.members, renamed.members)
56-
expectEqual(structDecl, structDecl.root)
56+
expectEqual(structDecl, structDecl.root as? StructDeclSyntax)
5757
expectNil(structDecl.parent)
5858
expectNotNil(structDecl.members.parent)
59-
expectEqual(structDecl.members.parent, structDecl)
59+
expectEqual(structDecl.members.parent as? StructDeclSyntax, structDecl)
6060

6161
// Ensure that accessing children via named identifiers is exactly the
6262
// same as accessing them as their underlying data.
63-
expectEqual(structDecl.members, structDecl.child(at: 7))
64-
63+
expectEqual(structDecl.members,
64+
structDecl.child(at: 7) as? MemberDeclBlockSyntax)
65+
6566
expectEqual("\(structDecl.members.rightBrace)",
6667
"""
6768

test/SwiftSyntax/VisitorTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ VisitorTests.test("Basic") {
3838
})
3939
}
4040

41-
runAllTests()
41+
runAllTests()

tools/SwiftSyntax/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ add_swift_library(swiftSwiftSyntax SHARED
1414
Syntax.swift
1515
SyntaxData.swift
1616
SyntaxChildren.swift
17-
SyntaxCollection.swift
17+
SyntaxCollections.swift.gyb
1818
SyntaxBuilders.swift.gyb
1919
SyntaxFactory.swift.gyb
2020
SyntaxKind.swift.gyb

tools/SwiftSyntax/SwiftSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension Syntax {
4444
}
4545
let decoder = JSONDecoder()
4646
let raw = try decoder.decode(RawSyntax.self, from: result.stdoutData)
47-
guard let file = Syntax.fromRaw(raw) as? SourceFileSyntax else {
47+
guard let file = makeSyntax(raw) as? SourceFileSyntax else {
4848
throw ParserError.invalidFile
4949
}
5050
return file

tools/SwiftSyntax/Syntax.swift

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,47 @@ import Foundation
1515
/// A Syntax node represents a tree of nodes with tokens at the leaves.
1616
/// Each node has accessors for its known children, and allows efficient
1717
/// iteration over the children through its `children` property.
18-
public class Syntax: CustomStringConvertible {
18+
public protocol Syntax:
19+
CustomStringConvertible, TextOutputStreamable {}
20+
21+
internal protocol _SyntaxBase: Syntax {
1922
/// The type of sequence containing the indices of present children.
20-
internal typealias PresentChildIndicesSequence =
23+
typealias PresentChildIndicesSequence =
2124
LazyFilterSequence<CountableRange<Int>>
22-
25+
2326
/// The root of the tree this node is currently in.
24-
internal let _root: SyntaxData
27+
var _root: SyntaxData { get } // Must be of type SyntaxData
2528

2629
/// The data backing this node.
2730
/// - note: This is unowned, because the reference to the root data keeps it
2831
/// alive. This means there is an implicit relationship -- the data
2932
/// property must be a descendent of the root. This relationship must
3033
/// be preserved in all circumstances where Syntax nodes are created.
31-
internal unowned var data: SyntaxData
34+
var _data: SyntaxData { get }
3235

3336
#if DEBUG
34-
func validate() {
35-
// This is for subclasses to override to perform structural validation.
36-
}
37+
func validate()
3738
#endif
39+
}
40+
extension _SyntaxBase {
41+
public func validate() {
42+
// This is for implementers to override to perform structural validation.
43+
}
44+
}
3845

39-
/// Creates a Syntax node from the provided root and data.
40-
internal init(root: SyntaxData, data: SyntaxData) {
41-
self._root = root
42-
self.data = data
43-
#if DEBUG
44-
validate()
45-
#endif
46+
extension Syntax {
47+
var data: SyntaxData {
48+
guard let base = self as? _SyntaxBase else {
49+
fatalError("only first-class syntax nodes can conform to Syntax")
50+
}
51+
return base._data
52+
}
53+
54+
var _root: SyntaxData {
55+
guard let base = self as? _SyntaxBase else {
56+
fatalError("only first-class syntax nodes can conform to Syntax")
57+
}
58+
return base._root
4659
}
4760

4861
/// Access the raw syntax assuming the node is a Syntax.
@@ -93,7 +106,7 @@ public class Syntax: CustomStringConvertible {
93106
/// The parent of this syntax node, or `nil` if this node is the root.
94107
public var parent: Syntax? {
95108
guard let parentData = data.parent else { return nil }
96-
return Syntax.make(root: _root, data: parentData)
109+
return makeSyntax(root: _root, data: parentData)
97110
}
98111

99112
/// The index of this node in the parent's children.
@@ -103,14 +116,14 @@ public class Syntax: CustomStringConvertible {
103116

104117
/// The root of the tree in which this node resides.
105118
public var root: Syntax {
106-
return Syntax.make(root: _root, data: _root)
119+
return makeSyntax(root: _root, data: _root)
107120
}
108121

109122
/// The sequence of indices that correspond to child nodes that are not
110123
/// missing.
111124
///
112125
/// This property is an implementation detail of `SyntaxChildren`.
113-
internal var presentChildIndices: PresentChildIndicesSequence {
126+
internal var presentChildIndices: _SyntaxBase.PresentChildIndicesSequence {
114127
return raw.layout.indices.lazy.filter { self.raw.layout[$0].isPresent }
115128
}
116129

@@ -121,7 +134,7 @@ public class Syntax: CustomStringConvertible {
121134
public func child(at index: Int) -> Syntax? {
122135
guard raw.layout.indices.contains(index) else { return nil }
123136
if raw.layout[index].isMissing { return nil }
124-
return Syntax.make(root: _root, data: data.cachedChild(at: index))
137+
return makeSyntax(root: _root, data: data.cachedChild(at: index))
125138
}
126139

127140
/// A source-accurate description of this node.
@@ -130,9 +143,7 @@ public class Syntax: CustomStringConvertible {
130143
self.write(to: &s)
131144
return s
132145
}
133-
}
134-
135-
extension Syntax: TextOutputStreamable {
146+
136147
/// Prints the raw value of this node to the provided stream.
137148
/// - Parameter stream: The stream to which to print the raw tree.
138149
public func write<Target>(to target: inout Target)
@@ -141,21 +152,27 @@ extension Syntax: TextOutputStreamable {
141152
}
142153
}
143154

144-
extension Syntax: Hashable {
145-
/// Determines if two nodes are equal to each other.
146-
public static func ==(lhs: Syntax, rhs: Syntax) -> Bool {
147-
return lhs.hashValue == rhs.hashValue
148-
}
149-
/// Use reference value as the hashValue for this Syntax
150-
public var hashValue: Int {
151-
return ObjectIdentifier(data).hashValue
152-
}
155+
/// Determines if two nodes are equal to each other.
156+
public func ==(lhs: Syntax, rhs: Syntax) -> Bool {
157+
return lhs.data === rhs.data
153158
}
154159

155160
/// MARK: - Nodes
156161

157162
/// A Syntax node representing a single token.
158-
public class TokenSyntax: Syntax {
163+
public struct TokenSyntax: _SyntaxBase, Hashable {
164+
var _root: SyntaxData
165+
unowned var _data: SyntaxData
166+
167+
/// Creates a Syntax node from the provided root and data.
168+
internal init(root: SyntaxData, data: SyntaxData) {
169+
self._root = root
170+
self._data = data
171+
#if DEBUG
172+
validate()
173+
#endif
174+
}
175+
159176
/// The text of the token as written in the source code.
160177
public var text: String {
161178
return tokenKind.text
@@ -230,4 +247,12 @@ public class TokenSyntax: Syntax {
230247
}
231248
return kind
232249
}
250+
251+
public static func ==(lhs: TokenSyntax, rhs: TokenSyntax) -> Bool {
252+
return lhs._data === rhs._data
253+
}
254+
255+
public var hashValue: Int {
256+
return ObjectIdentifier(_data).hashValue
257+
}
233258
}

tools/SwiftSyntax/SyntaxBuilders.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ extension ${node.name} {
7171
/// incrementally build the structure of the node.
7272
/// - Returns: A `${node.name}` with all the fields populated in the builder
7373
/// closure.
74-
public convenience init(_ build: (inout ${Builder}) -> Void) {
74+
public init(_ build: (inout ${Builder}) -> Void) {
7575
var builder = ${Builder}()
7676
build(&builder)
7777
let data = builder.buildData()

tools/SwiftSyntax/SyntaxChildren.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Foundation
1515
public struct SyntaxChildren: Sequence {
1616
public struct Iterator: IteratorProtocol {
1717
let node: Syntax
18-
var indexIterator: Syntax.PresentChildIndicesSequence.Iterator
18+
var indexIterator: _SyntaxBase.PresentChildIndicesSequence.Iterator
1919

2020
init(node: Syntax) {
2121
self.indexIterator = node.presentChildIndices.makeIterator()

tools/SwiftSyntax/SyntaxCollection.swift

Lines changed: 0 additions & 150 deletions
This file was deleted.

0 commit comments

Comments
 (0)