Skip to content

Commit 1b341fe

Browse files
committed
Suggest impl Iterator when possible for _ return type
Address rust-lang#106096.
1 parent caa64e5 commit 1b341fe

File tree

5 files changed

+96
-3
lines changed

5 files changed

+96
-3
lines changed

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,20 @@ use rustc_hir as hir;
2424
use rustc_hir::def_id::{DefId, LocalDefId};
2525
use rustc_hir::intravisit::{self, Visitor};
2626
use rustc_hir::{GenericParamKind, Node};
27+
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
28+
use rustc_infer::infer::TyCtxtInferExt;
2729
use rustc_middle::hir::nested_filter;
2830
use rustc_middle::ty::query::Providers;
2931
use rustc_middle::ty::util::{Discr, IntTypeExt};
30-
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt};
32+
use rustc_middle::ty::{
33+
self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeVisitable,
34+
};
3135
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3236
use rustc_span::Span;
3337
use rustc_target::spec::abi;
38+
use rustc_trait_selection::infer::InferCtxtExt;
3439
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
40+
use rustc_trait_selection::traits::ObligationCtxt;
3541
use std::iter;
3642

3743
mod generics_of;
@@ -1224,7 +1230,17 @@ fn infer_return_ty_for_fn_sig<'tcx>(
12241230
// to prevent the user from getting a papercut while trying to use the unique closure
12251231
// syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
12261232
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
1227-
diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
1233+
diag.note(
1234+
"for more information on `Fn` traits and closure types, see \
1235+
https://doc.rust-lang.org/book/ch13-01-closures.html",
1236+
);
1237+
} else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) {
1238+
diag.span_suggestion(
1239+
ty.span,
1240+
"replace with an appropriate return type",
1241+
format!("impl Iterator<Item = {}>", i_ty),
1242+
Applicability::MachineApplicable,
1243+
);
12281244
}
12291245
diag.emit();
12301246

@@ -1242,6 +1258,52 @@ fn infer_return_ty_for_fn_sig<'tcx>(
12421258
}
12431259
}
12441260

1261+
fn suggest_impl_iterator<'tcx>(
1262+
tcx: TyCtxt<'tcx>,
1263+
ret_ty: Ty<'tcx>,
1264+
span: Span,
1265+
hir_id: hir::HirId,
1266+
def_id: LocalDefId,
1267+
) -> Option<Ty<'tcx>> {
1268+
let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; };
1269+
let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; };
1270+
if !tcx
1271+
.infer_ctxt()
1272+
.build()
1273+
.type_implements_trait(iter_trait, [ret_ty], tcx.param_env(iter_trait))
1274+
.must_apply_modulo_regions()
1275+
{
1276+
return None;
1277+
}
1278+
let infcx = tcx.infer_ctxt().build();
1279+
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
1280+
// Find the type of `Iterator::Item`.
1281+
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
1282+
let ty_var = infcx.next_ty_var(origin);
1283+
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
1284+
ty::ProjectionPredicate {
1285+
projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())),
1286+
term: ty_var.into(),
1287+
},
1288+
)));
1289+
// Add `<ret_ty as Iterator>::Item = _` obligation.
1290+
ocx.register_obligation(crate::traits::Obligation::misc(
1291+
tcx,
1292+
span,
1293+
hir_id,
1294+
tcx.param_env(def_id),
1295+
projection,
1296+
));
1297+
if ocx.select_where_possible().is_empty()
1298+
&& let item_ty = infcx.resolve_vars_if_possible(ty_var)
1299+
&& !item_ty.references_error()
1300+
&& !item_ty.has_placeholders()
1301+
{
1302+
return Some(item_ty);
1303+
}
1304+
None
1305+
}
1306+
12451307
fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
12461308
let icx = ItemCtxt::new(tcx, def_id);
12471309
let item = tcx.hir().expect_item(def_id.expect_local());

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ symbols! {
213213
Is,
214214
ItemContext,
215215
Iterator,
216+
IteratorItem,
216217
Layout,
217218
Left,
218219
LinkedList,

library/core/src/iter/traits/iterator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
6666
#[must_use = "iterators are lazy and do nothing unless consumed"]
6767
pub trait Iterator {
6868
/// The type of the elements being iterated over.
69+
#[rustc_diagnostic_item = "IteratorItem"]
6970
#[stable(feature = "rust1", since = "1.0.0")]
7071
type Item;
7172

src/test/ui/typeck/typeck_type_placeholder_item.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,11 @@ fn value() -> Option<&'static _> {
220220

221221
const _: Option<_> = map(value);
222222
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants
223+
224+
fn evens_squared(n: usize) -> _ {
225+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
226+
(1..n).filter(|x| x % 2 == 0).map(|x| x * x)
227+
}
228+
229+
const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
230+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants

src/test/ui/typeck/typeck_type_placeholder_item.stderr

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,27 @@ LL | const _: Option<_> = map(value);
428428
| not allowed in type signatures
429429
| help: replace with the correct type: `Option<u8>`
430430

431+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
432+
--> $DIR/typeck_type_placeholder_item.rs:224:31
433+
|
434+
LL | fn evens_squared(n: usize) -> _ {
435+
| ^
436+
| |
437+
| not allowed in type signatures
438+
| help: replace with an appropriate return type: `impl Iterator<Item = usize>`
439+
440+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants
441+
--> $DIR/typeck_type_placeholder_item.rs:229:10
442+
|
443+
LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
444+
| ^ not allowed in type signatures
445+
|
446+
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
447+
--> $DIR/typeck_type_placeholder_item.rs:229:14
448+
|
449+
LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
450+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
451+
431452
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
432453
--> $DIR/typeck_type_placeholder_item.rs:140:31
433454
|
@@ -636,7 +657,7 @@ LL | const D: _ = 42;
636657
| not allowed in type signatures
637658
| help: replace with the correct type: `i32`
638659

639-
error: aborting due to 69 previous errors
660+
error: aborting due to 71 previous errors
640661

641662
Some errors have detailed explanations: E0121, E0282, E0403.
642663
For more information about an error, try `rustc --explain E0121`.

0 commit comments

Comments
 (0)