Skip to content

Commit 021a4ff

Browse files
authored
Rollup merge of #96409 - marmeladema:fix-nll-introduce-named-lifetime-suggestion, r=jackh726
Recover suggestions to introduce named lifetime under NLL Fixes #96157 r? `@jackh726` Built on top of #96385 so only the second commit is relevant
2 parents 16d029b + 2c94218 commit 021a4ff

20 files changed

+331
-76
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
33
use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
44
use rustc_infer::infer::{
5-
error_reporting::nice_region_error::{self, find_param_with_region, NiceRegionError},
5+
error_reporting::nice_region_error::{
6+
self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
7+
NiceRegionError,
8+
},
69
error_reporting::unexpected_hidden_region_diagnostic,
710
NllRegionVariableOrigin, RelateParamBound,
811
};
@@ -630,6 +633,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
630633
}
631634

632635
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
636+
self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
633637

634638
diag
635639
}
@@ -694,4 +698,33 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
694698
);
695699
}
696700
}
701+
702+
fn suggest_adding_lifetime_params(
703+
&self,
704+
diag: &mut Diagnostic,
705+
sub: RegionVid,
706+
sup: RegionVid,
707+
) {
708+
let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
709+
return
710+
};
711+
712+
let Some((ty_sub, _)) = self
713+
.infcx
714+
.tcx
715+
.is_suitable_region(sub)
716+
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
717+
return
718+
};
719+
720+
let Some((ty_sup, _)) = self
721+
.infcx
722+
.tcx
723+
.is_suitable_region(sup)
724+
.and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
725+
return
726+
};
727+
728+
suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
729+
}
697730
}

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs

+74-74
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo;
66
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
77
use crate::infer::lexical_region_resolve::RegionResolutionError;
88
use crate::infer::SubregionOrigin;
9+
use crate::infer::TyCtxt;
910

1011
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
1112
use rustc_hir as hir;
@@ -145,84 +146,83 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
145146
}
146147
}
147148

148-
self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
149+
if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) {
150+
err.note("each elided lifetime in input position becomes a distinct lifetime");
151+
}
149152

150153
let reported = err.emit();
151154
Some(reported)
152155
}
156+
}
153157

154-
fn suggest_adding_lifetime_params(
155-
&self,
156-
sub: Region<'tcx>,
157-
ty_sup: &Ty<'_>,
158-
ty_sub: &Ty<'_>,
159-
err: &mut Diagnostic,
160-
) {
161-
if let (
162-
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
163-
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
164-
) = (ty_sub, ty_sup)
165-
{
166-
if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
167-
if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
168-
let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
169-
170-
let node = self.tcx().hir().get(hir_id);
171-
let is_impl = matches!(&node, hir::Node::ImplItem(_));
172-
let generics = match node {
173-
hir::Node::Item(&hir::Item {
174-
kind: hir::ItemKind::Fn(_, ref generics, ..),
175-
..
176-
})
177-
| hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
178-
| hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
179-
_ => return,
180-
};
181-
182-
let (suggestion_param_name, introduce_new) = generics
183-
.params
184-
.iter()
185-
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
186-
.and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
187-
.map(|name| (name, false))
188-
.unwrap_or_else(|| ("'a".to_string(), true));
189-
190-
let mut suggestions = vec![
191-
if let hir::LifetimeName::Underscore = lifetime_sub.name {
192-
(lifetime_sub.span, suggestion_param_name.clone())
193-
} else {
194-
(lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
195-
},
196-
if let hir::LifetimeName::Underscore = lifetime_sup.name {
197-
(lifetime_sup.span, suggestion_param_name.clone())
198-
} else {
199-
(lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
200-
},
201-
];
202-
203-
if introduce_new {
204-
let new_param_suggestion = match &generics.params {
205-
[] => (generics.span, format!("<{}>", suggestion_param_name)),
206-
[first, ..] => {
207-
(first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
208-
}
209-
};
210-
211-
suggestions.push(new_param_suggestion);
212-
}
213-
214-
let mut sugg = String::from("consider introducing a named lifetime parameter");
215-
if is_impl {
216-
sugg.push_str(" and update trait if needed");
217-
}
218-
err.multipart_suggestion(
219-
sugg.as_str(),
220-
suggestions,
221-
Applicability::MaybeIncorrect,
222-
);
223-
err.note("each elided lifetime in input position becomes a distinct lifetime");
224-
}
225-
}
226-
}
158+
pub fn suggest_adding_lifetime_params<'tcx>(
159+
tcx: TyCtxt<'tcx>,
160+
sub: Region<'tcx>,
161+
ty_sup: &Ty<'_>,
162+
ty_sub: &Ty<'_>,
163+
err: &mut Diagnostic,
164+
) -> bool {
165+
let (
166+
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
167+
hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
168+
) = (ty_sub, ty_sup) else {
169+
return false;
170+
};
171+
172+
if !lifetime_sub.name.is_elided() || !lifetime_sup.name.is_elided() {
173+
return false;
174+
};
175+
176+
let Some(anon_reg) = tcx.is_suitable_region(sub) else {
177+
return false;
178+
};
179+
180+
let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
181+
182+
let node = tcx.hir().get(hir_id);
183+
let is_impl = matches!(&node, hir::Node::ImplItem(_));
184+
let generics = match node {
185+
hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. })
186+
| hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
187+
| hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
188+
_ => return false,
189+
};
190+
191+
let (suggestion_param_name, introduce_new) = generics
192+
.params
193+
.iter()
194+
.find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
195+
.and_then(|p| tcx.sess.source_map().span_to_snippet(p.span).ok())
196+
.map(|name| (name, false))
197+
.unwrap_or_else(|| ("'a".to_string(), true));
198+
199+
let mut suggestions = vec![
200+
if let hir::LifetimeName::Underscore = lifetime_sub.name {
201+
(lifetime_sub.span, suggestion_param_name.clone())
202+
} else {
203+
(lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
204+
},
205+
if let hir::LifetimeName::Underscore = lifetime_sup.name {
206+
(lifetime_sup.span, suggestion_param_name.clone())
207+
} else {
208+
(lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
209+
},
210+
];
211+
212+
if introduce_new {
213+
let new_param_suggestion = match &generics.params {
214+
[] => (generics.span, format!("<{}>", suggestion_param_name)),
215+
[first, ..] => (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)),
216+
};
217+
218+
suggestions.push(new_param_suggestion);
227219
}
220+
221+
let mut sugg = String::from("consider introducing a named lifetime parameter");
222+
if is_impl {
223+
sugg.push_str(" and update trait if needed");
224+
}
225+
err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect);
226+
227+
true
228228
}

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::ty::{self, Region, TyCtxt};
2020
/// ```
2121
/// The function returns the nested type corresponding to the anonymous region
2222
/// for e.g., `&u8` and `Vec<&u8>`.
23-
pub(crate) fn find_anon_type<'tcx>(
23+
pub fn find_anon_type<'tcx>(
2424
tcx: TyCtxt<'tcx>,
2525
region: Region<'tcx>,
2626
br: &ty::BoundRegionKind,

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ mod static_impl_trait;
1414
mod trait_impl_difference;
1515
mod util;
1616

