Change:
In long method chains we might inadvertently call a method that is not meant to be chained. When this happens, the most likely error to occur is a type error involving . We should instead look if the rhs expression is a method chain and look at all the chained call's type to find where is introduced. Given Ideally the output would be closer to <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Hi, I'd like to try working on this :) claim

Discussion:
self.annotate_expected_due_to_let_ty(err, expr, error); self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error); self.note_type_is_not_clone(err, expected, expr_ty, expr); <del> self.note_internal_mutation_in_method(err, expr, expected, expr_ty); </del> <ins> self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty); </ins> self.check_for_range_as_method_call(err, expr, expr_ty, expected); self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected); self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);

&self, err: &mut Diagnostic, expr: &hir::Expr<'_>, <del> expected: Ty<'tcx>, </del> <ins> expected: Option<Ty<'tcx>>, </ins> found: Ty<'tcx>, ) { if found != self.tcx.types.unit { return; } <del> if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { if self .typeck_results </del> <ins> let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else { return; }; let rcvr_has_the_expected_type = self .typeck_results .borrow() .expr_ty_adjusted_opt(rcvr) .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs())) .unwrap_or(false); let prev_call_mutates_and_returns_unit = || { self.typeck_results </ins> .borrow() <del> .expr_ty_adjusted_opt(rcvr) .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) { return; } let mut sp = MultiSpan::from_span(path_segment.ident.span); sp.push_span_label( path_segment.ident.span, format!( "this call modifies {} in-place", match rcvr.kind { ExprKind::Path(QPath::Resolved( None, hir::Path { segments: [segment], .. }, )) => format!("`{}`", segment.ident), _ => "its receiver".to_string(), } ), ); </del> <ins> .type_dependent_def_id(expr.hir_id) .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder()) .and_then(|sig| sig.inputs_and_output.split_last()) .map(|(output, inputs)| { output.is_unit() && inputs .get(0) .and_then(|self_ty| self_ty.ref_mutability()) .map_or(false, rustc_ast::Mutability::is_mut) }) .unwrap_or(false) }; if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) { return; } let mut sp = MultiSpan::from_span(path_segment.ident.span); sp.push_span_label( path_segment.ident.span, format!( "this call modifies {} in-place", match rcvr.kind { ExprKind::Path(QPath::Resolved( None, hir::Path { segments: [segment], .. }, )) => format!("`{}`", segment.ident), _ => "its receiver".to_string(), } ), ); let modifies_rcvr_note = format!("method `{}` modifies its receiver in-place", path_segment.ident); if rcvr_has_the_expected_type { </ins> sp.push_span_label( rcvr.span, "you probably want to use this value after calling the method...", ); <ins> err.span_note(sp, &modifies_rcvr_note); err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); } else if let ExprKind::MethodCall(..) = rcvr.kind { </ins> err.span_note( sp, <del> &format!("method `{}` modifies its receiver in-place", path_segment.ident), </del> <ins> modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", </ins> ); <del> err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); </del> <ins> } else { err.span_note(sp, &modifies_rcvr_note); </ins> } }

); probe.is_ok() }); <ins> self.note_internal_mutation_in_method( &mut err, rcvr_expr, expected.to_option(&self), rcvr_ty, ); </ins> } let mut custom_span_label = false;

<del> fn main() {} </del> <ins> fn main() { let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i); //~ ERROR mismatched types vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); //~ ERROR no method named `sort` found for unit type `()` in the current scope } </ins> fn foo(mut s: String) -> String { s.push_str("asdf") //~ ERROR mismatched types }

error[E0308]: mismatched types <del> --> $DIR/chain-method-call-mutation-in-place.rs:3:5 </del> <ins> --> $DIR/chain-method-call-mutation-in-place.rs:2:23 | LL |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i); |            --------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Vec<i32>`, found `()` |            | |            expected due to this | = note: expected struct `Vec<i32>` found unit type `()` note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains. --> $DIR/chain-method-call-mutation-in-place.rs:2:71 | LL |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i); |                                                                       ^^^^^^^^^^^ this call modifies its receiver in-place error[E0599]: no method named `sort` found for unit type `()` in the current scope --> $DIR/chain-method-call-mutation-in-place.rs:3:72 | LL |     vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); |                                                                        ^^^^ method not found in `()` | note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains. --> $DIR/chain-method-call-mutation-in-place.rs:3:53 | LL |     vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); |                                                     ^^^^^^^^^^^ this call modifies its receiver in-place error[E0308]: mismatched types --> $DIR/chain-method-call-mutation-in-place.rs:7:5 </ins> | LL | fn foo(mut s: String) -> String { |                          ------ expected `String` because of return type

|     ^^^^^^^^^^^^^^^^^^ expected `String`, found `()` | note: method `push_str` modifies its receiver in-place <del> --> $DIR/chain-method-call-mutation-in-place.rs:3:7 </del> <ins> --> $DIR/chain-method-call-mutation-in-place.rs:7:7 </ins> | LL |     s.push_str("asdf") |     - ^^^^^^^^ this call modifies `s` in-place

|     you probably want to use this value after calling the method... = note: ...instead of the `()` output of method `push_str` <del> error: aborting due to previous error </del> <ins> error: aborting due to 3 previous errors </ins> <del> For more information about this error, try `rustc --explain E0308`. </del> <ins> Some errors have detailed explanations: E0308, E0599. For more information about an error, try `rustc --explain E0308`. </ins>