diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8fd228211f3c3..c4391c3ec4fc4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -270,6 +270,7 @@ symbols! { Into, IntoFuture, IntoIterator, + IntoIteratorItem, IoBufRead, IoLines, IoRead, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 1c08b5e331422..d54f3812350d9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -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 diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index cdf81385bdafb..c3b9a0f0b7a4e 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -281,6 +281,7 @@ pub trait FromIterator: 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; diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.fixed b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed new file mode 100644 index 0000000000000..e841b1605f11a --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed @@ -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]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.rs b/tests/ui/iterators/into_iter-when-iter-was-intended.rs new file mode 100644 index 0000000000000..8d4376aa0ae6d --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.rs @@ -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]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.stderr b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr new file mode 100644 index 0000000000000..f26db9781b13a --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr @@ -0,0 +1,48 @@ +error[E0271]: type mismatch resolving ` 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 ` 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`.