Skip to content

Commit 9048506

Browse files
committed
Add a recursive hasError flag to RawSyntax
This allows us to check whether a subtree contains a syntax error in O(1). rdar://98575249
1 parent 23d38a1 commit 9048506

File tree

5 files changed

+78
-1
lines changed

5 files changed

+78
-1
lines changed

Sources/SwiftSyntax/RawSyntax.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ internal struct RawSyntaxData {
3636
var byteLength: Int
3737
/// Number of nodes in this subtree, excluding this node.
3838
var descendantCount: Int
39+
/// Whether the tree contained by this layout has any missing or unexpected nodes.
40+
var hasError: Bool
3941
}
4042

4143
fileprivate var payload: Payload
@@ -172,6 +174,13 @@ extension RawSyntax {
172174
kind.isUnknown
173175
}
174176

177+
var hasError: Bool {
178+
switch rawData.payload {
179+
case .materializedToken(_): return self.presence == .missing
180+
case .layout(let dat): return dat.hasError
181+
}
182+
}
183+
175184
/// Child nodes.
176185
var children: RawSyntaxBuffer {
177186
switch rawData.payload {
@@ -736,9 +745,20 @@ extension RawSyntax {
736745
arena: SyntaxArena
737746
) -> RawSyntax {
738747
validateLayout(layout: layout, as: kind)
748+
var hasError = false
749+
if kind == .unexpectedNodes || kind.isMissing {
750+
hasError = true
751+
} else {
752+
for node in layout {
753+
if let node = node, node.hasError {
754+
hasError = true
755+
break
756+
}
757+
}
758+
}
739759
let payload = RawSyntaxData.Layout(
740760
kind: kind, layout: layout,
741-
byteLength: byteLength, descendantCount: descendantCount)
761+
byteLength: byteLength, descendantCount: descendantCount, hasError: hasError)
742762
return RawSyntax(arena: arena, payload: .layout(payload))
743763
}
744764

Sources/SwiftSyntax/Syntax.swift

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

176+
/// Whether this tree contains a missing token or unexpected node.
177+
var hasError: Bool {
178+
return raw.hasError
179+
}
180+
176181
/// The parent of this syntax node, or `nil` if this node is the root.
177182
var parent: Syntax? {
178183
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)