Skip to content

Commit d1c23b7

Browse files
committed
Make layoutView and tokenView properties Optional
This makes it clear at the call site that the node might not be a layout or token.
1 parent 5baa961 commit d1c23b7

19 files changed

+694
-466
lines changed

Sources/SwiftSyntax/IncrementalParseTransition.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ public struct IncrementalParseLookup {
305305
return true
306306
}
307307
if let nextToken = nextSibling.raw.firstToken(viewMode: .sourceAccurate) {
308-
nextLeafNodeLength = nextToken.totalLength - nextToken.trailingTriviaLength
308+
nextLeafNodeLength = nextToken.raw.totalLength - nextToken.trailingTriviaLength
309309
}
310310
}
311311
let nodeAffectRange = ByteSourceRange(offset: node.position.utf8Offset,

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ extension RawSyntax {
215215
// We always consider collections 'present' because they can just be empty.
216216
return .present
217217
}
218-
if isToken && (self.tokenView.rawKind == .eof || self.tokenView.rawKind == .stringSegment) {
218+
if isToken && (self.tokenView!.rawKind == .eof || self.tokenView!.rawKind == .stringSegment) {
219219
// The end of file token never has source code associated with it but we
220220
// still consider it valid.
221221
// String segments can be empty if they occur in an empty string literal or in between two interpolation segments.
@@ -246,41 +246,43 @@ extension RawSyntax {
246246
/// - Parameters:
247247
/// - leadingTrivia: The trivia to attach.
248248
func withLeadingTrivia(_ leadingTrivia: Trivia) -> RawSyntax? {
249-
if isToken {
249+
switch view {
250+
case .token(let tokenView):
250251
return .makeMaterializedToken(
251252
kind: tokenView.formKind(),
252253
leadingTrivia: leadingTrivia,
253254
trailingTrivia: tokenView.formTrailingTrivia(),
254255
arena: arena)
255-
}
256-
257-
for (index, child) in children.enumerated() {
258-
if let replaced = child?.withLeadingTrivia(leadingTrivia) {
259-
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
256+
case .layout(let layoutView):
257+
for (index, child) in children.enumerated() {
258+
if let replaced = child?.withLeadingTrivia(leadingTrivia) {
259+
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
260+
}
260261
}
262+
return nil
261263
}
262-
return nil
263264
}
264265

265266
/// Replaces the trailing trivia of the last token in this syntax tree by `trailingTrivia`.
266267
/// If the syntax tree did not contain a token and thus no trivia could be attached to it, `nil` is returned.
267268
/// - Parameters:
268269
/// - trailingTrivia: The trivia to attach.
269270
func withTrailingTrivia(_ trailingTrivia: Trivia) -> RawSyntax? {
270-
if isToken {
271+
switch view {
272+
case .token(let tokenView):
271273
return .makeMaterializedToken(
272274
kind: tokenView.formKind(),
273275
leadingTrivia: tokenView.formLeadingTrivia(),
274276
trailingTrivia: trailingTrivia,
275277
arena: arena)
276-
}
277-
278-
for (index, child) in children.enumerated().reversed() {
279-
if let replaced = child?.withTrailingTrivia(trailingTrivia) {
280-
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
278+
case .layout(let layoutView):
279+
for (index, child) in children.enumerated().reversed() {
280+
if let replaced = child?.withTrailingTrivia(trailingTrivia) {
281+
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
282+
}
281283
}
284+
return nil
282285
}
283-
return nil
284286
}
285287

286288
/// Returns the child at the provided cursor in the layout.
@@ -334,45 +336,53 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
334336

335337
extension RawSyntax {
336338
/// Return the first token of a layout node that should be traversed by `viewMode`.
337-
func firstToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
339+
func firstToken(viewMode: SyntaxTreeViewMode) -> RawSyntaxTokenView? {
338340
guard viewMode.shouldTraverse(node: self) else { return nil }
339-
if isToken { return self }
340-
for child in children {
341-
if let token = child?.firstToken(viewMode: viewMode) {
342-
return token
341+
switch view {
342+
case .token(let tokenView):
343+
return tokenView
344+
case .layout:
345+
for child in children {
346+
if let token = child?.firstToken(viewMode: viewMode) {
347+
return token
348+
}
343349
}
350+
return nil
344351
}
345-
return nil
346352
}
347353

348354
/// Return the last token of a layout node that should be traversed by `viewMode`.
349-
func lastToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
355+
func lastToken(viewMode: SyntaxTreeViewMode) -> RawSyntaxTokenView? {
350356
guard viewMode.shouldTraverse(node: self) else { return nil }
351-
if isToken { return self }
352-
for child in children.reversed() {
353-
if let token = child?.lastToken(viewMode: viewMode) {
354-
return token
357+
switch view {
358+
case .token(let tokenView):
359+
return tokenView
360+
case .layout:
361+
for child in children.reversed() {
362+
if let token = child?.lastToken(viewMode: viewMode) {
363+
return token
364+
}
355365
}
366+
return nil
356367
}
357-
return nil
358368
}
359369

360370
func formLeadingTrivia() -> Trivia? {
361-
firstToken(viewMode: .sourceAccurate)?.tokenView.formLeadingTrivia()
371+
firstToken(viewMode: .sourceAccurate)?.formLeadingTrivia()
362372
}
363373

364374
func formTrailingTrivia() -> Trivia? {
365-
lastToken(viewMode: .sourceAccurate)?.tokenView.formTrailingTrivia()
375+
lastToken(viewMode: .sourceAccurate)?.formTrailingTrivia()
366376
}
367377
}
368378

369379
extension RawSyntax {
370380
var leadingTriviaByteLength: Int {
371-
firstToken(viewMode: .sourceAccurate)?.tokenView.leadingTriviaByteLength ?? 0
381+
firstToken(viewMode: .sourceAccurate)?.leadingTriviaByteLength ?? 0
372382
}
373383

374384
var trailingTriviaByteLength: Int {
375-
lastToken(viewMode: .sourceAccurate)?.tokenView.trailingTriviaByteLength ?? 0
385+
lastToken(viewMode: .sourceAccurate)?.trailingTriviaByteLength ?? 0
376386
}
377387

378388
/// The length of this node’s content, without the first leading and the last
@@ -672,3 +682,20 @@ extension RawSyntax: CustomReflectable {
672682
return Mirror(self, unlabeledChildren: mirrorChildren)
673683
}
674684
}
685+
686+
enum RawSyntaxView {
687+
case token(RawSyntaxTokenView)
688+
case layout(RawSyntaxLayoutView)
689+
}
690+
691+
extension RawSyntax {
692+
var view: RawSyntaxView {
693+
switch raw.payload {
694+
case .parsedToken, .materializedToken:
695+
return .token(tokenView!)
696+
case .layout:
697+
return .layout(layoutView!)
698+
}
699+
}
700+
}
701+

Sources/SwiftSyntax/Raw/RawSyntaxLayoutView.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
extension RawSyntax {
1414
/// A view into the `RawSyntax` that exposes functionality that's specific to layout nodes.
1515
/// The token's payload must be a layout, otherwise this traps.
16-
var layoutView: RawSyntaxLayout {
17-
return RawSyntaxLayout(raw: self)
16+
var layoutView: RawSyntaxLayoutView? {
17+
switch raw.payload {
18+
case .parsedToken, .materializedToken:
19+
return nil
20+
case .layout:
21+
return RawSyntaxLayoutView(raw: self)
22+
}
1823
}
1924
}
2025

2126
/// A view into `RawSyntax` that exposes functionality that only applies to layout nodes.
22-
struct RawSyntaxLayout {
27+
struct RawSyntaxLayoutView {
2328
private let raw: RawSyntax
2429

2530
fileprivate init(raw: RawSyntax) {

Sources/SwiftSyntax/Raw/RawSyntaxNodeProtocol.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ extension RawSyntax: RawSyntaxNodeProtocol {
6262

6363
@_spi(RawSyntax)
6464
public struct RawTokenSyntax: RawSyntaxNodeProtocol {
65+
var tokenView: RawSyntaxTokenView {
66+
return raw.tokenView!
67+
}
68+
6569
public static func isKindOf(_ raw: RawSyntax) -> Bool {
6670
return raw.kind == .token
6771
}
@@ -79,11 +83,11 @@ public struct RawTokenSyntax: RawSyntaxNodeProtocol {
7983
}
8084

8185
public var tokenKind: RawTokenKind {
82-
return raw.tokenView.rawKind
86+
return tokenView.rawKind
8387
}
8488

8589
public var tokenText: SyntaxText {
86-
return raw.tokenView.rawText
90+
return tokenView.rawText
8791
}
8892

8993
public var byteLength: Int {
@@ -99,11 +103,11 @@ public struct RawTokenSyntax: RawSyntaxNodeProtocol {
99103
}
100104

101105
public var leadingTriviaPieces: [RawTriviaPiece] {
102-
raw.tokenView.leadingRawTriviaPieces
106+
tokenView.leadingRawTriviaPieces
103107
}
104108

105109
public var trailingTriviaPieces: [RawTriviaPiece] {
106-
raw.tokenView.trailingRawTriviaPieces
110+
tokenView.trailingRawTriviaPieces
107111
}
108112

109113
/// Creates a `RawTokenSyntax`. `wholeText` must be managed by the same

Sources/SwiftSyntax/Raw/RawSyntaxTokenView.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@
1313
extension RawSyntax {
1414
/// A view into the `RawSyntax` that exposes functionality that's specific to tokens.
1515
/// The token's payload must be a token, otherwise this traps.
16-
var tokenView: RawSyntaxTokenView {
17-
return RawSyntaxTokenView(raw: self)
16+
var tokenView: RawSyntaxTokenView? {
17+
switch raw.payload {
18+
case .parsedToken, .materializedToken:
19+
return RawSyntaxTokenView(raw: self)
20+
case .layout(_):
21+
return nil
22+
}
1823
}
1924
}
2025

2126
/// A view into `RawSyntax` that exposes functionality that only applies to tokens.
2227
struct RawSyntaxTokenView {
23-
private let raw: RawSyntax
28+
let raw: RawSyntax
2429

2530
fileprivate init(raw: RawSyntax) {
2631
self.raw = raw

Sources/SwiftSyntax/SyntaxClassifier.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ extension TokenSyntax {
2828
/// The `SyntaxClassifiedRange` for the token text, excluding trivia.
2929
public var tokenClassification: SyntaxClassifiedRange {
3030
let contextualClassification = self.data.contextualClassification
31-
let relativeOffset = raw.tokenView.leadingTriviaLength.utf8Length
31+
let relativeOffset = tokenView.leadingTriviaLength.utf8Length
3232
let absoluteOffset = position.utf8Offset + relativeOffset
33-
return TokenKindAndText(kind: raw.tokenView.rawKind, text: raw.tokenView.rawText).classify(
33+
return TokenKindAndText(kind: tokenView.rawKind, text: tokenView.rawText).classify(
3434
offset: absoluteOffset, contextualClassification: contextualClassification)
3535
}
3636
}
@@ -282,14 +282,17 @@ fileprivate struct TokenClassificationIterator: IteratorProtocol {
282282
}
283283

284284
let token: AbsoluteNode
285+
var tokenView: RawSyntaxTokenView {
286+
return token.raw.tokenView!
287+
}
285288
var offset: Int
286289
var state: State
287290

288291
init(_ token: AbsoluteNode) {
289292
assert(token.raw.isToken)
290293
self.token = token
291294
self.offset = Int(token.position.offset)
292-
self.state = .atLeadingTrivia(token.raw.tokenView.leadingRawTriviaPieces, 0)
295+
self.state = .atLeadingTrivia(token.raw.tokenView!.leadingRawTriviaPieces, 0)
293296
}
294297

295298
var relativeOffset: Int { return offset - Int(token.position.offset) }
@@ -310,11 +313,11 @@ fileprivate struct TokenClassificationIterator: IteratorProtocol {
310313

311314
case .atTokenText:
312315
let classifiedRange = TokenKindAndText(
313-
kind: token.raw.tokenView.rawKind, text: token.raw.tokenView.rawText)
316+
kind: tokenView.rawKind, text: tokenView.rawText)
314317
.classify(offset: offset, contextualClassification: token.classification)
315318

316319
// Move on to trailing trivia.
317-
state = .atTrailingTrivia(token.raw.tokenView.trailingRawTriviaPieces, 0)
320+
state = .atTrailingTrivia(tokenView.trailingRawTriviaPieces, 0)
318321
offset = classifiedRange.endOffset
319322
guard classifiedRange.kind != .none else { break }
320323
return classifiedRange

Sources/SwiftSyntax/SyntaxCollections.swift.gyb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public protocol SyntaxCollection: SyntaxProtocol, Sequence {
4040
public struct ${node.name}: SyntaxCollection, SyntaxHashable {
4141
public let _syntaxNode: Syntax
4242

43+
var layoutView: RawSyntaxLayoutView {
44+
data.raw.layoutView!
45+
}
46+
4347
/// Converts the given `Syntax` node to a `${node.name}` if possible. Returns
4448
/// `nil` if the conversion is not possible.
4549
public init?(_ syntax: Syntax) {
@@ -77,7 +81,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
7781
/// - Returns: A new `${node.name}` with the new layout underlying it.
7882
internal func replacingLayout(
7983
_ layout: [RawSyntax?]) -> ${node.name} {
80-
let newRaw = data.raw.layoutView.replacingLayout(with: layout, arena: .default)
84+
let newRaw = layoutView.replacingLayout(with: layout, arena: .default)
8185
let newData = data.replacingSelf(newRaw)
8286
return ${node.name}(newData)
8387
}
@@ -89,7 +93,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
8993
/// - Returns: A new `${node.name}` with that element appended to the end.
9094
public func appending(
9195
_ syntax: ${node.collection_element_type}) -> ${node.name} {
92-
var newLayout = data.raw.layoutView.formLayoutArray()
96+
var newLayout = layoutView.formLayoutArray()
9397
newLayout.append(syntax.raw)
9498
return replacingLayout(newLayout)
9599
}
@@ -115,7 +119,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
115119
/// - Returns: A new `${node.name}` with that element appended to the end.
116120
public func inserting(_ syntax: ${node.collection_element_type},
117121
at index: Int) -> ${node.name} {
118-
var newLayout = data.raw.layoutView.formLayoutArray()
122+
var newLayout = layoutView.formLayoutArray()
119123
/// Make sure the index is a valid insertion index (0 to 1 past the end)
120124
precondition((newLayout.startIndex...newLayout.endIndex).contains(index),
121125
"inserting node at invalid index \(index)")
@@ -133,7 +137,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
133137
/// - Returns: A new `${node.name}` with the new element at the provided index.
134138
public func replacing(childAt index: Int,
135139
with syntax: ${node.collection_element_type}) -> ${node.name} {
136-
var newLayout = data.raw.layoutView.formLayoutArray()
140+
var newLayout = layoutView.formLayoutArray()
137141
/// Make sure the index is a valid index for replacing
138142
precondition((newLayout.startIndex..<newLayout.endIndex).contains(index),
139143
"replacing node at invalid index \(index)")
@@ -148,7 +152,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
148152
/// - Returns: A new `${node.name}` with the element at the provided index
149153
/// removed.
150154
public func removing(childAt index: Int) -> ${node.name} {
151-
var newLayout = data.raw.layoutView.formLayoutArray()
155+
var newLayout = layoutView.formLayoutArray()
152156
newLayout.remove(at: index)
153157
return replacingLayout(newLayout)
154158
}
@@ -157,7 +161,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
157161
///
158162
/// - Returns: A new `${node.name}` with the first element removed.
159163
public func removingFirst() -> ${node.name} {
160-
var newLayout = data.raw.layoutView.formLayoutArray()
164+
var newLayout = layoutView.formLayoutArray()
161165
newLayout.removeFirst()
162166
return replacingLayout(newLayout)
163167
}
@@ -166,7 +170,7 @@ public struct ${node.name}: SyntaxCollection, SyntaxHashable {
166170
///
167171
/// - Returns: A new `${node.name}` with the last element removed.
168172
public func removingLast() -> ${node.name} {
169-
var newLayout = data.raw.layoutView.formLayoutArray()
173+
var newLayout = layoutView.formLayoutArray()
170174
newLayout.removeLast()
171175
return replacingLayout(newLayout)
172176
}

Sources/SwiftSyntax/SyntaxData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ struct SyntaxData {
345345
/// syntax data.
346346
/// - SeeAlso: replacingSelf(_:)
347347
func replacingChild(_ child: RawSyntax?, at index: Int) -> SyntaxData {
348-
let newRaw = raw.layoutView.replacingChild(at: index, with: child, arena: .default)
348+
let newRaw = raw.layoutView!.replacingChild(at: index, with: child, arena: .default)
349349
return replacingSelf(newRaw)
350350
}
351351

Sources/SwiftSyntax/SyntaxNodes.swift.gyb.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public struct ${node.name}: ${base_type}Protocol, SyntaxHashable {
161161
public func add${child_elt}(_ element: ${child_elt_type}) -> ${node.name} {
162162
var collection: RawSyntax
163163
if let col = raw[Cursor.${child.swift_name}] {
164-
collection = col.layoutView.appending(element.raw, arena: .default)
164+
collection = col.layoutView!.appending(element.raw, arena: .default)
165165
} else {
166166
collection = RawSyntax.makeLayout(kind: SyntaxKind.${child_node.swift_syntax_kind},
167167
from: [element.raw], arena: .default)

0 commit comments

Comments
 (0)