Skip to content

Commit 7b27b99

Browse files
authored
Rollup merge of rust-lang#135602 - estebank:issue-135589, r=Nadrieril
Tweak output of missing lifetime on associated type Each commit can be reviewed independently. Address parts of rust-lang#135589. --- When an associated type is missing a lifetime, point at its enclosing `impl`, whether it has or doesn't have lifetimes defined. If it does have a lifetime, suggest using it. ``` error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-1.rs:8:17 | LL | impl<'a> IntoIterator for &S { | ---- there is a named lifetime specified on the impl block you could use ... LL | type Item = &T; | ^ this lifetime must come from the implemented type | help: consider using the lifetime from the impl block | LL | type Item = &'a T; | ++ ``` ``` error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17 | LL | impl IntoIterator for &S { | - you could add a lifetime on the impl block, if the trait or the self type can have one LL | type Item = &T; | ^ this lifetime must come from the implemented type ``` --- On unconstrained lifetime on impl block, suggest using it if there's an implicit borrow in the self type ``` error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6 | LL | impl<'a> IntoIterator for &S { | ^^ unconstrained lifetime parameter | help: consider using the named lifetime here instead of an implict lifetime | LL | impl<'a> IntoIterator for &'a S { | ++ ``` --- Do not suggest introducing lifetime in impl assoc type --- Previously we only showed the trait's assoc item if the trait was local, because we were looking for a small span only for the generics, which we don't have for foreign traits. We now use `def_span` for the item, so we at least provide some context, even if its span is too wide. ``` error[E0195]: lifetime parameters or bounds on type `IntoIter` do not match the trait declaration --> tests/ui/lifetimes/missing-lifetime-in-assoc-type-4.rs:7:18 | 7 | type IntoIter<'a> = std::collections::btree_map::Values<'a, i32, T>; | ^^^^ lifetimes do not match type in trait | ::: /home/gh-estebank/rust/library/core/src/iter/traits/collect.rs:292:5 | 292 | type IntoIter: Iterator<Item = Self::Item>; | ------------------------------------------ lifetimes in impl do not match this type in trait ```
2 parents ff5be13 + 6fcc9f5 commit 7b27b99

17 files changed

+287
-12
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ use std::assert_matches::debug_assert_matches;
1212

1313
use min_specialization::check_min_specialization;
1414
use rustc_data_structures::fx::FxHashSet;
15+
use rustc_errors::Applicability;
1516
use rustc_errors::codes::*;
1617
use rustc_hir::def::DefKind;
1718
use rustc_hir::def_id::LocalDefId;
1819
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
19-
use rustc_span::ErrorGuaranteed;
20+
use rustc_span::{ErrorGuaranteed, kw};
2021

2122
use crate::constrained_generic_params as cgp;
2223
use crate::errors::UnconstrainedGenericParameter;
@@ -158,6 +159,27 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
158159
const_param_note2: false,
159160
});
160161
diag.code(E0207);
162+
for p in &impl_generics.own_params {
163+
if p.name == kw::UnderscoreLifetime {
164+
let span = tcx.def_span(p.def_id);
165+
let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else {
166+
continue;
167+
};
168+
169+
let (span, sugg) = if &snippet == "'_" {
170+
(span, param.name.to_string())
171+
} else {
172+
(span.shrink_to_hi(), format!("{} ", param.name))
173+
};
174+
diag.span_suggestion_verbose(
175+
span,
176+
"consider using the named lifetime here instead of an implict \
177+
lifetime",
178+
sugg,
179+
Applicability::MaybeIncorrect,
180+
);
181+
}
182+
}
161183
res = Err(diag.emit());
162184
}
163185
}

