Skip to content

Commit b17ee10

Browse files
authored
Rollup merge of #109116 - MaciejWas:add-modifies-receiver-diagn-when-method-not-found, r=petrochenkov
Emit diagnostic when calling methods on the unit type in method chains Fixes #104204. What this PR does: If a method is not found somewhere in a call chain, we check if we called earlier a method with signature `(&mut T, ...) -> ()`. If this is the case then we emit a diagnostic message. For example given input: ``` vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); ``` the current output is: ``` error[E0599]: no method named `sort` found for unit type `()` in the current scope --> hello.rs:3:72 | 3 | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); | ^^^^ method not found in `()` ``` after this PR it will be: ``` error[E0599]: no method named `sort` found for unit type `()` in the current scope --> ./hello.rs:3:72 | 3 | 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. --> ./hello.rs:3:53 | 3 | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort(); | ^^^^^^^^^^^ this call modifies its receiver in-place ```
2 parents b88c675 + 6a2a6fe commit b17ee10

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
@@ -950,44 +950,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
950950
&self,
951951
err: &mut Diagnostic,
952952
expr: &hir::Expr<'_>,
953-
expected: Ty<'tcx>,
953+
expected: Option<Ty<'tcx>>,
954954
found: Ty<'tcx>,
955955
) {
956956
if found != self.tcx.types.unit {
957957
return;
958958
}
959-
if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
960-
if self
961-
.typeck_results
959+
960+
let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else {
961+
return;
962+
};
963+
964+
let rcvr_has_the_expected_type = self
965+
.typeck_results
966+
.borrow()
967+
.expr_ty_adjusted_opt(rcvr)
968+
.and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs()))
969+
.unwrap_or(false);
970+
971+
let prev_call_mutates_and_returns_unit = || {
972+
self.typeck_results
962973
.borrow()
963-
.expr_ty_adjusted_opt(rcvr)
964-
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
965-
{
966-
return;
967-
}
968-
let mut sp = MultiSpan::from_span(path_segment.ident.span);
969-
sp.push_span_label(
970-
path_segment.ident.span,
971-
format!(
972-
"this call modifies {} in-place",
973-
match rcvr.kind {
974-
ExprKind::Path(QPath::Resolved(
975-
None,
976-
hir::Path { segments: [segment], .. },
977-
)) => format!("`{}`", segment.ident),
978-
_ => "its receiver".to_string(),
979-
}
980-
),
981-
);
974+
.type_dependent_def_id(expr.hir_id)
975+
.map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder())
976+
.and_then(|sig| sig.inputs_and_output.split_last())
977+
.map(|(output, inputs)| {
978+
output.is_unit()
979+
&& inputs
980+
.get(0)
981+
.and_then(|self_ty| self_ty.ref_mutability())
982+
.map_or(false, rustc_ast::Mutability::is_mut)
983+
})
984+
.unwrap_or(false)
985+
};
986+
987+
if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) {
988+
return;
989+
}
990+
991+
let mut sp = MultiSpan::from_span(path_segment.ident.span);
992+
sp.push_span_label(
993+
path_segment.ident.span,
994+
format!(
995+
"this call modifies {} in-place",
996+
match rcvr.kind {
997+
ExprKind::Path(QPath::Resolved(
998+
None,
999+
hir::Path { segments: [segment], .. },
1000+
)) => format!("`{}`", segment.ident),
1001+
_ => "its receiver".to_string(),
1002+
}
1003+
),
1004+
);
1005+
1006+
let modifies_rcvr_note =
1007+
format!("method `{}` modifies its receiver in-place", path_segment.ident);
1008+
if rcvr_has_the_expected_type {
9821009
sp.push_span_label(
9831010
rcvr.span,
9841011
"you probably want to use this value after calling the method...",
9851012
);
1013+
err.span_note(sp, &modifies_rcvr_note);
1014+
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
1015+
} else if let ExprKind::MethodCall(..) = rcvr.kind {
9861016
err.span_note(
9871017
sp,
988-
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
1018+
modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
9891019
);
990-
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
1020+
} else {
1021+
err.span_note(sp, &modifies_rcvr_note);
9911022
}
9921023
}
9931024

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)