Skip to content

Commit cdf111e

Browse files
authored
Merge pull request #596 from ahoppen/pr/has-error
Add a recursive `hasError` flag to `RawSyntax`
2 parents 3f83d98 + 84d24e9 commit cdf111e

File tree

5 files changed

+110
-5
lines changed

5 files changed

+110
-5
lines changed

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313
typealias RawSyntaxBuffer = UnsafeBufferPointer<RawSyntax?>
1414
typealias RawTriviaPieceBuffer = UnsafeBufferPointer<RawTriviaPiece>
1515

16+
fileprivate extension SyntaxKind {
17+
/// Whether this node kind should be considered as `hasError` for purposes of `RecursiveRawSyntaxFlags`.
18+
var hasError: Bool {
19+
return self == .unexpectedNodes || self.isMissing
20+
}
21+
}
22+
23+
struct RecursiveRawSyntaxFlags: OptionSet {
24+
let rawValue: UInt8
25+
26+
/// Whether the tree contained by this layout has any missing or unexpected nodes.
27+
static let hasError = RecursiveRawSyntaxFlags(rawValue: 1 << 0)
28+
}
29+
1630
/// Node data for RawSyntax tree. Tagged union plus common data.
1731
internal struct RawSyntaxData {
1832
internal enum Payload {
@@ -54,6 +68,7 @@ internal struct RawSyntaxData {
5468
var byteLength: Int
5569
/// Number of nodes in this subtree, excluding this node.
5670
var descendantCount: Int
71+
var recursiveFlags: RecursiveRawSyntaxFlags
5772
}
5873

5974
fileprivate var payload: Payload
@@ -215,6 +230,19 @@ extension RawSyntax {
215230
kind.isUnknown
216231
}
217232

233+
var recursiveFlags: RecursiveRawSyntaxFlags {
234+
switch rawData.payload {
235+
case .materializedToken, .parsedToken:
236+
var recursiveFlags: RecursiveRawSyntaxFlags = []
237+
if presence == .missing {
238+
recursiveFlags.insert(.hasError)
239+
}
240+
return recursiveFlags
241+
case .layout(let dat):
242+
return dat.recursiveFlags
243+
}
244+
}
245+
218246
/// Child nodes.
219247
var children: RawSyntaxBuffer {
220248
switch rawData.payload {
@@ -816,12 +844,13 @@ extension RawSyntax {
816844
layout: RawSyntaxBuffer,
817845
byteLength: Int,
818846
descendantCount: Int,
847+
recursiveFlags: RecursiveRawSyntaxFlags,
819848
arena: SyntaxArena
820849
) -> RawSyntax {
821850
validateLayout(layout: layout, as: kind)
822851
let payload = RawSyntaxData.Layout(
823852
kind: kind, layout: layout,
824-
byteLength: byteLength, descendantCount: descendantCount)
853+
byteLength: byteLength, descendantCount: descendantCount, recursiveFlags: recursiveFlags)
825854
return RawSyntax(arena: arena, payload: .layout(payload))
826855
}
827856

@@ -845,23 +874,42 @@ extension RawSyntax {
845874
// Calculate the "byte width".
846875
var byteLength = 0
847876
var descendantCount = 0
877+
var recursiveFlags = RecursiveRawSyntaxFlags()
878+
if kind.hasError {
879+
recursiveFlags.insert(.hasError)
880+
}
848881
for case let node? in layoutBuffer {
849882
byteLength += node.byteLength
850883
descendantCount += node.totalNodes
884+
recursiveFlags.insert(node.recursiveFlags)
851885
arena.addChild(node.arenaReference)
852886
}
853887
return .layout(
854-
kind: kind, layout: RawSyntaxBuffer(layoutBuffer),
855-
byteLength: byteLength, descendantCount: descendantCount, arena: arena)
888+
kind: kind,
889+
layout: RawSyntaxBuffer(layoutBuffer),
890+
byteLength: byteLength,
891+
descendantCount: descendantCount,
892+
recursiveFlags: recursiveFlags,
893+
arena: arena
894+
)
856895
}
857896

858897
static func makeEmptyLayout(
859898
kind: SyntaxKind,
860899
arena: SyntaxArena
861900
) -> RawSyntax {
901+
var recursiveFlags = RecursiveRawSyntaxFlags()
902+
if kind.hasError {
903+
recursiveFlags.insert(.hasError)
904+
}
862905
return .layout(
863-
kind: kind, layout: .init(start: nil, count: 0),
864-
byteLength: 0, descendantCount: 0, arena: arena)
906+
kind: kind,
907+
layout: .init(start: nil, count: 0),
908+
byteLength: 0,
909+
descendantCount: 0,
910+
recursiveFlags: recursiveFlags,
911+
arena: arena
912+
)
865913
}
866914

867915
static func makeLayout<C: Collection>(

Sources/SwiftSyntax/Syntax.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ public extension SyntaxProtocol {
174174
return raw.kind.isUnknown
175175
}
176176

177+
/// Whether this tree contains a missing token or unexpected node.
178+
var hasError: Bool {
179+
return raw.recursiveFlags.contains(.hasError)
180+
}
181+
177182
/// The parent of this syntax node, or `nil` if this node is the root.
178183
var parent: Syntax? {
179184
return data.parent

Sources/SwiftSyntax/SyntaxKind.swift.gyb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ internal enum SyntaxKind: CSyntaxKind {
4949
default: return false
5050
}
5151
}
52+
53+
var isMissing: Bool {
54+
switch self {
55+
% for name in SYNTAX_BASE_KINDS:
56+
% if name not in ["Syntax", "SyntaxCollection"]:
57+
case .missing${name}: return true
58+
% end
59+
% end
60+
case .missing: return true
61+
default: return false
62+
}
63+
}
5264
}
5365

5466
extension SyntaxKind {

Sources/SwiftSyntax/gyb_generated/SyntaxKind.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,18 @@ internal enum SyntaxKind: CSyntaxKind {
339339
default: return false
340340
}
341341
}
342+
343+
var isMissing: Bool {
344+
switch self {
345+
case .missingDecl: return true
346+
case .missingExpr: return true
347+
case .missingPattern: return true
348+
case .missingStmt: return true
349+
case .missingType: return true
350+
case .missing: return true
351+
default: return false
352+
}
353+
}
342354
}
343355

344356
extension SyntaxKind {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- SyntaxTests.swift ------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import XCTest
14+
import SwiftSyntax
15+
16+
public class SyntaxTests: XCTestCase {
17+
18+
public func testHasError() {
19+
XCTAssertTrue(TokenSyntax.funcKeyword(presence: .missing).hasError)
20+
XCTAssertFalse(TokenSyntax.funcKeyword(presence: .present).hasError)
21+
XCTAssertTrue(UnexpectedNodesSyntax([]).hasError)
22+
XCTAssertTrue(MissingExprSyntax().hasError)
23+
XCTAssertFalse(CodeBlockItemListSyntax([]).hasError)
24+
25+
XCTAssertTrue(TokenListSyntax([TokenSyntax.funcKeyword(presence: .missing)]).hasError)
26+
XCTAssertFalse(TokenListSyntax([TokenSyntax.funcKeyword(presence: .present)]).hasError)
27+
}
28+
}

0 commit comments

Comments
 (0)