From ef59afe2d4437e0c24421f501fafe88cf3ca5740 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Nov 2024 15:10:00 -0500 Subject: [PATCH 1/2] Delete support for raw identifiers inside format string --- impl/src/fmt.rs | 28 ++++++++++++++++------------ tests/test_display.rs | 15 ++------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs index 3d1394c8..5d026d1f 100644 --- a/impl/src/fmt.rs +++ b/impl/src/fmt.rs @@ -1,7 +1,7 @@ use crate::ast::Field; use crate::attr::{Display, Trait}; use crate::scan_expr::scan_expr; -use proc_macro2::{TokenStream, TokenTree}; +use proc_macro2::{Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use std::collections::{BTreeSet as Set, HashMap as Map}; use syn::ext::IdentExt; @@ -87,14 +87,10 @@ impl Display<'_> { }; implied_bounds.insert((field, bound)); } - let local = match &member { + let formatvar = match &member { Member::Unnamed(index) => format_ident!("_{}", index), Member::Named(ident) => ident.clone(), }; - let mut formatvar = local.clone(); - if formatvar.to_string().starts_with("r#") { - formatvar = format_ident!("r_{}", formatvar); - } out += &formatvar.to_string(); if !named_args.insert(formatvar.clone()) { // Already specified in the format argument list. @@ -103,6 +99,7 @@ impl Display<'_> { if !has_trailing_comma { args.extend(quote_spanned!(span=> ,)); } + let local = raw_if_needed(&formatvar); args.extend(quote_spanned!(span=> #formatvar = #local)); if read.starts_with('}') && member_index.contains_key(&member) { has_bonus_display = true; @@ -233,11 +230,6 @@ fn take_int(read: &mut &str) -> String { fn take_ident(read: &mut &str) -> Ident { let mut ident = String::new(); - let raw = read.starts_with("r#"); - if raw { - ident.push_str("r#"); - *read = &read[2..]; - } for (i, ch) in read.char_indices() { match ch { 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch), @@ -247,5 +239,17 @@ fn take_ident(read: &mut &str) -> Ident { } } } - Ident::parse_any.parse_str(&ident).unwrap() + Ident::new(&ident, Span::call_site()) +} + +fn raw_if_needed(ident: &Ident) -> Ident { + let repr = ident.to_string(); + if syn::parse_str::(&repr).is_err() { + if let "_" | "super" | "self" | "Self" | "crate" = repr.as_str() { + // Some identifiers are never allowed to appear as raw, like r#self and r#_. + } else { + return Ident::new_raw(&repr, Span::call_site()); + } + } + ident.clone() } diff --git a/tests/test_display.rs b/tests/test_display.rs index 7fad3861..27bb72ca 100644 --- a/tests/test_display.rs +++ b/tests/test_display.rs @@ -279,7 +279,7 @@ fn test_macro_rules() { #[test] fn test_raw() { #[derive(Error, Debug)] - #[error("braced raw error: {r#fn}")] + #[error("braced raw error: {fn}")] struct Error { r#fn: &'static str, } @@ -291,24 +291,13 @@ fn test_raw() { fn test_raw_enum() { #[derive(Error, Debug)] enum Error { - #[error("braced raw error: {r#fn}")] + #[error("braced raw error: {fn}")] Braced { r#fn: &'static str }, } assert("braced raw error: T", Error::Braced { r#fn: "T" }); } -#[test] -fn test_raw_conflict() { - #[derive(Error, Debug)] - enum Error { - #[error("braced raw error: {r#func}, {func}", func = "U")] - Braced { r#func: &'static str }, - } - - assert("braced raw error: T, U", Error::Braced { r#func: "T" }); -} - #[test] fn test_keyword() { #[derive(Error, Debug)] From 11428341395052b8201683cc12763c10fccc6761 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Nov 2024 15:22:02 -0500 Subject: [PATCH 2/2] Add ui test of raw identifier in format string --- tests/ui/raw-identifier.rs | 12 ++++++++++++ tests/ui/raw-identifier.stderr | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/raw-identifier.rs create mode 100644 tests/ui/raw-identifier.stderr diff --git a/tests/ui/raw-identifier.rs b/tests/ui/raw-identifier.rs new file mode 100644 index 00000000..e7e66d05 --- /dev/null +++ b/tests/ui/raw-identifier.rs @@ -0,0 +1,12 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("error: {r#fn}")] +pub struct Error { + r#fn: &'static str, +} + +fn main() { + let r#fn = "..."; + let _ = format!("error: {r#fn}"); +} diff --git a/tests/ui/raw-identifier.stderr b/tests/ui/raw-identifier.stderr new file mode 100644 index 00000000..a3ce94da --- /dev/null +++ b/tests/ui/raw-identifier.stderr @@ -0,0 +1,21 @@ +error: invalid format string: raw identifiers are not supported + --> tests/ui/raw-identifier.rs:4:18 + | +4 | #[error("error: {r#fn}")] + | --^^ + | | + | raw identifier used here in format string + | help: remove the `r#` + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` + +error: invalid format string: raw identifiers are not supported + --> tests/ui/raw-identifier.rs:11:30 + | +11 | let _ = format!("error: {r#fn}"); + | --^^ + | | + | raw identifier used here in format string + | help: remove the `r#` + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`