diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b2b339d25217f..852b19731a99d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -653,6 +653,14 @@ struct DiagMetadata<'ast> { current_elision_failures: Vec, } +#[allow(unused)] +#[derive(Debug, Default)] +struct RedundantPolyTraitRef { + poly_trait: Span, + trait_ref: Span, + generic_param_idents: Vec, +} + struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { r: &'b mut Resolver<'a, 'tcx>, @@ -693,6 +701,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { /// Count the number of places a lifetime is used. lifetime_uses: FxHashMap, + + /// Record poly-trait-ref, only used for diagnostic. + with_poly_trait_ref: FxHashMap, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -1197,6 +1208,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { debug!("visit_where_predicate {:?}", p); + self.record_where_bound_predicate_with_poly_trait(p); let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p)); self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { if let WherePredicate::BoundPredicate(WhereBoundPredicate { @@ -1306,6 +1318,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // errors at module scope should always be reported in_func_body: false, lifetime_uses: Default::default(), + with_poly_trait_ref: Default::default(), } } @@ -4702,6 +4715,39 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }); } } + + fn record_where_bound_predicate_with_poly_trait(&mut self, p: &WherePredicate) { + if let ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + bounded_ty, + bounds, + .. + }) = p + { + for bound in bounds { + if let ast::GenericBound::Trait( + ast::PolyTraitRef { + span: poly_span, + trait_ref: ast::TraitRef { path: ast::Path { span, .. }, .. }, + bound_generic_params, + .. + }, + _, + ) = bound + { + let names = bound_generic_params.iter().map(|v| v.ident).collect(); + self.with_poly_trait_ref.insert( + bounded_ty.node_id(), + RedundantPolyTraitRef { + poly_trait: *poly_span, + trait_ref: *span, + generic_param_idents: names, + }, + ); + break; + } + } + } + } } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1b8f2fc005cfc..79f0814dba3f0 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -31,7 +31,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_middle::ty; @@ -2718,10 +2718,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool, ) { let mut suggest_note = true; + for rib in self.lifetime_ribs.iter().rev() { let mut should_continue = true; match rib.kind { - LifetimeRibKind::Generics { binder: _, span, kind } => { + LifetimeRibKind::Generics { binder: node_id, span, kind } => { // Avoid suggesting placing lifetime parameters on constant items unless the relevant // feature is enabled. Suggest the parent item as a possible location if applicable. if let LifetimeBinderKind::ConstItem = kind @@ -2750,14 +2751,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { | LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound ); - let (span, sugg) = if span.is_empty() { + + let (span, sugg, rm_poly_trait_span) = if span.is_empty() { + let (generic_params, poly_trait_span, trait_ref_span) = + if let Some(with_poly_trait_ref) = + self.with_poly_trait_ref.get(&node_id) + && higher_ranked + { + debug!("surechen1 with_poly_trait_ref:{:?}", with_poly_trait_ref); + let mut generic_params = with_poly_trait_ref + .generic_param_idents + .iter() + .fold("".to_string(), |mut generic_params, x| { + generic_params += x.as_str(); + generic_params += ","; + generic_params + }); + if !generic_params.is_empty() { + generic_params = + generic_params[0..generic_params.len() - 1].to_string(); + } + ( + generic_params, + with_poly_trait_ref.poly_trait, + with_poly_trait_ref.trait_ref, + ) + } else { + ("".to_string(), DUMMY_SP, DUMMY_SP) + }; + + let rm_poly_trait_span = if generic_params.is_empty() { + DUMMY_SP + } else { + poly_trait_span.with_hi(trait_ref_span.lo()) + }; + + let lifetime_list = format!("{},{}", generic_params, name.unwrap_or("'a")); let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - name.unwrap_or("'a"), + if generic_params.is_empty() { + name.unwrap_or("'a") + } else { + &lifetime_list + }, if higher_ranked { " " } else { "" }, ); - (span, sugg) + (span, sugg, rm_poly_trait_span) } else { let span = self .r @@ -2767,15 +2807,30 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .span_through_char(span, '<') .shrink_to_hi(); let sugg = format!("{}, ", name.unwrap_or("'a")); - (span, sugg) + (span, sugg, DUMMY_SP) }; + if higher_ranked { let message = Cow::from(format!( "consider making the {} lifetime-generic with a new `{}` lifetime", kind.descr(), name.unwrap_or("'a"), )); - should_continue = suggest(err, true, span, message, sugg); + should_continue = if !rm_poly_trait_span.is_dummy() { + // For poly-trait-ref like `for<'a> Trait` in + // `T: for<'a> Trait + 'b { }`. + // We should merge the higher-ranked lifetimes: existed `for<'a>` and suggestion `for<'b>` + // or will get another err: + // `[E0316] nested quantification of lifetimes`. + err.multipart_suggestion_verbose( + message, + vec![(span, sugg), (rm_poly_trait_span, "".to_string())], + Applicability::MaybeIncorrect, + ); + false + } else { + suggest(err, true, span, message.clone(), sugg.clone()) + }; err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", @@ -3298,7 +3353,6 @@ fn mk_where_bound_predicate( poly_trait_ref: &ast::PolyTraitRef, ty: &Ty, ) -> Option { - use rustc_span::DUMMY_SP; let modified_segments = { let mut segments = path.segments.clone(); let [preceding @ .., second_last, last] = segments.as_mut_slice() else { diff --git a/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.fixed b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.fixed new file mode 100644 index 0000000000000..6265a30a20b86 --- /dev/null +++ b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.fixed @@ -0,0 +1,8 @@ +//@ run-rustfix + +#![allow(dead_code)] + +trait Trait + where for<'a,'b> T: Trait + 'b { } //~ ERROR use of undeclared lifetime name `'b` + +fn main() {} diff --git a/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs new file mode 100644 index 0000000000000..6252805e5e413 --- /dev/null +++ b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs @@ -0,0 +1,8 @@ +//@ run-rustfix + +#![allow(dead_code)] + +trait Trait + where T: for<'a> Trait + 'b { } //~ ERROR use of undeclared lifetime name `'b` + +fn main() {} diff --git a/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr new file mode 100644 index 0000000000000..1e0e939dacd8a --- /dev/null +++ b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr @@ -0,0 +1,16 @@ +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:6:31 + | +LL | where T: for<'a> Trait + 'b { } + | ^^ 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 `'b` lifetime + | +LL - where T: for<'a> Trait + 'b { } +LL + where for<'a,'b> T: Trait + 'b { } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0261`.