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
This commit is contained in:
Yuki Okushi 2020-08-18 09:27:47 +09:00 committed by GitHub
commit 48da6758e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 0 deletions

View file

@ -36,6 +36,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_missing_await(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
}
// Requires that the two types unify, and prints an error message if

View file

@ -5198,6 +5198,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn note_internal_mutation_in_method(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &hir::Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) {
if found != self.tcx.types.unit {
return;
}
if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind {
if self
.typeck_results
.borrow()
.expr_ty_adjusted_opt(rcvr)
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
{
return;
}
let mut sp = MultiSpan::from_span(path_segment.ident.span);
sp.push_span_label(
path_segment.ident.span,
format!(
"this call modifies {} in-place",
match rcvr.kind {
ExprKind::Path(QPath::Resolved(
None,
hir::Path { segments: [segment], .. },
)) => format!("`{}`", segment.ident),
_ => "its receiver".to_string(),
}
),
);
sp.push_span_label(
rcvr.span,
"you probably want to use this value after calling the method...".to_string(),
);
err.span_note(
sp,
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
);
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
}
}
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
fn suggest_calling_boxed_future_when_appropriate(
&self,

View file

@ -0,0 +1,4 @@
fn main() {}
fn foo(mut s: String) -> String {
s.push_str("asdf") //~ ERROR mismatched types
}

View file

@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> $DIR/chain-method-call-mutation-in-place.rs:3:5
|
LL | fn foo(mut s: String) -> String {
| ------ expected `std::string::String` because of return type
LL | s.push_str("asdf")
| ^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `()`
|
note: method `push_str` modifies its receiver in-place
--> $DIR/chain-method-call-mutation-in-place.rs:3:7
|
LL | s.push_str("asdf")
| - ^^^^^^^^ this call modifies `s` in-place
| |
| you probably want to use this value after calling the method...
= note: ...instead of the `()` output of method `push_str`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.