diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 1eae258c1b2..a283cd1abf5 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -4,7 +4,7 @@ use super::{has_expected_num_generic_args, FnCtxt}; use crate::Expectation; use rustc_ast as ast; -use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; +use rustc_errors::{self, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::ObligationCauseCode; @@ -380,33 +380,93 @@ fn check_overloaded_binop( } }; - let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { - if self - .lookup_op_method( - lhs_deref_ty, - Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let msg = format!( - "`{}{}` can be used on `{}` if you dereference the left-hand side", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, - lhs_deref_ty, - ); - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_lo(), - msg, - "*", - rustc_errors::Applicability::MachineApplicable, - ); - } - }; + let suggest_deref_binop = + |err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| { + if self + .lookup_op_method( + lhs_deref_ty, + Some((rhs_expr, rhs_ty)), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + { + let msg = format!( + "`{}{}` can be used on `{}` if you dereference the left-hand side", + op.node.as_str(), + match is_assign { + IsAssign::Yes => "=", + IsAssign::No => "", + }, + lhs_deref_ty, + ); + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_lo(), + msg, + "*", + rustc_errors::Applicability::MachineApplicable, + ); + } + }; + + let suggest_different_borrow = + |err: &mut DiagnosticBuilder<'_, _>, + lhs_adjusted_ty, + lhs_new_mutbl: Option, + rhs_adjusted_ty, + rhs_new_mutbl: Option| { + if self + .lookup_op_method( + lhs_adjusted_ty, + Some((rhs_expr, rhs_adjusted_ty)), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + { + let op_str = op.node.as_str(); + err.note(format!("an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists")); + + if let Some(lhs_new_mutbl) = lhs_new_mutbl + && let Some(rhs_new_mutbl) = rhs_new_mutbl + && lhs_new_mutbl.is_not() + && rhs_new_mutbl.is_not() { + err.multipart_suggestion_verbose( + "consider reborrowing both sides", + vec![ + (lhs_expr.span.shrink_to_lo(), "&*".to_string()), + (rhs_expr.span.shrink_to_lo(), "&*".to_string()) + ], + rustc_errors::Applicability::MachineApplicable, + ); + } else { + let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| { + // Can reborrow (&mut -> &) + if new_mutbl.is_not() { + err.span_suggestion_verbose( + sp.shrink_to_lo(), + "consider reborrowing this side", + "&*", + rustc_errors::Applicability::MachineApplicable, + ); + // Works on &mut but have & + } else { + err.span_help( + sp, + "consider making this expression a mutable borrow", + ); + } + }; + + if let Some(lhs_new_mutbl) = lhs_new_mutbl { + suggest_new_borrow(lhs_new_mutbl, lhs_expr.span); + } + if let Some(rhs_new_mutbl) = rhs_new_mutbl { + suggest_new_borrow(rhs_new_mutbl, rhs_expr.span); + } + } + } + }; let is_compatible_after_call = |lhs_ty, rhs_ty| { self.lookup_op_method( @@ -429,15 +489,60 @@ fn check_overloaded_binop( } else if is_assign == IsAssign::Yes && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - suggest_deref_binop(lhs_deref_ty); + suggest_deref_binop(&mut err, lhs_deref_ty); } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() + && let Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind() { if self.type_is_copy_modulo_regions( self.param_env, *lhs_deref_ty, ) { - suggest_deref_binop(*lhs_deref_ty); + suggest_deref_binop(&mut err, *lhs_deref_ty); + } else { + let lhs_inv_mutbl = mutbl.invert(); + let lhs_inv_mutbl_ty = Ty::new_ref( + self.tcx, + *region, + ty::TypeAndMut { + ty: *lhs_deref_ty, + mutbl: lhs_inv_mutbl, + }, + ); + + suggest_different_borrow( + &mut err, + lhs_inv_mutbl_ty, + Some(lhs_inv_mutbl), + rhs_ty, + None, + ); + + if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { + let rhs_inv_mutbl = mutbl.invert(); + let rhs_inv_mutbl_ty = Ty::new_ref( + self.tcx, + *region, + ty::TypeAndMut { + ty: *rhs_deref_ty, + mutbl: rhs_inv_mutbl, + }, + ); + + suggest_different_borrow( + &mut err, + lhs_ty, + None, + rhs_inv_mutbl_ty, + Some(rhs_inv_mutbl), + ); + suggest_different_borrow( + &mut err, + lhs_inv_mutbl_ty, + Some(lhs_inv_mutbl), + rhs_inv_mutbl_ty, + Some(rhs_inv_mutbl), + ); + } } } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { is_compatible_after_call(lhs_ty, rhs_ty) diff --git a/tests/ui/binop/borrow-suggestion-109352-2.rs b/tests/ui/binop/borrow-suggestion-109352-2.rs new file mode 100644 index 00000000000..43dab952962 --- /dev/null +++ b/tests/ui/binop/borrow-suggestion-109352-2.rs @@ -0,0 +1,27 @@ +struct Bar; + +impl std::ops::Mul for &mut Bar { + type Output = Bar; + + fn mul(self, _rhs: Self) -> Self::Output { + unimplemented!() + } +} + +fn main() { + let ref_mut_bar: &mut Bar = &mut Bar; + let ref_bar: &Bar = &Bar; + let owned_bar: Bar = Bar; + + let _ = ref_mut_bar * ref_mut_bar; + + // FIXME: we should be able to suggest borrowing both side + let _ = owned_bar * owned_bar; + //~^ ERROR cannot multiply + let _ = ref_bar * ref_bar; + //~^ ERROR cannot multiply + let _ = ref_bar * ref_mut_bar; + //~^ ERROR cannot multiply + let _ = ref_mut_bar * ref_bar; + //~^ ERROR mismatched types +} diff --git a/tests/ui/binop/borrow-suggestion-109352-2.stderr b/tests/ui/binop/borrow-suggestion-109352-2.stderr new file mode 100644 index 00000000000..18ed08d73dd --- /dev/null +++ b/tests/ui/binop/borrow-suggestion-109352-2.stderr @@ -0,0 +1,64 @@ +error[E0369]: cannot multiply `Bar` by `Bar` + --> $DIR/borrow-suggestion-109352-2.rs:19:23 + | +LL | let _ = owned_bar * owned_bar; + | --------- ^ --------- Bar + | | + | Bar + | +note: an implementation of `Mul` might be missing for `Bar` + --> $DIR/borrow-suggestion-109352-2.rs:1:1 + | +LL | struct Bar; + | ^^^^^^^^^^ must implement `Mul` +note: the trait `Mul` must be implemented + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL + +error[E0369]: cannot multiply `&Bar` by `&Bar` + --> $DIR/borrow-suggestion-109352-2.rs:21:21 + | +LL | let _ = ref_bar * ref_bar; + | ------- ^ ------- &Bar + | | + | &Bar + | + = note: an implementation for `&mut Bar * &mut Bar` exists +help: consider making this expression a mutable borrow + --> $DIR/borrow-suggestion-109352-2.rs:21:13 + | +LL | let _ = ref_bar * ref_bar; + | ^^^^^^^ +help: consider making this expression a mutable borrow + --> $DIR/borrow-suggestion-109352-2.rs:21:23 + | +LL | let _ = ref_bar * ref_bar; + | ^^^^^^^ + +error[E0369]: cannot multiply `&Bar` by `&mut Bar` + --> $DIR/borrow-suggestion-109352-2.rs:23:21 + | +LL | let _ = ref_bar * ref_mut_bar; + | ------- ^ ----------- &mut Bar + | | + | &Bar + | + = note: an implementation for `&mut Bar * &mut Bar` exists +help: consider making this expression a mutable borrow + --> $DIR/borrow-suggestion-109352-2.rs:23:13 + | +LL | let _ = ref_bar * ref_mut_bar; + | ^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/borrow-suggestion-109352-2.rs:25:27 + | +LL | let _ = ref_mut_bar * ref_bar; + | ^^^^^^^ types differ in mutability + | + = note: expected mutable reference `&mut Bar` + found reference `&Bar` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0369. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/binop/borrow-suggestion-109352.fixed b/tests/ui/binop/borrow-suggestion-109352.fixed new file mode 100644 index 00000000000..3374a9d78b2 --- /dev/null +++ b/tests/ui/binop/borrow-suggestion-109352.fixed @@ -0,0 +1,27 @@ +// run-rustfix + +struct Foo; + +impl std::ops::Mul for &Foo { + type Output = Foo; + + fn mul(self, _rhs: Self) -> Self::Output { + unimplemented!() + } +} + +fn main() { + let ref_mut_foo: &mut Foo = &mut Foo; + let ref_foo: &Foo = &Foo; + let owned_foo: Foo = Foo; + + let _ = ref_foo * ref_foo; + let _ = ref_foo * ref_mut_foo; + + let _ = &*ref_mut_foo * ref_foo; + //~^ ERROR cannot multiply + let _ = &*ref_mut_foo * &*ref_mut_foo; + //~^ ERROR cannot multiply + let _ = &*ref_mut_foo * &owned_foo; + //~^ ERROR cannot multiply +} diff --git a/tests/ui/binop/borrow-suggestion-109352.rs b/tests/ui/binop/borrow-suggestion-109352.rs new file mode 100644 index 00000000000..4e8510e0de5 --- /dev/null +++ b/tests/ui/binop/borrow-suggestion-109352.rs @@ -0,0 +1,27 @@ +// run-rustfix + +struct Foo; + +impl std::ops::Mul for &Foo { + type Output = Foo; + + fn mul(self, _rhs: Self) -> Self::Output { + unimplemented!() + } +} + +fn main() { + let ref_mut_foo: &mut Foo = &mut Foo; + let ref_foo: &Foo = &Foo; + let owned_foo: Foo = Foo; + + let _ = ref_foo * ref_foo; + let _ = ref_foo * ref_mut_foo; + + let _ = ref_mut_foo * ref_foo; + //~^ ERROR cannot multiply + let _ = ref_mut_foo * ref_mut_foo; + //~^ ERROR cannot multiply + let _ = ref_mut_foo * &owned_foo; + //~^ ERROR cannot multiply +} diff --git a/tests/ui/binop/borrow-suggestion-109352.stderr b/tests/ui/binop/borrow-suggestion-109352.stderr new file mode 100644 index 00000000000..71e44f54b17 --- /dev/null +++ b/tests/ui/binop/borrow-suggestion-109352.stderr @@ -0,0 +1,45 @@ +error[E0369]: cannot multiply `&mut Foo` by `&Foo` + --> $DIR/borrow-suggestion-109352.rs:21:25 + | +LL | let _ = ref_mut_foo * ref_foo; + | ----------- ^ ------- &Foo + | | + | &mut Foo + | + = note: an implementation for `&Foo * &Foo` exists +help: consider reborrowing this side + | +LL | let _ = &*ref_mut_foo * ref_foo; + | ++ + +error[E0369]: cannot multiply `&mut Foo` by `&mut Foo` + --> $DIR/borrow-suggestion-109352.rs:23:25 + | +LL | let _ = ref_mut_foo * ref_mut_foo; + | ----------- ^ ----------- &mut Foo + | | + | &mut Foo + | + = note: an implementation for `&Foo * &Foo` exists +help: consider reborrowing both sides + | +LL | let _ = &*ref_mut_foo * &*ref_mut_foo; + | ++ ++ + +error[E0369]: cannot multiply `&mut Foo` by `&Foo` + --> $DIR/borrow-suggestion-109352.rs:25:25 + | +LL | let _ = ref_mut_foo * &owned_foo; + | ----------- ^ ---------- &Foo + | | + | &mut Foo + | + = note: an implementation for `&Foo * &Foo` exists +help: consider reborrowing this side + | +LL | let _ = &*ref_mut_foo * &owned_foo; + | ++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0369`.