Skip to content

Commit ac73474

Browse files
committed
Add explanation for &mut self method call when expecting -> Self
When a user tries to use a method as if it returned a new value of the same type as its receiver, we will emit a type error. Try to detect this and provide extra explanation that the method modifies the receiver in-place. This has confused people in the wild, like in https://users.rust-lang.org/t/newbie-why-the-commented-line-stops-the-snippet-from-compiling/47322
1 parent 3df25ae commit ac73474

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

src/librustc_typeck/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3535
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
3636
self.suggest_missing_await(err, expr, expected, expr_ty);
3737
self.note_need_for_fn_pointer(err, expected, expr_ty);
38+
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
3839
}
3940

4041
// Requires that the two types unify, and prints an error message if

src/librustc_typeck/check/mod.rs

+45
Original file line numberDiff line numberDiff line change
@@ -5176,6 +5176,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
51765176
}
51775177
}
51785178

5179+
fn note_internal_mutation_in_method(
5180+
&self,
5181+
err: &mut DiagnosticBuilder<'_>,
5182+
expr: &hir::Expr<'_>,
5183+
expected: Ty<'tcx>,
5184+
found: Ty<'tcx>,
5185+
) {
5186+
if found != self.tcx.types.unit {
5187+
return;
5188+
}
5189+
if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
5190+
if self
5191+
.typeck_results
5192+
.borrow()
5193+
.expr_ty_adjusted_opt(rcvr)
5194+
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
5195+
{
5196+
return;
5197+
}
5198+
let mut sp = MultiSpan::from_span(path_segment.ident.span);
5199+
sp.push_span_label(
5200+
path_segment.ident.span,
5201+
format!(
5202+
"this call modifies {} in-place",
5203+
match rcvr.kind {
5204+
ExprKind::Path(QPath::Resolved(
5205+
None,
5206+
hir::Path { segments: [segment], .. },
5207+
)) => format!("`{}`", segment.ident),
5208+
_ => "its receiver".to_string(),
5209+
}
5210+
),
5211+
);
5212+
sp.push_span_label(
5213+
rcvr.span,
5214+
"you probably want to use this value after calling the method...".to_string(),
5215+
);
5216+
err.span_note(
5217+
sp,
5218+
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
5219+
);
5220+
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
5221+
}
5222+
}
5223+
51795224
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
51805225
fn suggest_calling_boxed_future_when_appropriate(
51815226
&self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {}
2+
fn foo(mut s: String) -> String {
3+
s.push_str("asdf") //~ ERROR mismatched types
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/chain-method-call-mutation-in-place.rs:3:5
3+
|
4+
LL | fn foo(mut s: String) -> String {
5+
| ------ expected `std::string::String` because of return type
6+
LL | s.push_str("asdf")
7+
| ^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`
8+
|
9+
note: method `push_str` modifies its receiver in-place
10+
--> $DIR/chain-method-call-mutation-in-place.rs:3:7
11+
|
12+
LL | s.push_str("asdf")
13+
| - ^^^^^^^^ this call modifies `s` in-place
14+
| |
15+
| you probably want to use this value after calling the method...
16+
= note: ...instead of the `()` output of method `push_str`
17+
18+
error: aborting due to previous error
19+
20+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)