diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index be225ceb84321..9a2f242ec4e15 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -953,12 +953,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); }); + self.check_for_missing_semi(lhs, &mut err); adjust_err(&mut err); err.emit(); } + /// Check if the expression that could not be assigned to was a typoed expression that + fn check_for_missing_semi(&self, expr: &'tcx hir::Expr<'tcx>, err: &mut Diagnostic) { + if let hir::ExprKind::Binary(binop, lhs, rhs) = expr.kind + && let hir::BinOpKind::Mul = binop.node + && self.tcx.sess.source_map().is_multiline(lhs.span.between(rhs.span)) + && rhs.is_syntactic_place_expr() + { + // v missing semicolon here + // foo() + // *bar = baz; + // (#80446). + err.span_suggestion_verbose( + lhs.span.shrink_to_hi(), + "you might have meant to write a semicolon here", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + } + // Check if an expression `original_expr_id` comes from the condition of a while loop, /// as opposed from the body of a while loop, which we can naively check by iterating /// parents until we find a loop... diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index d0d3b0e5b73cf..92075c7832a97 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -379,6 +379,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, output_def_id) } }; + if self.tcx.sess.source_map().is_multiline(lhs_expr.span.between(rhs_expr.span)) + && let IsAssign::No = is_assign + && let hir::BinOpKind::Mul = op.node + && rhs_expr.is_syntactic_place_expr() + { + // v missing semicolon here + // foo() + // *bar = baz; + // (#80446). + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_hi(), + "you might have meant to write a semicolon here", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + } let suggest_deref_binop = |err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| { diff --git a/tests/ui/binop/false-binop-caused-by-missing-semi.rs b/tests/ui/binop/false-binop-caused-by-missing-semi.rs new file mode 100644 index 0000000000000..9cd32e1db500c --- /dev/null +++ b/tests/ui/binop/false-binop-caused-by-missing-semi.rs @@ -0,0 +1,9 @@ +fn foo() {} +fn main() { + let mut y = 42; + let x = &mut y; + foo() + *x = 0; //~ ERROR invalid left-hand side of assignment + //~^ ERROR cannot multiply `()` by `&mut {integer}` + println!("{y}"); +} diff --git a/tests/ui/binop/false-binop-caused-by-missing-semi.stderr b/tests/ui/binop/false-binop-caused-by-missing-semi.stderr new file mode 100644 index 0000000000000..8d0f15fe2ea16 --- /dev/null +++ b/tests/ui/binop/false-binop-caused-by-missing-semi.stderr @@ -0,0 +1,31 @@ +error[E0369]: cannot multiply `()` by `&mut {integer}` + --> $DIR/false-binop-caused-by-missing-semi.rs:6:5 + | +LL | foo() + | ----- () +LL | *x = 0; + | ^- &mut {integer} + | +help: you might have meant to write a semicolon here + | +LL | foo(); + | + + +error[E0070]: invalid left-hand side of assignment + --> $DIR/false-binop-caused-by-missing-semi.rs:6:8 + | +LL | / foo() +LL | | *x = 0; + | | - ^ + | |______| + | cannot assign to this expression + | +help: you might have meant to write a semicolon here + | +LL | foo(); + | + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0070, E0369. +For more information about an error, try `rustc --explain E0070`.