Skip to content

Commit 3ef5682

Browse files
committed
Introduce a source accurate and structural view of a SwiftSyntax tree
`SyntaxTreeViewMode` allows two different views on a SwiftSyntax tree: 1. Either a purely `sourceAccurate` view, which skips over all missing children but traverses garbage syntax (once implemented). This mode is intended for clients that operate at the source code level, like swift-format 2. For clients that don’t care about 100% about source fidelity but would like to view the syntax tree for structural analysis with fixes from the parser applied, there’s the `fixedUp` view. This includes missing nodes (because the user probably meant to write these) and skips over garbage nodes (because the client will likely have a hard time reasoning about these nodes if the parser couldn’t make sense of them)
1 parent d9af173 commit 3ef5682

25 files changed

+270
-161
lines changed

Sources/SwiftSyntax/IncrementalParseTransition.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public struct IncrementalParseLookup {
304304
if !edits.edits.isEmpty && edits.edits.first!.range.offset > nextSibling.endPosition.utf8Offset {
305305
return true
306306
}
307-
if let nextToken = nextSibling.raw.firstPresentToken {
307+
if let nextToken = nextSibling.raw.firstToken(viewMode: .sourceAccurate) {
308308
nextLeafNodeLength = nextToken.totalLength - nextToken.trailingTriviaLength
309309
}
310310
}
@@ -350,6 +350,7 @@ fileprivate struct SyntaxCursor {
350350
var parents: [AbsoluteRawSyntax]
351351
var node: AbsoluteRawSyntax
352352
var finished: Bool
353+
let viewMode = SyntaxTreeViewMode.sourceAccurate
353354

354355
init(root: AbsoluteRawSyntax) {
355356
self.node = root
@@ -368,7 +369,7 @@ fileprivate struct SyntaxCursor {
368369
var parents = ArraySlice(self.parents)
369370
var node = self.node
370371
while !parents.isEmpty {
371-
if let sibling = node.nextSibling(parent: parents.last!) {
372+
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
372373
return sibling
373374
}
374375
node = parents.removeLast()
@@ -380,7 +381,7 @@ fileprivate struct SyntaxCursor {
380381
/// Moves to the first child of the current node.
381382
/// - Returns: False if the node has no children.
382383
mutating func advanceToFirstChild() -> Bool {
383-
guard let child = node.firstChild else { return false }
384+
guard let child = node.firstChild(viewMode: viewMode) else { return false }
384385
parents.append(node)
385386
node = child
386387
return true
@@ -391,7 +392,7 @@ fileprivate struct SyntaxCursor {
391392
/// - Returns: False if it run out of nodes to walk to.
392393
mutating func advanceToNextSibling() -> Bool {
393394
while !parents.isEmpty {
394-
if let sibling = node.nextSibling(parent: parents.last!) {
395+
if let sibling = node.nextSibling(parent: parents.last!, viewMode: viewMode) {
395396
node = sibling
396397
return true
397398
}

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -993,18 +993,8 @@ final class RawSyntax: ManagedBuffer<RawSyntaxBase, RawSyntaxDataElement> {
993993
return header.totalNodes
994994
}
995995

996-
/// Whether this node is present in the original source.
997-
var isPresent: Bool {
998-
return header.isPresent
999-
}
1000-
1001-
/// Whether this node is missing from the original source.
1002-
var isMissing: Bool {
1003-
return !isPresent
1004-
}
1005-
1006996
var presence: SourcePresence {
1007-
return isPresent ? .present : .missing
997+
return header.isPresent ? .present : .missing
1008998
}
1009999

10101000
var totalLength: SourceLength {
@@ -1247,7 +1237,7 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
12471237
/// - Parameter stream: The stream on which to output this node.
12481238
func write<Target>(to target: inout Target)
12491239
where Target: TextOutputStream {
1250-
guard isPresent else { return }
1240+
guard SyntaxTreeViewMode.sourceAccurate.shouldTraverse(node: self) else { return }
12511241
if isToken {
12521242
withUnsafeMutablePointers {
12531243
$0.pointee.writeToken(to: &target, extraPtr: $1)
@@ -1269,48 +1259,48 @@ extension RawSyntax: TextOutputStreamable, CustomStringConvertible {
12691259

12701260
extension RawSyntax {
12711261
/// Return the first `present` token of a layout node or self if it is a token.
1272-
var firstPresentToken: RawSyntax? {
1273-
guard isPresent else { return nil }
1262+
func firstToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
1263+
guard viewMode.shouldTraverse(node: self) else { return nil }
12741264
if isToken { return self }
12751265
for i in 0..<self.numberOfChildren {
1276-
if let token = self.child(at: i)?.firstPresentToken {
1266+
if let token = self.child(at: i)?.firstToken(viewMode: viewMode) {
12771267
return token
12781268
}
12791269
}
12801270
return nil
12811271
}
12821272

12831273
/// Return the last `present` token of a layout node or self if it is a token.
1284-
var lastPresentToken: RawSyntax? {
1285-
guard isPresent else { return nil }
1274+
func lastToken(viewMode: SyntaxTreeViewMode) -> RawSyntax? {
1275+
guard viewMode.shouldTraverse(node: self) else { return nil }
12861276
if isToken { return self }
12871277
for i in (0..<self.numberOfChildren).reversed() {
1288-
if let token = self.child(at: i)?.lastPresentToken {
1278+
if let token = self.child(at: i)?.lastToken(viewMode: viewMode) {
12891279
return token
12901280
}
12911281
}
12921282
return nil
12931283
}
12941284

12951285
func formLeadingTrivia() -> Trivia? {
1296-
guard let token = self.firstPresentToken else { return nil }
1286+
guard let token = self.firstToken(viewMode: .sourceAccurate) else { return nil }
12971287
return token.formTokenLeadingTrivia()
12981288
}
12991289

13001290
func formTrailingTrivia() -> Trivia? {
1301-
guard let token = self.lastPresentToken else { return nil }
1291+
guard let token = self.lastToken(viewMode: .sourceAccurate) else { return nil }
13021292
return token.formTokenTrailingTrivia()
13031293
}
13041294
}
13051295

13061296
extension RawSyntax {
13071297
var leadingTriviaLength: SourceLength {
1308-
guard let token = self.firstPresentToken else { return .zero }
1298+
guard let token = self.firstToken(viewMode: .sourceAccurate) else { return .zero }
13091299
return token.tokenLeadingTriviaLength
13101300
}
13111301

13121302
var trailingTriviaLength: SourceLength {
1313-
guard let token = self.lastPresentToken else { return .zero }
1303+
guard let token = self.lastToken(viewMode: .sourceAccurate) else { return .zero }
13141304
return token.tokenTrailingTriviaLength
13151305
}
13161306

Sources/SwiftSyntax/SourceLocation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ fileprivate func computeLines(
346346
lines.append(position)
347347
}
348348
var curPrefix: SourceLength = .zero
349-
for token in tree.tokens {
349+
for token in tree.tokens(viewMode: .sourceAccurate) {
350350
curPrefix = token.forEachLineLength(prefix: curPrefix, body: addLine)
351351
}
352352
position += curPrefix

Sources/SwiftSyntax/Syntax.swift

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -136,25 +136,15 @@ internal extension SyntaxProtocol {
136136

137137
public extension SyntaxProtocol {
138138
/// A sequence over the `present` children of this node.
139-
var children: SyntaxChildren {
140-
return SyntaxChildren(_syntaxNode)
139+
func children(viewMode: SyntaxTreeViewMode) -> SyntaxChildren {
140+
return SyntaxChildren(_syntaxNode, viewMode: viewMode)
141141
}
142142

143143
/// The index of this node in a `SyntaxChildren` collection.
144144
var index: SyntaxChildrenIndex {
145145
return SyntaxChildrenIndex(self.data.absoluteRaw.info)
146146
}
147147

148-
/// Whether or not this node is marked as `present`.
149-
var isPresent: Bool {
150-
return raw.isPresent
151-
}
152-
153-
/// Whether or not this node is marked as `missing`.
154-
var isMissing: Bool {
155-
return raw.isMissing
156-
}
157-
158148
/// Whether or not this node is a token one.
159149
var isToken: Bool {
160150
return raw.isToken
@@ -191,45 +181,63 @@ public extension SyntaxProtocol {
191181
/// Recursively walks through the tree to find the token semantically before
192182
/// this node.
193183
var previousToken: TokenSyntax? {
184+
return self.previousToken(viewMode: .sourceAccurate)
185+
}
186+
187+
/// Recursively walks through the tree to find the token semantically before
188+
/// this node.
189+
func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
194190
guard let parent = self.parent else {
195191
return nil
196192
}
197-
let siblings = PresentRawSyntaxChildren(parent)
193+
let siblings = NonNilRawSyntaxChildren(parent, viewMode: viewMode)
198194
for absoluteRaw in siblings[..<self.index].reversed() {
199195
let child = Syntax(SyntaxData(absoluteRaw, parent: parent))
200-
if let token = child.lastToken {
196+
if let token = child.lastToken(viewMode: viewMode) {
201197
return token
202198
}
203199
}
204-
return parent.previousToken
200+
return parent.previousToken(viewMode: viewMode)
205201
}
206202

207203
/// Recursively walks through the tree to find the next token semantically
208204
/// after this node.
209205
var nextToken: TokenSyntax? {
206+
return self.nextToken(viewMode: .sourceAccurate)
207+
}
208+
209+
/// Recursively walks through the tree to find the next token semantically
210+
/// after this node.
211+
func nextToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
210212
guard let parent = self.parent else {
211213
return nil
212214
}
213-
let siblings = PresentRawSyntaxChildren(parent)
215+
let siblings = NonNilRawSyntaxChildren(parent, viewMode: viewMode)
214216
let nextSiblingIndex = siblings.index(after: self.index)
215217
for absoluteRaw in siblings[nextSiblingIndex...] {
216218
let child = Syntax(SyntaxData(absoluteRaw, parent: parent))
217-
if let token = child.firstToken {
219+
if let token = child.firstToken(viewMode: viewMode) {
218220
return token
219221
}
220222
}
221-
return parent.nextToken
223+
return parent.nextToken(viewMode: viewMode)
222224
}
223225

224-
/// Returns the first token node that is part of this syntax node.
226+
/// Returns the first token in this syntax node in the source accurate view of
227+
/// the syntax tree.
225228
var firstToken: TokenSyntax? {
226-
if isMissing { return nil }
229+
return self.firstToken(viewMode: .sourceAccurate)
230+
}
231+
232+
/// Returns the first token node that is part of this syntax node.
233+
func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
234+
guard viewMode.shouldTraverse(node: raw) else { return nil }
227235
if let token = _syntaxNode.as(TokenSyntax.self) {
228236
return token
229237
}
230238

231-
for child in children {
232-
if let token = child.firstToken {
239+
for child in children(viewMode: viewMode) {
240+
if let token = child.firstToken(viewMode: viewMode) {
233241
return token
234242
}
235243
}
@@ -238,13 +246,18 @@ public extension SyntaxProtocol {
238246

239247
/// Returns the last token node that is part of this syntax node.
240248
var lastToken: TokenSyntax? {
241-
if isMissing { return nil }
249+
return self.lastToken(viewMode: .sourceAccurate)
250+
}
251+
252+
/// Returns the last token node that is part of this syntax node.
253+
func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
254+
guard viewMode.shouldTraverse(node: raw) else { return nil }
242255
if let token = _syntaxNode.as(TokenSyntax.self) {
243256
return token
244257
}
245258

246-
for child in children.reversed() {
247-
if let tok = child.lastToken {
259+
for child in children(viewMode: viewMode).reversed() {
260+
if let tok = child.lastToken(viewMode: viewMode) {
248261
return tok
249262
}
250263
}
@@ -377,8 +390,8 @@ public extension SyntaxProtocol {
377390
}
378391

379392
/// Sequence of tokens that are part of this Syntax node.
380-
var tokens: TokenSequence {
381-
return TokenSequence(_syntaxNode)
393+
func tokens(viewMode: SyntaxTreeViewMode) -> TokenSequence {
394+
return TokenSequence(_syntaxNode, viewMode: viewMode)
382395
}
383396

384397
/// Sequence of `SyntaxClassifiedRange`s for this syntax node.
@@ -466,15 +479,17 @@ public struct TokenSequence: Sequence {
466479
public struct Iterator: IteratorProtocol {
467480
var nextToken: TokenSyntax?
468481
let endPosition: AbsolutePosition
482+
let viewMode: SyntaxTreeViewMode
469483

470-
init(_ token: TokenSyntax?, endPosition: AbsolutePosition) {
484+
init(_ token: TokenSyntax?, endPosition: AbsolutePosition, viewMode: SyntaxTreeViewMode) {
471485
self.nextToken = token
472486
self.endPosition = endPosition
487+
self.viewMode = viewMode
473488
}
474489

475490
public mutating func next() -> TokenSyntax? {
476491
guard let token = self.nextToken else { return nil }
477-
self.nextToken = token.nextToken
492+
self.nextToken = token.nextToken(viewMode: viewMode)
478493
// Make sure we stop once we reach the end of the containing node.
479494
if let nextTok = self.nextToken, nextTok.position >= self.endPosition {
480495
self.nextToken = nil
@@ -484,17 +499,19 @@ public struct TokenSequence: Sequence {
484499
}
485500

486501
let node: Syntax
502+
let viewMode: SyntaxTreeViewMode
487503

488-
public init(_ node: Syntax) {
504+
public init(_ node: Syntax, viewMode: SyntaxTreeViewMode) {
489505
self.node = node
506+
self.viewMode = viewMode
490507
}
491508

492509
public func makeIterator() -> Iterator {
493-
return Iterator(node.firstToken, endPosition: node.endPosition)
510+
return Iterator(node.firstToken(viewMode: viewMode), endPosition: node.endPosition, viewMode: viewMode)
494511
}
495512

496513
public func reversed() -> ReversedTokenSequence {
497-
return ReversedTokenSequence(node)
514+
return ReversedTokenSequence(node, viewMode: viewMode)
498515
}
499516
}
500517

@@ -509,15 +526,17 @@ public struct ReversedTokenSequence: Sequence {
509526
public struct Iterator: IteratorProtocol {
510527
var nextToken: TokenSyntax?
511528
let startPosition: AbsolutePosition
529+
let viewMode: SyntaxTreeViewMode
512530

513-
init(_ token: TokenSyntax?, startPosition: AbsolutePosition) {
531+
init(_ token: TokenSyntax?, startPosition: AbsolutePosition, viewMode: SyntaxTreeViewMode) {
514532
self.nextToken = token
515533
self.startPosition = startPosition
534+
self.viewMode = viewMode
516535
}
517536

518537
public mutating func next() -> TokenSyntax? {
519538
guard let token = self.nextToken else { return nil }
520-
self.nextToken = token.previousToken
539+
self.nextToken = token.previousToken(viewMode: viewMode)
521540
// Make sure we stop once we went beyond the start of the containing node.
522541
if let nextTok = self.nextToken, nextTok.position < self.startPosition {
523542
self.nextToken = nil
@@ -527,17 +546,19 @@ public struct ReversedTokenSequence: Sequence {
527546
}
528547

529548
let node: Syntax
549+
let viewMode: SyntaxTreeViewMode
530550

531-
public init(_ node: Syntax) {
551+
public init(_ node: Syntax, viewMode: SyntaxTreeViewMode) {
532552
self.node = node
553+
self.viewMode = viewMode
533554
}
534555

535556
public func makeIterator() -> Iterator {
536-
return Iterator(node.lastToken, startPosition: node.position)
557+
return Iterator(node.lastToken(viewMode: viewMode), startPosition: node.position, viewMode: viewMode)
537558
}
538559

539560
public func reversed() -> TokenSequence {
540-
return TokenSequence(node)
561+
return TokenSequence(node, viewMode: viewMode)
541562
}
542563
}
543564

0 commit comments

Comments
 (0)