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
7 changed files with 140 additions and 8 deletions
|
@ -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 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
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 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
);
|
||||
}
|
||||
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 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
});
|
||||
},
|
||||
);
|
||||
self.diag_metadata.in_non_gat_assoc_type = None;
|
||||
}
|
||||
AssocItemKind::Delegation(box delegation) => {
|
||||
debug!("resolve_implementation AssocItemKind::Delegation");
|
||||
|
@ -4829,3 +4859,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue