@@ -39,8 +39,20 @@ public extension LexerError {
39
39
/// Please order the cases in this enum alphabetically by case name.
40
40
public enum StaticLexerError : String , DiagnosticMessage {
41
41
case expectedBinaryExponentInHexFloatLiteral = " hexadecimal floating point literal must end with an exponent "
42
+ case expectedClosingBraceInUnicodeEscape = #"expected '}' in \u{...} escape sequence"#
42
43
case expectedDigitInFloatLiteral = " expected a digit in floating point exponent "
44
+ case expectedHexCodeInUnicodeEscape = #"expected hexadecimal code in \u{...} escape sequence"#
45
+ case expectedHexDigitInHexLiteral = " expected hexadecimal digit (0-9, A-F) in integer literal "
46
+ case invalidCharacter = " invalid character in source file "
47
+ case invalidEscapeSequenceInStringLiteral = " invalid escape sequence in literal "
48
+ case invalidIdentifierStartCharacter = " an identifier cannot begin with this character "
49
+ case invalidNumberOfHexDigitsInUnicodeEscape = #"\u{...} escape sequence expects between 1 and 8 hex digits"#
50
+ case invalidUtf8 = " invalid UTF-8 found in source file "
43
51
case lexerErrorOffsetOverflow = " the lexer dicovered an error in this token but was not able to represent its offset due to overflow; please split the token "
52
+ case sourceConflictMarker = " source control conflict marker in source file "
53
+ case unexpectedBlockCommentEnd = " unexpected end of block comment "
54
+ case unicodeCurlyQuote = #"unicode curly quote found; use '"' instead"#
55
+ case unprintableAsciiCharacter = " unprintable ASCII character found in source file "
44
56
45
57
public var message : String { self . rawValue }
46
58
@@ -51,6 +63,20 @@ public enum StaticLexerError: String, DiagnosticMessage {
51
63
public var severity : DiagnosticSeverity { . error }
52
64
}
53
65
66
+ /// Please order the cases in this enum alphabetically by case name.
67
+ public enum StaticLexerWarning : String , DiagnosticMessage {
68
+ case nonBreakingSpace = " non-breaking space (U+00A0) used instead of regular space "
69
+ case nulCharacter = " nul character embedded in middle of file "
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 { . warning }
78
+ }
79
+
54
80
public struct InvalidFloatingPointExponentDigit : LexerError {
55
81
public enum Kind {
56
82
case digit( Unicode . Scalar )
@@ -98,45 +124,82 @@ public extension SwiftSyntax.LexerError {
98
124
/// `tokenText` is the entire text of the token in which the `LexerError`
99
125
/// occurred, including trivia.
100
126
@_spi ( RawSyntax)
101
- func diagnostic ( wholeTextBytes: [ UInt8 ] ) -> DiagnosticMessage {
127
+ func diagnosticMessage ( wholeTextBytes: [ UInt8 ] ) -> DiagnosticMessage {
102
128
var scalarAtErrorOffset : UnicodeScalar {
103
129
// Fall back to the Unicode replacement character U+FFFD in case we can't
104
130
// lex the unicode character at `byteOffset`. It's the best we can do
105
131
Unicode . Scalar. lexing ( from: wholeTextBytes [ Int ( self . byteOffset) ... ] ) ?? UnicodeScalar ( " � " )
106
132
}
107
133
108
134
switch self . kind {
109
- case . expectedBinaryExponentInHexFloatLiteral:
110
- return StaticLexerError . expectedBinaryExponentInHexFloatLiteral
111
- case . expectedDigitInFloatLiteral:
112
- return StaticLexerError . expectedDigitInFloatLiteral
135
+ case . expectedBinaryExponentInHexFloatLiteral: return StaticLexerError . expectedBinaryExponentInHexFloatLiteral
136
+ case . expectedClosingBraceInUnicodeEscape: return StaticLexerError . expectedClosingBraceInUnicodeEscape
137
+ case . expectedDigitInFloatLiteral: return StaticLexerError . expectedDigitInFloatLiteral
138
+ case . expectedHexCodeInUnicodeEscape: return StaticLexerError . expectedHexCodeInUnicodeEscape
139
+ case . expectedHexDigitInHexLiteral: return StaticLexerError . expectedHexDigitInHexLiteral
113
140
case . insufficientIndentationInMultilineStringLiteral:
114
141
// This should be diagnosed when visiting the `StringLiteralExprSyntax`
115
142
// inside `ParseDiagnosticsGenerator` but fall back to an error message
116
143
// here in case the error is not diagnosed.
117
144
return InvalidIndentationInMultiLineStringLiteralError ( kind: . insufficientIndentation, lines: 1 )
118
- case . invalidBinaryDigitInIntegerLiteral:
119
- return InvalidDigitInIntegerLiteral ( kind : . binary ( scalarAtErrorOffset ) )
120
- case . invalidDecimalDigitInIntegerLiteral:
121
- return InvalidDigitInIntegerLiteral ( kind : . decimal ( scalarAtErrorOffset ) )
122
- case . invalidFloatingPointCharacter :
123
- fatalError ( )
124
- case . invalidFloatingPointDigit :
125
- fatalError ( )
126
- case . invalidFloatingPointExponentCharacter :
127
- return InvalidFloatingPointExponentDigit ( kind: . character ( scalarAtErrorOffset) )
128
- case . invalidFloatingPointExponentDigit :
129
- return InvalidFloatingPointExponentDigit ( kind : . digit ( scalarAtErrorOffset ) )
130
- case . invalidHexDigitInIntegerLiteral :
131
- return InvalidDigitInIntegerLiteral ( kind : . hex ( scalarAtErrorOffset ) )
132
- case . invalidOctalDigitInIntegerLiteral :
133
- return InvalidDigitInIntegerLiteral ( kind : . octal ( scalarAtErrorOffset ) )
134
- case . lexerErrorOffsetOverflow :
135
- return StaticLexerError . lexerErrorOffsetOverflow
145
+ case . invalidBinaryDigitInIntegerLiteral: return InvalidDigitInIntegerLiteral ( kind : . binary ( scalarAtErrorOffset ) )
146
+ case . invalidCharacter : return StaticLexerError . invalidCharacter
147
+ case . invalidDecimalDigitInIntegerLiteral: return InvalidDigitInIntegerLiteral ( kind : . decimal ( scalarAtErrorOffset ) )
148
+ case . invalidEscapeSequenceInStringLiteral : return StaticLexerError . invalidEscapeSequenceInStringLiteral
149
+ case . invalidFloatingPointExponentCharacter : return InvalidFloatingPointExponentDigit ( kind : . character ( scalarAtErrorOffset ) )
150
+ case . invalidFloatingPointExponentDigit : return InvalidFloatingPointExponentDigit ( kind : . digit ( scalarAtErrorOffset ) )
151
+ case . invalidHexDigitInIntegerLiteral : return InvalidDigitInIntegerLiteral ( kind : . hex ( scalarAtErrorOffset ) )
152
+ case . invalidIdentifierStartCharacter : return StaticLexerError . invalidIdentifierStartCharacter
153
+ case . invalidNumberOfHexDigitsInUnicodeEscape : return StaticLexerError . invalidNumberOfHexDigitsInUnicodeEscape
154
+ case . invalidOctalDigitInIntegerLiteral : return InvalidDigitInIntegerLiteral ( kind: . octal ( scalarAtErrorOffset) )
155
+ case . invalidUtf8 : return StaticLexerError . invalidUtf8
156
+ case . lexerErrorOffsetOverflow : return StaticLexerError . lexerErrorOffsetOverflow
157
+ case . nonBreakingSpace : return StaticLexerWarning . nonBreakingSpace
158
+ case . nulCharacter : return StaticLexerWarning . nulCharacter
159
+ case . sourceConflictMarker : return StaticLexerError . sourceConflictMarker
160
+ case . unexpectedBlockCommentEnd : return StaticLexerError . unexpectedBlockCommentEnd
161
+ case . unicodeCurlyQuote : return StaticLexerError . unicodeCurlyQuote
162
+ case . unprintableAsciiCharacter : return StaticLexerError . unprintableAsciiCharacter
136
163
}
137
164
}
138
165
139
- func diagnostic( in token: TokenSyntax ) -> DiagnosticMessage {
140
- return self . diagnostic ( wholeTextBytes: token. syntaxTextBytes)
166
+ func diagnosticMessage( in token: TokenSyntax ) -> DiagnosticMessage {
167
+ return self . diagnosticMessage ( wholeTextBytes: token. syntaxTextBytes)
168
+ }
169
+
170
+ func fixIts( in token: TokenSyntax ) -> [ FixIt ] {
171
+ switch self . kind {
172
+ case . nonBreakingSpace:
173
+ let replaceNonBreakingSpace = { ( piece: TriviaPiece ) -> TriviaPiece in
174
+ if piece == . unexpectedText( " \u{a0} " ) {
175
+ return . spaces( 1 )
176
+ } else {
177
+ return piece
178
+ }
179
+ }
180
+ let fixedToken =
181
+ token
182
+ . with ( \. leadingTrivia, Trivia ( pieces: token. leadingTrivia. map ( replaceNonBreakingSpace) ) )
183
+ . with ( \. trailingTrivia, Trivia ( pieces: token. trailingTrivia. map ( replaceNonBreakingSpace) ) )
184
+ return [
185
+ FixIt ( message: . replaceNonBreakingSpaceBySpace, changes: [ [ . replace( oldNode: Syntax ( token) , newNode: Syntax ( fixedToken) ) ] ] )
186
+ ]
187
+ case . unicodeCurlyQuote:
188
+ let ( rawKind, text) = token. tokenKind. decomposeToRaw ( )
189
+ guard let text = text else {
190
+ return [ ]
191
+ }
192
+ let replacedText =
193
+ text
194
+ . replacingFirstOccurence ( of: " “ " , with: #"""# )
195
+ . replacingLastOccurence ( of: " ” " , with: #"""# )
196
+
197
+ let fixedToken = token. withKind ( TokenKind . fromRaw ( kind: rawKind, text: replacedText) )
198
+ return [
199
+ FixIt ( message: . replaceCurlyQuoteByNormalQuote, changes: [ [ . replace( oldNode: Syntax ( token) , newNode: Syntax ( fixedToken) ) ] ] )
200
+ ]
201
+ default :
202
+ return [ ]
203
+ }
141
204
}
142
205
}
0 commit comments