Skip to content

Commit d37fe3b

Browse files
committed
When a token has both a lexer warning and a lexer error, report the error
Otherwise, we would be surpressing lexer errors by warnings, accepting source code that should be invalid.
1 parent a19ab8b commit d37fe3b

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ extension Lexer.Cursor {
309309
if let leadingTriviaMode = self.currentState.leadingTriviaLexingMode(cursor: self) {
310310
let triviaResult = self.lexTrivia(mode: leadingTriviaMode)
311311
newlineInLeadingTrivia = triviaResult.newlinePresence
312-
diagnostic = diagnostic ?? triviaResult.error?.tokenDiagnostic(tokenStart: cursor)
312+
diagnostic = TokenDiagnostic(combining: diagnostic, triviaResult.error?.tokenDiagnostic(tokenStart: cursor))
313313
} else {
314314
newlineInLeadingTrivia = .absent
315315
}
@@ -345,7 +345,7 @@ extension Lexer.Cursor {
345345
let trailingTriviaStart = self
346346
if let trailingTriviaMode = result.trailingTriviaLexingMode ?? currentState.trailingTriviaLexingMode(cursor: self) {
347347
let triviaResult = self.lexTrivia(mode: trailingTriviaMode)
348-
diagnostic = diagnostic ?? triviaResult.error?.tokenDiagnostic(tokenStart: cursor)
348+
diagnostic = TokenDiagnostic(combining: diagnostic, triviaResult.error?.tokenDiagnostic(tokenStart: cursor))
349349
}
350350

351351
if self.currentState.shouldPopStateWhenReachingNewlineInTrailingTrivia && self.is(at: "\r", "\n") {
@@ -358,7 +358,7 @@ extension Lexer.Cursor {
358358
}
359359

360360
self.previousTokenKind = result.tokenKind.base
361-
diagnostic = diagnostic ?? result.error?.tokenDiagnostic(tokenStart: cursor)
361+
diagnostic = TokenDiagnostic(combining: diagnostic, result.error?.tokenDiagnostic(tokenStart: cursor))
362362

363363
return .init(
364364
tokenKind: result.tokenKind,

Sources/SwiftParser/StringLiterals.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fileprivate class StringLiteralExpressionIndentationChecker {
5555
if hasSufficientIndentation {
5656
return nil
5757
}
58-
if token.tokenView.tokenDiagnostic != nil {
58+
if let tokenDiagnostic = token.tokenView.tokenDiagnostic, tokenDiagnostic.severity == .error {
5959
// Token already has a lexer error, ignore the indentation error until that
6060
// error is fixed
6161
return nil

Sources/SwiftSyntax/TokenDiagnostic.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
/// surrounding structure, this defines the type of the error.
1515
/// `byteOffset` specifies at which offset the error occurred.
1616
public struct TokenDiagnostic: Hashable {
17-
public enum Severity {
18-
case error
17+
public enum Severity: Comparable {
1918
case warning
19+
case error
2020
}
2121

2222
public enum Kind {
@@ -72,6 +72,26 @@ public struct TokenDiagnostic: Hashable {
7272
}
7373
}
7474

75+
/// Picks the more severe error of `lhs` and `rhs`, preferring `lhs` if they
76+
/// have the same severity.
77+
public init?(combining lhs: TokenDiagnostic?, _ rhs: TokenDiagnostic?) {
78+
switch (lhs, rhs) {
79+
case (let lhs?, let rhs?):
80+
if rhs.severity > lhs.severity {
81+
// Prefer the rhs if it is more severe, otherwise continue using lhs.
82+
self = rhs
83+
} else {
84+
self = lhs
85+
}
86+
case (let lhs?, nil):
87+
self = lhs
88+
case (nil, let rhs?):
89+
self = rhs
90+
case (nil, nil):
91+
return nil
92+
}
93+
}
94+
7595
public var severity: Severity {
7696
switch kind {
7797
case .expectedBinaryExponentInHexFloatLiteral: return .error

Tests/SwiftParserTest/LexerTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,4 +1238,15 @@ public class LexerTests: XCTestCase {
12381238
]
12391239
)
12401240
}
1241+
1242+
func testLexerErrorOverridesLexerWarning() {
1243+
// Make sure we output the error about the malformed hex literal instead of
1244+
// the warning about the non-breaking whitespace.
1245+
AssertLexemes(
1246+
"\u{a0}0x1️⃣r",
1247+
lexemes: [
1248+
LexemeSpec(.integerLiteral, leading: "\u{a0}", text: "0xr", diagnostic: "'r' is not a valid hexadecimal digit (0-9, A-F) in integer literal")
1249+
]
1250+
)
1251+
}
12411252
}

0 commit comments

Comments
 (0)