Skip to content

Commit ab924a6

Browse files
committed
Propagate an "Error" Bit Into the Lexemes
1 parent 41da025 commit ab924a6

14 files changed

+169
-18
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,7 @@ extension Parser {
13821382
wholeText: wholeText,
13831383
textRange: textRange,
13841384
presence: .present,
1385+
hasError: false,
13851386
arena: self.arena
13861387
)
13871388
}

Sources/SwiftParser/Lexer.swift

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
@_spi(RawSyntax) import SwiftSyntax
14+
import SwiftDiagnostics
1415

1516
/// A lexical analyzer for the Swift programming language.
1617
///
@@ -32,6 +33,7 @@ public struct Lexer {
3233

3334
public static let isAtStartOfLine = Flags(rawValue: 1 << 0)
3435
public static let isMultilineStringLiteral = Flags(rawValue: 1 << 1)
36+
public static let isErroneous = Flags(rawValue: 1 << 2)
3537
}
3638

3739
@_spi(RawSyntax)
@@ -219,6 +221,10 @@ extension Lexer {
219221
return self.pointer.distance(to: other.pointer)
220222
}
221223

224+
func absoluteDistance(to other: Self) -> AbsolutePosition {
225+
return AbsolutePosition(utf8Offset: self.distance(to: other))
226+
}
227+
222228
func peek(at offset: Int = 0) -> UInt8 {
223229
assert(!self.isAtEndOfFile)
224230
assert(offset >= 0)
@@ -750,14 +756,19 @@ extension Lexer.Cursor {
750756
}
751757

752758
extension Lexer.Cursor {
753-
mutating func nextToken(_ ContentStart: Lexer.Cursor) -> Lexer.Lexeme {
759+
mutating func nextToken(
760+
_ ContentStart: Lexer.Cursor,
761+
diagnosticHandler: ((Int, DiagnosticMessage) -> Void)? = nil
762+
) -> Lexer.Lexeme {
754763
// Leading trivia.
755764
let leadingTriviaStart = self
756765
let newlineInLeadingTrivia = self.lexTrivia(.leading)
757766

758767
// Token text.
759768
let textStart = self
760-
var (kind, flags) = self.lexImpl(ContentStart: ContentStart)
769+
var (kind, flags) = self.lexImpl(
770+
ContentStart: ContentStart,
771+
diagnosticHandler: diagnosticHandler)
761772

762773
// Trailing trivia.
763774
let trailingTriviaStart = self
@@ -777,7 +788,10 @@ extension Lexer.Cursor {
777788
trailingTriviaLength: trailingTriviaStart.distance(to: self))
778789
}
779790

780-
private mutating func lexImpl(ContentStart: Lexer.Cursor) -> (RawTokenKind, Lexer.Lexeme.Flags) {
791+
private mutating func lexImpl(
792+
ContentStart: Lexer.Cursor,
793+
diagnosticHandler: ((Int, DiagnosticMessage) -> Void)?
794+
) -> (RawTokenKind, Lexer.Lexeme.Flags) {
781795
let start = self
782796
switch self.advance() {
783797
case UInt8(ascii: "@"): return (.atSign, [])
@@ -866,7 +880,7 @@ extension Lexer.Cursor {
866880
UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"),
867881
UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"),
868882
UInt8(ascii: "9"):
869-
return self.lexNumber(start, ContentStart)
883+
return self.lexNumber(start, ContentStart, diagnosticHandler)
870884
case UInt8(ascii: #"'"#), UInt8(ascii: #"""#):
871885
return self.lexStringLiteral(start)
872886

@@ -1109,11 +1123,15 @@ extension Lexer.Cursor {
11091123
// diagnoseSingleQuoteStringLiteral(TokStart, CurPtr)
11101124
// }
11111125

1126+
var flags: Lexer.Lexeme.Flags = []
1127+
if IsMultilineString {
1128+
flags.insert(.isMultilineStringLiteral)
1129+
}
11121130
if wasErroneous {
1113-
return (.unknown, [])
1131+
flags.insert(.isErroneous)
11141132
}
11151133

1116-
return (.stringLiteral, IsMultilineString ? .isMultilineStringLiteral : [])
1134+
return (.stringLiteral, flags)
11171135
}
11181136
}
11191137

@@ -1298,7 +1316,11 @@ extension Lexer.Cursor {
12981316
/// floating_literal ::= [0-9][0-9_]*[eE][+-]?[0-9][0-9_]*
12991317
/// floating_literal ::= 0x[0-9A-Fa-f][0-9A-Fa-f_]*
13001318
/// (\.[0-9A-Fa-f][0-9A-Fa-f_]*)?[pP][+-]?[0-9][0-9_]*
1301-
mutating func lexNumber(_ TokStart: Lexer.Cursor, _ ContentStart: Lexer.Cursor) -> (RawTokenKind, Lexer.Lexeme.Flags) {
1319+
mutating func lexNumber(
1320+
_ TokStart: Lexer.Cursor,
1321+
_ ContentStart: Lexer.Cursor,
1322+
_ diagnosticHandler: ((Int, DiagnosticMessage) -> Void)? = nil
1323+
) -> (RawTokenKind, Lexer.Lexeme.Flags) {
13021324
assert((Unicode.Scalar(self.previous).isDigit || self.previous == UInt8(ascii: ".")),
13031325
"Unexpected start")
13041326

@@ -1312,7 +1334,7 @@ extension Lexer.Cursor {
13121334
// }
13131335

13141336
if !self.isAtEndOfFile && self.previous == UInt8(ascii: "0") && self.peek() == UInt8(ascii: "x") {
1315-
return self.lexHexNumber(TokStart)
1337+
return self.lexHexNumber(TokStart, diagnosticHandler)
13161338
}
13171339

13181340
if !self.isAtEndOfFile && self.previous == UInt8(ascii: "0") && self.peek() == UInt8(ascii: "o") {
@@ -1427,7 +1449,10 @@ extension Lexer.Cursor {
14271449
return (.floatingLiteral, [])
14281450
}
14291451

1430-
mutating func lexHexNumber(_ TokStart: Lexer.Cursor) -> (RawTokenKind, Lexer.Lexeme.Flags) {
1452+
mutating func lexHexNumber(
1453+
_ TokStart: Lexer.Cursor,
1454+
_ diagnosticHandler: ((Int, DiagnosticMessage) -> Void)?
1455+
) -> (RawTokenKind, Lexer.Lexeme.Flags) {
14311456
// We assume we're starting from the 'x' in a '0x...' floating-point literal.
14321457
assert(self.peek() == UInt8(ascii: "x"), "not a hex literal")
14331458
assert(self.previous == UInt8(ascii: "0"), "not a hex literal")
@@ -1438,9 +1463,10 @@ extension Lexer.Cursor {
14381463
return (.unknown, [])
14391464
}
14401465

1441-
let expected_hex_digit = { (loc: Lexer.Cursor) -> (RawTokenKind, Lexer.Lexeme.Flags) in
1466+
let expected_hex_digit = { (loc: Lexer.Cursor) -> (RawTokenKind, Lexer.Lexeme.Flags) in
14421467
// diagnose(loc, diag::lex_invalid_digit_in_hex_literal, StringRef(loc, 1),
14431468
// (unsigned)kind)
1469+
14441470
return expected_digit(loc)
14451471
}
14461472

@@ -1477,14 +1503,14 @@ extension Lexer.Cursor {
14771503

14781504
self.advance(while: { $0.isHexDigit || $0 == Unicode.Scalar("_") })
14791505

1480-
if !self.isAtEndOfFile, self.peek() != UInt8(ascii: "p") && self.peek() != UInt8(ascii: "P") {
1506+
if self.isAtEndOfFile || (self.peek() != UInt8(ascii: "p") && self.peek() != UInt8(ascii: "P")) {
14811507
if !Unicode.Scalar(PtrOnDot!.peek(at: 1)).isDigit {
14821508
// e.g: 0xff.description
14831509
self = PtrOnDot!
14841510
return (.integerLiteral, [])
14851511
}
1486-
// diagnose(CurPtr, diag::lex_expected_binary_exponent_in_hex_float_literal)
1487-
return (.unknown, [])
1512+
diagnosticHandler?(TokStart.distance(to: self), StaticLexerError.lex_expected_binary_exponent_in_hex_float_literal)
1513+
return (.integerLiteral, [ .isErroneous ])
14881514
}
14891515
} else {
14901516
PtrOnDot = nil

Sources/SwiftParser/Parser.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ public struct Parser: TokenConsumer {
196196
wholeText: tok.wholeText,
197197
textRange: tok.textRange,
198198
presence: .present,
199+
hasError: tok.flags.contains(.isErroneous),
199200
arena: arena
200201
)
201202
}
@@ -551,6 +552,7 @@ extension Parser {
551552
wholeText: SyntaxText(rebasing: current.wholeText[..<endIndex]),
552553
textRange: current.textRange.lowerBound ..< endIndex,
553554
presence: .present,
555+
hasError: current.flags.contains(.isErroneous),
554556
arena: self.arena
555557
)
556558

Sources/SwiftParserDiagnostics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
add_library(SwiftParserDiagnostics STATIC
1010
DiagnosticExtensions.swift
11+
LexerDiagnosticMessages.swift
1112
MissingNodesError.swift
1213
ParserDiagnosticMessages.swift
1314
ParseDiagnosticsGenerator.swift
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===--- LexerDiagnosticMessages.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 SwiftDiagnostics
14+
@_spi(RawSyntax) import SwiftSyntax
15+
16+
fileprivate let diagnosticDomain: String = "SwiftLexer"
17+
18+
/// A error diagnostic whose ID is determined by the diagnostic's type.
19+
public protocol LexerError: DiagnosticMessage {
20+
var diagnosticID: MessageID { get }
21+
}
22+
23+
public extension LexerError {
24+
static var diagnosticID: MessageID {
25+
return MessageID(domain: diagnosticDomain, id: "\(self)")
26+
}
27+
28+
var diagnosticID: MessageID {
29+
return Self.diagnosticID
30+
}
31+
32+
var severity: DiagnosticSeverity {
33+
return .error
34+
}
35+
}
36+
37+
public protocol LexerNote: NoteMessage {
38+
var fixItID: MessageID { get }
39+
}
40+
41+
public extension LexerNote {
42+
static var fixItID: MessageID {
43+
return MessageID(domain: diagnosticDomain, id: "\(self)")
44+
}
45+
46+
var fixItID: MessageID {
47+
return Self.fixItID
48+
}
49+
}
50+
51+
public protocol LexerFixIt: FixItMessage {
52+
var fixItID: MessageID { get }
53+
}
54+
55+
public extension LexerFixIt {
56+
static var fixItID: MessageID {
57+
return MessageID(domain: diagnosticDomain, id: "\(self)")
58+
}
59+
60+
var fixItID: MessageID {
61+
return Self.fixItID
62+
}
63+
}
64+
65+
// MARK: - Errors (please sort alphabetically)
66+
67+
/// Please order the cases in this enum alphabetically by case name.
68+
public enum StaticLexerError: String, DiagnosticMessage {
69+
case lex_expected_binary_exponent_in_hex_float_literal = "hexadecimal floating point literal must end with an exponent"
70+
71+
public var message: String { self.rawValue }
72+
73+
public var diagnosticID: MessageID {
74+
MessageID(domain: diagnosticDomain, id: "\(type(of: self)).\(self)")
75+
}
76+
77+
public var severity: DiagnosticSeverity { .error }
78+
}
79+

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,13 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
242242
} else {
243243
return handleMissingSyntax(node)
244244
}
245+
} else if node.hasError {
246+
node.syntaxTextBytes.withUnsafeBufferPointer { buf in
247+
var cursor = Lexer.Cursor(input: buf, previous: 0)
248+
_ = cursor.nextToken(cursor) { [self] offset, diagnostic in
249+
self.addDiagnostic(node, position: node.position.advanced(by: offset), diagnostic)
250+
}
251+
}
245252
}
246253
return .skipChildren
247254
}

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import SwiftDiagnostics
1414
@_spi(RawSyntax) import SwiftSyntax
1515

16-
let diagnosticDomain: String = "SwiftParser"
16+
fileprivate let diagnosticDomain: String = "SwiftParser"
1717

1818
/// A error diagnostic whose ID is determined by the diagnostic's type.
1919
public protocol ParserError: DiagnosticMessage {

Sources/SwiftSyntax/AbsolutePosition.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public struct AbsolutePosition: Comparable {
2121
self.utf8Offset = utf8Offset
2222
}
2323

24+
public func advanced(by offset: Int) -> AbsolutePosition {
25+
return AbsolutePosition(utf8Offset: self.utf8Offset + offset)
26+
}
27+
2428
public static func <(lhs: AbsolutePosition, rhs: AbsolutePosition) -> Bool {
2529
return lhs.utf8Offset < rhs.utf8Offset
2630
}

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ internal struct RawSyntaxData {
5454
var textRange: Range<SyntaxText.Index>
5555

5656
var presence: SourcePresence
57+
58+
var hasError: Bool
5759
}
5860

5961
/// Token typically created with `TokenSyntax.<someToken>`.
@@ -168,7 +170,7 @@ extension RawSyntax {
168170
switch view {
169171
case .token(let tokenView):
170172
var recursiveFlags: RecursiveRawSyntaxFlags = []
171-
if tokenView.presence == .missing {
173+
if tokenView.hasError || tokenView.presence == .missing {
172174
recursiveFlags.insert(.hasError)
173175
}
174176
return recursiveFlags
@@ -450,15 +452,17 @@ extension RawSyntax {
450452
wholeText: SyntaxText,
451453
textRange: Range<SyntaxText.Index>,
452454
presence: SourcePresence,
453-
arena: SyntaxArena
455+
arena: SyntaxArena,
456+
hasError: Bool
454457
) -> RawSyntax {
455458
assert(arena.contains(text: wholeText),
456459
"token text must be managed by the arena")
457460
let payload = RawSyntaxData.ParsedToken(
458461
tokenKind: kind,
459462
wholeText: wholeText,
460463
textRange: textRange,
461-
presence: presence
464+
presence: presence,
465+
hasError: hasError
462466
)
463467
return RawSyntax(arena: arena, payload: .parsedToken(payload))
464468
}

Sources/SwiftSyntax/Raw/RawSyntaxNodeProtocol.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,16 @@ public struct RawTokenSyntax: RawSyntaxToSyntax, RawSyntaxNodeProtocol {
132132
wholeText: SyntaxText,
133133
textRange: Range<SyntaxText.Index>,
134134
presence: SourcePresence,
135+
hasError: Bool,
135136
arena: __shared SyntaxArena
136137
) {
137138
let raw = RawSyntax.parsedToken(
138139
kind: kind,
139140
wholeText: wholeText,
140141
textRange: textRange,
141142
presence: presence,
142-
arena: arena
143+
arena: arena,
144+
hasError: hasError
143145
)
144146
self = RawTokenSyntax(raw: raw)
145147
}
@@ -161,6 +163,7 @@ public struct RawTokenSyntax: RawSyntaxToSyntax, RawSyntaxNodeProtocol {
161163
wholeText: text,
162164
textRange: 0 ..< text.count,
163165
presence: presence,
166+
hasError: false,
164167
arena: arena
165168
)
166169
} else {

Sources/SwiftSyntax/Raw/RawSyntaxTokenView.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,15 @@ public struct RawSyntaxTokenView {
216216
preconditionFailure("'presence' is a token-only property")
217217
}
218218
}
219+
220+
var hasError: Bool {
221+
switch raw.rawData.payload {
222+
case .parsedToken(let dat):
223+
return dat.hasError
224+
case .materializedToken(_):
225+
return false
226+
case .layout(_):
227+
preconditionFailure("'hasError' is a token-only property")
228+
}
229+
}
219230
}

0 commit comments

Comments
 (0)