Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 6a2a6fe

Browse files
committed
Emit "modifies receiver" diagnostic when no method is found
If no method is found when checking method call, we check if we called a method with signature (&mut T, ...) -> (). If this is the case then we emit a diagnostic message
1 parent bd43458 commit 6a2a6fe

File tree

5 files changed

+102
-31
lines changed

5 files changed

+102
-31
lines changed

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8383
self.annotate_expected_due_to_let_ty(err, expr, error);
8484
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
8585
self.note_type_is_not_clone(err, expected, expr_ty, expr);
86-
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
86+
self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty);
8787
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
8888
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
8989
self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -955,44 +955,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
955955
&self,
956956
err: &mut Diagnostic,
957957
expr: &hir::Expr<'_>,
958-
expected: Ty<'tcx>,
958+
expected: Option<Ty<'tcx>>,
959959
found: Ty<'tcx>,
960960
) {
961961
if found != self.tcx.types.unit {
962962
return;
963963
}
964-
if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
965-
if self
966-
.typeck_results
964+
965+
let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else {
966+
return;
967+
};
968+
969+
let rcvr_has_the_expected_type = self
970+
.typeck_results
971+
.borrow()
972+
.expr_ty_adjusted_opt(rcvr)
973+
.and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs()))
974+
.unwrap_or(false);
975+
976+
let prev_call_mutates_and_returns_unit = || {
977+
self.typeck_results
967978
.borrow()
968-
.expr_ty_adjusted_opt(rcvr)
969-
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
970-
{
971-
return;
972-
}
973-
let mut sp = MultiSpan::from_span(path_segment.ident.span);
974-
sp.push_span_label(
975-
path_segment.ident.span,
976-
format!(
977-
"this call modifies {} in-place",
978-
match rcvr.kind {
979-
ExprKind::Path(QPath::Resolved(
980-
None,
981-
hir::Path { segments: [segment], .. },
982-
)) => format!("`{}`", segment.ident),
983-
_ => "its receiver".to_string(),
984-
}
985-
),
986-
);
979+
.type_dependent_def_id(expr.hir_id)
980+
.map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder())
981+
.and_then(|sig| sig.inputs_and_output.split_last())
982+
.map(|(output, inputs)| {
983+
output.is_unit()
984+
&& inputs
985+
.get(0)
986+
.and_then(|self_ty| self_ty.ref_mutability())
987+
.map_or(false, rustc_ast::Mutability::is_mut)
988+
})
989+
.unwrap_or(false)
990+
};
991+
992+
if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) {
993+
return;
994+
}
995+
996+
let mut sp = MultiSpan::from_span(path_segment.ident.span);
997+
sp.push_span_label(
998+
path_segment.ident.span,
999+
format!(
1000+
"this call modifies {} in-place",
1001+
match rcvr.kind {
1002+
ExprKind::Path(QPath::Resolved(
1003+
None,
1004+
hir::Path { segments: [segment], .. },
1005+
)) => format!("`{}`", segment.ident),
1006+
_ => "its receiver".to_string(),
1007+
}
1008+
),
1009+
);
1010+
1011+
let modifies_rcvr_note =
1012+
format!("method `{}` modifies its receiver in-place", path_segment.ident);
1013+
if rcvr_has_the_expected_type {
9871014
sp.push_span_label(
9881015
rcvr.span,
9891016
"you probably want to use this value after calling the method...",
9901017
);
1018+
err.span_note(sp, &modifies_rcvr_note);
1019+
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
1020+
} else if let ExprKind::MethodCall(..) = rcvr.kind {
9911021
err.span_note(
9921022
sp,
993-
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
1023+
modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
9941024
);
995-
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
1025+
} else {
1026+
err.span_note(sp, &modifies_rcvr_note);
9961027
}
9971028
}
9981029

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
416416
);
417417
probe.is_ok()
418418
});
419+
420+
self.note_internal_mutation_in_method(
421+
&mut err,
422+
rcvr_expr,
423+
expected.to_option(&self),
424+
rcvr_ty,
425+
);
419426
}
420427

421428
let mut custom_span_label = false;
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
fn main() {}
1+
fn main() {
2+
let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i); //~ ERROR mismatched types
3+
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
4+
}
5+
26
fn foo(mut s: String) -> String {
37
s.push_str("asdf") //~ ERROR mismatched types
48
}
Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
11
error[E0308]: mismatched types
2-
--> $DIR/chain-method-call-mutation-in-place.rs:3:5
2+
--> $DIR/chain-method-call-mutation-in-place.rs:2:23
3+
|
4+
LL | let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
5+
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Vec<i32>`, found `()`
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected struct `Vec<i32>`
10+
found unit type `()`
11+
note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains.
12+
--> $DIR/chain-method-call-mutation-in-place.rs:2:71
13+
|
14+
LL | let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
15+
| ^^^^^^^^^^^ this call modifies its receiver in-place
16+
17+
error[E0599]: no method named `sort` found for unit type `()` in the current scope
18+
--> $DIR/chain-method-call-mutation-in-place.rs:3:72
19+
|
20+
LL | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
21+
| ^^^^ method not found in `()`
22+
|
23+
note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains.
24+
--> $DIR/chain-method-call-mutation-in-place.rs:3:53
25+
|
26+
LL | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
27+
| ^^^^^^^^^^^ this call modifies its receiver in-place
28+
29+
error[E0308]: mismatched types
30+
--> $DIR/chain-method-call-mutation-in-place.rs:7:5
331
|
432
LL | fn foo(mut s: String) -> String {
533
| ------ expected `String` because of return type
634
LL | s.push_str("asdf")
735
| ^^^^^^^^^^^^^^^^^^ expected `String`, found `()`
836
|
937
note: method `push_str` modifies its receiver in-place
10-
--> $DIR/chain-method-call-mutation-in-place.rs:3:7
38+
--> $DIR/chain-method-call-mutation-in-place.rs:7:7
1139
|
1240
LL | s.push_str("asdf")
1341
| - ^^^^^^^^ this call modifies `s` in-place
1442
| |
1543
| you probably want to use this value after calling the method...
1644
= note: ...instead of the `()` output of method `push_str`
1745

18-
error: aborting due to previous error
46+
error: aborting due to 3 previous errors
1947

20-
For more information about this error, try `rustc --explain E0308`.
48+
Some errors have detailed explanations: E0308, E0599.
49+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)