From 6a0eb08569a951d0bd9a1f67d7b8e733c130b997 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Nov 2024 22:18:46 -0500 Subject: [PATCH] Recognize infinite recursion caused by {self} --- impl/src/fmt.rs | 47 +++++++++++++++---------- impl/src/unraw.rs | 9 +++++ tests/ui/unconditional-recursion.rs | 9 +++++ tests/ui/unconditional-recursion.stderr | 21 +++++++++++ 4 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 tests/ui/unconditional-recursion.rs create mode 100644 tests/ui/unconditional-recursion.stderr diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs index 1c59d10..baff7e0 100644 --- a/impl/src/fmt.rs +++ b/impl/src/fmt.rs @@ -102,29 +102,40 @@ impl Display<'_> { MemberUnraw::Unnamed(index) => format_ident!("_{}", index), MemberUnraw::Named(ident) => ident.to_local(), }); - if let Some(&field) = member_index.get(&member) { - let end_spec = match read.find('}') { - Some(end_spec) => end_spec, - None => return Ok(()), - }; - let bound = match read[..end_spec].chars().next_back() { - Some('?') => Trait::Debug, - Some('o') => Trait::Octal, - Some('x') => Trait::LowerHex, - Some('X') => Trait::UpperHex, - Some('p') => Trait::Pointer, - Some('b') => Trait::Binary, - Some('e') => Trait::LowerExp, - Some('E') => Trait::UpperExp, - Some(_) => Trait::Display, - None => { + let end_spec = match read.find('}') { + Some(end_spec) => end_spec, + None => return Ok(()), + }; + let bound = match read[..end_spec].chars().next_back() { + Some('?') => Trait::Debug, + Some('o') => Trait::Octal, + Some('x') => Trait::LowerHex, + Some('X') => Trait::UpperHex, + Some('p') => Trait::Pointer, + Some('b') => Trait::Binary, + Some('e') => Trait::LowerExp, + Some('E') => Trait::UpperExp, + Some(_) => Trait::Display, + None => { + if member_index.contains_key(&member) { has_bonus_display = true; binding_value.extend(quote_spanned!(span=> .as_display())); - Trait::Display } - }; + Trait::Display + } + }; + if let Some(&field) = member_index.get(&member) { implied_bounds.insert((field, bound)); } + if member == *"self" && bound == Trait::Display { + binding_value = quote_spanned!(member.span()=> + { + #[warn(unconditional_recursion)] + fn _fmt() { _fmt() } + #member + } + ); + } bindings.push((local, binding_value)); } diff --git a/impl/src/unraw.rs b/impl/src/unraw.rs index 267bd02..c459b69 100644 --- a/impl/src/unraw.rs +++ b/impl/src/unraw.rs @@ -101,6 +101,15 @@ impl PartialEq for MemberUnraw { } } +impl PartialEq for MemberUnraw { + fn eq(&self, other: &str) -> bool { + match self { + MemberUnraw::Named(this) => this == other, + MemberUnraw::Unnamed(_) => false, + } + } +} + impl Hash for MemberUnraw { fn hash(&self, hasher: &mut H) { match self { diff --git a/tests/ui/unconditional-recursion.rs b/tests/ui/unconditional-recursion.rs new file mode 100644 index 0000000..9e406e8 --- /dev/null +++ b/tests/ui/unconditional-recursion.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +#[error("{self}")] +pub struct Error; + +fn main() { + @//fail +} diff --git a/tests/ui/unconditional-recursion.stderr b/tests/ui/unconditional-recursion.stderr new file mode 100644 index 0000000..72ee5dc --- /dev/null +++ b/tests/ui/unconditional-recursion.stderr @@ -0,0 +1,21 @@ +error: expected expression, found `@` + --> tests/ui/unconditional-recursion.rs:8:5 + | +8 | @//fail + | ^ expected expression + +warning: function cannot return without recursing + --> tests/ui/unconditional-recursion.rs:4:9 + | +4 | #[error("{self}")] + | ^^^^^^^^ + | | + | cannot return without recursing + | recursive call site + | + = help: a `loop` may express intention better if this is on purpose +note: the lint level is defined here + --> tests/ui/unconditional-recursion.rs:4:9 + | +4 | #[error("{self}")] + | ^^^^^^^^