Skip to content

Commit 48da675

Browse files
authored
Rollup merge of #75613 - estebank:explain-mut-method, r=petrochenkov
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
2 parents 8eb805c + ac73474 commit 48da675

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

src/librustc_typeck/check/demand.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3636
self.suggest_missing_await(err, expr, expected, expr_ty);
3737
self.suggest_missing_parentheses(err, expr);
3838
self.note_need_for_fn_pointer(err, expected, expr_ty);
39+
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
3940
}
4041

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

src/librustc_typeck/check/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5198,6 +5198,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
51985198
}
51995199
}
52005200

5201+
fn note_internal_mutation_in_method(
5202+
&self,
5203+
err: &mut DiagnosticBuilder<'_>,
5204+
expr: &hir::Expr<'_>,
5205+
expected: Ty<'tcx>,
5206+
found: Ty<'tcx>,
5207+
) {
5208+
if found != self.tcx.types.unit {
5209+
return;
5210+
}
5211+
if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
5212+
if self
5213+
.typeck_results
5214+
.borrow()
5215+
.expr_ty_adjusted_opt(rcvr)
5216+
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
5217+
{
5218+
return;
5219+
}
5220+
let mut sp = MultiSpan::from_span(path_segment.ident.span);
5221+
sp.push_span_label(
5222+
path_segment.ident.span,
5223+
format!(
5224+
"this call modifies {} in-place",
5225+
match rcvr.kind {
5226+
ExprKind::Path(QPath::Resolved(
5227+
None,
5228+
hir::Path { segments: [segment], .. },
5229+
)) => format!("`{}`", segment.ident),
5230+
_ => "its receiver".to_string(),
5231+
}
5232+
),
5233+
);
5234+
sp.push_span_label(
5235+
rcvr.span,
5236+
"you probably want to use this value after calling the method...".to_string(),
5237+
);
5238+
err.span_note(
5239+
sp,
5240+
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
5241+
);
5242+
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
5243+
}
5244+
}
5245+
52015246
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
52025247
fn suggest_calling_boxed_future_when_appropriate(
52035248
&self,
Lines changed: 4 additions & 0 deletions
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+
}
Lines changed: 20 additions & 0 deletions
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)