1 //! Utilities for rendering escape sequence errors as diagnostics.
6 use rustc_errors::{pluralize, Applicability, Handler};
7 use rustc_lexer::unescape::{EscapeError, Mode};
8 use rustc_span::{BytePos, Span};
10 pub(crate) fn emit_unescape_error(
12 // interior part of the literal, without quotes
14 // full span of the literal, including quotes
15 span_with_quotes: Span,
16 // interior span of the literal, without quotes
19 // range of the error inside `lit`
24 "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
32 let c = lit[range.clone()].chars().rev().next().unwrap();
33 let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
37 EscapeError::LoneSurrogateUnicodeEscape => {
39 .struct_span_err(span, "invalid unicode character escape")
40 .span_label(span, "invalid escape")
41 .help("unicode escape must not be a surrogate")
44 EscapeError::OutOfRangeUnicodeEscape => {
46 .struct_span_err(span, "invalid unicode character escape")
47 .span_label(span, "invalid escape")
48 .help("unicode escape must be at most 10FFFF")
51 EscapeError::MoreThanOneChar => {
52 use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
54 let mut has_help = false;
55 let mut handler = handler.struct_span_err(
57 "character literal may only contain one codepoint",
60 if lit.chars().skip(1).all(|c| is_combining_mark(c)) {
62 lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
66 "this `{}` is followed by the combining mark{} `{}`",
67 lit.chars().next().unwrap(),
68 pluralize!(escaped_marks.len()),
69 escaped_marks.join(""),
72 let normalized = lit.nfc().to_string();
73 if normalized.chars().count() == 1 {
75 handler.span_suggestion(
78 "consider using the normalized form `{}` of this character",
79 normalized.chars().next().unwrap().escape_default()
82 Applicability::MachineApplicable,
86 let printable: Vec<char> = lit
89 unicode_width::UnicodeWidthChar::width(x).unwrap_or(0) != 0
94 if let [ch] = printable.as_slice() {
100 "there are non-printing characters, the full sequence is `{}`",
101 lit.escape_default(),
105 handler.span_suggestion(
107 "consider removing the non-printing characters",
109 Applicability::MaybeIncorrect,
115 let (prefix, msg) = if mode.is_bytes() {
116 ("b", "if you meant to write a byte string literal, use double quotes")
118 ("", "if you meant to write a `str` literal, use double quotes")
121 handler.span_suggestion(
124 format!("{}\"{}\"", prefix, lit),
125 Applicability::MachineApplicable,
131 EscapeError::EscapeOnlyChar => {
132 let (c, char_span) = last_char();
134 let msg = if mode.is_bytes() {
135 "byte constant must be escaped"
137 "character constant must be escaped"
140 .struct_span_err(span, &format!("{}: `{}`", msg, escaped_char(c)))
143 "escape the character",
145 Applicability::MachineApplicable,
149 EscapeError::BareCarriageReturn => {
150 let msg = if mode.in_double_quotes() {
151 "bare CR not allowed in string, use `\\r` instead"
153 "character constant must be escaped: `\\r`"
156 .struct_span_err(span, msg)
159 "escape the character",
161 Applicability::MachineApplicable,
165 EscapeError::BareCarriageReturnInRawString => {
166 assert!(mode.in_double_quotes());
167 let msg = "bare CR not allowed in raw string";
168 handler.span_err(span, msg);
170 EscapeError::InvalidEscape => {
171 let (c, span) = last_char();
174 if mode.is_bytes() { "unknown byte escape" } else { "unknown character escape" };
175 let ec = escaped_char(c);
176 let mut diag = handler.struct_span_err(span, &format!("{}: `{}`", label, ec));
177 diag.span_label(span, label);
178 if c == '{' || c == '}' && !mode.is_bytes() {
180 "if used in a formatting string, curly braces are escaped with `{{` and `}}`",
182 } else if c == '\r' {
184 "this is an isolated carriage return; consider checking your editor and \
185 version control settings",
188 if !mode.is_bytes() {
189 diag.span_suggestion(
191 "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",
192 format!("r\"{}\"", lit),
193 Applicability::MaybeIncorrect,
198 "for more information, visit \
199 <https://static.rust-lang.org/doc/master/reference.html#literals>",
204 EscapeError::TooShortHexEscape => {
205 handler.span_err(span, "numeric character escape is too short");
207 EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
208 let (c, span) = last_char();
210 let msg = if error == EscapeError::InvalidCharInHexEscape {
211 "invalid character in numeric character escape"
213 "invalid character in unicode escape"
215 let c = escaped_char(c);
218 .struct_span_err(span, &format!("{}: `{}`", msg, c))
219 .span_label(span, msg)
222 EscapeError::NonAsciiCharInByte => {
223 assert!(mode.is_bytes());
224 let (c, span) = last_char();
225 let mut err = handler.struct_span_err(span, "non-ASCII character in byte constant");
226 let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 {
227 format!(" but is {:?}", c)
231 err.span_label(span, &format!("byte constant must be ASCII{}", postfix));
232 if (c as u32) <= 0xFF {
236 "if you meant to use the unicode code point for {:?}, use a \\xHH escape",
239 format!("\\x{:X}", c as u32),
240 Applicability::MaybeIncorrect,
242 } else if matches!(mode, Mode::Byte) {
243 err.span_label(span, "this multibyte character does not fit into a single byte");
244 } else if matches!(mode, Mode::ByteStr) {
245 let mut utf8 = String::new();
250 "if you meant to use the UTF-8 encoding of {:?}, use \\xHH escapes",
255 .map(|b: &u8| format!("\\x{:X}", *b))
256 .fold("".to_string(), |a, c| a + &c),
257 Applicability::MaybeIncorrect,
262 EscapeError::NonAsciiCharInByteString => {
263 assert!(mode.is_bytes());
264 let (c, span) = last_char();
265 let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 {
266 format!(" but is {:?}", c)
271 .struct_span_err(span, "raw byte string must be ASCII")
272 .span_label(span, &format!("must be ASCII{}", postfix))
275 EscapeError::OutOfRangeHexEscape => {
277 .struct_span_err(span, "out of range hex escape")
278 .span_label(span, "must be a character in the range [\\x00-\\x7f]")
281 EscapeError::LeadingUnderscoreUnicodeEscape => {
282 let (c, span) = last_char();
283 let msg = "invalid start of unicode escape";
285 .struct_span_err(span, &format!("{}: `{}`", msg, c))
286 .span_label(span, msg)
289 EscapeError::OverlongUnicodeEscape => {
291 .struct_span_err(span, "overlong unicode escape")
292 .span_label(span, "must have at most 6 hex digits")
295 EscapeError::UnclosedUnicodeEscape => {
297 .struct_span_err(span, "unterminated unicode escape")
298 .span_label(span, "missing a closing `}`")
299 .span_suggestion_verbose(
301 "terminate the unicode escape",
303 Applicability::MaybeIncorrect,
307 EscapeError::NoBraceInUnicodeEscape => {
308 let msg = "incorrect unicode escape sequence";
309 let mut diag = handler.struct_span_err(span, msg);
311 let mut suggestion = "\\u{".to_owned();
312 let mut suggestion_len = 0;
313 let (c, char_span) = last_char();
314 let chars = once(c).chain(lit[range.end..].chars());
315 for c in chars.take(6).take_while(|c| c.is_digit(16)) {
317 suggestion_len += c.len_utf8();
320 if suggestion_len > 0 {
321 suggestion.push('}');
322 let hi = char_span.lo() + BytePos(suggestion_len as u32);
323 diag.span_suggestion(
325 "format of unicode escape sequences uses braces",
327 Applicability::MaybeIncorrect,
330 diag.span_label(span, msg);
331 diag.help("format of unicode escape sequences is `\\u{...}`");
336 EscapeError::UnicodeEscapeInByte => {
337 let msg = "unicode escape in byte string";
339 .struct_span_err(span, msg)
340 .span_label(span, msg)
341 .help("unicode escape sequences cannot be used as a byte or in a byte string")
344 EscapeError::EmptyUnicodeEscape => {
346 .struct_span_err(span, "empty unicode escape")
347 .span_label(span, "this escape must have at least 1 hex digit")
350 EscapeError::ZeroChars => {
351 let msg = "empty character literal";
352 handler.struct_span_err(span, msg).span_label(span, msg).emit();
354 EscapeError::LoneSlash => {
355 let msg = "invalid trailing slash in literal";
356 handler.struct_span_err(span, msg).span_label(span, msg).emit();
358 EscapeError::UnskippedWhitespaceWarning => {
359 let (c, char_span) = last_char();
361 format!("non-ASCII whitespace symbol '{}' is not skipped", c.escape_unicode());
362 handler.struct_span_warn(span, &msg).span_label(char_span, &msg).emit();
364 EscapeError::MultipleSkippedLinesWarning => {
365 let msg = "multiple lines skipped by escaped newline";
366 let bottom_msg = "skipping everything up to and including this point";
367 handler.struct_span_warn(span, msg).span_label(span, bottom_msg).emit();
372 /// Pushes a character to a message string for error reporting
373 pub(crate) fn escaped_char(c: char) -> String {
375 '\u{20}'..='\u{7e}' => {
376 // Don't escape \, ' or " for user-facing messages
379 _ => c.escape_default().to_string(),