Skip to content

When a token has both a lexer warning and a lexer error, report the error #1321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Sources/SwiftParser/Lexer/Cursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ extension Lexer.Cursor {
if let leadingTriviaMode = self.currentState.leadingTriviaLexingMode(cursor: self) {
let triviaResult = self.lexTrivia(mode: leadingTriviaMode)
newlineInLeadingTrivia = triviaResult.newlinePresence
diagnostic = diagnostic ?? triviaResult.error?.tokenDiagnostic(tokenStart: cursor)
diagnostic = TokenDiagnostic(combining: diagnostic, triviaResult.error?.tokenDiagnostic(tokenStart: cursor))
} else {
newlineInLeadingTrivia = .absent
}
Expand Down Expand Up @@ -345,7 +345,7 @@ extension Lexer.Cursor {
let trailingTriviaStart = self
if let trailingTriviaMode = result.trailingTriviaLexingMode ?? currentState.trailingTriviaLexingMode(cursor: self) {
let triviaResult = self.lexTrivia(mode: trailingTriviaMode)
diagnostic = diagnostic ?? triviaResult.error?.tokenDiagnostic(tokenStart: cursor)
diagnostic = TokenDiagnostic(combining: diagnostic, triviaResult.error?.tokenDiagnostic(tokenStart: cursor))
}

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

self.previousTokenKind = result.tokenKind.base
diagnostic = diagnostic ?? result.error?.tokenDiagnostic(tokenStart: cursor)
diagnostic = TokenDiagnostic(combining: diagnostic, result.error?.tokenDiagnostic(tokenStart: cursor))

return .init(
tokenKind: result.tokenKind,
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/StringLiterals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fileprivate class StringLiteralExpressionIndentationChecker {
if hasSufficientIndentation {
return nil
}
if token.tokenView.tokenDiagnostic != nil {
if let tokenDiagnostic = token.tokenView.tokenDiagnostic, tokenDiagnostic.severity == .error {
// Token already has a lexer error, ignore the indentation error until that
// error is fixed
return nil
Expand Down
24 changes: 22 additions & 2 deletions Sources/SwiftSyntax/TokenDiagnostic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
/// surrounding structure, this defines the type of the error.
/// `byteOffset` specifies at which offset the error occurred.
public struct TokenDiagnostic: Hashable {
public enum Severity {
case error
public enum Severity: Comparable {
case warning
case error
}

public enum Kind {
Expand Down Expand Up @@ -72,6 +72,26 @@ public struct TokenDiagnostic: Hashable {
}
}

/// Picks the more severe error of `lhs` and `rhs`, preferring `lhs` if they
/// have the same severity.
public init?(combining lhs: TokenDiagnostic?, _ rhs: TokenDiagnostic?) {
switch (lhs, rhs) {
case (let lhs?, let rhs?):
if rhs.severity > lhs.severity {
// Prefer the rhs if it is more severe, otherwise continue using lhs.
self = rhs
} else {
self = lhs
}
case (let lhs?, nil):
self = lhs
case (nil, let rhs?):
self = rhs
case (nil, nil):
return nil
}
}

public var severity: Severity {
switch kind {
case .expectedBinaryExponentInHexFloatLiteral: return .error
Expand Down
11 changes: 11 additions & 0 deletions Tests/SwiftParserTest/LexerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1238,4 +1238,15 @@ public class LexerTests: XCTestCase {
]
)
}

func testLexerErrorOverridesLexerWarning() {
// Make sure we output the error about the malformed hex literal instead of
// the warning about the non-breaking whitespace.
AssertLexemes(
"\u{a0}0x1️⃣r",
lexemes: [
LexemeSpec(.integerLiteral, leading: "\u{a0}", text: "0xr", diagnostic: "'r' is not a valid hexadecimal digit (0-9, A-F) in integer literal")
]
)
}
}