Skip to content

Commit 6e41e61

Browse files
committed
Auto merge of #145314 - estebank:issue-135589-all, r=Nadrieril
Tweak output of missing lifetime on associated type Follow up to #135602. 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 ``` Given an associated item that needs a named lifetime, look at the enclosing `impl` item for one. If there is none, look at the self type and the implemented trait to see if either of those has an anonimous lifetime. If so, suggest adding a named lifetime. ``` 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 | type Item = &T; | ^ this lifetime must come from the implemented type | help: add a lifetime to the impl block and use it in the self type and associated type | LL ~ impl<'a> IntoIterator for &'a S { LL ~ type Item = &'a T; | ``` Move the previous long message to a note and use a shorter primary message: ``` error: missing lifetime in associated type --> $DIR/missing-lifetime-in-assoc-type-1.rs:9: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 | note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider using the lifetime from the impl block | LL | type Item = &'a T; | ++ ``` r? `@Nadrieril`
2 parents f15a7f3 + 817cf4b commit 6e41e61

21 files changed

+257
-44
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,14 +1097,14 @@ fn check_region_bounds_on_impl_item<'tcx>(
10971097
.expect("expected impl item to have generics or else we can't compare them")
10981098
.span;
10991099

1100-
let mut generics_span = None;
1100+
let mut generics_span = tcx.def_span(trait_m.def_id);
11011101
let mut bounds_span = vec![];
11021102
let mut where_span = None;
11031103

11041104
if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
11051105
&& let Some(trait_generics) = trait_node.generics()
11061106
{
1107-
generics_span = Some(trait_generics.span);
1107+
generics_span = trait_generics.span;
11081108
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
11091109
// *are* present in the impl.
11101110
for p in trait_generics.predicates {

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub(crate) struct LifetimesOrBoundsMismatchOnTrait {
191191
#[label]
192192
pub span: Span,
193193
#[label(hir_analysis_generics_label)]
194-
pub generics_span: Option<Span>,
194+
pub generics_span: Span,
195195
#[label(hir_analysis_where_label)]
196196
pub where_span: Option<Span>,
197197
#[label(hir_analysis_bounds_label)]

compiler/rustc_resolve/messages.ftl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ resolve_added_macro_use =
1111
resolve_ancestor_only =
1212
visibilities can only be restricted to ancestor modules
1313
14-
resolve_anonymous_lifetime_non_gat_report_error =
15-
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
14+
resolve_anonymous_lifetime_non_gat_report_error = missing lifetime in associated type
1615
.label = this lifetime must come from the implemented type
16+
.note = in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
1717
1818
resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here
1919

compiler/rustc_resolve/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,8 @@ pub(crate) struct AnonymousLifetimeNonGatReportError {
958958
#[primary_span]
959959
#[label]
960960
pub(crate) lifetime: Span,
961+
#[note]
962+
pub(crate) decl: MultiSpan,
961963
}
962964

963965
#[derive(Subdiagnostic)]

compiler/rustc_resolve/src/late.rs

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::assert_matches::debug_assert_matches;
1010
use std::borrow::Cow;
1111
use std::collections::hash_map::Entry;
1212
use std::mem::{replace, swap, take};
13+
use std::ops::ControlFlow;
1314

1415
use rustc_ast::visit::{
1516
AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, try_visit, visit_opt, walk_list,
@@ -19,21 +20,21 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
1920
use rustc_data_structures::unord::{UnordMap, UnordSet};
2021
use rustc_errors::codes::*;
2122
use rustc_errors::{
22-
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
23-
pluralize,
23+
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan, StashKey,
24+
Suggestions, pluralize,
2425
};
2526
use rustc_hir::def::Namespace::{self, *};
2627
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
2728
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
2829
use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate};
2930
use rustc_middle::middle::resolve_bound_vars::Set1;
30-
use rustc_middle::ty::{DelegationFnSig, Visibility};
31+
use rustc_middle::ty::{AssocTag, DelegationFnSig, Visibility};
3132
use rustc_middle::{bug, span_bug};
3233
use rustc_session::config::{CrateType, ResolveDocLinks};
3334
use rustc_session::lint;
3435
use rustc_session::parse::feature_err;
3536
use rustc_span::source_map::{Spanned, respan};
36-
use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym};
37+
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym};
3738
use smallvec::{SmallVec, smallvec};
3839
use thin_vec::ThinVec;
3940
use tracing::{debug, instrument, trace};
@@ -373,11 +374,14 @@ enum LifetimeBinderKind {
373374
FnPtrType,
374375
PolyTrait,
375376
WhereBound,
377+
// Item covers foreign items, ADTs, type aliases, trait associated items and
378+
// trait alias associated items.
376379
Item,
377380
ConstItem,
378381
Function,
379382
Closure,
380383
ImplBlock,
384+
// Covers only `impl` associated types.
381385
ImplAssocType,
382386
}
383387

@@ -724,6 +728,9 @@ pub(crate) struct DiagMetadata<'ast> {
724728
/// The current impl items (used to suggest).
725729
current_impl_items: Option<&'ast [Box<AssocItem>]>,
726730

731+
/// The current impl items (used to suggest).
732+
current_impl_item: Option<&'ast AssocItem>,
733+
727734
/// When processing impl trait
728735
currently_processing_impl_trait: Option<(TraitRef, Ty)>,
729736

@@ -1880,9 +1887,31 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
18801887
ty: ty.span,
18811888
});
18821889
} else {
1890+
let decl = if !trait_id.is_local()
1891+
&& let Some(assoc) = self.diag_metadata.current_impl_item
1892+
&& let AssocItemKind::Type(_) = assoc.kind
1893+
&& let assocs = self.r.tcx.associated_items(trait_id)
1894+
&& let Some(ident) = assoc.kind.ident()
1895+
&& let Some(assoc) = assocs.find_by_ident_and_kind(
1896+
self.r.tcx,
1897+
ident,
1898+
AssocTag::Type,
1899+
trait_id,
1900+
) {
1901+
let mut decl: MultiSpan =
1902+
self.r.tcx.def_span(assoc.def_id).into();
1903+
decl.push_span_label(
1904+
self.r.tcx.def_span(trait_id),
1905+
String::new(),
1906+
);
1907+
decl
1908+
} else {
1909+
DUMMY_SP.into()
1910+
};
18831911
let mut err = self.r.dcx().create_err(
18841912
errors::AnonymousLifetimeNonGatReportError {
18851913
lifetime: lifetime.ident.span,
1914+
decl,
18861915
},
18871916
);
18881917
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
@@ -1924,17 +1953,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
19241953
}
19251954

