Skip to content

Commit

Permalink
A custom error message for lending iterators
Browse files Browse the repository at this point in the history
  • Loading branch information
pacak committed May 22, 2024
1 parent f0038a7 commit 6651570
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 5 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,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
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,15 @@ 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(Subdiagnostic)]
#[multipart_suggestion(
resolve_elided_anonymous_lifetime_report_error_suggestion,
Expand Down
40 changes: 35 additions & 5 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
Expand Down Expand Up @@ -1703,10 +1703,28 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
break;
}
}
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
span: lifetime.ident.span,
suggestion,
});

// Is it caused by user trying to implement a lending iterator?
if !self.in_func_body
&& let Some((module, _)) = &self.current_trait_ref
&& let Some(ty) = &self.diag_metadata.current_self_type
&& let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind
&& 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::ElidedAnonymousLivetimeReportError {
span: lifetime.ident.span,
suggestion,
});
}
} else {
self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError {
span: lifetime.ident.span,
Expand Down Expand Up @@ -4824,3 +4842,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;
}
12 changes: 12 additions & 0 deletions tests/ui/lifetimes/no_lending_iterators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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)
}
}

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/lifetimes/no_lending_iterators.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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: aborting due to 1 previous error

0 comments on commit 6651570

Please sign in to comment.