Repository: dtolnay/thiserror
Pull request #399 (2024-12-18 03:14:36)

Fix spans on macro-generated bindings and format variables
Fixes https://github.com/dtolnay/thiserror/issues/398.


Patch:
diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs
index 5c0ff50..1da28c1 100644
--- a/impl/src/fmt.rs
+++ b/impl/src/fmt.rs
@@ -107,12 +107,14 @@ impl Display<'_> {
                 }
             };
             infinite_recursive |= member == *"self" && bound == Trait::Display;
-            if let Some(&field) = member_index.get(&member) {
-                implied_bounds.insert((field, bound));
-            } else {
-                out += &member.to_string();
-                continue;
-            }
+            let field = match member_index.get(&member) {
+                Some(&field) => field,
+                None => {
+                    out += &member.to_string();
+                    continue;
+                }
+            };
+            implied_bounds.insert((field, bound));
             let formatvar_prefix = if bonus_display {
                 "__display"
             } else if bound == Trait::Pointer {
@@ -129,15 +131,17 @@ impl Display<'_> {
             while user_named_args.contains(&formatvar) {
                 formatvar = IdentUnraw::new(format_ident!("_{}", formatvar.to_string()));
             }
+            formatvar.set_span(span);
             out += &formatvar.to_string();
             if !macro_named_args.insert(formatvar.clone()) {
                 // Already added to bindings by a previous use.
                 continue;
             }
-            let binding_value = match &member {
+            let mut binding_value = match &member {
                 MemberUnraw::Unnamed(index) => format_ident!("_{}", index),
                 MemberUnraw::Named(ident) => ident.to_local(),
             };
+            binding_value.set_span(span.resolved_at(fields[field].member.span()));
             let wrapped_binding_value = if bonus_display {
                 quote_spanned!(span=> #binding_value.as_display())
             } else if bound == Trait::Pointer {
diff --git a/impl/src/unraw.rs b/impl/src/unraw.rs
index a232621..73b9970 100644
--- a/impl/src/unraw.rs
+++ b/impl/src/unraw.rs
@@ -28,6 +28,10 @@ impl IdentUnraw {
         }
         unraw
     }
+
+    pub fn set_span(&mut self, span: Span) {
+        self.0.set_span(span);
+    }
 }
 
 impl Display for IdentUnraw {


Test patch:
diff --git a/tests/test_display.rs b/tests/test_display.rs
index 5ee6e36..71c4a4a 100644
--- a/tests/test_display.rs
+++ b/tests/test_display.rs
@@ -266,7 +266,7 @@ fn test_pointer() {
 }
 
 #[test]
-fn test_macro_rules() {
+fn test_macro_rules_variant_from_call_site() {
     // Regression test for https://github.com/dtolnay/thiserror/issues/86
 
     macro_rules! decl_error {
@@ -291,6 +291,30 @@ fn test_macro_rules() {
     assert("0", Error1::Repro(0));
 }
 
+#[test]
+fn test_macro_rules_message_from_call_site() {
+    // Regression test for https://github.com/dtolnay/thiserror/issues/398
+
+    macro_rules! decl_error {
+        ($($errors:tt)*) => {
+            #[derive(Error, Debug)]
+            pub enum Error {
+                $($errors)*
+            }
+        };
+    }
+
+    decl_error! {
+        #[error("{0}")]
+        Unnamed(u8),
+        #[error("{x}")]
+        Named { x: u8 },
+    }
+
+    assert("0", Error::Unnamed(0));
+    assert("0", Error::Named { x: 0 });
+}
+
 #[test]
 fn test_raw() {
     #[derive(Error, Debug)]