1
Fork 0

Introduce AssocOp::Binary.

It mirrors `ExprKind::Binary`, and contains a `BinOpKind`. This makes
`AssocOp` more like `ExprKind`. Note that the variants removed from
`AssocOp` are all named differently to `BinOpToken`, e.g. `Multiply`
instead of `Mul`, so that's an inconsistency removed.

The commit adds `precedence` and `fixity` methods to `BinOpKind`, and
calls them from the corresponding methods in `AssocOp`. This avoids the
need to create an `AssocOp` from a `BinOpKind` in a bunch of places, and
`AssocOp::from_ast_binop` is removed.

`AssocOp::to_ast_binop` is also no longer needed.

Overall things are shorter and nicer.
This commit is contained in:
Nicholas Nethercote 2024-12-19 18:24:07 +11:00
parent a8364f3b2a
commit ceafbad81f
10 changed files with 152 additions and 256 deletions

View file

@ -1350,13 +1350,13 @@ impl<'a> Parser<'a> {
}
return match (op.node, &outer_op.node) {
// `x == y == z`
(BinOpKind::Eq, AssocOp::Equal) |
(BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |
// `x < y < z` and friends.
(BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) |
(BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) |
(BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
(BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
// `x > y > z` and friends.
(BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) |
(BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => {
(BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |
(BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {
let expr_to_str = |e: &Expr| {
self.span_to_snippet(e.span)
.unwrap_or_else(|_| pprust::expr_to_string(e))
@ -1368,7 +1368,10 @@ impl<'a> Parser<'a> {
false // Keep the current parse behavior, where the AST is `(x < y) < z`.
}
// `x == y < z`
(BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => {
(
BinOpKind::Eq,
AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge)
) => {
// Consume `z`/outer-op-rhs.
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_expr() {
@ -1389,7 +1392,10 @@ impl<'a> Parser<'a> {
}
}
// `x > y == z`
(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => {
(
BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,
AssocOp::Binary(BinOpKind::Eq)
) => {
let snapshot = self.create_snapshot_for_diagnostic();
// At this point it is always valid to enclose the lhs in parentheses, no
// further checks are necessary.
@ -1457,10 +1463,10 @@ impl<'a> Parser<'a> {
// Include `<` to provide this recommendation even in a case like
// `Foo<Bar<Baz<Qux, ()>>>`
if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less
|| outer_op.node == AssocOp::Greater
if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)
|| outer_op.node == AssocOp::Binary(BinOpKind::Gt)
{
if outer_op.node == AssocOp::Less {
if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {
let snapshot = self.create_snapshot_for_diagnostic();
self.bump();
// So far we have parsed `foo<bar<`, consume the rest of the type args.
@ -2635,10 +2641,12 @@ impl<'a> Parser<'a> {
) -> PResult<'a, GenericArg> {
let is_op_or_dot = AssocOp::from_token(&self.token)
.and_then(|op| {
if let AssocOp::Greater
| AssocOp::Less
| AssocOp::ShiftRight
| AssocOp::GreaterEqual
if let AssocOp::Binary(
BinOpKind::Gt
| BinOpKind::Lt
| BinOpKind::Shr
| BinOpKind::Ge
)
// Don't recover from `foo::<bar = baz>`, because this could be an attempt to
// assign a value to a defaulted generic parameter.
| AssocOp::Assign

View file

@ -188,17 +188,12 @@ impl<'a> Parser<'a> {
}
// Look for JS' `===` and `!==` and recover
if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual)
if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node
&& self.token == token::Eq
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
let sugg = match op.node {
AssocOp::Equal => "==",
AssocOp::NotEqual => "!=",
_ => unreachable!(),
}
.into();
let sugg = bop.as_str().into();
let invalid = format!("{sugg}=");
self.dcx().emit_err(errors::InvalidComparisonOperator {
span: sp,
@ -213,7 +208,7 @@ impl<'a> Parser<'a> {
}
// Look for PHP's `<>` and recover
if op.node == AssocOp::Less
if op.node == AssocOp::Binary(BinOpKind::Lt)
&& self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo()
{
@ -231,7 +226,7 @@ impl<'a> Parser<'a> {
}
// Look for C++'s `<=>` and recover
if op.node == AssocOp::LessEqual
if op.node == AssocOp::Binary(BinOpKind::Le)
&& self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo()
{
@ -290,25 +285,7 @@ impl<'a> Parser<'a> {
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
lhs = match op {
AssocOp::Add
| AssocOp::Subtract
| AssocOp::Multiply
| AssocOp::Divide
| AssocOp::Modulus
| AssocOp::LAnd
| AssocOp::LOr
| AssocOp::BitXor
| AssocOp::BitAnd
| AssocOp::BitOr
| AssocOp::ShiftLeft
| AssocOp::ShiftRight
| AssocOp::Equal
| AssocOp::Less
| AssocOp::LessEqual
| AssocOp::NotEqual
| AssocOp::Greater
| AssocOp::GreaterEqual => {
let ast_op = op.to_ast_binop().unwrap();
AssocOp::Binary(ast_op) => {
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
self.mk_expr(span, binary)
}
@ -335,13 +312,14 @@ impl<'a> Parser<'a> {
// An exhaustive check is done in the following block, but these are checked first
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we
// want to keep their span info to improve diagnostics in these cases in a later stage.
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
(true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus)
(true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
(true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure)
(true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42`
=> {
(true, Some(AssocOp::Binary(
BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
BinOpKind::Sub | // `{ 42 } -5`
BinOpKind::Add | // `{ 42 } + 42` (unary plus)
BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure)
BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42`
))) => {
// These cases are ambiguous and can't be identified in the parser alone.
//
// Bitwise AND is left out because guessing intent is hard. We can make
@ -380,21 +358,20 @@ impl<'a> Parser<'a> {
// When parsing const expressions, stop parsing when encountering `>`.
(
Some(
AssocOp::ShiftRight
| AssocOp::Greater
| AssocOp::GreaterEqual
AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
| AssocOp::AssignOp(BinOpKind::Shr),
),
_,
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
return None;
}
// When recovering patterns as expressions, stop parsing when encountering an assignment `=`, an alternative `|`, or a range `..`.
// When recovering patterns as expressions, stop parsing when encountering an
// assignment `=`, an alternative `|`, or a range `..`.
(
Some(
AssocOp::Assign
| AssocOp::AssignOp(_)
| AssocOp::BitOr
| AssocOp::Binary(BinOpKind::BitOr)
| AssocOp::DotDot
| AssocOp::DotDotEq,
),
@ -411,7 +388,7 @@ impl<'a> Parser<'a> {
incorrect: "and".into(),
sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span),
});
(AssocOp::LAnd, span)
(AssocOp::Binary(BinOpKind::And), span)
}
(None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => {
self.dcx().emit_err(errors::InvalidLogicalOperator {
@ -419,7 +396,7 @@ impl<'a> Parser<'a> {
incorrect: "or".into(),
sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span),
});
(AssocOp::LOr, span)
(AssocOp::Binary(BinOpKind::Or), span)
}
_ => return None,
};