Skip to content

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

+1-1
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

+56-25
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

+7
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;
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
}
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)