Skip to content

Commit d359c25

Browse files
authored
Rollup merge of rust-lang#116717 - estebank:issue-9082, r=oli-obk
Special case iterator chain checks for suggestion When encountering method call chains of `Iterator`, check for trailing `;` in the body of closures passed into `Iterator::map`, as well as calls to `<T as Clone>::clone` when `T` is a type param and `T: !Clone`. Fix rust-lang#9082.
2 parents 616e379 + 26954f6 commit d359c25

File tree

5 files changed

+393
-1
lines changed

5 files changed

+393
-1
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+126-1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,18 @@ pub trait TypeErrCtxtExt<'tcx> {
313313
predicate: ty::Predicate<'tcx>,
314314
call_hir_id: HirId,
315315
);
316+
317+
fn look_for_iterator_item_mistakes(
318+
&self,
319+
assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
320+
typeck_results: &TypeckResults<'tcx>,
321+
type_diffs: &[TypeError<'tcx>],
322+
param_env: ty::ParamEnv<'tcx>,
323+
path_segment: &hir::PathSegment<'_>,
324+
args: &[hir::Expr<'_>],
325+
err: &mut Diagnostic,
326+
);
327+
316328
fn point_at_chain(
317329
&self,
318330
expr: &hir::Expr<'_>,
@@ -321,6 +333,7 @@ pub trait TypeErrCtxtExt<'tcx> {
321333
param_env: ty::ParamEnv<'tcx>,
322334
err: &mut Diagnostic,
323335
);
336+
324337
fn probe_assoc_types_at_expr(
325338
&self,
326339
type_diffs: &[TypeError<'tcx>],
@@ -3612,6 +3625,109 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
36123625
}
36133626
}
36143627

3628+
fn look_for_iterator_item_mistakes(
3629+
&self,
3630+
assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
3631+
typeck_results: &TypeckResults<'tcx>,
3632+
type_diffs: &[TypeError<'tcx>],
3633+
param_env: ty::ParamEnv<'tcx>,
3634+
path_segment: &hir::PathSegment<'_>,
3635+
args: &[hir::Expr<'_>],
3636+
err: &mut Diagnostic,
3637+
) {
3638+
let tcx = self.tcx;
3639+
// Special case for iterator chains, we look at potential failures of `Iterator::Item`
3640+
// not being `: Clone` and `Iterator::map` calls with spurious trailing `;`.
3641+
for entry in assocs_in_this_method {
3642+
let Some((_span, (def_id, ty))) = entry else {
3643+
continue;
3644+
};
3645+
for diff in type_diffs {
3646+
let Sorts(expected_found) = diff else {
3647+
continue;
3648+
};
3649+
if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
3650+
&& path_segment.ident.name == sym::map
3651+
&& self.can_eq(param_env, expected_found.found, *ty)
3652+
&& let [arg] = args
3653+
&& let hir::ExprKind::Closure(closure) = arg.kind
3654+
{
3655+
let body = tcx.hir().body(closure.body);
3656+
if let hir::ExprKind::Block(block, None) = body.value.kind
3657+
&& let None = block.expr
3658+
&& let [.., stmt] = block.stmts
3659+
&& let hir::StmtKind::Semi(expr) = stmt.kind
3660+
// FIXME: actually check the expected vs found types, but right now
3661+
// the expected is a projection that we need to resolve.
3662+
// && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
3663+
&& expected_found.found.is_unit()
3664+
{
3665+
err.span_suggestion_verbose(
3666+
expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
3667+
"consider removing this semicolon",
3668+
String::new(),
3669+
Applicability::MachineApplicable,
3670+
);
3671+
}
3672+
let expr = if let hir::ExprKind::Block(block, None) = body.value.kind
3673+
&& let Some(expr) = block.expr
3674+
{
3675+
expr
3676+
} else {
3677+
body.value
3678+
};
3679+
if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind
3680+
&& path_segment.ident.name == sym::clone
3681+
&& let Some(expr_ty) = typeck_results.expr_ty_opt(expr)
3682+
&& let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr)
3683+
&& self.can_eq(param_env, expr_ty, rcvr_ty)
3684+
&& let ty::Ref(_, ty, _) = expr_ty.kind()
3685+
{
3686+
err.span_label(
3687+
span,
3688+
format!(
3689+
"this method call is cloning the reference `{expr_ty}`, not \
3690+
`{ty}` which doesn't implement `Clone`",
3691+
),
3692+
);
3693+
let ty::Param(..) = ty.kind() else {
3694+
continue;
3695+
};
3696+
let hir = tcx.hir();
3697+
let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id);
3698+
3699+
let pred = ty::Binder::dummy(ty::TraitPredicate {
3700+
trait_ref: ty::TraitRef::from_lang_item(
3701+
tcx,
3702+
LangItem::Clone,
3703+
span,
3704+
[*ty],
3705+
),
3706+
polarity: ty::ImplPolarity::Positive,
3707+
});
3708+
let Some(generics) = node.generics() else {
3709+
continue;
3710+
};
3711+
let Some(body_id) = node.body_id() else {
3712+
continue;
3713+
};
3714+
suggest_restriction(
3715+
tcx,
3716+
hir.body_owner_def_id(body_id),
3717+
&generics,
3718+
&format!("type parameter `{ty}`"),
3719+
err,
3720+
node.fn_sig(),
3721+
None,
3722+
pred,
3723+
None,
3724+
);
3725+
}
3726+
}
3727+
}
3728+
}
3729+
}
3730+
36153731
fn point_at_chain(
36163732
&self,
36173733
expr: &hir::Expr<'_>,
@@ -3631,13 +3747,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
36313747
let mut prev_ty = self.resolve_vars_if_possible(
36323748
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
36333749
);
3634-
while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
3750+
while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
36353751
// Point at every method call in the chain with the resulting type.
36363752
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
36373753
// ^^^^^^ ^^^^^^^^^^^
36383754
expr = rcvr_expr;
36393755
let assocs_in_this_method =
36403756
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
3757+
self.look_for_iterator_item_mistakes(
3758+
&assocs_in_this_method,
3759+
typeck_results,
3760+
&type_diffs,
3761+
param_env,
3762+
path_segment,
3763+
args,
3764+
err,
3765+
);
36413766
assocs.push(assocs_in_this_method);
36423767
prev_ty = self.resolve_vars_if_possible(
36433768
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// run-rustfix
2+
use std::collections::hash_set::Iter;
3+
use std::collections::HashSet;
4+
5+
fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> where X: Clone {
6+
let i = i.map(|x| x.clone());
7+
i.collect() //~ ERROR E0277
8+
}
9+
10+
fn main() {
11+
let v = vec![(0, 0)];
12+
let scores = v
13+
.iter()
14+
.map(|(a, b)| {
15+
a + b
16+
});
17+
println!("{}", scores.sum::<i32>()); //~ ERROR E0277
18+
println!(
19+
"{}",
20+
vec![0, 1]
21+
.iter()
22+
.map(|x| x * 2)
23+
.map(|x| { x })
24+
.map(|x| { x })
25+
.sum::<i32>(), //~ ERROR E0277
26+
);
27+
println!("{}", vec![0, 1].iter().map(|x| { x }).sum::<i32>()); //~ ERROR E0277
28+
let a = vec![0];
29+
let b = a.into_iter();
30+
let c = b.map(|x| x + 1);
31+
let d = c.filter(|x| *x > 10 );
32+
let e = d.map(|x| {
33+
x + 1
34+
});
35+
let f = e.filter(|_| false);
36+
let g: Vec<i32> = f.collect(); //~ ERROR E0277
37+
println!("{g:?}");
38+
39+
let mut s = HashSet::new();
40+
s.insert(1u8);
41+
println!("{:?}", iter_to_vec(s.iter()));
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// run-rustfix
2+
use std::collections::hash_set::Iter;
3+
use std::collections::HashSet;
4+
5+
fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec<X> {
6+
let i = i.map(|x| x.clone());
7+
i.collect() //~ ERROR E0277
8+
}
9+
10+
fn main() {
11+
let v = vec![(0, 0)];
12+
let scores = v
13+
.iter()
14+
.map(|(a, b)| {
15+
a + b;
16+
});
17+
println!("{}", scores.sum::<i32>()); //~ ERROR E0277
18+
println!(
19+
"{}",
20+
vec![0, 1]
21+
.iter()
22+
.map(|x| x * 2)
23+
.map(|x| { x; })
24+
.map(|x| { x })
25+
.sum::<i32>(), //~ ERROR E0277
26+
);
27+
println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::<i32>()); //~ ERROR E0277
28+
let a = vec![0];
29+
let b = a.into_iter();
30+
let c = b.map(|x| x + 1);
31+
let d = c.filter(|x| *x > 10 );
32+
let e = d.map(|x| {
33+
x + 1;
34+
});
35+
let f = e.filter(|_| false);
36+
let g: Vec<i32> = f.collect(); //~ ERROR E0277
37+
println!("{g:?}");
38+
39+
let mut s = HashSet::new();
40+
s.insert(1u8);
41+
println!("{:?}", iter_to_vec(s.iter()));
42+
}

0 commit comments

Comments
 (0)