Rollup merge of #133603 - dtolnay:precedence, r=lcnr

Eliminate magic numbers from expression precedence

Context: see https://github.com/rust-lang/rust/pull/133140.

This PR continues on backporting Syn's expression precedence design into rustc. Rustc's design used mysterious integer quantities represented variously as `i8` or `usize` (e.g. `PREC_CLOSURE = -40i8`), a special significance around `0` that is never named, and an extra `PREC_FORCE_PAREN` precedence level that does not correspond to any expression. Syn's design uses a C-like enum with variants that clearly correspond to specific sets of expression kinds.

This PR is a refactoring that has no intended behavior change on its own, but it unblocks other precedence work that rustc's precedence design was poorly suited to accommodate.

- Asymmetrical precedence, so that a pretty-printer can tell `(return 1) + 1` needs parens but `1 + return 1` does not.

- Squashing the `Closure` and `Jump` cases into a single precedence level.

- Numerous remaining false positives and false negatives in rustc pretty-printer's parenthesization of macro metavariables, for example in `$e < rhs` where $e is `lhs as Thing<T>`.

FYI `@fmease` &mdash; you don't need to review if rustbot picks someone else, but you mentioned being interested in the followup PRs.
This commit is contained in:
Guillaume Gomez 2024-12-02 17:36:03 +01:00 committed by GitHub
commit 7dd0c8314d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 220 additions and 196 deletions

View file

