suggest into_iter()
when Iterator
method called on impl IntoIterator
This commit is contained in:
parent
de4d615e6b
commit
e99766d885
@ -109,6 +109,93 @@ fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
|||||||
self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
|
self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn impl_into_iterator_should_be_iterator(
|
||||||
|
&self,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
unsatisfied_predicates: &Vec<(
|
||||||
|
ty::Predicate<'_>,
|
||||||
|
Option<ty::Predicate<'_>>,
|
||||||
|
Option<ObligationCause<'_>>,
|
||||||
|
)>,
|
||||||
|
) -> bool {
|
||||||
|
fn predicate_bounds_generic_param<'tcx>(
|
||||||
|
predicate: ty::Predicate<'_>,
|
||||||
|
generics: &'tcx ty::Generics,
|
||||||
|
generic_param: &ty::GenericParamDef,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
|
||||||
|
predicate.kind().as_ref().skip_binder()
|
||||||
|
{
|
||||||
|
let ty::TraitPredicate { trait_ref: ty::TraitRef { args, .. }, .. } = trait_pred;
|
||||||
|
if args.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(arg_ty) = args[0].as_type() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let ty::Param(param) = arg_ty.kind() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// Is `generic_param` the same as the arg for this trait predicate?
|
||||||
|
generic_param.index == generics.type_param(¶m, tcx).index
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_iterator_predicate(predicate: ty::Predicate<'_>, tcx: TyCtxt<'_>) -> bool {
|
||||||
|
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
|
||||||
|
predicate.kind().as_ref().skip_binder()
|
||||||
|
{
|
||||||
|
tcx.is_diagnostic_item(sym::Iterator, trait_pred.trait_ref.def_id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the `ty` implement `IntoIterator`?
|
||||||
|
let Some(into_iterator_trait) = self.tcx.get_diagnostic_item(sym::IntoIterator) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let trait_ref = ty::TraitRef::new(self.tcx, into_iterator_trait, [ty]);
|
||||||
|
let cause = ObligationCause::new(span, self.body_id, ObligationCauseCode::MiscObligation);
|
||||||
|
let obligation = Obligation::new(self.tcx, cause, self.param_env, trait_ref);
|
||||||
|
if !self.predicate_must_hold_modulo_regions(&obligation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match ty.kind() {
|
||||||
|
ty::Param(param) => {
|
||||||
|
let generics = self.tcx.generics_of(self.body_id);
|
||||||
|
let generic_param = generics.type_param(¶m, self.tcx);
|
||||||
|
for unsatisfied in unsatisfied_predicates.iter() {
|
||||||
|
// The parameter implements `IntoIterator`
|
||||||
|
// but it has called a method that requires it to implement `Iterator`
|
||||||
|
if predicate_bounds_generic_param(
|
||||||
|
unsatisfied.0,
|
||||||
|
generics,
|
||||||
|
generic_param,
|
||||||
|
self.tcx,
|
||||||
|
) && is_iterator_predicate(unsatisfied.0, self.tcx)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Alias(ty::AliasKind::Opaque, _) => {
|
||||||
|
for unsatisfied in unsatisfied_predicates.iter() {
|
||||||
|
if is_iterator_predicate(unsatisfied.0, self.tcx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub fn report_method_error(
|
pub fn report_method_error(
|
||||||
&self,
|
&self,
|
||||||
@ -555,6 +642,15 @@ pub fn report_no_match_method_error(
|
|||||||
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
|
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
} else if self.impl_into_iterator_should_be_iterator(rcvr_ty, span, unsatisfied_predicates)
|
||||||
|
{
|
||||||
|
err.span_label(span, format!("`{rcvr_ty}` is not an iterator"));
|
||||||
|
err.multipart_suggestion_verbose(
|
||||||
|
"call `.into_iter()` first",
|
||||||
|
vec![(span.shrink_to_lo(), format!("into_iter()."))],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
return Some(err);
|
||||||
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
|
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
|
||||||
// We special case the situation where we are looking for `_` in
|
// We special case the situation where we are looking for `_` in
|
||||||
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
|
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
|
||||||
|
19
tests/ui/did_you_mean/collect-without-into-iter-call.rs
Normal file
19
tests/ui/did_you_mean/collect-without-into-iter-call.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Tests that the compiler suggests an `into_iter` call when an `Iterator` method
|
||||||
|
// is called on something that implements `IntoIterator`
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let items = items();
|
||||||
|
let other_items = items.map(|i| i + 1);
|
||||||
|
//~^ ERROR no method named `map` found for opaque type `impl IntoIterator<Item = i32>` in the current scope
|
||||||
|
let vec: Vec<i32> = items.collect();
|
||||||
|
//~^ ERROR no method named `collect` found for opaque type `impl IntoIterator<Item = i32>` in the current scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn items() -> impl IntoIterator<Item = i32> {
|
||||||
|
vec![1, 2, 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(items: impl IntoIterator<Item = String>) -> Vec<String> {
|
||||||
|
items.collect()
|
||||||
|
//~^ ERROR no method named `collect` found for type parameter `impl IntoIterator<Item = String>` in the current scope
|
||||||
|
}
|
36
tests/ui/did_you_mean/collect-without-into-iter-call.stderr
Normal file
36
tests/ui/did_you_mean/collect-without-into-iter-call.stderr
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
error[E0599]: no method named `map` found for opaque type `impl IntoIterator<Item = i32>` in the current scope
|
||||||
|
--> $DIR/collect-without-into-iter-call.rs:6:29
|
||||||
|
|
|
||||||
|
LL | let other_items = items.map(|i| i + 1);
|
||||||
|
| ^^^ `impl IntoIterator<Item = i32>` is not an iterator
|
||||||
|
|
|
||||||
|
help: call `.into_iter()` first
|
||||||
|
|
|
||||||
|
LL | let other_items = items.into_iter().map(|i| i + 1);
|
||||||
|
| ++++++++++++
|
||||||
|
|
||||||
|
error[E0599]: no method named `collect` found for opaque type `impl IntoIterator<Item = i32>` in the current scope
|
||||||
|
--> $DIR/collect-without-into-iter-call.rs:8:31
|
||||||
|
|
|
||||||
|
LL | let vec: Vec<i32> = items.collect();
|
||||||
|
| ^^^^^^^ `impl IntoIterator<Item = i32>` is not an iterator
|
||||||
|
|
|
||||||
|
help: call `.into_iter()` first
|
||||||
|
|
|
||||||
|
LL | let vec: Vec<i32> = items.into_iter().collect();
|
||||||
|
| ++++++++++++
|
||||||
|
|
||||||
|
error[E0599]: no method named `collect` found for type parameter `impl IntoIterator<Item = String>` in the current scope
|
||||||
|
--> $DIR/collect-without-into-iter-call.rs:17:11
|
||||||
|
|
|
||||||
|
LL | items.collect()
|
||||||
|
| ^^^^^^^ `impl IntoIterator<Item = String>` is not an iterator
|
||||||
|
|
|
||||||
|
help: call `.into_iter()` first
|
||||||
|
|
|
||||||
|
LL | items.into_iter().collect()
|
||||||
|
| ++++++++++++
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0599`.
|
Loading…
Reference in New Issue
Block a user