Skip to content

Use a token name for diagnostics to generate diagnostic messages #737

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 3 commits into from
Sep 20, 2022
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
33 changes: 19 additions & 14 deletions Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//

import SwiftDiagnostics
import SwiftSyntax
@_spi(RawSyntax) import SwiftSyntax

let diagnosticDomain: String = "SwiftParser"

Expand Down Expand Up @@ -139,22 +139,27 @@ public struct MissingTokenError: ParserError {
public let missingToken: TokenSyntax

public var message: String {
guard let parent = missingToken.parent, let parentTypeName = parent.nodeTypeNameForDiagnostics() else {
return "Expected '\(missingToken.text)'"
var message = "Expected"
if missingToken.text.isEmpty {
message += " \(missingToken.tokenKind.decomposeToRaw().rawKind.nameForDiagnostics)"
} else {
message += " '\(missingToken.text)'"
}
switch missingToken.tokenKind {
case .leftAngle, .leftBrace, .leftParen, .leftSquareBracket:
if parent.children(viewMode: .fixedUp).first?.as(TokenSyntax.self) == missingToken {
return "Expected '\(missingToken.text)' to start \(parentTypeName)"
}
case .rightAngle, .rightBrace, .rightParen, .rightSquareBracket:
if parent.children(viewMode: .fixedUp).last?.as(TokenSyntax.self) == missingToken {
return "Expected '\(missingToken.text)' to end \(parentTypeName)"
if let parent = missingToken.parent, let parentTypeName = parent.nodeTypeNameForDiagnostics() {
switch missingToken.tokenKind {
case .leftAngle, .leftBrace, .leftParen, .leftSquareBracket:
if parent.children(viewMode: .fixedUp).first?.as(TokenSyntax.self) == missingToken {
message += " to start \(parentTypeName)"
}
case .rightAngle, .rightBrace, .rightParen, .rightSquareBracket:
if parent.children(viewMode: .fixedUp).last?.as(TokenSyntax.self) == missingToken {
message += " to end \(parentTypeName)"
}
default:
message += " in \(parentTypeName)"
}
default:
break
}
return "Expected '\(missingToken.text)' in \(parentTypeName)"
return message
}

public var handledNodes: [Syntax] {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftSyntax/Raw/RawSyntaxTokenView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,9 @@ struct RawSyntaxTokenView {
func formKind() -> TokenKind {
switch raw.rawData.payload {
case .parsedToken(let dat):
return TokenKind.fromRaw(kind: dat.tokenKind, text: dat.tokenText)
return TokenKind.fromRaw(kind: dat.tokenKind, text: String(syntaxText: dat.tokenText))
case .materializedToken(let dat):
return TokenKind.fromRaw(kind: dat.tokenKind, text: dat.tokenText)
return TokenKind.fromRaw(kind: dat.tokenKind, text: String(syntaxText: dat.tokenText))
case .layout(_):
preconditionFailure("Must be invoked on a token")
}
Expand Down
19 changes: 15 additions & 4 deletions Sources/SwiftSyntax/TokenKind.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ public enum RawTokenKind: Equatable, Hashable {
}
}

public var nameForDiagnostics: String {
switch self {
case .eof: return "end of file"
% for token in SYNTAX_TOKENS:
case .${token.swift_kind()}: return "${token.name_for_diagnostics}"
% end
}
}

/// Returns `true` if the token is a Swift keyword.
///
/// Keywords are reserved unconditionally for use by Swift and may not
Expand Down Expand Up @@ -222,24 +231,26 @@ for token in SYNTAX_TOKENS:

extension TokenKind {
/// If the `rawKind` has a `defaultText`, `text` can be empty.
static func fromRaw(kind rawKind: RawTokenKind, text: SyntaxText) -> TokenKind {
@_spi(RawSyntax)
public static func fromRaw(kind rawKind: RawTokenKind, text: String) -> TokenKind {
switch rawKind {
case .eof: return .eof
% for token in SYNTAX_TOKENS:
case .${token.swift_kind()}:
% if token.text:
assert(text.isEmpty || rawKind.defaultText == text)
assert(text.isEmpty || rawKind.defaultText.map(String.init) == text)
return .${token.swift_kind()}
% else:
return .${token.swift_kind()}(String(syntaxText: text))
return .${token.swift_kind()}(text)
% end
% end
}
}

/// Returns the `RawTokenKind` of this `TokenKind` and, if this `TokenKind`
/// has associated text, the associated text, otherwise `nil`.
func decomposeToRaw() -> (rawKind: RawTokenKind, string: String?) {
@_spi(RawSyntax)
public func decomposeToRaw() -> (rawKind: RawTokenKind, string: String?) {
switch self {
case .eof: return (.eof, nil)
% for token in SYNTAX_TOKENS:
Expand Down
Loading