19261955
fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) {
1927-
let Some((rib, span)) = self.lifetime_ribs[..i]
1928-
.iter()
1929-
.rev()
1930-
.skip(1)
1931-
.filter_map(|rib| match rib.kind {
1956+
let Some((rib, span)) =
1957+
self.lifetime_ribs[..i].iter().rev().find_map(|rib| match rib.kind {
19321958
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
19331959
Some((rib, span))
19341960
}
19351961
_ => None,
19361962
})
1937-
.next()
19381963
else {
19391964
return;
19401965
};
@@ -1956,11 +1981,63 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
19561981
);
19571982
}
19581983
} else {
1959-
err.span_label(
1960-
span,
1961-
"you could add a lifetime on the impl block, if the trait or the self type can \
1962-
have one",
1963-
);
1984+
struct AnonRefFinder;
1985+
impl<'ast> Visitor<'ast> for AnonRefFinder {
1986+
type Result = ControlFlow<Span>;
1987+
1988+
fn visit_ty(&mut self, ty: &'ast ast::Ty) -> Self::Result {
1989+
if let ast::TyKind::Ref(None, mut_ty) = &ty.kind {
1990+
return ControlFlow::Break(mut_ty.ty.span.shrink_to_lo());
1991+
}
1992+
visit::walk_ty(self, ty)
1993+
}
1994+
1995+
fn visit_lifetime(
1996+
&mut self,
1997+
lt: &'ast ast::Lifetime,
1998+
_cx: visit::LifetimeCtxt,
1999+
) -> Self::Result {
2000+
if lt.ident.name == kw::UnderscoreLifetime {
2001+
return ControlFlow::Break(lt.ident.span);
2002+
}
2003+
visit::walk_lifetime(self, lt)
2004+
}
2005+
}
2006+
2007+
if let Some(ty) = &self.diag_metadata.current_self_type
2008+
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_ty(ty)
2009+
{
2010+
err.multipart_suggestion_verbose(
2011+
"add a lifetime to the impl block and use it in the self type and associated \
2012+
type",
2013+
vec![
2014+
(span, "<'a>".to_string()),
2015+
(sp, "'a ".to_string()),
2016+
(lifetime.shrink_to_hi(), "'a ".to_string()),
2017+
],
2018+
Applicability::MaybeIncorrect,
2019+
);
2020+
} else if let Some(item) = &self.diag_metadata.current_item
2021+
&& let ItemKind::Impl(impl_) = &item.kind
2022+
&& let Some(of_trait) = &impl_.of_trait
2023+
&& let ControlFlow::Break(sp) = AnonRefFinder.visit_trait_ref(&of_trait.trait_ref)
2024+
{
2025+
err.multipart_suggestion_verbose(
2026+
"add a lifetime to the impl block and use it in the trait and associated type",
2027+
vec![
2028+
(span, "<'a>".to_string()),
2029+
(sp, "'a".to_string()),
2030+
(lifetime.shrink_to_hi(), "'a ".to_string()),
2031+
],
2032+
Applicability::MaybeIncorrect,
2033+
);
2034+
} else {
2035+
err.span_label(
2036+
span,
2037+
"you could add a lifetime on the impl block, if the trait or the self type \
2038+
could have one",
2039+
);
2040+
}
19642041
}
19652042
}
19662043

