Based on issue #64732, when creating a byte literal with single quotes, the suggestion message would indicate that you meant to write a `str` literal, but we actually meant to write a byte string literal. So I changed the unescape_error_reporting.rs to decide whether to print out "if you meant to write a `str` literal, use double quotes", or "if you meant to write a byte string literal, use double quotes".
215 lines
7.9 KiB
Rust
215 lines
7.9 KiB
Rust
//! Utilities for rendering escape sequence errors as diagnostics.
|
|
|
|
use std::ops::Range;
|
|
use std::iter::once;
|
|
|
|
use rustc_lexer::unescape::{EscapeError, Mode};
|
|
use syntax_pos::{Span, BytePos};
|
|
|
|
use crate::errors::{Handler, Applicability};
|
|
|
|
pub(crate) fn emit_unescape_error(
|
|
handler: &Handler,
|
|
// interior part of the literal, without quotes
|
|
lit: &str,
|
|
// full span of the literal, including quotes
|
|
span_with_quotes: Span,
|
|
mode: Mode,
|
|
// range of the error inside `lit`
|
|
range: Range<usize>,
|
|
error: EscapeError,
|
|
) {
|
|
log::debug!("emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}",
|
|
lit, span_with_quotes, mode, range, error);
|
|
let span = {
|
|
let Range { start, end } = range;
|
|
let (start, end) = (start as u32, end as u32);
|
|
let lo = span_with_quotes.lo() + BytePos(start + 1);
|
|
let hi = lo + BytePos(end - start);
|
|
span_with_quotes
|
|
.with_lo(lo)
|
|
.with_hi(hi)
|
|
};
|
|
let last_char = || {
|
|
let c = lit[range.clone()].chars().rev().next().unwrap();
|
|
let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32));
|
|
(c, span)
|
|
};
|
|
match error {
|
|
EscapeError::LoneSurrogateUnicodeEscape => {
|
|
handler.struct_span_err(span, "invalid unicode character escape")
|
|
.help("unicode escape must not be a surrogate")
|
|
.emit();
|
|
}
|
|
EscapeError::OutOfRangeUnicodeEscape => {
|
|
handler.struct_span_err(span, "invalid unicode character escape")
|
|
.help("unicode escape must be at most 10FFFF")
|
|
.emit();
|
|
}
|
|
EscapeError::MoreThanOneChar => {
|
|
let msg = if mode.is_bytes() {
|
|
"if you meant to write a byte string literal, use double quotes"
|
|
} else {
|
|
"if you meant to write a `str` literal, use double quotes"
|
|
};
|
|
|
|
handler
|
|
.struct_span_err(
|
|
span_with_quotes,
|
|
"character literal may only contain one codepoint",
|
|
)
|
|
.span_suggestion(
|
|
span_with_quotes,
|
|
msg,
|
|
format!("\"{}\"", lit),
|
|
Applicability::MachineApplicable,
|
|
).emit()
|
|
}
|
|
EscapeError::EscapeOnlyChar => {
|
|
let (c, _span) = last_char();
|
|
|
|
let mut msg = if mode.is_bytes() {
|
|
"byte constant must be escaped: "
|
|
} else {
|
|
"character constant must be escaped: "
|
|
}.to_string();
|
|
push_escaped_char(&mut msg, c);
|
|
|
|
handler.span_err(span, msg.as_str())
|
|
}
|
|
EscapeError::BareCarriageReturn => {
|
|
let msg = if mode.in_double_quotes() {
|
|
"bare CR not allowed in string, use \\r instead"
|
|
} else {
|
|
"character constant must be escaped: \\r"
|
|
};
|
|
handler.span_err(span, msg);
|
|
}
|
|
EscapeError::BareCarriageReturnInRawString => {
|
|
assert!(mode.in_double_quotes());
|
|
let msg = "bare CR not allowed in raw string";
|
|
handler.span_err(span, msg);
|
|
}
|
|
EscapeError::InvalidEscape => {
|
|
let (c, span) = last_char();
|
|
|
|
let label = if mode.is_bytes() {
|
|
"unknown byte escape"
|
|
} else {
|
|
"unknown character escape"
|
|
};
|
|
let mut msg = label.to_string();
|
|
msg.push_str(": ");
|
|
push_escaped_char(&mut msg, c);
|
|
|
|
let mut diag = handler.struct_span_err(span, msg.as_str());
|
|
diag.span_label(span, label);
|
|
if c == '{' || c == '}' && !mode.is_bytes() {
|
|
diag.help("if used in a formatting string, \
|
|
curly braces are escaped with `{{` and `}}`");
|
|
} else if c == '\r' {
|
|
diag.help("this is an isolated carriage return; \
|
|
consider checking your editor and version control settings");
|
|
}
|
|
diag.emit();
|
|
}
|
|
EscapeError::TooShortHexEscape => {
|
|
handler.span_err(span, "numeric character escape is too short")
|
|
}
|
|
EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
|
|
let (c, span) = last_char();
|
|
|
|
let mut msg = if error == EscapeError::InvalidCharInHexEscape {
|
|
"invalid character in numeric character escape: "
|
|
} else {
|
|
"invalid character in unicode escape: "
|
|
}.to_string();
|
|
push_escaped_char(&mut msg, c);
|
|
|
|
handler.span_err(span, msg.as_str())
|
|
}
|
|
EscapeError::NonAsciiCharInByte => {
|
|
assert!(mode.is_bytes());
|
|
let (_c, span) = last_char();
|
|
handler.span_err(span, "byte constant must be ASCII. \
|
|
Use a \\xHH escape for a non-ASCII byte")
|
|
}
|
|
EscapeError::NonAsciiCharInByteString => {
|
|
assert!(mode.is_bytes());
|
|
let (_c, span) = last_char();
|
|
handler.span_err(span, "raw byte string must be ASCII")
|
|
}
|
|
EscapeError::OutOfRangeHexEscape => {
|
|
handler.span_err(span, "this form of character escape may only be used \
|
|
with characters in the range [\\x00-\\x7f]")
|
|
}
|
|
EscapeError::LeadingUnderscoreUnicodeEscape => {
|
|
let (_c, span) = last_char();
|
|
handler.span_err(span, "invalid start of unicode escape")
|
|
}
|
|
EscapeError::OverlongUnicodeEscape => {
|
|
handler.span_err(span, "overlong unicode escape (must have at most 6 hex digits)")
|
|
}
|
|
EscapeError::UnclosedUnicodeEscape => {
|
|
handler.span_err(span, "unterminated unicode escape (needed a `}`)")
|
|
}
|
|
EscapeError::NoBraceInUnicodeEscape => {
|
|
let msg = "incorrect unicode escape sequence";
|
|
let mut diag = handler.struct_span_err(span, msg);
|
|
|
|
let mut suggestion = "\\u{".to_owned();
|
|
let mut suggestion_len = 0;
|
|
let (c, char_span) = last_char();
|
|
let chars = once(c).chain(lit[range.end..].chars());
|
|
for c in chars.take(6).take_while(|c| c.is_digit(16)) {
|
|
suggestion.push(c);
|
|
suggestion_len += c.len_utf8();
|
|
}
|
|
|
|
if suggestion_len > 0 {
|
|
suggestion.push('}');
|
|
let lo = char_span.lo();
|
|
let hi = lo + BytePos(suggestion_len as u32);
|
|
diag.span_suggestion(
|
|
span.with_lo(lo).with_hi(hi),
|
|
"format of unicode escape sequences uses braces",
|
|
suggestion,
|
|
Applicability::MaybeIncorrect,
|
|
);
|
|
} else {
|
|
diag.span_label(span, msg);
|
|
diag.help(
|
|
"format of unicode escape sequences is `\\u{...}`",
|
|
);
|
|
}
|
|
|
|
diag.emit();
|
|
}
|
|
EscapeError::UnicodeEscapeInByte => {
|
|
handler.span_err(span, "unicode escape sequences cannot be used \
|
|
as a byte or in a byte string")
|
|
}
|
|
EscapeError::EmptyUnicodeEscape => {
|
|
handler.span_err(span, "empty unicode escape (must have at least 1 hex digit)")
|
|
}
|
|
EscapeError::ZeroChars => {
|
|
handler.span_err(span, "empty character literal")
|
|
}
|
|
EscapeError::LoneSlash => {
|
|
handler.span_err(span, "invalid trailing slash in literal")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Pushes a character to a message string for error reporting
|
|
pub(crate) fn push_escaped_char(msg: &mut String, c: char) {
|
|
match c {
|
|
'\u{20}'..='\u{7e}' => {
|
|
// Don't escape \, ' or " for user-facing messages
|
|
msg.push(c);
|
|
}
|
|
_ => {
|
|
msg.extend(c.escape_default());
|
|
}
|
|
}
|
|
}
|