Rollup merge of #151661 - estebank:issue-68095, r=mati865

Suggest changing `iter`/`into_iter` when the other was meant

When encountering a call to `iter` that should have been `into_iter` and vice-versa, provide a structured suggestion:

```
error[E0271]: type mismatch resolving `<IntoIter<{integer}, 3> as IntoIterator>::Item == &{integer}`
  --> $DIR/into_iter-when-iter-was-intended.rs:5:37
   |
LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
   |                               ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
   |                               |
   |                               required by a bound introduced by this call
   |
note: the method call chain might not have had the expected associated types
  --> $DIR/into_iter-when-iter-was-intended.rs:5:47
   |
LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
   |                                     --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here
   |                                     |
   |                                     this expression has type `[{integer}; 3]`
note: required by a bound in `std::iter::Iterator::chain`
  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider not consuming the `[{integer}, 3]` to construct the `Iterator`
   |
LL -     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
LL +     let _a = [0, 1, 2].iter().chain([3, 4, 5].iter());
   |
```

Finish addressing the original case in rust-lang/rust#68095. Only the case of chaining a `Vec` or `[]` is left unhandled.
This commit is contained in:
Jonathan Brouwer 2026-01-26 18:19:17 +01:00 committed by GitHub
commit 9ad4ae88cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 116 additions and 3 deletions

View file

@ -270,6 +270,7 @@ symbols! {
Into,
IntoFuture,
IntoIterator,
IntoIteratorItem,
IoBufRead,
IoLines,
IoRead,

View file

@ -4390,6 +4390,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
path_segment: &hir::PathSegment<'_>,
args: &[hir::Expr<'_>],
prev_ty: Ty<'_>,
err: &mut Diag<'_, G>,
) {
let tcx = self.tcx;
@ -4403,6 +4404,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let TypeError::Sorts(expected_found) = diff else {
continue;
};
if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id)
&& path_segment.ident.name == sym::iter
&& self.can_eq(
param_env,
Ty::new_ref(
tcx,
tcx.lifetimes.re_erased,
expected_found.found,
ty::Mutability::Not,
),
*ty,
)
&& let [] = args
{
// Used `.iter()` when `.into_iter()` was likely meant.
err.span_suggestion_verbose(
path_segment.ident.span,
format!("consider consuming the `{prev_ty}` to construct the `Iterator`"),
"into_iter".to_string(),
Applicability::MachineApplicable,
);
}
if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id)
&& path_segment.ident.name == sym::into_iter
&& self.can_eq(
param_env,
expected_found.found,
Ty::new_ref(tcx, tcx.lifetimes.re_erased, *ty, ty::Mutability::Not),
)
&& let [] = args
{
// Used `.into_iter()` when `.iter()` was likely meant.
err.span_suggestion_verbose(
path_segment.ident.span,
format!(
"consider not consuming the `{prev_ty}` to construct the `Iterator`"
),
"iter".to_string(),
Applicability::MachineApplicable,
);
}
if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
&& path_segment.ident.name == sym::map
&& self.can_eq(param_env, expected_found.found, *ty)
@ -4515,6 +4557,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
expr = rcvr_expr;
let assocs_in_this_method =
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
prev_ty = self.resolve_vars_if_possible(
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
self.look_for_iterator_item_mistakes(
&assocs_in_this_method,
typeck_results,
@ -4522,12 +4567,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
param_env,
path_segment,
args,
prev_ty,
err,
);
assocs.push(assocs_in_this_method);
prev_ty = self.resolve_vars_if_possible(
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
&& let hir::Path { res: Res::Local(hir_id), .. } = path

View file

@ -281,6 +281,7 @@ pub trait FromIterator<A>: Sized {
#[stable(feature = "rust1", since = "1.0.0")]
pub trait IntoIterator {
/// The type of the elements being iterated over.
#[rustc_diagnostic_item = "IntoIteratorItem"]
#[stable(feature = "rust1", since = "1.0.0")]
type Item;

View file

@ -0,0 +1,10 @@
//@ run-rustfix
//@ edition:2021
// Suggest using the right `IntoIterator` method. #68095
fn main() {
let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); //~ ERROR E0271
let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271
// These don't have appropriate suggestions yet.
// let c = [0, 1, 2].iter().chain([3, 4, 5]);
// let d = [0, 1, 2].iter().chain(vec![3, 4, 5]);
}

View file

@ -0,0 +1,10 @@
//@ run-rustfix
//@ edition:2021
// Suggest using the right `IntoIterator` method. #68095
fn main() {
let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271
let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); //~ ERROR E0271
// These don't have appropriate suggestions yet.
// let c = [0, 1, 2].iter().chain([3, 4, 5]);
// let d = [0, 1, 2].iter().chain(vec![3, 4, 5]);
}

View file

@ -0,0 +1,48 @@
error[E0271]: type mismatch resolving `<IntoIter<{integer}, 3> as IntoIterator>::Item == &{integer}`
--> $DIR/into_iter-when-iter-was-intended.rs:5:37
|
LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
| ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
| |
| required by a bound introduced by this call
|
note: the method call chain might not have had the expected associated types
--> $DIR/into_iter-when-iter-was-intended.rs:5:47
|
LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
| --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here
| |
| this expression has type `[{integer}; 3]`
note: required by a bound in `std::iter::Iterator::chain`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider not consuming the `[{integer}; 3]` to construct the `Iterator`
|
LL - let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
LL + let _a = [0, 1, 2].iter().chain([3, 4, 5].iter());
|
error[E0271]: type mismatch resolving `<Iter<'_, {integer}> as IntoIterator>::Item == {integer}`
--> $DIR/into_iter-when-iter-was-intended.rs:6:42
|
LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter());
| ----- ^^^^^^^^^^^^^^^^ expected integer, found `&{integer}`
| |
| required by a bound introduced by this call
|
note: the method call chain might not have had the expected associated types
--> $DIR/into_iter-when-iter-was-intended.rs:6:52
|
LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter());
| --------- ^^^^^^ `IntoIterator::Item` is `&{integer}` here
| |
| this expression has type `[{integer}; 3]`
note: required by a bound in `std::iter::Iterator::chain`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider consuming the `&[{integer}]` to construct the `Iterator`
|
LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter());
| +++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0271`.