about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs66
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/iter/traits/iterator.rs1
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.rs8
-rw-r--r--src/test/ui/typeck/typeck_type_placeholder_item.stderr23
5 files changed, 96 insertions, 3 deletions
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index b7d599f57fd..0a7a3038d00 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -24,14 +24,20 @@ use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{GenericParamKind, Node};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{
+    self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeVisitable,
+};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::spec::abi;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
+use rustc_trait_selection::traits::ObligationCtxt;
 use std::iter;
 
 mod generics_of;
@@ -1224,7 +1230,17 @@ fn infer_return_ty_for_fn_sig<'tcx>(
                 // to prevent the user from getting a papercut while trying to use the unique closure
                 // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
                 diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
-                diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
+                diag.note(
+                    "for more information on `Fn` traits and closure types, see \
+                     https://doc.rust-lang.org/book/ch13-01-closures.html",
+                );
+            } else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) {
+                diag.span_suggestion(
+                    ty.span,
+                    "replace with an appropriate return type",
+                    format!("impl Iterator<Item = {}>", i_ty),
+                    Applicability::MachineApplicable,
+                );
             }
             diag.emit();
 
@@ -1242,6 +1258,52 @@ fn infer_return_ty_for_fn_sig<'tcx>(
     }
 }
 
+fn suggest_impl_iterator<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ret_ty: Ty<'tcx>,
+    span: Span,
+    hir_id: hir::HirId,
+    def_id: LocalDefId,
+) -> Option<Ty<'tcx>> {
+    let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; };
+    let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; };
+    if !tcx
+        .infer_ctxt()
+        .build()
+        .type_implements_trait(iter_trait, [ret_ty], tcx.param_env(iter_trait))
+        .must_apply_modulo_regions()
+    {
+        return None;
+    }
+    let infcx = tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new_in_snapshot(&infcx);
+    // Find the type of `Iterator::Item`.
+    let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
+    let ty_var = infcx.next_ty_var(origin);
+    let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
+        ty::ProjectionPredicate {
+            projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())),
+            term: ty_var.into(),
+        },
+    )));
+    // Add `<ret_ty as Iterator>::Item = _` obligation.
+    ocx.register_obligation(crate::traits::Obligation::misc(
+        tcx,
+        span,
+        hir_id,
+        tcx.param_env(def_id),
+        projection,
+    ));
+    if ocx.select_where_possible().is_empty()
+        && let item_ty = infcx.resolve_vars_if_possible(ty_var)
+        && !item_ty.references_error()
+        && !item_ty.has_placeholders()
+    {
+        return Some(item_ty);
+    }
+    None
+}
+
 fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
     let icx = ItemCtxt::new(tcx, def_id);
     let item = tcx.hir().expect_item(def_id.expect_local());
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 90f654c68ec..85510fa2c66 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -213,6 +213,7 @@ symbols! {
         Is,
         ItemContext,
         Iterator,
+        IteratorItem,
         Layout,
         Left,
         LinkedList,
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index bac836292f8..dcbed4e963f 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -66,6 +66,7 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub trait Iterator {
     /// The type of the elements being iterated over.
+    #[rustc_diagnostic_item = "IteratorItem"]
     #[stable(feature = "rust1", since = "1.0.0")]
     type Item;
 
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs
index 22fedb22d66..b96c5271339 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.rs
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs
@@ -220,3 +220,11 @@ fn value() -> Option<&'static _> {
 
 const _: Option<_> = map(value);
 //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants
+
+fn evens_squared(n: usize) -> _ {
+//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+    (1..n).filter(|x| x % 2 == 0).map(|x| x * x)
+}
+
+const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
+//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
index c57f71b8057..034bdae5f2e 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
@@ -428,6 +428,27 @@ LL | const _: Option<_> = map(value);
    |          not allowed in type signatures
    |          help: replace with the correct type: `Option<u8>`
 
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+  --> $DIR/typeck_type_placeholder_item.rs:224:31
+   |
+LL | fn evens_squared(n: usize) -> _ {
+   |                               ^
+   |                               |
+   |                               not allowed in type signatures
+   |                               help: replace with an appropriate return type: `impl Iterator<Item = usize>`
+
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants
+  --> $DIR/typeck_type_placeholder_item.rs:229:10
+   |
+LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
+   |          ^ not allowed in type signatures
+   |
+note: however, the inferred type `Map<Filter<std::ops::Range<i32>, [closure@$DIR/typeck_type_placeholder_item.rs:229:29: 229:32]>, [closure@$DIR/typeck_type_placeholder_item.rs:229:49: 229:52]>` cannot be named
+  --> $DIR/typeck_type_placeholder_item.rs:229:14
+   |
+LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:140:31
    |
@@ -636,7 +657,7 @@ LL |     const D: _ = 42;
    |              not allowed in type signatures
    |              help: replace with the correct type: `i32`
 
-error: aborting due to 69 previous errors
+error: aborting due to 71 previous errors
 
 Some errors have detailed explanations: E0121, E0282, E0403.
 For more information about an error, try `rustc --explain E0121`.