Skip to content

Commit a15e00d

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 0479cf8 commit a15e00d

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
@@ -187,7 +187,7 @@ extension RawSyntax {
187187
// We always consider collections 'present' because they can just be empty.
188188
return .present
189189
}
190-
if isToken && (self.tokenView.rawKind == .eof || self.tokenView.rawKind == .stringSegment) {
190+
if isToken && (self.tokenView!.rawKind == .eof || self.tokenView!.rawKind == .stringSegment) {
191191
// The end of file token never has source code associated with it but we
192192
// still consider it valid.
193193
// String segments can be empty if they occur in an empty string literal or in between two interpolation segments.
@@ -218,41 +218,43 @@ extension RawSyntax {
218218
/// - Parameters:
219219
/// - leadingTrivia: The trivia to attach.
220220
func withLeadingTrivia(_ leadingTrivia: Trivia) -> RawSyntax? {
221-
if isToken {
221+
switch view {
222+
case .token(let tokenView):
222223
return .makeMaterializedToken(
223224
kind: tokenView.formKind(),
224225
leadingTrivia: leadingTrivia,
225226
trailingTrivia: tokenView.formTrailingTrivia(),
226227
arena: arena)
227-
}
228-
229-
for (index, child) in children.enumerated() {
230-
if let replaced = child?.withLeadingTrivia(leadingTrivia) {
231-
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
228+
case .layout(let layoutView):
229+
for (index, child) in children.enumerated() {
230+
if let replaced = child?.withLeadingTrivia(leadingTrivia) {
231+
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
232+
}
232233
}
234+
return nil
233235
}
234-
return nil
235236
}
236237

237238
/// Replaces the trailing trivia of the last token in this syntax tree by `trailingTrivia`.
238239
/// If the syntax tree did not contain a token and thus no trivia could be attached to it, `nil` is returned.
239240
/// - Parameters:
240241
/// - trailingTrivia: The trivia to attach.
241242
func withTrailingTrivia(_ trailingTrivia: Trivia) -> RawSyntax? {
242-
if isToken {
243+
switch view {
244+
case .token(let tokenView):
243245
return .makeMaterializedToken(
244246
kind: tokenView.formKind(),
245247
leadingTrivia: tokenView.formLeadingTrivia(),
246248
trailingTrivia: trailingTrivia,
247249
arena: arena)
248-
}
249-
250-
for (index, child) in children.enumerated().reversed() {
251-
if let replaced = child?.withTrailingTrivia(trailingTrivia) {
252-
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
250+
case .layout(let layoutView):
251+
for (index, child) in children.enumerated().reversed() {
252+
if let replaced = child?.withTrailingTrivia(trailingTrivia) {
253+
return layoutView.replacingChild(at: index, with: replaced, arena: arena)
254+
}
253255
}
256+
return nil
254257
}
255-
return nil
256258
}
257259

258260
/// Returns the child at the provided cursor in the layout.
@@ -306,45 +308,53 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
306308

307309
extension RawSyntax {
308310
/// Return the first token of a layout node that should be traversed by `viewMode`.
309-
func firstToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
311+
func firstToken(viewMode: SyntaxTreeViewMode) -> RawSyntaxTokenView? {
310312
guard viewMode.shouldTraverse(node: self) else { return nil }
311-
if isToken { return self }
312-
for child in children {
313-
if let token = child?.firstToken(viewMode: viewMode) {
314-
return token
313+
switch view {
314+
case .token(let tokenView):
315+
return tokenView
316+
case .layout:
317+
for child in children {
318+
if let token = child?.firstToken(viewMode: viewMode) {
319+
return token
320+
}
315321
}
322+
return nil
316323
}
317-
return nil
318324
}
319325

320326
/// Return the last token of a layout node that should be traversed by `viewMode`.
321-
func lastToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
327+
func lastToken(viewMode: SyntaxTreeViewMode) -> RawSyntaxTokenView? {
322328
guard viewMode.shouldTraverse(node: self) else { return nil }
323-
if isToken { return self }
324-
for child in children.reversed() {
325-
if let token = child?.lastToken(viewMode: viewMode) {
326-
return token
329+
switch view {
330+
case .token(let tokenView):
331+
return tokenView
332+
case .layout:
333+
for child in children.reversed() {
334+
if let token = child?.lastToken(viewMode: viewMode) {
335+
return token
336+
}
327337
}
338+
return nil
328339
}
329-
return nil
330340
}
331341

332342
func formLeadingTrivia() -> Trivia? {
333-
firstToken(viewMode: .sourceAccurate)?.tokenView.formLeadingTrivia()
343+
firstToken(viewMode: .sourceAccurate)?.formLeadingTrivia()
334344
}
335345

336346
func formTrailingTrivia() -> Trivia? {
337-
lastToken(viewMode: .sourceAccurate)?.tokenView.formTrailingTrivia()
347+
lastToken(viewMode: .sourceAccurate)?.formTrailingTrivia()
338348
}
339349
}
340350

341351
extension RawSyntax {
342352
var leadingTriviaByteLength: Int {
343-
firstToken(viewMode: .sourceAccurate)?.tokenView.leadingTriviaByteLength ?? 0
353+
firstToken(viewMode: .sourceAccurate)?.leadingTriviaByteLength ?? 0
344354
}
345355

346356
var trailingTriviaByteLength: Int {
347-
lastToken(viewMode: .sourceAccurate)?.tokenView.trailingTriviaByteLength ?? 0
357+
lastToken(viewMode: .sourceAccurate)?.trailingTriviaByteLength ?? 0
348358
}
349359

350360
/// The length of this node’s content, without the first leading and the last
@@ -624,3 +634,20 @@ extension RawSyntax: CustomReflectable {
624634
return Mirror(self, unlabeledChildren: mirrorChildren)
625635
}
626636
}
637+
638+
enum RawSyntaxView {
639+
case token(RawSyntaxTokenView)
640+
case layout(RawSyntaxLayoutView)
641+
}
642+
643+
extension RawSyntax {
644+
var view: RawSyntaxView {
645+
switch raw.payload {
646+
case .parsedToken, .materializedToken:
647+
return .token(tokenView!)
648+
case .layout:
649+
return .layout(layoutView!)
650+
}
651+
}
652+
}
653+

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)