@@ -3304,6 +3381,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
33043381
) {
33053382
use crate::ResolutionError::*;
33063383
self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
3384+
let prev = self.diag_metadata.current_impl_item.take();
3385+
self.diag_metadata.current_impl_item = Some(&item);
33073386
match &item.kind {
33083387
AssocItemKind::Const(box ast::ConstItem {
33093388
ident,
@@ -3452,6 +3531,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
34523531
panic!("unexpanded macro in resolve!")
34533532
}
34543533
}
3534+
self.diag_metadata.current_impl_item = prev;
34553535
}
34563536

34573537
fn check_trait_item<F>(

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,27 @@ trait MyTrait {
99

1010
impl MyTrait for &i32 {
1111
type Output = &i32;
12-
//~^ 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
12+
//~^ ERROR missing lifetime in associated type
1313
}
1414

1515
impl MyTrait for &u32 {
1616
type Output = &'_ i32;
1717
//~^ ERROR `'_` cannot be used here
1818
}
1919

20+
impl<'a> MyTrait for &f64 {
21+
type Output = &f64;
22+
//~^ ERROR missing lifetime in associated type
23+
}
24+
25+
trait OtherTrait<'a> {
26+
type Output;
27+
}
28+
impl OtherTrait<'_> for f64 {
29+
type Output = &f64;
30+
//~^ ERROR missing lifetime in associated type
31+
}
32+
2033
// This is what you have to do:
2134
impl<'a> MyTrait for &'a f32 {
2235
type Output = &'a f32;
Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,49 @@
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
1+
error: missing lifetime in associated 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
64
LL | type Output = &i32;
75
| ^ this lifetime must come from the implemented type
6+
|
7+
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
8+
help: add a lifetime to the impl block and use it in the self type and associated type
9+
|
10+
LL ~ impl<'a> MyTrait for &'a i32 {
11+
LL ~ type Output = &'a i32;
12+
|
813

914
error[E0637]: `'_` cannot be used here
1015
--> $DIR/assoc-type.rs:16:20
1116
|
1217
LL | type Output = &'_ i32;
1318
| ^^ `'_` is a reserved lifetime name
1419

15-
error: aborting due to 2 previous errors
20+
error: missing lifetime in associated type
21+
--> $DIR/assoc-type.rs:21:19
22+
|
23+
LL | impl<'a> MyTrait for &f64 {
24+
| ---- there is a named lifetime specified on the impl block you could use
25+
LL | type Output = &f64;
26+
| ^ this lifetime must come from the implemented type
27+
|
28+
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
29+
help: consider using the lifetime from the impl block
30+
|
31+
LL | type Output = &'a f64;
32+
| ++
33+
34+
error: missing lifetime in associated type
35+
--> $DIR/assoc-type.rs:29:19
36+
|
37+
LL | type Output = &f64;
38+
| ^ this lifetime must come from the implemented type
39+
|
40+
= note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
41+
help: add a lifetime to the impl block and use it in the trait and associated type
42+
|
43+
LL ~ impl<'a> OtherTrait<'a> for f64 {
44+
LL ~ type Output = &'a f64;
45+
|
46+
47+
error: aborting due to 4 previous errors
1648

1749
For more information about this error, try `rustc --explain E0637`.

tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ impl<'a> IntoIterator for &S {
77
//~| NOTE unconstrained lifetime parameter
88
//~| HELP consider using the named lifetime here instead of an implicit lifetime
99
type Item = &T;
10-
//~^ ERROR in the trait associated type
10+
//~^ ERROR missing lifetime in associated type
1111
//~| HELP consider using the lifetime from the impl block
1212
//~| NOTE this lifetime must come from the implemented type
13+
//~| NOTE in the trait the associated type is declared without lifetime parameters
1314
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
1415

1516
fn into_iter(self) -> Self::IntoIter {

tests/ui/lifetimes/missing-lifetime-in-assoc-type-1.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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
1+
error: missing lifetime in associated type
22
--> $DIR/missing-lifetime-in-assoc-type-1.rs:9:17
33
|
44
LL | impl<'a> IntoIterator for &S {
@@ -7,6 +7,8 @@ LL | impl<'a> IntoIterator for &S {
77
LL | type Item = &T;
88
| ^ this lifetime must come from the implemented type
99
|
10+
note: in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
11+
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
1012
help: consider using the lifetime from the impl block
1113
|
1214
LL | type Item = &'a T;

tests/ui/lifetimes/missing-lifetime-in-assoc-type-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ struct T;
33

44
impl IntoIterator for &S {
55
type Item = &T;
6-
//~^ ERROR in the trait associated type
6+
//~^ ERROR missing lifetime in associated type
77
type IntoIter = std::collections::btree_map::Values<'a, i32, T>;
88
//~^ ERROR use of undeclared lifetime name `'a`
99

0 commit comments

Comments
 (0)