@ -39,9 +39,7 @@ pub use crate::format::*;
use crate::ptr::P; use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter}; use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
use crate::util::parser::{ use crate::util::parser::{AssocOp, ExprPrecedence};
AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS,
};
/// A "Label" is an identifier of some point in sources, /// A "Label" is an identifier of some point in sources,
/// e.g. in the following code: /// e.g. in the following code:
@ -1317,29 +1315,29 @@ impl Expr {
Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) 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 { match self.kind {
ExprKind::Closure(..) => PREC_CLOSURE, ExprKind::Closure(..) => ExprPrecedence::Closure,
ExprKind::Break(..) ExprKind::Break(..)
| ExprKind::Continue(..) | ExprKind::Continue(..)
| ExprKind::Ret(..) | ExprKind::Ret(..)
| ExprKind::Yield(..) | ExprKind::Yield(..)
| ExprKind::Yeet(..) | ExprKind::Yeet(..)
| ExprKind::Become(..) => PREC_JUMP, | ExprKind::Become(..) => ExprPrecedence::Jump,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to // `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 // 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 // ensures that `pprust` will add parentheses in the right places to get the desired
// parse. // parse.
ExprKind::Range(..) => PREC_RANGE, ExprKind::Range(..) => ExprPrecedence::Range,
// Binop-like expr kinds, handled by `AssocOp`. // Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
ExprKind::Cast(..) => AssocOp::As.precedence() as i8, ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) | ExprKind::Assign(..) |
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, ExprKind::AssignOp(..) => ExprPrecedence::Assign,
// Unary, prefix // Unary, prefix
ExprKind::AddrOf(..) ExprKind::AddrOf(..)
@ -1348,7 +1346,7 @@ impl Expr {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` // 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. // but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..) | ExprKind::Let(..)
| ExprKind::Unary(..) => PREC_PREFIX, | ExprKind::Unary(..) => ExprPrecedence::Prefix,
// Never need parens // Never need parens
ExprKind::Array(_) ExprKind::Array(_)
@ -1381,7 +1379,7 @@ impl Expr {
| ExprKind::Underscore | ExprKind::Underscore
| ExprKind::While(..) | ExprKind::While(..)
| ExprKind::Err(_) | ExprKind::Err(_)
| ExprKind::Dummy => PREC_UNAMBIGUOUS, | ExprKind::Dummy => ExprPrecedence::Unambiguous,
} }
} }

View file

@ -128,21 +128,21 @@ impl AssocOp {
} }
/// Gets the precedence of this operator /// Gets the precedence of this operator
pub fn precedence(&self) -> usize { pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*; use AssocOp::*;
match *self { match *self {
As => 14, As => ExprPrecedence::Cast,
Multiply | Divide | Modulus => 13, Multiply | Divide | Modulus => ExprPrecedence::Product,
Add | Subtract => 12, Add | Subtract => ExprPrecedence::Sum,
ShiftLeft | ShiftRight => 11, ShiftLeft | ShiftRight => ExprPrecedence::Shift,
BitAnd => 10, BitAnd => ExprPrecedence::BitAnd,
BitXor => 9, BitXor => ExprPrecedence::BitXor,
BitOr => 8, BitOr => ExprPrecedence::BitOr,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
LAnd => 6, LAnd => ExprPrecedence::LAnd,
LOr => 5, LOr => ExprPrecedence::LOr,
DotDot | DotDotEq => 4, DotDot | DotDotEq => ExprPrecedence::Range,
Assign | AssignOp(_) => 2, Assign | AssignOp(_) => ExprPrecedence::Assign,
} }
} }
@ -229,17 +229,44 @@ impl AssocOp {
} }
} }
pub const PREC_CLOSURE: i8 = -40; #[derive(Clone, Copy, PartialEq, PartialOrd)]
pub const PREC_JUMP: i8 = -30; pub enum ExprPrecedence {
pub const PREC_RANGE: i8 = -10; Closure,
// The range 2..=14 is reserved for AssocOp binary operator precedences. // return, break, yield
pub const PREC_PREFIX: i8 = 50; Jump,
pub const PREC_UNAMBIGUOUS: i8 = 60; // = += -= *= /= %= &= |= ^= <<= >>=
pub const PREC_FORCE_PAREN: i8 = 100; 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`. /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
pub fn prec_let_scrutinee_needs_par() -> usize { pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
AssocOp::LAnd.precedence() ExprPrecedence::LAnd
} }
/// Suppose we have `let _ = e` and the `order` of `e`. /// Suppose we have `let _ = e` and the `order` of `e`.
@ -247,8 +274,8 @@ pub fn prec_let_scrutinee_needs_par() -> usize {
/// ///
/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
/// Can we print this as `let _ = a OP b`? /// Can we print this as `let _ = a OP b`?
pub fn needs_par_as_let_scrutinee(order: i8) -> bool { pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
order <= prec_let_scrutinee_needs_par() as i8 order <= prec_let_scrutinee_needs_par()
} }
/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any

View file

@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol; 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::{ use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token, FormatDebugHex, FormatSign, FormatTrait, token,
@ -212,9 +212,9 @@ impl<'a> State<'a> {
} }
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) { fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
let prec = match func.kind { let needs_paren = match func.kind {
ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, ast::ExprKind::Field(..) => true,
_ => parser::PREC_UNAMBIGUOUS, _ => func.precedence() < ExprPrecedence::Unambiguous,
}; };
// Independent of parenthesization related to precedence, we must // Independent of parenthesization related to precedence, we must
@ -233,7 +233,7 @@ impl<'a> State<'a> {
// because the latter is valid syntax but with the incorrect meaning. // because the latter is valid syntax but with the incorrect meaning.
// It's a match-expression followed by tuple-expression, not a function // It's a match-expression followed by tuple-expression, not a function
// call. // call.
self.print_expr_cond_paren(func, func.precedence() < prec, fixup.leftmost_subexpression()); self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression());
self.print_call_post(args) self.print_call_post(args)
} }
@ -256,7 +256,7 @@ impl<'a> State<'a> {
// a statement containing an expression. // a statement containing an expression.
self.print_expr_cond_paren( self.print_expr_cond_paren(
receiver, receiver,
receiver.precedence() < parser::PREC_UNAMBIGUOUS, receiver.precedence() < ExprPrecedence::Unambiguous,
fixup, fixup,
); );
@ -276,21 +276,22 @@ impl<'a> State<'a> {
fixup: FixupContext, fixup: FixupContext,
) { ) {
let assoc_op = AssocOp::from_ast_binop(op.node); let assoc_op = AssocOp::from_ast_binop(op.node);
let prec = assoc_op.precedence() as i8; let binop_prec = assoc_op.precedence();
let fixity = assoc_op.fixity(); let left_prec = lhs.precedence();
let right_prec = rhs.precedence();
let (left_prec, right_prec) = match fixity { let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
Fixity::Left => (prec, prec + 1), Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
Fixity::Right => (prec + 1, prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
Fixity::None => (prec + 1, prec + 1), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
}; };
let left_prec = match (&lhs.kind, op.node) { match (&lhs.kind, op.node) {
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
// of `(x as i32) < ...`. We need to convince it _not_ to do that. // of `(x as i32) < ...`. We need to convince it _not_ to do that.
(&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => {
parser::PREC_FORCE_PAREN left_needs_paren = true;
} }
// We are given `(let _ = a) OP b`. // We are given `(let _ = a) OP b`.
// //
@ -300,33 +301,23 @@ impl<'a> State<'a> {
// - Otherwise, e.g. when we have `(let a = b) < c` in AST, // - Otherwise, e.g. when we have `(let a = b) < c` in AST,
// parens are required since the parser would interpret `let a = b < c` as // parens are required since the parser would interpret `let a = b < c` as
// `let a = (b < c)`. To achieve this, we force parens. // `let a = (b < c)`. To achieve this, we force parens.
(&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
parser::PREC_FORCE_PAREN left_needs_paren = true;
}
_ => {}
} }
_ => left_prec,
};
self.print_expr_cond_paren(
lhs,
lhs.precedence() < left_prec,
fixup.leftmost_subexpression(),
);
self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
self.space(); self.space();
self.word_space(op.node.as_str()); self.word_space(op.node.as_str());
self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
self.print_expr_cond_paren(
rhs,
rhs.precedence() < right_prec,
fixup.subsequent_subexpression(),
);
} }
fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) {
self.word(op.as_str()); self.word(op.as_str());
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_PREFIX, expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
@ -348,7 +339,7 @@ impl<'a> State<'a> {
} }
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_PREFIX, expr.precedence() < ExprPrecedence::Prefix,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
@ -432,10 +423,9 @@ impl<'a> State<'a> {
self.print_token_literal(lit, expr.span) self.print_token_literal(lit, expr.span)
} }
ast::ExprKind::Cast(expr, ty) => { ast::ExprKind::Cast(expr, ty) => {
let prec = AssocOp::As.precedence() as i8;
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < prec, expr.precedence() < ExprPrecedence::Cast,
fixup.leftmost_subexpression(), fixup.leftmost_subexpression(),
); );
self.space(); self.space();
@ -512,7 +502,7 @@ impl<'a> State<'a> {
MatchKind::Postfix => { MatchKind::Postfix => {
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS, expr.precedence() < ExprPrecedence::Unambiguous,
fixup, fixup,
); );
self.word_nbsp(".match"); self.word_nbsp(".match");
@ -576,31 +566,31 @@ impl<'a> State<'a> {
ast::ExprKind::Await(expr, _) => { ast::ExprKind::Await(expr, _) => {
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS, expr.precedence() < ExprPrecedence::Unambiguous,
fixup, fixup,
); );
self.word(".await"); self.word(".await");
} }
ast::ExprKind::Assign(lhs, rhs, _) => { ast::ExprKind::Assign(lhs, rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_cond_paren( self.print_expr_cond_paren(
lhs, 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(), fixup.leftmost_subexpression(),
); );
self.space(); self.space();
self.word_space("="); self.word_space("=");
self.print_expr_cond_paren( self.print_expr_cond_paren(
rhs, rhs,
rhs.precedence() < prec, rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
ast::ExprKind::AssignOp(op, lhs, rhs) => { ast::ExprKind::AssignOp(op, lhs, rhs) => {
let prec = AssocOp::Assign.precedence() as i8;
self.print_expr_cond_paren( self.print_expr_cond_paren(
lhs, lhs,
lhs.precedence() <= prec, lhs.precedence() <= ExprPrecedence::Range,
fixup.leftmost_subexpression(), fixup.leftmost_subexpression(),
); );
self.space(); self.space();
@ -608,14 +598,14 @@ impl<'a> State<'a> {
self.word_space("="); self.word_space("=");
self.print_expr_cond_paren( self.print_expr_cond_paren(
rhs, rhs,
rhs.precedence() < prec, rhs.precedence() < ExprPrecedence::Assign,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
ast::ExprKind::Field(expr, ident) => { ast::ExprKind::Field(expr, ident) => {
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS, expr.precedence() < ExprPrecedence::Unambiguous,
fixup, fixup,
); );
self.word("."); self.word(".");
@ -624,7 +614,7 @@ impl<'a> State<'a> {
ast::ExprKind::Index(expr, index, _) => { ast::ExprKind::Index(expr, index, _) => {
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_UNAMBIGUOUS, expr.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression(), fixup.leftmost_subexpression(),
); );
self.word("["); self.word("[");
@ -636,7 +626,7 @@ impl<'a> State<'a> {
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. // 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 // 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.) // 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 { if let Some(e) = start {
self.print_expr_cond_paren( self.print_expr_cond_paren(
e, e,
@ -671,7 +661,7 @@ impl<'a> State<'a> {
expr, expr,
// Parenthesize if required by precedence, or in the // Parenthesize if required by precedence, or in the
// case of `break 'inner: loop { break 'inner 1 } + 1` // 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)), || (opt_label.is_none() && classify::leading_labeled_expr(expr)),
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
@ -690,7 +680,7 @@ impl<'a> State<'a> {
self.word(" "); self.word(" ");
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_JUMP, expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
@ -703,7 +693,7 @@ impl<'a> State<'a> {
self.word(" "); self.word(" ");
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_JUMP, expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
@ -713,7 +703,7 @@ impl<'a> State<'a> {
self.word(" "); self.word(" ");
self.print_expr_cond_paren( self.print_expr_cond_paren(
result, result,
result.precedence() < parser::PREC_JUMP, result.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
@ -767,13 +757,13 @@ impl<'a> State<'a> {
self.space(); self.space();
self.print_expr_cond_paren( self.print_expr_cond_paren(
expr, expr,
expr.precedence() < parser::PREC_JUMP, expr.precedence() < ExprPrecedence::Jump,
fixup.subsequent_subexpression(), fixup.subsequent_subexpression(),
); );
} }
} }
ast::ExprKind::Try(e) => { 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("?") self.word("?")
} }
ast::ExprKind::TryBlock(blk) => { ast::ExprKind::TryBlock(blk) => {

View file

@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use std::process::ExitStatus; use std::process::ExitStatus;
use rustc_abi::TargetDataLayoutErrors; use rustc_abi::TargetDataLayoutErrors;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_macros::Subdiagnostic; use rustc_macros::Subdiagnostic;
use rustc_span::Span; use rustc_span::Span;
@ -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)] #[derive(Clone)]
pub struct DiagSymbolList<S = Symbol>(Vec<S>); pub struct DiagSymbolList<S = Symbol>(Vec<S>);

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS}; use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
use rustc_ast::{ use rustc_ast::{
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
LitKind, TraitObjectSyntax, UintTy, LitKind, TraitObjectSyntax, UintTy,
@ -1695,22 +1695,22 @@ pub struct Expr<'hir> {
} }
impl Expr<'_> { impl Expr<'_> {
pub fn precedence(&self) -> i8 { pub fn precedence(&self) -> ExprPrecedence {
match self.kind { match self.kind {
ExprKind::Closure { .. } => PREC_CLOSURE, ExprKind::Closure { .. } => ExprPrecedence::Closure,
ExprKind::Break(..) ExprKind::Break(..)
| ExprKind::Continue(..) | ExprKind::Continue(..)
| ExprKind::Ret(..) | ExprKind::Ret(..)
| ExprKind::Yield(..) | ExprKind::Yield(..)
| ExprKind::Become(..) => PREC_JUMP, | ExprKind::Become(..) => ExprPrecedence::Jump,
// Binop-like expr kinds, handled by `AssocOp`. // Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
ExprKind::Cast(..) => AssocOp::As.precedence() as i8, ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) | ExprKind::Assign(..) |
ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, ExprKind::AssignOp(..) => ExprPrecedence::Assign,
// Unary, prefix // Unary, prefix
ExprKind::AddrOf(..) ExprKind::AddrOf(..)
@ -1719,7 +1719,7 @@ impl Expr<'_> {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` // 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. // but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..) | ExprKind::Let(..)
| ExprKind::Unary(..) => PREC_PREFIX, | ExprKind::Unary(..) => ExprPrecedence::Prefix,
// Never need parens // Never need parens
ExprKind::Array(_) ExprKind::Array(_)
@ -1740,7 +1740,7 @@ impl Expr<'_> {
| ExprKind::Struct(..) | ExprKind::Struct(..)
| ExprKind::Tup(_) | ExprKind::Tup(_)
| ExprKind::Type(..) | ExprKind::Type(..)
| ExprKind::Err(_) => PREC_UNAMBIGUOUS, | ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(), ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
} }

View file

@ -10,7 +10,7 @@ use std::cell::Cell;
use std::vec; use std::vec;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pp::{self, Breaks};
use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_ast_pretty::pprust::{Comments, PrintState};
@ -1125,12 +1125,12 @@ impl<'a> State<'a> {
} }
fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let prec = match func.kind { let needs_paren = match func.kind {
hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, hir::ExprKind::Field(..) => true,
_ => parser::PREC_UNAMBIGUOUS, _ => func.precedence() < ExprPrecedence::Unambiguous,
}; };
self.print_expr_cond_paren(func, func.precedence() < prec); self.print_expr_cond_paren(func, needs_paren);
self.print_call_post(args) self.print_call_post(args)
} }
@ -1141,7 +1141,7 @@ impl<'a> State<'a> {
args: &[hir::Expr<'_>], args: &[hir::Expr<'_>],
) { ) {
let base_args = args; let base_args = args;
self.print_expr_cond_paren(receiver, receiver.precedence() < parser::PREC_UNAMBIGUOUS); self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous);
self.word("."); self.word(".");
self.print_ident(segment.ident); self.print_ident(segment.ident);
@ -1155,37 +1155,38 @@ impl<'a> State<'a> {
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
let assoc_op = AssocOp::from_ast_binop(op.node); let assoc_op = AssocOp::from_ast_binop(op.node);
let prec = assoc_op.precedence() as i8; let binop_prec = assoc_op.precedence();
let fixity = assoc_op.fixity(); let left_prec = lhs.precedence();
let right_prec = rhs.precedence();
let (left_prec, right_prec) = match fixity { let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
Fixity::Left => (prec, prec + 1), Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
Fixity::Right => (prec + 1, prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
Fixity::None => (prec + 1, prec + 1), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
}; };
let left_prec = match (&lhs.kind, op.node) { match (&lhs.kind, op.node) {
// These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
// the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
// of `(x as i32) < ...`. We need to convince it _not_ to do that. // of `(x as i32) < ...`. We need to convince it _not_ to do that.
(&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => { (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => {
parser::PREC_FORCE_PAREN left_needs_paren = true;
} }
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { (&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => {
parser::PREC_FORCE_PAREN left_needs_paren = true;
}
_ => {}
} }
_ => left_prec,
};
self.print_expr_cond_paren(lhs, lhs.precedence() < left_prec); self.print_expr_cond_paren(lhs, left_needs_paren);
self.space(); self.space();
self.word_space(op.node.as_str()); self.word_space(op.node.as_str());
self.print_expr_cond_paren(rhs, rhs.precedence() < right_prec) self.print_expr_cond_paren(rhs, right_needs_paren);
} }
fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) {
self.word(op.as_str()); self.word(op.as_str());
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX) self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix);
} }
fn print_expr_addr_of( fn print_expr_addr_of(
@ -1202,7 +1203,7 @@ impl<'a> State<'a> {
self.print_mutability(mutability, true); self.print_mutability(mutability, true);
} }
} }
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX) self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix);
} }
fn print_literal(&mut self, lit: &hir::Lit) { fn print_literal(&mut self, lit: &hir::Lit) {
@ -1340,8 +1341,7 @@ impl<'a> State<'a> {
self.print_literal(lit); self.print_literal(lit);
} }
hir::ExprKind::Cast(expr, ty) => { hir::ExprKind::Cast(expr, ty) => {
let prec = AssocOp::As.precedence() as i8; self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast);
self.print_expr_cond_paren(expr, expr.precedence() < prec);
self.space(); self.space();
self.word_space("as"); self.word_space("as");
self.print_type(ty); self.print_type(ty);
@ -1442,27 +1442,25 @@ impl<'a> State<'a> {
self.print_block(blk); self.print_block(blk);
} }
hir::ExprKind::Assign(lhs, rhs, _) => { hir::ExprKind::Assign(lhs, rhs, _) => {
let prec = AssocOp::Assign.precedence() as i8; self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
self.space(); self.space();
self.word_space("="); self.word_space("=");
self.print_expr_cond_paren(rhs, rhs.precedence() < prec); self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
} }
hir::ExprKind::AssignOp(op, lhs, rhs) => { hir::ExprKind::AssignOp(op, lhs, rhs) => {
let prec = AssocOp::Assign.precedence() as i8; self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
self.print_expr_cond_paren(lhs, lhs.precedence() <= prec);
self.space(); self.space();
self.word(op.node.as_str()); self.word(op.node.as_str());
self.word_space("="); self.word_space("=");
self.print_expr_cond_paren(rhs, rhs.precedence() < prec); self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
} }
hir::ExprKind::Field(expr, ident) => { hir::ExprKind::Field(expr, ident) => {
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous);
self.word("."); self.word(".");
self.print_ident(ident); self.print_ident(ident);
} }
hir::ExprKind::Index(expr, index, _) => { hir::ExprKind::Index(expr, index, _) => {
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous);
self.word("["); self.word("[");
self.print_expr(index); self.print_expr(index);
self.word("]"); self.word("]");
@ -1476,7 +1474,7 @@ impl<'a> State<'a> {
} }
if let Some(expr) = opt_expr { if let Some(expr) = opt_expr {
self.space(); self.space();
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
} }
} }
hir::ExprKind::Continue(destination) => { hir::ExprKind::Continue(destination) => {
@ -1490,13 +1488,13 @@ impl<'a> State<'a> {
self.word("return"); self.word("return");
if let Some(expr) = result { if let Some(expr) = result {
self.word(" "); self.word(" ");
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
} }
} }
hir::ExprKind::Become(result) => { hir::ExprKind::Become(result) => {
self.word("become"); self.word("become");
self.word(" "); self.word(" ");
self.print_expr_cond_paren(result, result.precedence() < parser::PREC_JUMP); self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump);
} }
hir::ExprKind::InlineAsm(asm) => { hir::ExprKind::InlineAsm(asm) => {
self.word("asm!"); self.word("asm!");
@ -1521,7 +1519,7 @@ impl<'a> State<'a> {
} }
hir::ExprKind::Yield(expr, _) => { hir::ExprKind::Yield(expr, _) => {
self.word_space("yield"); self.word_space("yield");
self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);
} }
hir::ExprKind::Err(_) => { hir::ExprKind::Err(_) => {
self.popen(); self.popen();

View file

@ -1,6 +1,6 @@
use std::iter; use std::iter;
use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def::{self, CtorKind, Namespace, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}; };
if let Ok(rest_snippet) = rest_snippet { if let Ok(rest_snippet) = rest_snippet {
let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS { let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous {
vec![ vec![
(up_to_rcvr_span, "".to_string()), (up_to_rcvr_span, "".to_string()),
(rest_span, format!(".{}({rest_snippet}", segment.ident)), (rest_span, format!(".{}({rest_snippet}", segment.ident)),

View file

@ -28,6 +28,7 @@
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! expression, `e as U2` is not necessarily so (in fact it will only be valid if
//! `U1` coerces to `U2`). //! `U1` coerces to `U2`).
use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
@ -1108,7 +1109,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
let expr_prec = self.expr.precedence(); let expr_prec = self.expr.precedence();
let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS; let needs_parens = expr_prec < ExprPrecedence::Unambiguous;
let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);

View file

@ -2,7 +2,7 @@ use core::cmp::min;
use core::iter; use core::iter;
use hir::def_id::LocalDefId; use hir::def_id::LocalDefId;
use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::packed::Pu128; use rustc_data_structures::packed::Pu128;
use rustc_errors::{Applicability, Diag, MultiSpan}; use rustc_errors::{Applicability, Diag, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// so we remove the user's `clone` call. // so we remove the user's `clone` call.
{ {
vec![(receiver_method.ident.span, conversion_method.name.to_string())] vec![(receiver_method.ident.span, conversion_method.name.to_string())]
} else if expr.precedence() < PREC_UNAMBIGUOUS { } else if expr.precedence() < ExprPrecedence::Unambiguous {
vec![ vec![
(expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{ {
let span = expr.span.find_oldest_ancestor_in_same_ctxt(); let span = expr.span.find_oldest_ancestor_in_same_ctxt();
let mut sugg = if expr.precedence() >= PREC_UNAMBIGUOUS { let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
vec![(span.shrink_to_hi(), ".into()".to_owned())] vec![(span.shrink_to_hi(), ".into()".to_owned())]
} else { } else {
vec![ vec![
@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
); );
let close_paren = if expr.precedence() < PREC_UNAMBIGUOUS { let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous {
sugg.push((expr.span.shrink_to_lo(), "(".to_string())); sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
")" ")"
} else { } else {
@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let len = src.trim_end_matches(&checked_ty.to_string()).len(); let len = src.trim_end_matches(&checked_ty.to_string()).len();
expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
}, },
if expr.precedence() < PREC_UNAMBIGUOUS { if expr.precedence() < ExprPrecedence::Unambiguous {
// Readd `)` // Readd `)`
format!("{expected_ty})") format!("{expected_ty})")
} else { } else {

View file

@ -3,6 +3,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use rustc_ast::token::Token; use rustc_ast::token::Token;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::{Path, Visibility}; use rustc_ast::{Path, Visibility};
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{ use rustc_errors::{
@ -2686,7 +2687,7 @@ pub(crate) struct UnexpectedExpressionInPattern {
/// Was a `RangePatternBound` expected? /// Was a `RangePatternBound` expected?
pub is_bound: bool, pub is_bound: bool,
/// The unexpected expr's precedence (used in match arm guard suggestions). /// The unexpected expr's precedence (used in match arm guard suggestions).
pub expr_precedence: i8, pub expr_precedence: ExprPrecedence,
} }
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]

View file

@ -1,7 +1,7 @@
// ignore-tidy-filelength // ignore-tidy-filelength
use core::mem; use core::mem;
use core::ops::ControlFlow; use core::ops::{Bound, ControlFlow};
use ast::mut_visit::{self, MutVisitor}; use ast::mut_visit::{self, MutVisitor};
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
@ -10,7 +10,7 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case; use rustc_ast::util::case::Case;
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par}; use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::visit::{Visitor, walk_expr};
use rustc_ast::{ use rustc_ast::{
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
@ -120,7 +120,7 @@ impl<'a> Parser<'a> {
r: Restrictions, r: Restrictions,
attrs: AttrWrapper, attrs: AttrWrapper,
) -> PResult<'a, (P<Expr>, bool)> { ) -> PResult<'a, (P<Expr>, bool)> {
self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs)) self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs))
} }
/// Parses an associative expression with operators of at least `min_prec` precedence. /// Parses an associative expression with operators of at least `min_prec` precedence.
@ -128,7 +128,7 @@ impl<'a> Parser<'a> {
/// followed by a subexpression (e.g. `1 + 2`). /// followed by a subexpression (e.g. `1 + 2`).
pub(super) fn parse_expr_assoc_with( pub(super) fn parse_expr_assoc_with(
&mut self, &mut self,
min_prec: usize, min_prec: Bound<ExprPrecedence>,
attrs: AttrWrapper, attrs: AttrWrapper,
) -> PResult<'a, (P<Expr>, bool)> { ) -> PResult<'a, (P<Expr>, bool)> {
let lhs = if self.token.is_range_separator() { let lhs = if self.token.is_range_separator() {
@ -144,7 +144,7 @@ impl<'a> Parser<'a> {
/// was actually parsed. /// was actually parsed.
pub(super) fn parse_expr_assoc_rest_with( pub(super) fn parse_expr_assoc_rest_with(
&mut self, &mut self,
min_prec: usize, min_prec: Bound<ExprPrecedence>,
starts_stmt: bool, starts_stmt: bool,
mut lhs: P<Expr>, mut lhs: P<Expr>,
) -> PResult<'a, (P<Expr>, bool)> { ) -> PResult<'a, (P<Expr>, bool)> {
@ -163,7 +163,11 @@ impl<'a> Parser<'a> {
self.restrictions self.restrictions
}; };
let prec = op.node.precedence(); let prec = op.node.precedence();
if prec < min_prec { if match min_prec {
Bound::Included(min_prec) => prec < min_prec,
Bound::Excluded(min_prec) => prec <= min_prec,
Bound::Unbounded => false,
} {
break; break;
} }
// Check for deprecated `...` syntax // Check for deprecated `...` syntax
@ -276,16 +280,16 @@ impl<'a> Parser<'a> {
} }
let fixity = op.fixity(); let fixity = op.fixity();
let prec_adjustment = match fixity { let min_prec = match fixity {
Fixity::Right => 0, Fixity::Right => Bound::Included(prec),
Fixity::Left => 1, Fixity::Left => Bound::Excluded(prec),
// We currently have no non-associative operators that are not handled above by // We currently have no non-associative operators that are not handled above by
// the special cases. The code is here only for future convenience. // the special cases. The code is here only for future convenience.
Fixity::None => 1, Fixity::None => Bound::Excluded(prec),
}; };
let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
let attrs = this.parse_outer_attributes()?; let attrs = this.parse_outer_attributes()?;
this.parse_expr_assoc_with(prec + prec_adjustment, attrs) this.parse_expr_assoc_with(min_prec, attrs)
})?; })?;
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
@ -451,7 +455,7 @@ impl<'a> Parser<'a> {
/// The other two variants are handled in `parse_prefix_range_expr` below. /// The other two variants are handled in `parse_prefix_range_expr` below.
fn parse_expr_range( fn parse_expr_range(
&mut self, &mut self,
prec: usize, prec: ExprPrecedence,
lhs: P<Expr>, lhs: P<Expr>,
op: AssocOp, op: AssocOp,
cur_op_span: Span, cur_op_span: Span,
@ -460,7 +464,7 @@ impl<'a> Parser<'a> {
let maybe_lt = self.token.clone(); let maybe_lt = self.token.clone();
let attrs = self.parse_outer_attributes()?; let attrs = self.parse_outer_attributes()?;
Some( Some(
self.parse_expr_assoc_with(prec + 1, attrs) self.parse_expr_assoc_with(Bound::Excluded(prec), attrs)
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))? .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
.0, .0,
) )
@ -518,7 +522,7 @@ impl<'a> Parser<'a> {
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
// RHS must be parsed with more associativity than the dots. // RHS must be parsed with more associativity than the dots.
let attrs = this.parse_outer_attributes()?; let attrs = this.parse_outer_attributes()?;
this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs) this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs)
.map(|(x, _)| (lo.to(x.span), Some(x))) .map(|(x, _)| (lo.to(x.span), Some(x)))
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
} else { } else {
@ -2643,7 +2647,8 @@ impl<'a> Parser<'a> {
self.expect(&token::Eq)?; self.expect(&token::Eq)?;
} }
let attrs = self.parse_outer_attributes()?; let attrs = self.parse_outer_attributes()?;
let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?; let (expr, _) =
self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?;
let span = lo.to(expr.span); let span = lo.to(expr.span);
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
} }

View file

@ -1,12 +1,13 @@
use std::ops::Bound;
use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token};
use rustc_ast::util::parser::AssocOp; use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::visit::{self, Visitor}; use rustc_ast::visit::{self, Visitor};
use rustc_ast::{ use rustc_ast::{
self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
StmtKind,
}; };
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
@ -435,8 +436,9 @@ impl<'a> Parser<'a> {
// Parse an associative expression such as `+ expr`, `% expr`, ... // Parse an associative expression such as `+ expr`, `% expr`, ...
// Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
let Ok((expr, _)) = let Ok((expr, _)) = snapshot
snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) .parse_expr_assoc_rest_with(Bound::Unbounded, false, expr)
.map_err(|err| err.cancel())
else { else {
// We got a trailing method/operator, but that wasn't an expression. // We got a trailing method/operator, but that wasn't an expression.
return None; return None;
@ -545,10 +547,7 @@ impl<'a> Parser<'a> {
// HACK: a neater way would be preferable. // HACK: a neater way would be preferable.
let expr = match &err.args["expr_precedence"] { let expr = match &err.args["expr_precedence"] {
DiagArgValue::Number(expr_precedence) => { DiagArgValue::Number(expr_precedence) => {
if *expr_precedence if *expr_precedence <= ExprPrecedence::Compare as i32 {
<= AssocOp::from_ast_binop(BinOpKind::Eq).precedence()
as i32
{
format!("({expr})") format!("({expr})")
} else { } else {
format!("{expr}") format!("{expr}")
@ -570,9 +569,7 @@ impl<'a> Parser<'a> {
} }
Some(guard) => { Some(guard) => {
// Are parentheses required around the old guard? // Are parentheses required around the old guard?
let wrap_guard = guard.precedence() let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd;
<= AssocOp::from_ast_binop(BinOpKind::And).precedence()
as i8;
err.subdiagnostic( err.subdiagnostic(
UnexpectedExpressionInPatternSugg::UpdateGuard { UnexpectedExpressionInPatternSugg::UpdateGuard {

View file

@ -1,5 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::mem; use std::mem;
use std::ops::Bound;
use ast::Label; use ast::Label;
use rustc_ast as ast; use rustc_ast as ast;
@ -207,7 +208,7 @@ impl<'a> Parser<'a> {
// Perform this outside of the `collect_tokens` closure, since our // Perform this outside of the `collect_tokens` closure, since our
// outer attributes do not apply to this part of the expression. // outer attributes do not apply to this part of the expression.
let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| { let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
this.parse_expr_assoc_rest_with(0, true, expr) this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr)
})?; })?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
} else { } else {
@ -240,7 +241,7 @@ impl<'a> Parser<'a> {
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.maybe_recover_from_bad_qpath(e)?;
let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?; let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?; let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?;
StmtKind::Expr(e) StmtKind::Expr(e)
}; };
Ok(self.mk_stmt(lo.to(hi), kind)) Ok(self.mk_stmt(lo.to(hi), kind))

View file

@ -7,7 +7,7 @@ use clippy_utils::{
peel_middle_ty_refs, peel_middle_ty_refs,
}; };
use core::mem; use core::mem;
use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS}; use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -963,7 +963,7 @@ fn report<'tcx>(
// expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
// `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
/* /*
expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX { expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix {
Cow::Owned(format!("({expr_str})")) Cow::Owned(format!("({expr_str})"))
} else { } else {
expr_str expr_str
@ -999,13 +999,13 @@ fn report<'tcx>(
data.first_expr.span, data.first_expr.span,
state.msg, state.msg,
|diag| { |diag| {
let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
Node::Expr(e) => match e.kind { Node::Expr(e) => match e.kind {
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false,
ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))), ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)),
_ => (e.precedence(), false), _ => expr.precedence() < e.precedence(),
}, },
_ => (0, false), _ => false,
}; };
let is_in_tuple = matches!( let is_in_tuple = matches!(
get_parent_expr(cx, data.first_expr), get_parent_expr(cx, data.first_expr),
@ -1016,7 +1016,7 @@ fn report<'tcx>(
); );
let sugg = if !snip_is_macro let sugg = if !snip_is_macro
&& (calls_field || expr.precedence() < precedence) && needs_paren
&& !has_enclosing_paren(&snip) && !has_enclosing_paren(&snip)
&& !is_in_tuple && !is_in_tuple
{ {
@ -1049,16 +1049,16 @@ fn report<'tcx>(
} }
} }
let (prefix, precedence) = match mutability { let (prefix, needs_paren) = match mutability {
Some(mutability) if !ty.is_ref() => { Some(mutability) if !ty.is_ref() => {
let prefix = match mutability { let prefix = match mutability {
Mutability::Not => "&", Mutability::Not => "&",
Mutability::Mut => "&mut ", Mutability::Mut => "&mut ",
}; };
(prefix, PREC_PREFIX) (prefix, expr.precedence() < ExprPrecedence::Prefix)
}, },
None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0), None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false),
_ => ("", 0), _ => ("", false),
}; };
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
@ -1070,7 +1070,7 @@ fn report<'tcx>(
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) = let (snip, snip_is_macro) =
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let sugg = if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) { let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) {
format!("{prefix}({snip})") format!("{prefix}({snip})")
} else { } else {
format!("{prefix}{snip}") format!("{prefix}{snip}")
@ -1157,7 +1157,7 @@ impl<'tcx> Dereferencing<'tcx> {
}, },
Some(parent) if !parent.span.from_expansion() => { Some(parent) if !parent.span.from_expansion() => {
// Double reference might be needed at this point. // Double reference might be needed at this point.
if parent.precedence() == PREC_UNAMBIGUOUS { if parent.precedence() == ExprPrecedence::Unambiguous {
// Parentheses would be needed here, don't lint. // Parentheses would be needed here, don't lint.
*outer_pat = None; *outer_pat = None;
} else { } else {

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, snippet, snippet_with_applicability}; use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
use clippy_utils::visitors::contains_break_or_continue; use clippy_utils::visitors::contains_break_or_continue;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal}; use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
if !prefix.is_empty() if !prefix.is_empty()
&& ( && (
// Precedence of internal expression is less than or equal to precedence of `&expr`. // Precedence of internal expression is less than or equal to precedence of `&expr`.
arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression) arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression)
) )
{ {
arg_snip = format!("({arg_snip})").into(); arg_snip = format!("({arg_snip})").into();

View file

@ -7,7 +7,7 @@ use clippy_utils::{
CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res,
path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
}; };
use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::def::Res; use rustc_hir::def::Res;
@ -117,7 +117,7 @@ where
// it's being passed by value. // it's being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0; let scrutinee = peel_hir_expr_refs(scrutinee).0;
let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS { let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous {
format!("({scrutinee_str})") format!("({scrutinee_str})")
} else { } else {
scrutinee_str.into() scrutinee_str.into()

View file

@ -2,7 +2,7 @@ use clippy_utils::consts::{self, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::sugg::has_enclosing_paren;
use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) { let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
format!("-({snip})") format!("-({snip})")
} else { } else {
format!("-{snip}") format!("-{snip}")

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_lang_item; use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; use clippy_utils::{get_parent_expr, peel_middle_ty_refs};
use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_lint::{LateContext, LateLintPass, Lint};
@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
let parent_expr = get_parent_expr(cx, expr); let parent_expr = get_parent_expr(cx, expr);
let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX); let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix);
if expr_ty == indexed_ty { if expr_ty == indexed_ty {
if expr_ref_count > indexed_ref_count { if expr_ref_count > indexed_ref_count {

View file

@ -1,7 +1,7 @@
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use rustc_ast::util::parser::AssocOp; use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, Node}; use rustc_hir::{Expr, Node};
use rustc_hir_typeck::cast::check_cast; use rustc_hir_typeck::cast::check_cast;
@ -44,8 +44,7 @@ pub(super) fn check<'tcx>(
}; };
if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
&& parent.precedence() && parent.precedence() > ExprPrecedence::Cast
> i8::try_from(AssocOp::As.precedence()).expect("AssocOp always returns a precedence < 128")
{ {
sugg = format!("({sugg})"); sugg = format!("({sugg})");
} }