From 9661d391b205d59b21bc8430f1f4e12883617a34 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 22 Nov 2025 11:52:22 +0800 Subject: [PATCH] Add suggest alternatives for Out-of-range \x escapes --- compiler/rustc_parse/messages.ftl | 2 - compiler/rustc_parse/src/errors.rs | 6 -- .../src/lexer/unescape_error_reporting.rs | 22 +++++- .../parser/ascii-only-character-escape.stderr | 9 +++ ...-of-range-hex-escape-suggestions-148917.rs | 19 +++++ ...range-hex-escape-suggestions-148917.stderr | 71 +++++++++++++++++++ 6 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 tests/ui/parser/out-of-range-hex-escape-suggestions-148917.rs create mode 100644 tests/ui/parser/out-of-range-hex-escape-suggestions-148917.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index c74333f5655ad..7055e60956a9c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -732,8 +732,6 @@ parse_or_in_let_chain = `||` operators are not supported in let chain conditions parse_or_pattern_not_allowed_in_fn_parameters = function parameters require top-level or-patterns in parentheses parse_or_pattern_not_allowed_in_let_binding = `let` bindings require top-level or-patterns in parentheses -parse_out_of_range_hex_escape = out of range hex escape - .label = must be a character in the range [\x00-\x7f] parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index bde179c9438f2..62a333fbf81d7 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2455,12 +2455,6 @@ pub(crate) enum UnescapeError { is_hex: bool, ch: String, }, - #[diag(parse_out_of_range_hex_escape)] - OutOfRangeHexEscape( - #[primary_span] - #[label] - Span, - ), #[diag(parse_leading_underscore_unicode_escape)] LeadingUnderscoreUnicodeEscape { #[primary_span] diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index ec59a1a01314e..482b89a187983 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -226,7 +226,27 @@ pub(crate) fn emit_unescape_error( err.emit() } EscapeError::OutOfRangeHexEscape => { - dcx.emit_err(UnescapeError::OutOfRangeHexEscape(err_span)) + let err_span = full_lit_span + .with_lo(full_lit_span.lo() + BytePos(1)) + .with_hi(full_lit_span.hi() - BytePos(1)); + let mut err = dcx.struct_span_err(err_span, "out of range hex escape"); + err.span_label(err_span, "must be a character in the range [\\x00-\\x7f]"); + + let escape_str = &lit[range]; + if lit.len() <= 4 + && escape_str.len() == 4 + && escape_str.starts_with("\\x") + && let Ok(value) = u8::from_str_radix(&escape_str[2..4], 16) + && matches!(mode, Mode::Char | Mode::Str) + { + err.help(format!("if you want to write a byte literal, use `b'{}'`", escape_str)); + err.help(format!( + "if you want to write a Unicode character, use `'\\u{{{:X}}}'`", + value + )); + } + + err.emit() } EscapeError::LeadingUnderscoreUnicodeEscape => { let (c, span) = last_char(); diff --git a/tests/ui/parser/ascii-only-character-escape.stderr b/tests/ui/parser/ascii-only-character-escape.stderr index b599b35f4b324..78844ae385e47 100644 --- a/tests/ui/parser/ascii-only-character-escape.stderr +++ b/tests/ui/parser/ascii-only-character-escape.stderr @@ -3,18 +3,27 @@ error: out of range hex escape | LL | let x = "\x80"; | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\x80'` + = help: if you want to write a Unicode character, use `'\u{80}'` error: out of range hex escape --> $DIR/ascii-only-character-escape.rs:3:14 | LL | let y = "\xff"; | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xff'` + = help: if you want to write a Unicode character, use `'\u{FF}'` error: out of range hex escape --> $DIR/ascii-only-character-escape.rs:4:14 | LL | let z = "\xe2"; | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xe2'` + = help: if you want to write a Unicode character, use `'\u{E2}'` error: aborting due to 3 previous errors diff --git a/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.rs b/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.rs new file mode 100644 index 0000000000000..201b9ebe9cefb --- /dev/null +++ b/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.rs @@ -0,0 +1,19 @@ +fn main() { + let _c = '\xFF'; //~ ERROR out of range hex escape + let _s = "\xFF"; //~ ERROR out of range hex escape + + let _c2 = '\xff'; //~ ERROR out of range hex escape + let _s2 = "\xff"; //~ ERROR out of range hex escape + + let _c3 = '\x80'; //~ ERROR out of range hex escape + let _s3 = "\x80"; //~ ERROR out of range hex escape + + // Byte literals should not get suggestions (they're already valid) + let _b = b'\xFF'; // OK + let _bs = b"\xFF"; // OK + + dbg!('\xFF'); //~ ERROR out of range hex escape + + // do not suggest for out of range escapes that are too long + dbg!("\xFFFFF"); //~ ERROR out of range hex escape +} diff --git a/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.stderr b/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.stderr new file mode 100644 index 0000000000000..867ab51534d7c --- /dev/null +++ b/tests/ui/parser/out-of-range-hex-escape-suggestions-148917.stderr @@ -0,0 +1,71 @@ +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:2:15 + | +LL | let _c = '\xFF'; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xFF'` + = help: if you want to write a Unicode character, use `'\u{FF}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:3:15 + | +LL | let _s = "\xFF"; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xFF'` + = help: if you want to write a Unicode character, use `'\u{FF}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:5:16 + | +LL | let _c2 = '\xff'; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xff'` + = help: if you want to write a Unicode character, use `'\u{FF}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:6:16 + | +LL | let _s2 = "\xff"; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xff'` + = help: if you want to write a Unicode character, use `'\u{FF}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:8:16 + | +LL | let _c3 = '\x80'; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\x80'` + = help: if you want to write a Unicode character, use `'\u{80}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:9:16 + | +LL | let _s3 = "\x80"; + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\x80'` + = help: if you want to write a Unicode character, use `'\u{80}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:15:11 + | +LL | dbg!('\xFF'); + | ^^^^ must be a character in the range [\x00-\x7f] + | + = help: if you want to write a byte literal, use `b'\xFF'` + = help: if you want to write a Unicode character, use `'\u{FF}'` + +error: out of range hex escape + --> $DIR/out-of-range-hex-escape-suggestions-148917.rs:18:11 + | +LL | dbg!("\xFFFFF"); + | ^^^^^^^ must be a character in the range [\x00-\x7f] + +error: aborting due to 8 previous errors +