Skip to content

Commit 8e42b2e

Browse files
committed
Fix incorrect suggestion for undeclared hrtb lifetimes in where clauses.
fixes #122714
1 parent 91d5e4a commit 8e42b2e

24 files changed

+410
-105
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+90-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;
@@ -17,6 +18,7 @@ use rustc_ast::{
1718
};
1819
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
1920
use rustc_data_structures::fx::FxHashSet;
21+
use rustc_data_structures::fx::FxIndexSet;
2022
use rustc_errors::{
2123
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
2224
SuggestionStyle,
@@ -31,7 +33,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
3133
use rustc_span::edition::Edition;
3234
use rustc_span::hygiene::MacroKind;
3335
use rustc_span::symbol::{kw, sym, Ident, Symbol};
34-
use rustc_span::Span;
36+
use rustc_span::{Span, DUMMY_SP};
3537

3638
use rustc_middle::ty;
3739

@@ -2711,8 +2713,16 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27112713
self.suggest_introducing_lifetime(
27122714
&mut err,
27132715
Some(lifetime_ref.ident.name.as_str()),
2714-
|err, _, span, message, suggestion| {
2715-
err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
2716+
|err, _, span, message, suggestion, span_suggs| {
2717+
err.multipart_suggestion_verbose(
2718+
message,
2719+
if span_suggs.len() == 0 {
2720+
vec![(span, suggestion)]
2721+
} else {
2722+
std::iter::once((span, suggestion)).chain(span_suggs.iter().cloned()).collect()
2723+
},
2724+
Applicability::MaybeIncorrect,
2725+
);
27162726
true
27172727
},
27182728
);
@@ -2723,13 +2733,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27232733
&self,
27242734
err: &mut Diag<'_>,
27252735
name: Option<&str>,
2726-
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
2736+
suggest: impl Fn(
2737+
&mut Diag<'_>,
2738+
bool,
2739+
Span,
2740+
Cow<'static, str>,
2741+
String,
2742+
&Vec<(Span, String)>,
2743+
) -> bool,
27272744
) {
27282745
let mut suggest_note = true;
27292746
for rib in self.lifetime_ribs.iter().rev() {
27302747
let mut should_continue = true;
27312748
match rib.kind {
2732-
LifetimeRibKind::Generics { binder: _, span, kind } => {
2749+
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
27332750
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
27342751
// feature is enabled. Suggest the parent item as a possible location if applicable.
27352752
if let LifetimeBinderKind::ConstItem = kind
@@ -2758,11 +2775,58 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27582775
| LifetimeBinderKind::PolyTrait
27592776
| LifetimeBinderKind::WhereBound
27602777
);
2778+
2779+
let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
27612780
let (span, sugg) = if span.is_empty() {
2781+
let mut binder_idents: FxIndexSet<Ident> = Default::default();
2782+
binder_idents.insert(if let Some(name) = name {
2783+
Ident::from_str(name)
2784+
} else {
2785+
Ident::from_str("'a")
2786+
});
2787+
2788+
// We need to special treat binders in following situation:
2789+
// Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
2790+
// T: for<'a> Trait<T> + 'b
2791+
// ^^^^^^^ remove existing inner binder `for<'a>`
2792+
// for<'a, 'b> T: Trait<T> + 'b
2793+
// ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>`
2794+
if let LifetimeBinderKind::WhereBound = kind
2795+
&& let Some(ast::WherePredicate::BoundPredicate(
2796+
ast::WhereBoundPredicate { bounded_ty, bounds, .. },
2797+
)) = self.diag_metadata.current_where_predicate
2798+
&& bounded_ty.node_id() == node_id
2799+
{
2800+
for bound in bounds {
2801+
let ast::GenericBound::Trait(poly_trait_ref, _) = bound else {
2802+
continue;
2803+
};
2804+
rm_inner_binders.insert(
2805+
poly_trait_ref
2806+
.span
2807+
.with_hi(poly_trait_ref.trait_ref.path.span.lo()),
2808+
);
2809+
poly_trait_ref.bound_generic_params.iter().for_each(|v| {
2810+
binder_idents.insert(v.ident);
2811+
});
2812+
}
2813+
}
2814+
2815+
let len = binder_idents.len();
2816+
let binders_sugg = binder_idents.into_iter().enumerate().fold(
2817+
"".to_string(),
2818+
|mut binders, (i, x)| {
2819+
binders += x.as_str();
2820+
if i != len - 1 {
2821+
binders += ", ";
2822+
}
2823+
binders
2824+
},
2825+
);
27622826
let sugg = format!(
27632827
"{}<{}>{}",
27642828
if higher_ranked { "for" } else { "" },
2765-
name.unwrap_or("'a"),
2829+
format!("{}", binders_sugg),
27662830
if higher_ranked { " " } else { "" },
27672831
);
27682832
(span, sugg)
@@ -2777,24 +2841,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27772841
let sugg = format!("{}, ", name.unwrap_or("'a"));
27782842
(span, sugg)
27792843
};
2844+
27802845
if higher_ranked {
27812846
let message = Cow::from(format!(
27822847
"consider making the {} lifetime-generic with a new `{}` lifetime",
27832848
kind.descr(),
27842849
name.unwrap_or("'a"),
27852850
));
2786-
should_continue = suggest(err, true, span, message, sugg);
2851+
should_continue = suggest(
2852+
err,
2853+
true,
2854+
span,
2855+
message,
2856+
sugg,
2857+
&if rm_inner_binders.len() > 0 {
2858+
rm_inner_binders
2859+
.into_iter()
2860+
.map(|v| (v, "".to_string()))
2861+
.collect::<Vec<_>>()
2862+
} else {
2863+
vec![]
2864+
},
2865+
);
27872866
err.note_once(
27882867
"for more information on higher-ranked polymorphism, visit \
27892868
https://doc.rust-lang.org/nomicon/hrtb.html",
27902869
);
27912870
} else if let Some(name) = name {
27922871
let message =
27932872
Cow::from(format!("consider introducing lifetime `{name}` here"));
2794-
should_continue = suggest(err, false, span, message, sugg);
2873+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27952874
} else {
27962875
let message = Cow::from("consider introducing a named lifetime parameter");
2797-
should_continue = suggest(err, false, span, message, sugg);
2876+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27982877
}
27992878
}
28002879
LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break,
@@ -3030,7 +3109,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30303109
self.suggest_introducing_lifetime(
30313110
err,
30323111
None,
3033-
|err, higher_ranked, span, message, intro_sugg| {
3112+
|err, higher_ranked, span, message, intro_sugg, _| {
30343113
err.multipart_suggestion_verbose(
30353114
message,
30363115
std::iter::once((span, intro_sugg))
@@ -3158,7 +3237,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
31583237
self.suggest_introducing_lifetime(
31593238
err,
31603239
None,
3161-
|err, higher_ranked, span, message, intro_sugg| {
3240+
|err, higher_ranked, span, message, intro_sugg, _| {
31623241
err.multipart_suggestion_verbose(
31633242
message,
31643243
std::iter::once((span, intro_sugg))
@@ -3306,7 +3385,6 @@ fn mk_where_bound_predicate(
33063385
poly_trait_ref: &ast::PolyTraitRef,
33073386
ty: &Ty,
33083387
) -> Option<ast::WhereBoundPredicate> {
3309-
use rustc_span::DUMMY_SP;
33103388
let modified_segments = {
33113389
let mut segments = path.segments.clone();
33123390
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)