Skip to content

Commit d6e4fe5

Browse files
committed
A custom error message for lending iterators
1 parent 4649877 commit d6e4fe5

File tree

5 files changed

+74
-5
lines changed

5 files changed

+74
-5
lines changed

compiler/rustc_resolve/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ resolve_items_in_traits_are_not_importable =
234234
resolve_label_with_similar_name_reachable =
235235
a label with a similar name is reachable
236236
237+
resolve_lending_iterator_report_error =
238+
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.
239+
.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.
240+
237241
resolve_lifetime_param_in_enum_discriminant =
238242
lifetime parameters may not be used in enum discriminant values
239243

compiler/rustc_resolve/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,15 @@ pub(crate) struct ElidedAnonymousLivetimeReportError {
882882
pub(crate) suggestion: Option<ElidedAnonymousLivetimeReportErrorSuggestion>,
883883
}
884884

885+
#[derive(Diagnostic)]
886+
#[diag(resolve_lending_iterator_report_error)]
887+
pub(crate) struct LendingIteratorReportError {
888+
#[primary_span]
889+
pub(crate) lifetime: Span,
890+
#[note]
891+
pub(crate) ty: Span,
892+
}
893+
885894
#[derive(Subdiagnostic)]
886895
#[multipart_suggestion(
887896
resolve_elided_anonymous_lifetime_report_error_suggestion,

compiler/rustc_resolve/src/late.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
1010
use crate::{BindingKey, Used};
1111
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
12-
use crate::{ResolutionError, Resolver, Segment, UseError};
12+
use crate::{ResolutionError, Resolver, Segment, TyCtxt, UseError};
1313

1414
use rustc_ast::ptr::P;
1515
use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
@@ -1703,10 +1703,28 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
17031703
break;
17041704
}
17051705
}
1706-
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
1707-
span: lifetime.ident.span,
1708-
suggestion,
1709-
});
1706+
1707+
// Is it caused by user trying to implement a lending iterator?
1708+
if !self.in_func_body
1709+
&& let Some((module, _)) = &self.current_trait_ref
1710+
&& let Some(ty) = &self.diag_metadata.current_self_type
1711+
&& let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind
1712+
&& def_id_matches_path(
1713+
self.r.tcx,
1714+
trait_id,
1715+
&["core", "iter", "traits", "iterator", "Iterator"],
1716+
)
1717+
{
1718+
self.r.dcx().emit_err(errors::LendingIteratorReportError {
1719+
lifetime: lifetime.ident.span,
1720+
ty: ty.span(),
1721+
});
1722+
} else {
1723+
self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError {
1724+
span: lifetime.ident.span,
1725+
suggestion,
1726+
});
1727+
}
17101728
} else {
17111729
self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError {
17121730
span: lifetime.ident.span,
@@ -4824,3 +4842,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
48244842
}
48254843
}
48264844
}
4845+
4846+
/// Check if definition matches a path
4847+
fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool {
4848+
let mut path = expected_path.iter().rev();
4849+
while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) {
4850+
if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) {
4851+
return false;
4852+
}
4853+
def_id = parent;
4854+
}
4855+
return true;
4856+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
struct Data(String);
2+
3+
impl Iterator for Data {
4+
type Item = &str;
5+
//~^ 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.
6+
7+
fn next(&mut self) -> Option<Self::Item> {
8+
Some(&self.0)
9+
}
10+
}
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
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.
2+
--> $DIR/no_lending_iterators.rs:4:17
3+
|
4+
LL | type Item = &str;
5+
| ^
6+
|
7+
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.
8+
--> $DIR/no_lending_iterators.rs:3:19
9+
|
10+
LL | impl Iterator for Data {
11+
| ^^^^
12+
13+
error: aborting due to 1 previous error
14+

0 commit comments

Comments
 (0)