@@ -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) ) ,
0 commit comments