Skip to content

Commit

Permalink
Detect misparsed binop caused by missing semi
Browse files Browse the repository at this point in the history
When encountering

```rust
foo()
*bar = baz;
```

We currently emit potentially two errors, one for the return type of
`foo` not being multiplyiable by the type of `bar`, and another for
`foo() * bar` not being assignable.

We now check for this case and suggest adding a semicolon in the right
place.

Fix #80446.
  • Loading branch information
estebank committed Oct 27, 2023
1 parent 6f65201 commit 231f935
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 0 deletions.
21 changes: 21 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>| {
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/binop/false-binop-caused-by-missing-semi.rs
Original file line number Diff line number Diff line change
@@ -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}");
}
31 changes: 31 additions & 0 deletions tests/ui/binop/false-binop-caused-by-missing-semi.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 231f935

Please sign in to comment.