Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty)
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|| self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_collect(err, expr, expected, expr_ty)
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
Expand Down
68 changes: 68 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

/// Suggests calling `.collect()` on an `Iterator` it can be collected in the return type
/// ```compile_fail
/// let x: String = "foo".chars().map(|c| c); // with a .collect() here the code compiles
/// ```
pub(crate) fn suggest_collect(
&self,
err: &mut Diag<'_>,
expr: &hir::Expr<'_>,
expected_type: Ty<'tcx>,
found_type: Ty<'tcx>,
) -> bool {
let tcx = self.tcx;
let expected = self.resolve_vars_if_possible(expected_type);
let found = self.resolve_vars_if_possible(found_type);

if expected.references_error() || found.references_error() || expected.is_unit() {
return false;
}

let Some(iterator_trait_id) = tcx.get_diagnostic_item(sym::Iterator) else {
return false;
};

if !self
.infcx
.type_implements_trait(iterator_trait_id, [found], self.param_env)
.must_apply_modulo_regions()
{
return false;
}

let Some(from_iterator_trait_id) = tcx.get_diagnostic_item(sym::FromIterator) else {
return false;
};

let Some(iterator_item_id) = tcx
.associated_items(iterator_trait_id)
.in_definition_order()
.find(|item| item.name() == sym::Item)
.map(|item| item.def_id)
else {
return false;
};

let item_type = Ty::new_projection(tcx, iterator_item_id, [found]);
let item_type =
self.normalize(expr.span, rustc_middle::ty::Unnormalized::new_wip(item_type));

let can_collect = self
.infcx
.type_implements_trait(from_iterator_trait_id, [expected, item_type], self.param_env)
.may_apply();

if can_collect {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
"consider using `.collect()` to convert the `Iterator` into a `{expected}`"
),
".collect()",
rustc_errors::Applicability::MaybeIncorrect,
);
return true;
}

false
}

pub(crate) fn suggest_remove_last_method_call(
&self,
err: &mut Diag<'_>,
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/suggestions/suggest-collect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
fn main() {
Comment thread
thiago-fealves marked this conversation as resolved.
let _x: String = "hello".chars().map(|c| c);
Comment thread
thiago-fealves marked this conversation as resolved.
//~^ ERROR mismatched types
//~| HELP consider using `.collect()` to convert the `Iterator` into a `String`

let _y: Vec<i32> = vec![1, 2, 3].into_iter().map(|x| x);
//~^ ERROR mismatched types
//~| HELP consider using `.collect()` to convert the `Iterator` into a `Vec<i32>`

let res: Result<Vec<i32>, _> = ["1", "2"].into_iter().map(|s| s.parse::<i32>());
//~^ ERROR mismatched types
//~| HELP consider using `.collect()` to convert the `Iterator` into a `Result<Vec<i32>, _>`
let (a, b): (Vec<i32>, Vec<i32>) = vec![1, 2].into_iter().map(|x| (x, x));
//~^ ERROR mismatched types
//~| HELP consider using `.collect()` to convert the `Iterator` into a `(Vec<i32>, Vec<i32>)`
}
63 changes: 63 additions & 0 deletions tests/ui/suggestions/suggest-collect.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
error[E0308]: mismatched types
--> $DIR/suggest-collect.rs:2:22
|
LL | let _x: String = "hello".chars().map(|c| c);
| ------ ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `String`, found `Map<Chars<'_>, {closure@...}>`
| |
| expected due to this
|
= note: expected struct `String`
found struct `Map<Chars<'_>, {closure@$DIR/suggest-collect.rs:2:42: 2:45}>`
help: consider using `.collect()` to convert the `Iterator` into a `String`
|
LL | let _x: String = "hello".chars().map(|c| c).collect();
| ++++++++++

error[E0308]: mismatched types
--> $DIR/suggest-collect.rs:6:24
|
LL | let _y: Vec<i32> = vec![1, 2, 3].into_iter().map(|x| x);
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Vec<i32>`, found `Map<IntoIter<{integer}>, {closure@...}>`
| |
| expected due to this
|
= note: expected struct `Vec<i32>`
found struct `Map<std::vec::IntoIter<{integer}>, {closure@$DIR/suggest-collect.rs:6:54: 6:57}>`
help: consider using `.collect()` to convert the `Iterator` into a `Vec<i32>`
|
LL | let _y: Vec<i32> = vec![1, 2, 3].into_iter().map(|x| x).collect();
| ++++++++++

error[E0308]: mismatched types
--> $DIR/suggest-collect.rs:10:36
|
LL | let res: Result<Vec<i32>, _> = ["1", "2"].into_iter().map(|s| s.parse::<i32>());
| ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<Vec<i32>, _>`, found `Map<Iter<'_, &str>, {closure@...}>`
| |
| expected due to this
|
= note: expected enum `Result<Vec<i32>, _>`
found struct `Map<std::slice::Iter<'_, &str>, {closure@$DIR/suggest-collect.rs:10:63: 10:66}>`
help: consider using `.collect()` to convert the `Iterator` into a `Result<Vec<i32>, _>`
|
LL | let res: Result<Vec<i32>, _> = ["1", "2"].into_iter().map(|s| s.parse::<i32>()).collect();
| ++++++++++

error[E0308]: mismatched types
--> $DIR/suggest-collect.rs:13:40
|
LL | let (a, b): (Vec<i32>, Vec<i32>) = vec![1, 2].into_iter().map(|x| (x, x));
| -------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(Vec<i32>, Vec<i32>)`, found `Map<IntoIter<{integer}>, {closure@...}>`
| |
| expected due to this
|
= note: expected tuple `(Vec<i32>, Vec<i32>)`
found struct `Map<std::vec::IntoIter<{integer}>, {closure@$DIR/suggest-collect.rs:13:67: 13:70}>`
help: consider using `.collect()` to convert the `Iterator` into a `(Vec<i32>, Vec<i32>)`
|
LL | let (a, b): (Vec<i32>, Vec<i32>) = vec![1, 2].into_iter().map(|x| (x, x)).collect();
| ++++++++++

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0308`.
Loading