Skip to content

Commit

Permalink
Eliminate magic numbers from expression precedence
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Dec 1, 2024
1 parent 539c863 commit 7ced18f
Show file tree
Hide file tree
Showing 18 changed files with 162 additions and 138 deletions.
22 changes: 10 additions & 12 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ pub use crate::format::*;
use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
use crate::util::parser::{
AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
};
use crate::util::parser::{AssocOp, ExprPrecedence};

/// A "Label" is an identifier of some point in sources,
/// e.g. in the following code:
Expand Down Expand Up @@ -1317,29 +1315,29 @@ impl Expr {
Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
}

pub fn precedence(&self) -> i8 {
pub fn precedence(&self) -> ExprPrecedence {
match self.kind {
ExprKind::Closure(..) => PREC_CLOSURE,
ExprKind::Closure(..) => ExprPrecedence::Closure,

ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::Yield(..)
| ExprKind::Yeet(..)
| ExprKind::Become(..) => PREC_JUMP,
| ExprKind::Become(..) => ExprPrecedence::Jump,

// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
// ensures that `pprust` will add parentheses in the right places to get the desired
// parse.
ExprKind::Range(..) => PREC_RANGE,
ExprKind::Range(..) => ExprPrecedence::Range,

// Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8,
ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast,

ExprKind::Assign(..) |
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
ExprKind::AssignOp(..) => ExprPrecedence::Assign,

// Unary, prefix
ExprKind::AddrOf(..)
Expand All @@ -1348,7 +1346,7 @@ impl Expr {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
// but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..)
| ExprKind::Unary(..) => PREC_PREFIX,
| ExprKind::Unary(..) => ExprPrecedence::Prefix,

// Never need parens
ExprKind::Array(_)
Expand Down Expand Up @@ -1381,7 +1379,7 @@ impl Expr {
| ExprKind::Underscore
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => PREC_UNAMBIGUOUS,
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
}
}

Expand Down
74 changes: 51 additions & 23 deletions compiler/rustc_ast/src/util/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,21 +128,21 @@ impl AssocOp {
}

/// Gets the precedence of this operator
pub fn precedence(&self) -> usize {
pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*;
match *self {
As => 14,
Multiply | Divide | Modulus => 13,
Add | Subtract => 12,
ShiftLeft | ShiftRight => 11,
BitAnd => 10,
BitXor => 9,
BitOr => 8,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
LAnd => 6,
LOr => 5,
DotDot | DotDotEq => 4,
Assign | AssignOp(_) => 2,
As => ExprPrecedence::Cast,
Multiply | Divide | Modulus => ExprPrecedence::Product,
Add | Subtract => ExprPrecedence::Sum,
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
BitAnd => ExprPrecedence::BitAnd,
BitXor => ExprPrecedence::BitXor,
BitOr => ExprPrecedence::BitOr,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
LAnd => ExprPrecedence::LAnd,
LOr => ExprPrecedence::LOr,
DotDot | DotDotEq => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign,
}
}

Expand Down Expand Up @@ -229,25 +229,53 @@ impl AssocOp {
}
}

pub const PREC_CLOSURE: i8 = -40;
pub const PREC_JUMP: i8 = -30;
pub const PREC_RANGE: i8 = -10;
// The range 2..=14 is reserved for AssocOp binary operator precedences.
pub const PREC_PREFIX: i8 = 50;
pub const PREC_UNAMBIGUOUS: i8 = 60;
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub enum ExprPrecedence {
Closure,
// return, break, yield
Jump,
// = += -= *= /= %= &= |= ^= <<= >>=
Assign,
// .. ..=
Range,
// ||
LOr,
// &&
LAnd,
// == != < > <= >=
Compare,
// |
BitOr,
// ^
BitXor,
// &
BitAnd,
// << >>
Shift,
// + -
Sum,
// * / %
Product,
// as
Cast,
// unary - * ! & &mut
Prefix,
// paths, loops, function calls, array indexing, field expressions, method calls
Unambiguous,
}

/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
pub fn prec_let_scrutinee_needs_par() -> usize {
AssocOp::LAnd.precedence()
pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
ExprPrecedence::LAnd
}

/// Suppose we have `let _ = e` and the `order` of `e`.
/// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS?
///
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
/// Can we print this as `let _ = a OP b`?
pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
order <= prec_let_scrutinee_needs_par() as i8
pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
order <= prec_let_scrutinee_needs_par()
}

/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
Expand Down
49 changes: 24 additions & 25 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token,
Expand Down Expand Up @@ -214,7 +214,7 @@ impl<'a> State<'a> {
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
let needs_paren = match func.kind {
ast::ExprKind::Field(..) => true,
_ => func.precedence() < parser::PREC_UNAMBIGUOUS,
_ => func.precedence() < ExprPrecedence::Unambiguous,
};

// Independent of parenthesization related to precedence, we must
Expand Down Expand Up @@ -256,7 +256,7 @@ impl<'a> State<'a> {
// a statement containing an expression.
self.print_expr_cond_paren(
receiver,
receiver.precedence() < parser::PREC_UNAMBIGUOUS,
receiver.precedence() < ExprPrecedence::Unambiguous,
fixup,
);

Expand All @@ -276,7 +276,7 @@ impl<'a> State<'a> {
fixup: FixupContext,
) {
let assoc_op = AssocOp::from_ast_binop(op.node);
let binop_prec = assoc_op.precedence() as i8;
let binop_prec = assoc_op.precedence();
let left_prec = lhs.precedence();
let right_prec = rhs.precedence();

Expand Down Expand Up @@ -317,7 +317,7 @@ impl<'a> State<'a> {
self.word(op.as_str());
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_PREFIX,
expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(),
);
}
Expand All @@ -339,7 +339,7 @@ impl<'a> State<'a> {
}
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_PREFIX,
expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(),
);
}
Expand Down Expand Up @@ -423,10 +423,9 @@ impl<'a> State<'a> {
self.print_token_literal(lit, expr.span)
}
ast::ExprKind::Cast(expr, ty) => {
let prec = AssocOp::As.precedence() as i8;
self.print_expr_cond_paren(
expr,
expr.precedence() < prec,
expr.precedence() < ExprPrecedence::Cast,
fixup.leftmost_subexpression(),
);
self.space();
Expand Down Expand Up @@ -503,7 +502,7 @@ impl<'a> State<'a> {
MatchKind::Postfix => {
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word_nbsp(".match");
Expand Down Expand Up @@ -567,46 +566,46 @@ impl<'a> State<'a> {
ast::ExprKind::Await(expr, _) => {
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".await");
}
ast::ExprKind::Assign(lhs, rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_cond_paren(
lhs,
lhs.precedence() <= prec,
// Ranges are allowed on the right-hand side of assignment,
// but not the left. `(a..b) = c` needs parentheses.
lhs.precedence() <= ExprPrecedence::Range,
fixup.leftmost_subexpression(),
);
self.space();
self.word_space("=");
self.print_expr_cond_paren(
rhs,
rhs.precedence() < prec,
rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(),
);
}
ast::ExprKind::AssignOp(op, lhs, rhs) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_cond_paren(
lhs,
lhs.precedence() <= prec,
lhs.precedence() <= ExprPrecedence::Range,
fixup.leftmost_subexpression(),
);
self.space();
self.word(op.node.as_str());
self.word_space("=");
self.print_expr_cond_paren(
rhs,
rhs.precedence() < prec,
rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(),
);
}
ast::ExprKind::Field(expr, ident) => {
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup,
);
self.word(".");
Expand All @@ -615,7 +614,7 @@ impl<'a> State<'a> {
ast::ExprKind::Index(expr, index, _) => {
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS,
expr.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression(),
);
self.word("[");
Expand All @@ -627,7 +626,7 @@ impl<'a> State<'a> {
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
// Here we use a fake precedence value so that any child with lower precedence than
// a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
let fake_prec = AssocOp::LOr.precedence() as i8;
let fake_prec = ExprPrecedence::LOr;
if let Some(e) = start {
self.print_expr_cond_paren(
e,
Expand Down Expand Up @@ -662,7 +661,7 @@ impl<'a> State<'a> {
expr,
// Parenthesize if required by precedence, or in the
// case of `break 'inner: loop { break 'inner 1 } + 1`
expr.precedence() < parser::PREC_JUMP
expr.precedence() < ExprPrecedence::Jump
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
fixup.subsequent_subexpression(),
);
Expand All @@ -681,7 +680,7 @@ impl<'a> State<'a> {
self.word(" ");
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
Expand All @@ -694,7 +693,7 @@ impl<'a> State<'a> {
self.word(" ");
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
Expand All @@ -704,7 +703,7 @@ impl<'a> State<'a> {
self.word(" ");
self.print_expr_cond_paren(
result,
result.precedence() < parser::PREC_JUMP,
result.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
Expand Down Expand Up @@ -758,13 +757,13 @@ impl<'a> State<'a> {
self.space();
self.print_expr_cond_paren(
expr,
expr.precedence() < parser::PREC_JUMP,
expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(),
);
}
}
ast::ExprKind::Try(e) => {
self.print_expr_cond_paren(e, e.precedence() < parser::PREC_UNAMBIGUOUS, fixup);
self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup);
self.word("?")
}
ast::ExprKind::TryBlock(blk) => {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use std::process::ExitStatus;

use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust;
use rustc_macros::Subdiagnostic;
use rustc_span::Span;
Expand Down Expand Up @@ -298,6 +299,12 @@ impl IntoDiagArg for hir::def::Namespace {
}
}

impl IntoDiagArg for ExprPrecedence {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Number(self as i32)
}
}

#[derive(Clone)]
pub struct DiagSymbolList<S = Symbol>(Vec<S>);

Expand Down
Loading

0 comments on commit 7ced18f

Please sign in to comment.