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
This commit is contained in:
parent
bd43458d4c
commit
6a2a6feca8
@ -83,7 +83,7 @@ pub fn emit_coerce_suggestions(
|
|||||||
self.annotate_expected_due_to_let_ty(err, expr, error);
|
self.annotate_expected_due_to_let_ty(err, expr, error);
|
||||||
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
|
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
|
||||||
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
self.note_type_is_not_clone(err, expected, expr_ty, expr);
|
||||||
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
|
self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty);
|
||||||
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
|
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
|
||||||
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
|
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
|
||||||
self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
|
self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
|
||||||
|
@ -955,44 +955,75 @@ pub(in super::super) fn note_internal_mutation_in_method(
|
|||||||
&self,
|
&self,
|
||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &hir::Expr<'_>,
|
||||||
expected: Ty<'tcx>,
|
expected: Option<Ty<'tcx>>,
|
||||||
found: Ty<'tcx>,
|
found: Ty<'tcx>,
|
||||||
) {
|
) {
|
||||||
if found != self.tcx.types.unit {
|
if found != self.tcx.types.unit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
|
|
||||||
if self
|
let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else {
|
||||||
.typeck_results
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let rcvr_has_the_expected_type = self
|
||||||
|
.typeck_results
|
||||||
|
.borrow()
|
||||||
|
.expr_ty_adjusted_opt(rcvr)
|
||||||
|
.and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs()))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let prev_call_mutates_and_returns_unit = || {
|
||||||
|
self.typeck_results
|
||||||
.borrow()
|
.borrow()
|
||||||
.expr_ty_adjusted_opt(rcvr)
|
.type_dependent_def_id(expr.hir_id)
|
||||||
.map_or(true, |ty| expected.peel_refs() != ty.peel_refs())
|
.map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder())
|
||||||
{
|
.and_then(|sig| sig.inputs_and_output.split_last())
|
||||||
return;
|
.map(|(output, inputs)| {
|
||||||
}
|
output.is_unit()
|
||||||
let mut sp = MultiSpan::from_span(path_segment.ident.span);
|
&& inputs
|
||||||
sp.push_span_label(
|
.get(0)
|
||||||
path_segment.ident.span,
|
.and_then(|self_ty| self_ty.ref_mutability())
|
||||||
format!(
|
.map_or(false, rustc_ast::Mutability::is_mut)
|
||||||
"this call modifies {} in-place",
|
})
|
||||||
match rcvr.kind {
|
.unwrap_or(false)
|
||||||
ExprKind::Path(QPath::Resolved(
|
};
|
||||||
None,
|
|
||||||
hir::Path { segments: [segment], .. },
|
if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) {
|
||||||
)) => format!("`{}`", segment.ident),
|
return;
|
||||||
_ => "its receiver".to_string(),
|
}
|
||||||
}
|
|
||||||
),
|
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(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let modifies_rcvr_note =
|
||||||
|
format!("method `{}` modifies its receiver in-place", path_segment.ident);
|
||||||
|
if rcvr_has_the_expected_type {
|
||||||
sp.push_span_label(
|
sp.push_span_label(
|
||||||
rcvr.span,
|
rcvr.span,
|
||||||
"you probably want to use this value after calling the method...",
|
"you probably want to use this value after calling the method...",
|
||||||
);
|
);
|
||||||
|
err.span_note(sp, &modifies_rcvr_note);
|
||||||
|
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
|
||||||
|
} else if let ExprKind::MethodCall(..) = rcvr.kind {
|
||||||
err.span_note(
|
err.span_note(
|
||||||
sp,
|
sp,
|
||||||
&format!("method `{}` modifies its receiver in-place", path_segment.ident),
|
modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.",
|
||||||
);
|
);
|
||||||
err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident));
|
} else {
|
||||||
|
err.span_note(sp, &modifies_rcvr_note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +416,13 @@ pub fn report_no_match_method_error(
|
|||||||
);
|
);
|
||||||
probe.is_ok()
|
probe.is_ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.note_internal_mutation_in_method(
|
||||||
|
&mut err,
|
||||||
|
rcvr_expr,
|
||||||
|
expected.to_option(&self),
|
||||||
|
rcvr_ty,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut custom_span_label = false;
|
let mut custom_span_label = false;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
fn main() {}
|
fn main() {
|
||||||
|
let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i); //~ ERROR mismatched types
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
fn foo(mut s: String) -> String {
|
fn foo(mut s: String) -> String {
|
||||||
s.push_str("asdf") //~ ERROR mismatched types
|
s.push_str("asdf") //~ ERROR mismatched types
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,33 @@
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/chain-method-call-mutation-in-place.rs:3:5
|
--> $DIR/chain-method-call-mutation-in-place.rs:2:23
|
||||||
|
|
|
||||||
|
LL | let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
|
||||||
|
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Vec<i32>`, found `()`
|
||||||
|
| |
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: expected struct `Vec<i32>`
|
||||||
|
found unit type `()`
|
||||||
|
note: method `sort_by_key` modifies its receiver in-place, it is not meant to be used in method chains.
|
||||||
|
--> $DIR/chain-method-call-mutation-in-place.rs:2:71
|
||||||
|
|
|
||||||
|
LL | let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
|
||||||
|
| ^^^^^^^^^^^ this call modifies its receiver in-place
|
||||||
|
|
||||||
|
error[E0599]: no method named `sort` found for unit type `()` in the current scope
|
||||||
|
--> $DIR/chain-method-call-mutation-in-place.rs:3:72
|
||||||
|
|
|
||||||
|
LL | 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.
|
||||||
|
--> $DIR/chain-method-call-mutation-in-place.rs:3:53
|
||||||
|
|
|
||||||
|
LL | vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
|
||||||
|
| ^^^^^^^^^^^ this call modifies its receiver in-place
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/chain-method-call-mutation-in-place.rs:7:5
|
||||||
|
|
|
|
||||||
LL | fn foo(mut s: String) -> String {
|
LL | fn foo(mut s: String) -> String {
|
||||||
| ------ expected `String` because of return type
|
| ------ expected `String` because of return type
|
||||||
@ -7,7 +35,7 @@ LL | s.push_str("asdf")
|
|||||||
| ^^^^^^^^^^^^^^^^^^ expected `String`, found `()`
|
| ^^^^^^^^^^^^^^^^^^ expected `String`, found `()`
|
||||||
|
|
|
|
||||||
note: method `push_str` modifies its receiver in-place
|
note: method `push_str` modifies its receiver in-place
|
||||||
--> $DIR/chain-method-call-mutation-in-place.rs:3:7
|
--> $DIR/chain-method-call-mutation-in-place.rs:7:7
|
||||||
|
|
|
|
||||||
LL | s.push_str("asdf")
|
LL | s.push_str("asdf")
|
||||||
| - ^^^^^^^^ this call modifies `s` in-place
|
| - ^^^^^^^^ this call modifies `s` in-place
|
||||||
@ -15,6 +43,7 @@ LL | s.push_str("asdf")
|
|||||||
| you probably want to use this value after calling the method...
|
| you probably want to use this value after calling the method...
|
||||||
= note: ...instead of the `()` output of method `push_str`
|
= note: ...instead of the `()` output of method `push_str`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
Some errors have detailed explanations: E0308, E0599.
|
||||||
|
For more information about an error, try `rustc --explain E0308`.
|
||||||
|
Loading…
Reference in New Issue
Block a user