Rollup merge of #125407 - pacak:no-lending-iterators, r=pnkfelix
Detect when user is trying to create a lending `Iterator` and give a custom explanation The scope for this diagnostic is to detect lending iterators specifically and it's main goal is to help beginners to understand that what they are trying to implement might not be possible for `Iterator` trait specifically. I ended up to changing the wording from originally proposed in the ticket because it might be misleading otherwise: `Data` might have a lifetime parameter but it can be unrelated to items user is planning to return. Fixes https://github.com/rust-lang/rust/issues/125337
This commit is contained in:
commit
49f950434b
@ -11,6 +11,10 @@ resolve_added_macro_use =
|
||||
resolve_ancestor_only =
|
||||
visibilities can only be restricted to ancestor modules
|
||||
|
||||
resolve_anonymous_livetime_non_gat_report_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
|
||||
.label = this lifetime must come from the implemented type
|
||||
|
||||
resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here
|
||||
|
||||
resolve_associated_const_with_similar_name_exists =
|
||||
@ -234,6 +238,10 @@ resolve_items_in_traits_are_not_importable =
|
||||
resolve_label_with_similar_name_reachable =
|
||||
a label with a similar name is reachable
|
||||
|
||||
resolve_lending_iterator_report_error =
|
||||
associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
|
||||
.note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type.
|
||||
|
||||
resolve_lifetime_param_in_enum_discriminant =
|
||||
lifetime parameters may not be used in enum discriminant values
|
||||
|
||||
|
@ -882,6 +882,23 @@ pub(crate) struct ElidedAnonymousLivetimeReportError {
|
||||
pub(crate) suggestion: Option<ElidedAnonymousLivetimeReportErrorSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_lending_iterator_report_error)]
|
||||
pub(crate) struct LendingIteratorReportError {
|
||||
#[primary_span]
|
||||
pub(crate) lifetime: Span,
|
||||
#[note]
|
||||
pub(crate) ty: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(resolve_anonymous_livetime_non_gat_report_error)]
|
||||
pub(crate) struct AnonymousLivetimeNonGatReportError {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub(crate) lifetime: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
resolve_elided_anonymous_lifetime_report_error_suggestion,
|
||||
|
@ -9,7 +9,7 @@
|
||||
use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
|
||||
use crate::{BindingKey, Used};
|
||||
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
|
||||
use crate::{ResolutionError, Resolver, Segment, UseError};
|
||||
use crate::{ResolutionError, Resolver, Segment, TyCtxt, UseError};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||
@ -629,6 +629,9 @@ struct DiagMetadata<'ast> {
|
||||
in_assignment: Option<&'ast Expr>,
|
||||
is_assign_rhs: bool,
|
||||
|
||||
/// If we are setting an associated type in trait impl, is it a non-GAT type?
|
||||
in_non_gat_assoc_type: Option<bool>,
|
||||
|
||||
/// Used to detect possible `.` -> `..` typo when calling methods.
|
||||
in_range: Option<(&'ast Expr, &'ast Expr)>,
|
||||
|
||||
@ -1703,10 +1706,35 @@ fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
|
||||
span: lifetime.ident.span,
|
||||
suggestion,
|
||||
});
|
||||
|
||||
// are we trying to use an anonymous lifetime
|
||||
// on a non GAT associated trait type?
|
||||
if !self.in_func_body
|
||||
&& let Some((module, _)) = &self.current_trait_ref
|
||||
&& let Some(ty) = &self.diag_metadata.current_self_type
|
||||
&& Some(true) == self.diag_metadata.in_non_gat_assoc_type
|
||||
&& let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind
|
||||
{
|
||||
if def_id_matches_path(
|
||||
self.r.tcx,
|
||||
trait_id,
|
||||
&["core", "iter", "traits", "iterator", "Iterator"],
|
||||
) {
|
||||
self.r.dcx().emit_err(errors::LendingIteratorReportError {
|
||||
lifetime: lifetime.ident.span,
|
||||
ty: ty.span(),
|
||||
});
|
||||
} else {
|
||||
self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError {
|
||||
lifetime: lifetime.ident.span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
|
||||
span: lifetime.ident.span,
|
||||
suggestion,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError {
|
||||
span: lifetime.ident.span,
|
||||
@ -3058,6 +3086,7 @@ fn resolve_impl_item(
|
||||
);
|
||||
}
|
||||
AssocItemKind::Type(box TyAlias { generics, .. }) => {
|
||||
self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty());
|
||||
debug!("resolve_implementation AssocItemKind::Type");
|
||||
// We also need a new scope for the impl item type parameters.
|
||||
self.with_generic_param_rib(
|
||||
@ -3086,6 +3115,7 @@ fn resolve_impl_item(
|
||||
});
|
||||
},
|
||||
);
|
||||
self.diag_metadata.in_non_gat_assoc_type = None;
|
||||
}
|
||||
AssocItemKind::Delegation(box delegation) => {
|
||||
debug!("resolve_implementation AssocItemKind::Delegation");
|
||||
@ -4829,3 +4859,15 @@ pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if definition matches a path
|
||||
fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool {
|
||||
let mut path = expected_path.iter().rev();
|
||||
while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) {
|
||||
if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) {
|
||||
return false;
|
||||
}
|
||||
def_id = parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ trait MyTrait {
|
||||
|
||||
impl MyTrait for &i32 {
|
||||
type Output = &i32;
|
||||
//~^ ERROR `&` without an explicit lifetime name cannot be used here
|
||||
//~^ ERROR 11:19: 11:20: 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
|
||||
}
|
||||
|
||||
impl MyTrait for &u32 {
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0637]: `&` without an explicit lifetime name cannot be used here
|
||||
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/assoc-type.rs:11:19
|
||||
|
|
||||
LL | type Output = &i32;
|
||||
| ^ explicit lifetime name needed here
|
||||
| ^ this lifetime must come from the implemented type
|
||||
|
||||
error[E0637]: `'_` cannot be used here
|
||||
--> $DIR/assoc-type.rs:16:20
|
||||
|
35
tests/ui/lifetimes/no_lending_iterators.rs
Normal file
35
tests/ui/lifetimes/no_lending_iterators.rs
Normal file
@ -0,0 +1,35 @@
|
||||
struct Data(String);
|
||||
|
||||
impl Iterator for Data {
|
||||
type Item = &str;
|
||||
//~^ ERROR 4:17: 4:18: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
type Item;
|
||||
fn poke(&mut self, item: Self::Item);
|
||||
}
|
||||
|
||||
impl Bar for usize {
|
||||
type Item = &usize;
|
||||
//~^ ERROR 18:17: 18:18: 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
|
||||
|
||||
fn poke(&mut self, item: Self::Item) {
|
||||
self += *item;
|
||||
}
|
||||
}
|
||||
|
||||
impl Bar for isize {
|
||||
type Item<'a> = &'a isize;
|
||||
//~^ ERROR 27:14: 27:18: lifetime parameters or bounds on type `Item` do not match the trait declaration [E0195]
|
||||
|
||||
fn poke(&mut self, item: Self::Item) {
|
||||
self += *item;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
30
tests/ui/lifetimes/no_lending_iterators.stderr
Normal file
30
tests/ui/lifetimes/no_lending_iterators.stderr
Normal file
@ -0,0 +1,30 @@
|
||||
error: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
|
||||
--> $DIR/no_lending_iterators.rs:4:17
|
||||
|
|
||||
LL | type Item = &str;
|
||||
| ^
|
||||
|
|
||||
note: you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type.
|
||||
--> $DIR/no_lending_iterators.rs:3:19
|
||||
|
|
||||
LL | impl Iterator for Data {
|
||||
| ^^^^
|
||||
|
||||
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/no_lending_iterators.rs:18:17
|
||||
|
|
||||
LL | type Item = &usize;
|
||||
| ^ this lifetime must come from the implemented type
|
||||
|
||||
error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration
|
||||
--> $DIR/no_lending_iterators.rs:27:14
|
||||
|
|
||||
LL | type Item;
|
||||
| - lifetimes in impl do not match this type in trait
|
||||
...
|
||||
LL | type Item<'a> = &'a isize;
|
||||
| ^^^^ lifetimes do not match type in trait
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0195`.
|
Loading…
Reference in New Issue
Block a user