Skip to content

Commit 8aad60d

Browse files
committed
Fix incorrect suggestion for undeclared hrtb lifetimes in where clauses.
fixes #122714
1 parent 6c90ac8 commit 8aad60d

25 files changed

+415
-105
lines changed

compiler/rustc_resolve/src/late.rs

+8
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,13 @@ struct DiagMetadata<'ast> {
651651
current_elision_failures: Vec<MissingLifetime>,
652652
}
653653

654+
// #[derive(Debug, Default)]
655+
// struct RedundantPolyTraitRef {
656+
// poly_trait: Span,
657+
// trait_ref: Span,
658+
// generic_param_idents: Vec<Ident>,
659+
// }
660+
654661
struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
655662
r: &'b mut Resolver<'a, 'tcx>,
656663

@@ -1222,6 +1229,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
12221229

12231230
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
12241231
debug!("visit_where_predicate {:?}", p);
1232+
//self.record_where_bound_predicate_with_poly_trait(p);
12251233
let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p));
12261234
self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
12271235
if let WherePredicate::BoundPredicate(WhereBoundPredicate {

compiler/rustc_resolve/src/late/diagnostics.rs

+87-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::ty::fast_reject::SimplifiedType;
77
use crate::{errors, path_names_to_string};
88
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
99
use crate::{PathResult, PathSource, Segment};
10+
use ast::HasNodeId;
1011
use rustc_hir::def::Namespace::{self, *};
1112

1213
use rustc_ast::ptr::P;
@@ -31,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
3132
use rustc_span::edition::Edition;
3233
use rustc_span::hygiene::MacroKind;
3334
use rustc_span::symbol::{kw, sym, Ident, Symbol};
34-
use rustc_span::Span;
35+
use rustc_span::{Span, DUMMY_SP};
3536

3637
use rustc_middle::ty;
3738

@@ -2711,8 +2712,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27112712
self.suggest_introducing_lifetime(
27122713
&mut err,
27132714
Some(lifetime_ref.ident.name.as_str()),
2714-
|err, _, span, message, suggestion| {
2715-
err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
2715+
|err, _, span, message, suggestion, span_suggs| {
2716+
err.multipart_suggestion_verbose(
2717+
message,
2718+
std::iter::once((span, suggestion)).chain(span_suggs.iter().cloned()).collect(),
2719+
Applicability::MaybeIncorrect,
2720+
);
27162721
true
27172722
},
27182723
);
@@ -2723,13 +2728,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27232728
&self,
27242729
err: &mut Diag<'_>,
27252730
name: Option<&str>,
2726-
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
2731+
suggest: impl Fn(
2732+
&mut Diag<'_>,
2733+
bool,
2734+
Span,
2735+
Cow<'static, str>,
2736+
String,
2737+
&Vec<(Span, String)>,
2738+
) -> bool,
27272739
) {
27282740
let mut suggest_note = true;
27292741
for rib in self.lifetime_ribs.iter().rev() {
27302742
let mut should_continue = true;
27312743
match rib.kind {
2732-
LifetimeRibKind::Generics { binder: _, span, kind } => {
2744+
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
27332745
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
27342746
// feature is enabled. Suggest the parent item as a possible location if applicable.
27352747
if let LifetimeBinderKind::ConstItem = kind
@@ -2758,11 +2770,58 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27582770
| LifetimeBinderKind::PolyTrait
27592771
| LifetimeBinderKind::WhereBound
27602772
);
2773+
2774+
let mut rm_inner_binders: FxHashSet<Span> = Default::default();
27612775
let (span, sugg) = if span.is_empty() {
2776+
let mut binder_idents: FxHashSet<Ident> = Default::default();
2777+
binder_idents.insert(if let Some(name) = name {
2778+
Ident::from_str(name)
2779+
} else {
2780+
Ident::from_str("'a")
2781+
});
2782+
2783+
// We need special treat binders in following situation:
2784+
// Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
2785+
// T: for<'a> Trait<T> + 'b
2786+
// ^^^^^^^ remove existing inner binder `for<'a>`
2787+
// for<'a, 'b> T: Trait<T> + 'b
2788+
// ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>`
2789+
if let LifetimeBinderKind::WhereBound = kind
2790+
&& let Some(ast::WherePredicate::BoundPredicate(
2791+
ast::WhereBoundPredicate { bounded_ty, bounds, .. },
2792+
)) = self.diag_metadata.current_where_predicate
2793+
&& bounded_ty.node_id() == node_id
2794+
{
2795+
for bound in bounds {
2796+
let ast::GenericBound::Trait(poly_trait_ref, _) = bound else {
2797+
continue;
2798+
};
2799+
rm_inner_binders.insert(
2800+
poly_trait_ref
2801+
.span
2802+
.with_hi(poly_trait_ref.trait_ref.path.span.lo()),
2803+
);
2804+
poly_trait_ref.bound_generic_params.iter().for_each(|v| {
2805+
binder_idents.insert(v.ident);
2806+
});
2807+
}
2808+
}
2809+
2810+
let len = binder_idents.len();
2811+
let binders_sugg = binder_idents.into_iter().enumerate().fold(
2812+
"".to_string(),
2813+
|mut binders, (i, x)| {
2814+
binders += x.as_str();
2815+
if i != len - 1 {
2816+
binders += ", ";
2817+
}
2818+
binders
2819+
},
2820+
);
27622821
let sugg = format!(
27632822
"{}<{}>{}",
27642823
if higher_ranked { "for" } else { "" },
2765-
name.unwrap_or("'a"),
2824+
format!("{}", binders_sugg),
27662825
if higher_ranked { " " } else { "" },
27672826
);
27682827
(span, sugg)
@@ -2777,24 +2836,41 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27772836
let sugg = format!("{}, ", name.unwrap_or("'a"));
27782837
(span, sugg)
27792838
};
2839+
27802840
if higher_ranked {
27812841
let message = Cow::from(format!(
27822842
"consider making the {} lifetime-generic with a new `{}` lifetime",
27832843
kind.descr(),
27842844
name.unwrap_or("'a"),
27852845
));
2786-
should_continue = suggest(err, true, span, message, sugg);
2846+
should_continue = suggest(
2847+
err,
2848+
true,
2849+
span,
2850+
message,
2851+
sugg,
2852+
&if rm_inner_binders.len() > 0 {
2853+
let mut rm_inner_binders = rm_inner_binders
2854+
.into_iter()
2855+
.map(|v| (v, "".to_string()))
2856+
.collect::<Vec<_>>();
2857+
rm_inner_binders.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
2858+
rm_inner_binders
2859+
} else {
2860+
vec![]
2861+
},
2862+
);
27872863
err.note_once(
27882864
"for more information on higher-ranked polymorphism, visit \
27892865
https://doc.rust-lang.org/nomicon/hrtb.html",
27902866
);
27912867
} else if let Some(name) = name {
27922868
let message =
27932869
Cow::from(format!("consider introducing lifetime `{name}` here"));
2794-
should_continue = suggest(err, false, span, message, sugg);
2870+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27952871
} else {
27962872
let message = Cow::from("consider introducing a named lifetime parameter");
2797-
should_continue = suggest(err, false, span, message, sugg);
2873+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27982874
}
27992875
}
28002876
LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break,
@@ -3030,7 +3106,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30303106
self.suggest_introducing_lifetime(
30313107
err,
30323108
None,
3033-
|err, higher_ranked, span, message, intro_sugg| {
3109+
|err, higher_ranked, span, message, intro_sugg, _| {
30343110
err.multipart_suggestion_verbose(
30353111
message,
30363112
std::iter::once((span, intro_sugg))
@@ -3158,7 +3234,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
31583234
self.suggest_introducing_lifetime(
31593235
err,
31603236
None,
3161-
|err, higher_ranked, span, message, intro_sugg| {
3237+
|err, higher_ranked, span, message, intro_sugg, _| {
31623238
err.multipart_suggestion_verbose(
31633239
message,
31643240
std::iter::once((span, intro_sugg))
@@ -3306,7 +3382,6 @@ fn mk_where_bound_predicate(
33063382
poly_trait_ref: &ast::PolyTraitRef,
33073383
ty: &Ty,
33083384
) -> Option<ast::WhereBoundPredicate> {
3309-
use rustc_span::DUMMY_SP;
33103385
let modified_segments = {
33113386
let mut segments = path.segments.clone();
33123387
let [preceding @ .., second_last, last] = segments.as_mut_slice() else {

tests/ui/associated-inherent-types/issue-109299.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'d`
22
--> $DIR/issue-109299.rs:6:12
33
|
44
LL | impl Lexer<'d> {
5-
| - ^^ undeclared lifetime
6-
| |
7-
| help: consider introducing lifetime `'d` here: `<'d>`
5+
| ^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'d` here
8+
|
9+
LL | impl<'d> Lexer<'d> {
10+
| ++++
811

912
error: lifetime may not live long enough
1013
--> $DIR/issue-109299.rs:10:1

tests/ui/borrowck/generic_const_early_param.stderr

+11-6
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,24 @@ LL | struct DataWrapper<'static> {
77
error[E0261]: use of undeclared lifetime name `'a`
88
--> $DIR/generic_const_early_param.rs:6:12
99
|
10-
LL | struct DataWrapper<'static> {
11-
| - help: consider introducing lifetime `'a` here: `'a,`
12-
LL |
1310
LL | data: &'a [u8; Self::SIZE],
1411
| ^^ undeclared lifetime
12+
|
13+
help: consider introducing lifetime `'a` here
14+
|
15+
LL | struct DataWrapper<'a, 'static> {
16+
| +++
1517

1618
error[E0261]: use of undeclared lifetime name `'a`
1719
--> $DIR/generic_const_early_param.rs:11:18
1820
|
1921
LL | impl DataWrapper<'a> {
20-
| - ^^ undeclared lifetime
21-
| |
22-
| help: consider introducing lifetime `'a` here: `<'a>`
22+
| ^^ undeclared lifetime
23+
|
24+
help: consider introducing lifetime `'a` here
25+
|
26+
LL | impl<'a> DataWrapper<'a> {
27+
| ++++
2328

2429
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
2530
--> $DIR/generic_const_early_param.rs:1:12

tests/ui/const-generics/generic_const_exprs/unresolved_lifetimes_error.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
error[E0261]: use of undeclared lifetime name `'a`
22
--> $DIR/unresolved_lifetimes_error.rs:5:13
33
|
4-
LL | fn foo() -> [(); {
5-
| - help: consider introducing lifetime `'a` here: `<'a>`
64
LL | let a: &'a ();
75
| ^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'a` here
8+
|
9+
LL | fn foo<'a>() -> [(); {
10+
| ++++
811

912
error: aborting due to 1 previous error
1013

tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ error[E0261]: use of undeclared lifetime name `'a`
1717
--> $DIR/ice-unexpected-inference-var-122549.rs:11:34
1818
|
1919
LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {}
20-
| - ^^ undeclared lifetime
21-
| |
22-
| help: consider introducing lifetime `'a` here: `'a,`
20+
| ^^ undeclared lifetime
21+
|
22+
help: consider introducing lifetime `'a` here
23+
|
24+
LL | struct ConstChunksExact<'a, 'rem, T: 'a, const N: usize> {}
25+
| +++
2326

2427
error[E0046]: not all trait items implemented, missing: `const_chunks_exact`
2528
--> $DIR/ice-unexpected-inference-var-122549.rs:9:1

tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'a`
22
--> $DIR/ice-type-mismatch-when-copying-112824.rs:5:21
33
|
44
LL | pub struct Opcode2(&'a S);
5-
| - ^^ undeclared lifetime
6-
| |
7-
| help: consider introducing lifetime `'a` here: `<'a>`
5+
| ^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'a` here
8+
|
9+
LL | pub struct Opcode2<'a>(&'a S);
10+
| ++++
811

912
error[E0412]: cannot find type `S` in this scope
1013
--> $DIR/ice-type-mismatch-when-copying-112824.rs:5:24

tests/ui/error-codes/E0261.stderr

+11-5
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@ error[E0261]: use of undeclared lifetime name `'a`
22
--> $DIR/E0261.rs:1:12
33
|
44
LL | fn foo(x: &'a str) { }
5-
| - ^^ undeclared lifetime
6-
| |
7-
| help: consider introducing lifetime `'a` here: `<'a>`
5+
| ^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'a` here
8+
|
9+
LL | fn foo<'a>(x: &'a str) { }
10+
| ++++
811

912
error[E0261]: use of undeclared lifetime name `'a`
1013
--> $DIR/E0261.rs:5:9
1114
|
12-
LL | struct Foo {
13-
| - help: consider introducing lifetime `'a` here: `<'a>`
1415
LL | x: &'a str,
1516
| ^^ undeclared lifetime
17+
|
18+
help: consider introducing lifetime `'a` here
19+
|
20+
LL | struct Foo<'a> {
21+
| ++++
1622

1723
error: aborting due to 2 previous errors
1824

tests/ui/generics/generic-extern-lifetime.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ error[E0261]: use of undeclared lifetime name `'a`
22
--> $DIR/generic-extern-lifetime.rs:6:26
33
|
44
LL | pub fn life2<'b>(x: &'a i32, y: &'b i32);
5-
| - ^^ undeclared lifetime
6-
| |
7-
| help: consider introducing lifetime `'a` here: `'a,`
5+
| ^^ undeclared lifetime
6+
|
7+
help: consider introducing lifetime `'a` here
8+
|
9+
LL | pub fn life2<'a, 'b>(x: &'a i32, y: &'b i32);
10+
| +++
811

912
error[E0261]: use of undeclared lifetime name `'a`
1013
--> $DIR/generic-extern-lifetime.rs:8:37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![allow(dead_code)]
2+
3+
trait Trait1<T>
4+
where T: for<'a> Trait1<T> + 'b { } //~ ERROR use of undeclared lifetime name `'b`
5+
6+
trait Trait2<T>
7+
where
8+
T: B<'b> + for<'a> A<'a>, //~ ERROR use of undeclared lifetime name `'b`
9+
{
10+
}
11+
12+
trait Trait3<T>
13+
where
14+
T: B<'b> + for<'a> A<'a> + 'c {}
15+
//~^ ERROR use of undeclared lifetime name `'b`
16+
//~| ERROR use of undeclared lifetime name `'c`
17+
18+
trait Trait4<T>
19+
where
20+
T: for<'a> A<'a> + 'x + for<'b> B<'b>, //~ ERROR use of undeclared lifetime name `'x`
21+
{
22+
}
23+
24+
trait A<'a> {}
25+
trait B<'a> {}
26+
27+
28+
fn main() {}

0 commit comments

Comments
 (0)