From 37096e7125899b77511ca360b92ff281454e4292 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Feb 2022 00:00:19 -0800 Subject: [PATCH 1/2] suggest dereferencing lhs of assignment --- compiler/rustc_typeck/src/check/expr.rs | 40 ++++++++++++++++--- compiler/rustc_typeck/src/check/op.rs | 4 +- src/test/ui/typeck/bad-lhs-should-deref.rs | 9 +++++ .../ui/typeck/bad-lhs-should-deref.stderr | 16 ++++++++ 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/typeck/bad-lhs-should-deref.rs create mode 100644 src/test/ui/typeck/bad-lhs-should-deref.stderr diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0e1dbc53806ff..ff3ed2d72d078 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -831,9 +831,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, op_span: Span, - ) { + ) -> Result<(), DiagnosticBuilder<'tcx>> { if lhs.is_syntactic_place_expr() { - return; + return Ok(()); } // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. @@ -885,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - err.emit(); + Err(err) } // A generic function for checking the 'then' and 'else' clauses in an 'if' @@ -1025,14 +1025,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.tcx.ty_error(); } - self.check_lhs_assignable(lhs, "E0070", *span); + let assignable = self.check_lhs_assignable(lhs, "E0070", *span); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); + let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); + let (mut coerce_ty, mut coerce_err) = + self.demand_coerce_diag(&rhs, rhs_ty, lhs_ty, Some(&lhs), AllowTwoPhase::No); + + // Dereference the lhs_ty if it's not assignable, but is a mut-ref. + if let Err(mut err) = assignable { + if let Some(coerce_err) = &mut coerce_err { + for (deref_ty, steps) in self.autoderef(lhs.span, lhs_ty) { + if self.can_coerce(deref_ty, rhs_ty) { + coerce_err.cancel(); + err.span_suggestion_verbose( + lhs.span.shrink_to_lo(), + "consider dereferencing here to assign a value to the left-hand side", + "*".repeat(steps), + Applicability::MaybeIncorrect, + ); + coerce_ty = lhs_ty; + break; + } + } + } + err.emit(); + } + + if let Some(mut coerce_err) = coerce_err { + if !coerce_err.cancelled() { + coerce_err.emit(); + } + } self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - if lhs_ty.references_error() || rhs_ty.references_error() { + if lhs_ty.references_error() || coerce_ty.references_error() { self.tcx.ty_error() } else { self.tcx.mk_unit() diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 74516acbfcff3..f8e8eb46b1913 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -42,7 +42,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_ty }; - self.check_lhs_assignable(lhs, "E0067", op.span); + if let Err(mut err) = self.check_lhs_assignable(lhs, "E0067", op.span) { + err.emit(); + } ty } diff --git a/src/test/ui/typeck/bad-lhs-should-deref.rs b/src/test/ui/typeck/bad-lhs-should-deref.rs new file mode 100644 index 0000000000000..eb31ca39cc999 --- /dev/null +++ b/src/test/ui/typeck/bad-lhs-should-deref.rs @@ -0,0 +1,9 @@ +fn mut_ref() -> &'static mut Box { + todo!(); +} + +fn main() { + mut_ref() = 1; + //~^ ERROR invalid left-hand side of assignment + //~| HELP consider dereferencing here to assign a value to the left-hand side +} diff --git a/src/test/ui/typeck/bad-lhs-should-deref.stderr b/src/test/ui/typeck/bad-lhs-should-deref.stderr new file mode 100644 index 0000000000000..3f638eac55f89 --- /dev/null +++ b/src/test/ui/typeck/bad-lhs-should-deref.stderr @@ -0,0 +1,16 @@ +error[E0070]: invalid left-hand side of assignment + --> $DIR/bad-lhs-should-deref.rs:6:15 + | +LL | mut_ref() = 1; + | --------- ^ + | | + | cannot assign to this expression + | +help: consider dereferencing here to assign a value to the left-hand side + | +LL | **mut_ref() = 1; + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0070`. From 59102631bedba691dbb44b48c4fbda5b978893c2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Feb 2022 00:26:03 -0800 Subject: [PATCH 2/2] generalize deref suggestion even when lhs is assignable --- compiler/rustc_typeck/src/check/expr.rs | 65 +++++++++++++------ src/test/ui/typeck/bad-lhs-should-deref.rs | 6 ++ .../ui/typeck/bad-lhs-should-deref.stderr | 25 ++++++- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index ff3ed2d72d078..536dec8442d84 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1025,36 +1025,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.tcx.ty_error(); } - let assignable = self.check_lhs_assignable(lhs, "E0070", *span); + let mut assignable_err = self.check_lhs_assignable(lhs, "E0070", *span); let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); + let (mut coerce_ty, mut coerce_err) = self.demand_coerce_diag(&rhs, rhs_ty, lhs_ty, Some(&lhs), AllowTwoPhase::No); - // Dereference the lhs_ty if it's not assignable, but is a mut-ref. - if let Err(mut err) = assignable { - if let Some(coerce_err) = &mut coerce_err { - for (deref_ty, steps) in self.autoderef(lhs.span, lhs_ty) { - if self.can_coerce(deref_ty, rhs_ty) { - coerce_err.cancel(); - err.span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "consider dereferencing here to assign a value to the left-hand side", - "*".repeat(steps), - Applicability::MaybeIncorrect, - ); - coerce_ty = lhs_ty; - break; + // If we either have a bad LHS type, or can't coerce the RHS into the LHS, it may be due + // to a missing deref on the LHS. + if assignable_err.is_err() || coerce_err.is_some() { + for (deref_ty, steps) in self.autoderef(lhs.span, lhs_ty) { + // Check if we can deref the LHS into something that the RHS can coerce into + if self.can_coerce(rhs_ty, deref_ty) { + coerce_ty = lhs_ty; + let span = lhs.span.shrink_to_lo(); + let msg = "consider dereferencing here to assign a value to the left-hand side"; + let suggest = "*".repeat(steps); + // We don't need to emit both an LHS error and a coercion error, + // so prefer emitting the LHS error over the coercion error + match (&mut assignable_err, &mut coerce_err) { + (Err(err), Some(coerce_err)) => { + // Cancel the coercion error if we have both + coerce_err.cancel(); + err.span_suggestion_verbose( + span, + msg, + suggest, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + (Err(err), None) | (Ok(()), Some(err)) => { + err.span_suggestion_verbose( + span, + msg, + suggest, + Applicability::MaybeIncorrect, + ); + err.emit(); + } + (Ok(()), None) => unreachable!(), } + break; } } - err.emit(); } - if let Some(mut coerce_err) = coerce_err { - if !coerce_err.cancelled() { - coerce_err.emit(); + // Emit any leftover errors if we still have them after checking for deref + if let Some(mut err) = coerce_err { + if !err.cancelled() { + err.emit(); + } + } + if let Err(mut err) = assignable_err { + if !err.cancelled() { + err.emit(); } } diff --git a/src/test/ui/typeck/bad-lhs-should-deref.rs b/src/test/ui/typeck/bad-lhs-should-deref.rs index eb31ca39cc999..c62f654e73267 100644 --- a/src/test/ui/typeck/bad-lhs-should-deref.rs +++ b/src/test/ui/typeck/bad-lhs-should-deref.rs @@ -6,4 +6,10 @@ fn main() { mut_ref() = 1; //~^ ERROR invalid left-hand side of assignment //~| HELP consider dereferencing here to assign a value to the left-hand side + + let x = Box::new(1); + x = 2; + //~^ ERROR mismatched types + //~| HELP store this in the heap by calling `Box::new` + //~| HELP consider dereferencing here to assign a value to the left-hand side } diff --git a/src/test/ui/typeck/bad-lhs-should-deref.stderr b/src/test/ui/typeck/bad-lhs-should-deref.stderr index 3f638eac55f89..caedc23019239 100644 --- a/src/test/ui/typeck/bad-lhs-should-deref.stderr +++ b/src/test/ui/typeck/bad-lhs-should-deref.stderr @@ -11,6 +11,27 @@ help: consider dereferencing here to assign a value to the left-hand side LL | **mut_ref() = 1; | ++ -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/bad-lhs-should-deref.rs:11:9 + | +LL | let x = Box::new(1); + | ----------- expected due to this value +LL | x = 2; + | ^ expected struct `Box`, found integer + | + = note: expected struct `Box<{integer}>` + found type `{integer}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | x = Box::new(2); + | +++++++++ + +help: consider dereferencing here to assign a value to the left-hand side + | +LL | *x = 2; + | + + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0070`. +Some errors have detailed explanations: E0070, E0308. +For more information about an error, try `rustc --explain E0070`.