17+
pub use different_lifetimes::suggest_adding_lifetime_params;
18+
pub use find_anon_type::find_anon_type;
1719
pub use static_impl_trait::suggest_new_region_bound;
1820
pub use util::find_param_with_region;
1921

src/test/ui/lifetimes/issue-90170-elision-mismatch.nll.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); }
66
| | |
77
| | let's call the lifetime of this reference `'1`
88
| let's call the lifetime of this reference `'2`
9+
|
10+
help: consider introducing a named lifetime parameter
11+
|
12+
LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
13+
| ++++ ++ ++
914

1015
error: lifetime may not live long enough
1116
--> $DIR/issue-90170-elision-mismatch.rs:5:44
@@ -15,6 +20,11 @@ LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); }
1520
| | |
1621
| | let's call the lifetime of this reference `'1`
1722
| let's call the lifetime of this reference `'2`
23+
|
24+
help: consider introducing a named lifetime parameter
25+
|
26+
LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
27+
| ++++ ~~ ++
1828

1929
error: lifetime may not live long enough
2030
--> $DIR/issue-90170-elision-mismatch.rs:7:63
@@ -24,6 +34,11 @@ LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); }
2434
| | |
2535
| | let's call the lifetime of this reference `'1`
2636
| let's call the lifetime of this reference `'2`
37+
|
38+
help: consider introducing a named lifetime parameter
39+
|
40+
LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
41+
| ++ ++
2742

2843
error: aborting due to 3 previous errors
2944

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) {
77
| let's call the lifetime of this reference `'2`
88
LL | *v = x;
99
| ^^^^^^ assignment requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
14+
| ++++ ++ ++
1015

1116
error: aborting due to previous error
1217

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.nll.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
77
| let's call the lifetime of this reference `'2`
88
LL | z.push((x,y));
99
| ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
14+
| ++++ ++ ++
1015

1116
error: lifetime may not live long enough
1217
--> $DIR/ex3-both-anon-regions-3.rs:2:5
@@ -17,6 +22,11 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
1722
| let's call the lifetime of this reference `'4`
1823
LL | z.push((x,y));
1924
| ^^^^^^^^^^^^^ argument requires that `'3` must outlive `'4`
25+
|
26+
help: consider introducing a named lifetime parameter
27+
|
28+
LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
29+
| ++++ ++ ++
2030

2131
error: aborting due to 2 previous errors
2232

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo<'a>(&self, x: &i32) -> &i32 {
77
| let's call the lifetime of this reference `'2`
88
LL | x
99
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
10+
|
11+
help: consider introducing a named lifetime parameter and update trait if needed
12+
|
13+
LL | fn foo<'a>(&'a self, x: &'a i32) -> &i32 {
14+
| ++ ++
1015

1116
error: aborting due to previous error
1217

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo<'a>(&self, x: &Foo) -> &Foo {
77
| let's call the lifetime of this reference `'2`
88
LL | if true { x } else { self }
99
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
10+
|
11+
help: consider introducing a named lifetime parameter and update trait if needed
12+
|
13+
LL | fn foo<'a>(&'a self, x: &'a Foo) -> &Foo {
14+
| ++ ++
1015

1116
error: aborting due to previous error
1217

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
77
| let's call the lifetime of this reference `'2`
88
LL | y.push(z);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
14+
| ++++ ++ ++
1015

1116
error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
1217
--> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
77
| let's call the lifetime of this reference `'2`
88
LL | x.push(y);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter and update trait if needed
12+
|
13+
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
14+
| ++++ ++ ++
1015

1116
error: aborting due to previous error
1217

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
77
| let's call the lifetime of this reference `'2`
88
LL | y.push(z);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
14+
| ++++ ++ ++
1015

1116
error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
1217
--> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3

src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.nll.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
77
| let's call the lifetime of this reference `'2`
88
LL | x.push(y);
99
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
10+
|
11+
help: consider introducing a named lifetime parameter
12+
|
13+
LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
14+
| ++++ ++ ++
1015

1116
error: aborting due to previous error
1217

0 commit comments

Comments
 (0)