Auto merge of #11265 - Alexendoo:print-literal-unicode-escapes, r=llogiq
Don't escape unicode escape braces in `print_literal` Fixes #11264 changelog: none
This commit is contained in:
commit
331d01e2bf
@ -486,9 +486,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
||||
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
|
||||
&& !arg.expr.span.from_expansion()
|
||||
&& let Some(value_string) = snippet_opt(cx, arg.expr.span)
|
||||
{
|
||||
{
|
||||
let (replacement, replace_raw) = match lit.kind {
|
||||
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
|
||||
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
|
||||
Some(extracted) => extracted,
|
||||
None => return,
|
||||
},
|
||||
@ -538,7 +538,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
||||
// `format!("{}", "a")`, `format!("{named}", named = "b")
|
||||
// ~~~~~ ~~~~~~~~~~~~~
|
||||
&& let Some(removal_span) = format_arg_removal_span(format_args, index) {
|
||||
let replacement = replacement.replace('{', "{{").replace('}', "}}");
|
||||
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
|
||||
suggestion.push((*placeholder_span, replacement));
|
||||
suggestion.push((removal_span, String::new()));
|
||||
}
|
||||
@ -631,3 +631,47 @@ fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
|
||||
|
||||
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
|
||||
}
|
||||
|
||||
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in
|
||||
/// `\u{xxxx}` are left unmodified
|
||||
#[expect(clippy::match_same_arms)]
|
||||
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
|
||||
#[derive(Clone, Copy)]
|
||||
enum State {
|
||||
Normal,
|
||||
Backslash,
|
||||
UnicodeEscape,
|
||||
}
|
||||
|
||||
let mut escaped = String::with_capacity(literal.len());
|
||||
let mut state = State::Normal;
|
||||
|
||||
for ch in literal.chars() {
|
||||
state = match (ch, state) {
|
||||
// Escape braces outside of unicode escapes by doubling them up
|
||||
('{' | '}', State::Normal) => {
|
||||
escaped.push(ch);
|
||||
State::Normal
|
||||
},
|
||||
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
|
||||
//
|
||||
// \u{aaaa} \\ \x01
|
||||
// ^ ^ ^
|
||||
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('u', State::Backslash) => State::UnicodeEscape,
|
||||
// \xAA \\
|
||||
// ^ ^
|
||||
(_, State::Backslash) => State::Normal,
|
||||
// \u{aaaa}
|
||||
// ^
|
||||
('}', State::UnicodeEscape) => State::Normal,
|
||||
_ => state,
|
||||
};
|
||||
|
||||
escaped.push(ch);
|
||||
}
|
||||
|
||||
escaped
|
||||
}
|
||||
|
@ -51,4 +51,18 @@ fn main() {
|
||||
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
|
||||
// expansion
|
||||
println!("file: {}", file!());
|
||||
|
||||
// Braces in unicode escapes should not be escaped
|
||||
println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
|
||||
println!("\\\u{1234}");
|
||||
// This does not lint because it would have to suggest unescaping the character
|
||||
println!(r"{}", "\u{ab123}");
|
||||
// These are not unicode escapes
|
||||
println!("\\u{{ab123}} \\u{{{{");
|
||||
println!(r"\u{{ab123}} \u{{{{");
|
||||
println!("\\{{ab123}} \\u{{{{");
|
||||
println!("\\u{{ab123}}");
|
||||
println!("\\\\u{{1234}}");
|
||||
|
||||
println!("mixed: {{hello}} {world}");
|
||||
}
|
||||
|
@ -51,4 +51,18 @@ fn main() {
|
||||
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
|
||||
// expansion
|
||||
println!("file: {}", file!());
|
||||
|
||||
// Braces in unicode escapes should not be escaped
|
||||
println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
|
||||
println!("{}", "\\\u{1234}");
|
||||
// This does not lint because it would have to suggest unescaping the character
|
||||
println!(r"{}", "\u{ab123}");
|
||||
// These are not unicode escapes
|
||||
println!("{}", r"\u{ab123} \u{{");
|
||||
println!(r"{}", r"\u{ab123} \u{{");
|
||||
println!("{}", r"\{ab123} \u{{");
|
||||
println!("{}", "\\u{ab123}");
|
||||
println!("{}", "\\\\u{1234}");
|
||||
|
||||
println!("mixed: {} {world}", "{hello}");
|
||||
}
|
||||
|
@ -96,5 +96,101 @@ LL - println!("{bar} {foo}", foo = "hello", bar = "world");
|
||||
LL + println!("world hello");
|
||||
|
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:56:20
|
||||
|
|
||||
LL | println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
|
||||
LL + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:57:20
|
||||
|
|
||||
LL | println!("{}", "\\\u{1234}");
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", "\\\u{1234}");
|
||||
LL + println!("\\\u{1234}");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:61:20
|
||||
|
|
||||
LL | println!("{}", r"\u{ab123} \u{{");
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", r"\u{ab123} \u{{");
|
||||
LL + println!("\\u{{ab123}} \\u{{{{");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:62:21
|
||||
|
|
||||
LL | println!(r"{}", r"\u{ab123} \u{{");
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!(r"{}", r"\u{ab123} \u{{");
|
||||
LL + println!(r"\u{{ab123}} \u{{{{");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:63:20
|
||||
|
|
||||
LL | println!("{}", r"\{ab123} \u{{");
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", r"\{ab123} \u{{");
|
||||
LL + println!("\\{{ab123}} \\u{{{{");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:64:20
|
||||
|
|
||||
LL | println!("{}", "\\u{ab123}");
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", "\\u{ab123}");
|
||||
LL + println!("\\u{{ab123}}");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:65:20
|
||||
|
|
||||
LL | println!("{}", "\\\\u{1234}");
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("{}", "\\\\u{1234}");
|
||||
LL + println!("\\\\u{{1234}}");
|
||||
|
|
||||
|
||||
error: literal with an empty format string
|
||||
--> $DIR/print_literal.rs:67:35
|
||||
|
|
||||
LL | println!("mixed: {} {world}", "{hello}");
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: try
|
||||
|
|
||||
LL - println!("mixed: {} {world}", "{hello}");
|
||||
LL + println!("mixed: {{hello}} {world}");
|
||||
|
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user