compiler/rustc_resolve/src/late.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
1919
use rustc_data_structures::unord::{UnordMap, UnordSet};
2020
use rustc_errors::codes::*;
2121
use rustc_errors::{
22-
Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
22+
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
23+
pluralize,
2324
};
2425
use rustc_hir::def::Namespace::{self, *};
2526
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
@@ -377,6 +378,7 @@ enum LifetimeBinderKind {
377378
Function,
378379
Closure,
379380
ImplBlock,
381+
ImplAssocType,
380382
}
381383

382384
impl LifetimeBinderKind {
@@ -387,6 +389,7 @@ impl LifetimeBinderKind {
387389
PolyTrait => "bound",
388390
WhereBound => "bound",
389391
Item | ConstItem => "item",
392+
ImplAssocType => "associated type",
390393
ImplBlock => "impl block",
391394
Function => "function",
392395
Closure => "closure",
@@ -1874,9 +1877,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
18741877
ty: ty.span,
18751878
});
18761879
} else {
1877-
self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError {
1878-
lifetime: lifetime.ident.span,
1879-
});
1880+
let mut err = self.r.dcx().create_err(
1881+
errors::AnonymousLifetimeNonGatReportError {
1882+
lifetime: lifetime.ident.span,
1883+
},
1884+
);
1885+
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
1886+
err.emit();
18801887
}
18811888
} else {
18821889
self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError {
@@ -1913,6 +1920,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
19131920
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
19141921
}
19151922

1923+
fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) {
1924+
let Some((rib, span)) = self.lifetime_ribs[..i]
1925+
.iter()
1926+
.rev()
1927+
.skip(1)
1928+
.filter_map(|rib| match rib.kind {
1929+
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
1930+
Some((rib, span))
1931+
}
1932+
_ => None,
1933+
})
1934+
.next()
1935+
else {
1936+
return;
1937+
};
1938+
if !rib.bindings.is_empty() {
1939+
err.span_label(
1940+
span,
1941+
format!(
1942+
"there {} named lifetime{} specified on the impl block you could use",
1943+
if rib.bindings.len() == 1 { "is a" } else { "are" },
1944+
pluralize!(rib.bindings.len()),
1945+
),
1946+
);
1947+
if rib.bindings.len() == 1 {
1948+
err.span_suggestion_verbose(
1949+
lifetime.shrink_to_hi(),
1950+
"consider using the lifetime from the impl block",
1951+
format!("{} ", rib.bindings.keys().next().unwrap()),
1952+
Applicability::MaybeIncorrect,
1953+
);
1954+
}
1955+
} else {
1956+
err.span_label(
1957+
span,
1958+
"you could add a lifetime on the impl block, if the trait or the self type can \
1959+
have one",
1960+
);
1961+
}
1962+
}
1963+
19161964
#[instrument(level = "debug", skip(self))]
19171965
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
19181966
let id = self.r.next_node_id();
@@ -3352,7 +3400,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
33523400
&generics.params,
33533401
RibKind::AssocItem,
33543402
item.id,
3355-
LifetimeBinderKind::Item,
3403+
LifetimeBinderKind::ImplAssocType,
33563404
generics.span,
33573405
|this| {
33583406
this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
31683168
{
31693169
continue;
31703170
}
3171+
if let LifetimeBinderKind::ImplAssocType = kind {
3172+
continue;
3173+
}
31713174

31723175
if !span.can_be_used_for_suggestions()
31733176
&& suggest_note

tests/ui/impl-header-lifetime-elision/assoc-type.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
22
--> $DIR/assoc-type.rs:11:19
33
|
4+
LL | impl MyTrait for &i32 {
5+
| - you could add a lifetime on the impl block, if the trait or the self type can have one
46
LL | type Output = &i32;
57
| ^ this lifetime must come from the implemented type
68

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct S;
2+
struct T;
3+
4+
impl<'a> IntoIterator for &S {
5+
//~^ ERROR E0207
6+
//~| NOTE there is a named lifetime specified on the impl block you could use
7+
//~| NOTE unconstrained lifetime parameter
8+
//~| HELP consider using the named lifetime here instead of an implict lifetime
9+
type Item = &T;
10+
//~^ ERROR in the trait associated type
11+
//~| HELP consider using the lifetime from the impl block
12+
//~| NOTE this lifetime must come from the implemented type
13+
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
14+
15+
fn into_iter(self) -> Self::IntoIter {
16+
todo!()
17+
}
18+
}
19+
fn main() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
2+
--> $DIR/missing-lifetime-in-assoc-type-1.rs:9:17
3+
|
4+
LL | impl<'a> IntoIterator for &S {
5+
| ---- there is a named lifetime specified on the impl block you could use
6+
...
7+
LL | type Item = &T;
8+
| ^ this lifetime must come from the implemented type
9+
|
10+
help: consider using the lifetime from the impl block
11+
|
12+
LL | type Item = &'a T;
13+
| ++
14+
15+
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
16+
--> $DIR/missing-lifetime-in-assoc-type-1.rs:4:6
17+
|
18+
LL | impl<'a> IntoIterator for &S {
19+
| ^^ unconstrained lifetime parameter
20+
|
21+
help: consider using the named lifetime here instead of an implict lifetime
22+
|
23+
LL | impl<'a> IntoIterator for &'a S {
24+
| ++
25+
26+
error: aborting due to 2 previous errors
27+
28+
For more information about this error, try `rustc --explain E0207`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct S;
2+
struct T;
3+
4+
impl IntoIterator for &S {
5+
type Item = &T;
6+
//~^ ERROR in the trait associated type
7+
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
8+
//~^ ERROR use of undeclared lifetime name `'a`
9+
10+
fn into_iter(self) -> Self::IntoIter {
11+
todo!()
12+
}
13+
}
14+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
2+
--> $DIR/missing-lifetime-in-assoc-type-2.rs:5:17
3+
|
4+
LL | impl IntoIterator for &S {
5+
| - you could add a lifetime on the impl block, if the trait or the self type can have one
6+
LL | type Item = &T;
7+
| ^ this lifetime must come from the implemented type
8+
9+
error[E0261]: use of undeclared lifetime name `'a`
10+
--> $DIR/missing-lifetime-in-assoc-type-2.rs:7:57
11+
|
12+
LL | type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
13+
| ^^ undeclared lifetime
14+
|
15+
help: consider introducing lifetime `'a` here
16+
|
17+
LL | impl<'a> IntoIterator for &S {
18+
| ++++
19+
20+
error: aborting due to 2 previous errors
21+
22+
For more information about this error, try `rustc --explain E0261`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct S;
2+
struct T;
3+
4+
impl IntoIterator for &S {
5+
type Item = &T;
6+
//~^ ERROR in the trait associated type
7+
type IntoIter = std::collections::btree_map::Values<i32, T>;
8+
//~^ ERROR missing lifetime specifier
9+
10+
fn into_iter(self) -> Self::IntoIter {
11+
todo!()
12+
}
13+
}
14+
fn main() {}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
2+
--> $DIR/missing-lifetime-in-assoc-type-3.rs:5:17
3+
|
4+
LL | impl IntoIterator for &S {
5+
| - you could add a lifetime on the impl block, if the trait or the self type can have one
6+
LL | type Item = &T;
7+
| ^ this lifetime must come from the implemented type
8+
9+
error[E0106]: missing lifetime specifier
10+
--> $DIR/missing-lifetime-in-assoc-type-3.rs:7:56
11+
|
12+
LL | type IntoIter = std::collections::btree_map::Values<i32, T>;
13+
| ^ expected named lifetime parameter
14+
|
15+
help: consider introducing a named lifetime parameter
16+
|
17+
LL ~ impl<'a> IntoIterator for &S {
18+
LL | type Item = &T;
19+
LL |
20+
LL ~ type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
21+
|
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0106`.

0 commit comments

Comments
 (0)