From da1608590e6e37a12578414438b747fd1de390cf Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 29 Jun 2025 18:36:39 +0800 Subject: [PATCH 1/2] Add ui test error-lifetime-name-issue-143150.rs Signed-off-by: xizheyin --- .../error-lifetime-name-issue-143150.rs | 20 ++++++++ .../error-lifetime-name-issue-143150.stderr | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs create mode 100644 tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr diff --git a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs new file mode 100644 index 0000000000000..6ebbb3c48437e --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs @@ -0,0 +1,20 @@ +//@ edition: 2021 +fn a(_: dyn Trait + 'r#fn) { //~ ERROR use of undeclared lifetime name `'fn` [E0261] + +} + +trait Trait {} + +#[derive(Eq, PartialEq)] +struct Test { + a: &'r#fn str, + //~^ ERROR use of undeclared lifetime name `'fn` [E0261] + //~| ERROR use of undeclared lifetime name `'fn` [E0261] +} + +trait Trait1 + where T: for<'a> Trait1 + 'r#fn { } //~ ERROR use of undeclared lifetime name `'fn` [E0261] + + + +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr new file mode 100644 index 0000000000000..6c60e1ec03fac --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr @@ -0,0 +1,46 @@ +error[E0261]: use of undeclared lifetime name `'fn` + --> $DIR/error-lifetime-name-issue-143150.rs:2:21 + | +LL | fn a(_: dyn Trait + 'r#fn) { + | - ^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'fn` here: `<'fn>` + +error[E0261]: use of undeclared lifetime name `'fn` + --> $DIR/error-lifetime-name-issue-143150.rs:10:9 + | +LL | struct Test { + | - help: consider introducing lifetime `'fn` here: `<'fn>` +LL | a: &'r#fn str, + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'fn` + --> $DIR/error-lifetime-name-issue-143150.rs:10:9 + | +LL | #[derive(Eq, PartialEq)] + | -- lifetime `'fn` is missing in item created through this procedural macro +LL | struct Test { + | - help: consider introducing lifetime `'fn` here: `<'fn>` +LL | a: &'r#fn str, + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'fn` + --> $DIR/error-lifetime-name-issue-143150.rs:16:32 + | +LL | where T: for<'a> Trait1 + 'r#fn { } + | ^^^^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'fn` lifetime + | +LL - where T: for<'a> Trait1 + 'r#fn { } +LL + where for<'fn, 'a> T: Trait1 + 'r#fn { } + | +help: consider introducing lifetime `'fn` here + | +LL | trait Trait1<'fn, T> + | ++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0261`. From 2839221fa83994fd294fecdb0914ca00d290f1a3 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Mon, 30 Jun 2025 11:11:27 +0800 Subject: [PATCH 2/2] Add lifetime-aware support for `Display` impl of `Ident` Signed-off-by: xizheyin --- compiler/rustc_parse/src/errors.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 5 +- .../rustc_resolve/src/late/diagnostics.rs | 13 +++--- compiler/rustc_span/src/symbol.rs | 25 +++++++++- .../error-lifetime-name-issue-143150.rs | 10 ++-- .../error-lifetime-name-issue-143150.stderr | 46 +++++++++++-------- 6 files changed, 69 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0f0c5434800a5..30fe662d70ea1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2176,7 +2176,7 @@ pub(crate) struct KeywordLifetime { pub(crate) struct InvalidLabel { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3cedc86dc0dbe..ec9bd1d4c8d9a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3077,7 +3077,10 @@ impl<'a> Parser<'a> { if let Some((ident, is_raw)) = self.token.lifetime() { // Disallow `'fn`, but with a better error message than `expect_lifetime`. if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() { - self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name }); + self.dcx().emit_err(errors::InvalidLabel { + span: ident.span, + name: ident.name.to_string(), + }); } self.bump(); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e7b8c988cd4af..2cdc7f3c08d05 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,6 +1,7 @@ // ignore-tidy-filelength use std::borrow::Cow; +use std::fmt::Write; use std::iter; use std::ops::Deref; @@ -3120,7 +3121,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else { self.suggest_introducing_lifetime( &mut err, - Some(lifetime_ref.ident.name.as_str()), + Some(lifetime_ref.ident), |err, _, span, message, suggestion, span_suggs| { err.multipart_suggestion_verbose( message, @@ -3138,7 +3139,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn suggest_introducing_lifetime( &self, err: &mut Diag<'_>, - name: Option<&str>, + name: Option, suggest: impl Fn( &mut Diag<'_>, bool, @@ -3185,7 +3186,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut rm_inner_binders: FxIndexSet = Default::default(); let (span, sugg) = if span.is_empty() { let mut binder_idents: FxIndexSet = Default::default(); - binder_idents.insert(Ident::from_str(name.unwrap_or("'a"))); + binder_idents.insert(name.unwrap_or(Ident::from_str("'a"))); // We need to special case binders in the following situation: // Change `T: for<'a> Trait + 'b` to `for<'a, 'b> T: Trait + 'b` @@ -3221,7 +3222,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if i != 0 { binders += ", "; } - binders += x.as_str(); + let _ = write!(binders, "{x}"); binders }, ); @@ -3240,7 +3241,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .source_map() .span_through_char(span, '<') .shrink_to_hi(); - let sugg = format!("{}, ", name.unwrap_or("'a")); + let sugg = format!("{}, ", name.unwrap_or(Ident::from_str("'a"))); (span, sugg) }; @@ -3248,7 +3249,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let message = Cow::from(format!( "consider making the {} lifetime-generic with a new `{}` lifetime", kind.descr(), - name.unwrap_or("'a"), + name.unwrap_or(Ident::from_str("'a")), )); should_continue = suggest( err, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4b8762d0dd1c2..2074c84cc6b42 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2473,6 +2473,10 @@ impl Ident { pub fn as_str(&self) -> &str { self.name.as_str() } + + pub fn as_lifetime(&self) -> Option { + self.name.as_lifetime().map(|sym| Ident::with_dummy_span(sym)) + } } impl PartialEq for Ident { @@ -2542,6 +2546,14 @@ impl IdentPrinter { impl fmt::Display for IdentPrinter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(lifetime) = self.symbol.as_lifetime() { + f.write_str("'")?; + if self.is_raw { + f.write_str("r#")?; + } + return fmt::Display::fmt(&lifetime, f); + } + if self.is_raw { f.write_str("r#")?; } else if self.symbol == kw::DollarCrate { @@ -2633,6 +2645,11 @@ impl Symbol { self == sym::empty } + pub fn as_lifetime(self) -> Option { + let name = self.as_str(); + name.strip_prefix("'").map(Symbol::intern) + } + /// This method is supposed to be used in error messages, so it's expected to be /// identical to printing the original identifier token written in source code /// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag @@ -2863,7 +2880,13 @@ impl Ident { /// We see this identifier in a normal identifier position, like variable name or a type. /// How was it written originally? Did it use the raw form? Let's try to guess. pub fn is_raw_guess(self) -> bool { - self.name.can_be_raw() && self.is_reserved() + if self.name == kw::StaticLifetime || self.name == kw::UnderscoreLifetime { + false + } else if let Some(lifetime) = self.as_lifetime() { + lifetime.is_raw_guess() + } else { + self.name.can_be_raw() && self.is_reserved() + } } /// Whether this would be the identifier for a tuple field like `self.0`, as diff --git a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs index 6ebbb3c48437e..00f279077af8c 100644 --- a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs +++ b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.rs @@ -1,5 +1,5 @@ //@ edition: 2021 -fn a(_: dyn Trait + 'r#fn) { //~ ERROR use of undeclared lifetime name `'fn` [E0261] +fn a(_: dyn Trait + 'r#fn) { //~ ERROR use of undeclared lifetime name `'r#fn` [E0261] } @@ -7,13 +7,13 @@ trait Trait {} #[derive(Eq, PartialEq)] struct Test { - a: &'r#fn str, - //~^ ERROR use of undeclared lifetime name `'fn` [E0261] - //~| ERROR use of undeclared lifetime name `'fn` [E0261] + a: &'r#fn str, + //~^ ERROR use of undeclared lifetime name `'r#fn` [E0261] + //~| ERROR use of undeclared lifetime name `'r#fn` [E0261] } trait Trait1 - where T: for<'a> Trait1 + 'r#fn { } //~ ERROR use of undeclared lifetime name `'fn` [E0261] + where T: for<'a> Trait1 + 'r#fn { } //~ ERROR use of undeclared lifetime name `'r#fn` [E0261] diff --git a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr index 6c60e1ec03fac..09a06184c30ff 100644 --- a/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr +++ b/tests/ui/lifetimes/lifetime-errors/error-lifetime-name-issue-143150.stderr @@ -1,45 +1,55 @@ -error[E0261]: use of undeclared lifetime name `'fn` +error[E0261]: use of undeclared lifetime name `'r#fn` --> $DIR/error-lifetime-name-issue-143150.rs:2:21 | LL | fn a(_: dyn Trait + 'r#fn) { - | - ^^^^^ undeclared lifetime - | | - | help: consider introducing lifetime `'fn` here: `<'fn>` + | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'r#fn` here + | +LL | fn a<'r#fn>(_: dyn Trait + 'r#fn) { + | +++++++ -error[E0261]: use of undeclared lifetime name `'fn` +error[E0261]: use of undeclared lifetime name `'r#fn` --> $DIR/error-lifetime-name-issue-143150.rs:10:9 | -LL | struct Test { - | - help: consider introducing lifetime `'fn` here: `<'fn>` -LL | a: &'r#fn str, +LL | a: &'r#fn str, | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'r#fn` here + | +LL | struct Test<'r#fn> { + | +++++++ -error[E0261]: use of undeclared lifetime name `'fn` +error[E0261]: use of undeclared lifetime name `'r#fn` --> $DIR/error-lifetime-name-issue-143150.rs:10:9 | LL | #[derive(Eq, PartialEq)] - | -- lifetime `'fn` is missing in item created through this procedural macro + | -- lifetime `'r#fn` is missing in item created through this procedural macro LL | struct Test { - | - help: consider introducing lifetime `'fn` here: `<'fn>` -LL | a: &'r#fn str, +LL | a: &'r#fn str, | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'r#fn` here + | +LL | struct Test<'r#fn> { + | +++++++ -error[E0261]: use of undeclared lifetime name `'fn` +error[E0261]: use of undeclared lifetime name `'r#fn` --> $DIR/error-lifetime-name-issue-143150.rs:16:32 | LL | where T: for<'a> Trait1 + 'r#fn { } | ^^^^^ undeclared lifetime | = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html -help: consider making the bound lifetime-generic with a new `'fn` lifetime +help: consider making the bound lifetime-generic with a new `'r#fn` lifetime | LL - where T: for<'a> Trait1 + 'r#fn { } -LL + where for<'fn, 'a> T: Trait1 + 'r#fn { } +LL + where for<'r#fn, 'a> T: Trait1 + 'r#fn { } | -help: consider introducing lifetime `'fn` here +help: consider introducing lifetime `'r#fn` here | -LL | trait Trait1<'fn, T> - | ++++ +LL | trait Trait1<'r#fn, T> + | ++++++ error: aborting due to 4 previous errors