Skip to content

Commit 82f9833

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

24 files changed

+406
-105
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+86-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,12 @@ 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+
std::iter::once((span, suggestion)).chain(span_suggs.iter().cloned()).collect(),
2720+
Applicability::MaybeIncorrect,
2721+
);
27162722
true
27172723
},
27182724
);
@@ -2723,13 +2729,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27232729
&self,
27242730
err: &mut Diag<'_>,
27252731
name: Option<&str>,
2726-
suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool,
2732+
suggest: impl Fn(
2733+
&mut Diag<'_>,
2734+
bool,
2735+
Span,
2736+
Cow<'static, str>,
2737+
String,
2738+
&Vec<(Span, String)>,
2739+
) -> bool,
27272740
) {
27282741
let mut suggest_note = true;
27292742
for rib in self.lifetime_ribs.iter().rev() {
27302743
let mut should_continue = true;
27312744
match rib.kind {
2732-
LifetimeRibKind::Generics { binder: _, span, kind } => {
2745+
LifetimeRibKind::Generics { binder: node_id, span, kind } => {
27332746
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
27342747
// feature is enabled. Suggest the parent item as a possible location if applicable.
27352748
if let LifetimeBinderKind::ConstItem = kind
@@ -2758,11 +2771,58 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27582771
| LifetimeBinderKind::PolyTrait
27592772
| LifetimeBinderKind::WhereBound
27602773
);
2774+
2775+
let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
27612776
let (span, sugg) = if span.is_empty() {
2777+
let mut binder_idents: FxIndexSet<Ident> = Default::default();
2778+
binder_idents.insert(if let Some(name) = name {
2779+
Ident::from_str(name)
2780+
} else {
2781+
Ident::from_str("'a")
2782+
});
2783+
2784+
// We need to special treat binders in following situation:
2785+
// Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
2786+
// T: for<'a> Trait<T> + 'b
2787+
// ^^^^^^^ remove existing inner binder `for<'a>`
2788+
// for<'a, 'b> T: Trait<T> + 'b
2789+
// ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>`
2790+
if let LifetimeBinderKind::WhereBound = kind
2791+
&& let Some(ast::WherePredicate::BoundPredicate(
2792+
ast::WhereBoundPredicate { bounded_ty, bounds, .. },
2793+
)) = self.diag_metadata.current_where_predicate
2794+
&& bounded_ty.node_id() == node_id
2795+
{
2796+
for bound in bounds {
2797+
if let ast::GenericBound::Trait(poly_trait_ref, _) = bound
2798+
&& let span = poly_trait_ref
2799+
.span
2800+
.with_hi(poly_trait_ref.trait_ref.path.span.lo())
2801+
&& !span.is_empty()
2802+
{
2803+
rm_inner_binders.insert(span);
2804+
poly_trait_ref.bound_generic_params.iter().for_each(|v| {
2805+
binder_idents.insert(v.ident);
2806+
});
2807+
}
2808+
}
2809+
}
2810+
2811+
let len = binder_idents.len();
2812+
let binders_sugg = binder_idents.into_iter().enumerate().fold(
2813+
"".to_string(),
2814+
|mut binders, (i, x)| {
2815+
binders += x.as_str();
2816+
if i != len - 1 {
2817+
binders += ", ";
2818+
}
2819+
binders
2820+
},
2821+
);
27622822
let sugg = format!(
27632823
"{}<{}>{}",
27642824
if higher_ranked { "for" } else { "" },
2765-
name.unwrap_or("'a"),
2825+
format!("{}", binders_sugg),
27662826
if higher_ranked { " " } else { "" },
27672827
);
27682828
(span, sugg)
@@ -2777,24 +2837,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
27772837
let sugg = format!("{}, ", name.unwrap_or("'a"));
27782838
(span, sugg)
27792839
};
2840+
27802841
if higher_ranked {
27812842
let message = Cow::from(format!(
27822843
"consider making the {} lifetime-generic with a new `{}` lifetime",
27832844
kind.descr(),
27842845
name.unwrap_or("'a"),
27852846
));
2786-
should_continue = suggest(err, true, span, message, sugg);
2847+
should_continue = suggest(
2848+
err,
2849+
true,
2850+
span,
2851+
message,
2852+
sugg,
2853+
&if rm_inner_binders.len() > 0 {
2854+
rm_inner_binders
2855+
.into_iter()
2856+
.map(|v| (v, "".to_string()))
2857+
.collect::<Vec<_>>()
2858+
} else {
2859+
vec![]
2860+
},
2861+
);
27872862
err.note_once(
27882863
"for more information on higher-ranked polymorphism, visit \
27892864
https://doc.rust-lang.org/nomicon/hrtb.html",
27902865
);
27912866
} else if let Some(name) = name {
27922867
let message =
27932868
Cow::from(format!("consider introducing lifetime `{name}` here"));
2794-
should_continue = suggest(err, false, span, message, sugg);
2869+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27952870
} else {
27962871
let message = Cow::from("consider introducing a named lifetime parameter");
2797-
should_continue = suggest(err, false, span, message, sugg);
2872+
should_continue = suggest(err, false, span, message, sugg, &vec![]);
27982873
}
27992874
}
28002875
LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break,
@@ -3030,7 +3105,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30303105
self.suggest_introducing_lifetime(
30313106
err,
30323107
None,
3033-
|err, higher_ranked, span, message, intro_sugg| {
3108+
|err, higher_ranked, span, message, intro_sugg, _| {
30343109
err.multipart_suggestion_verbose(
30353110
message,
30363111
std::iter::once((span, intro_sugg))
@@ -3158,7 +3233,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
31583233
self.suggest_introducing_lifetime(
31593234
err,
31603235
None,
3161-
|err, higher_ranked, span, message, intro_sugg| {
3236+
|err, higher_ranked, span, message, intro_sugg, _| {
31623237
err.multipart_suggestion_verbose(
31633238
message,
31643239
std::iter::once((span, intro_sugg))
@@ -3306,7 +3381,6 @@ fn mk_where_bound_predicate(
33063381
poly_trait_ref: &ast::PolyTraitRef,
33073382
ty: &Ty,
33083383
) -> Option<ast::WhereBoundPredicate> {
3309-
use rustc_span::DUMMY_SP;
33103384
let modified_segments = {
33113385
let mut segments = path.segments.clone();
33123386
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)