mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
[internal] Move Linter OperatorPrecedence
into ruff_python_ast
crate (#16162)
## Summary This change begins to resolve #16071 by moving the `OperatorPrecedence` structs from the `ruff_python_linter` crate into `ruff_python_ast`. This PR also implements `precedence()` methods on the `Expr` and `ExprRef` enums. ## Test Plan Since this change mainly shifts existing logic, I didn't add any additional tests. Existing tests do pass.
This commit is contained in:
parent
63dd68e0ed
commit
fa28dc5ccf
5 changed files with 193 additions and 168 deletions
|
@ -1,6 +1,6 @@
|
|||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{self as ast, BoolOp, Expr, Operator, Stmt, UnaryOp};
|
||||
use ruff_python_ast::{self as ast, Expr, OperatorPrecedence, Stmt};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
@ -572,167 +572,3 @@ fn in_dunder_method_definition(semantic: &SemanticModel) -> bool {
|
|||
func_def.name.starts_with("__") && func_def.name.ends_with("__")
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents the precedence levels for Python expressions.
|
||||
/// Variants at the top have lower precedence and variants at the bottom have
|
||||
/// higher precedence.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum OperatorPrecedence {
|
||||
/// The lowest (virtual) precedence level
|
||||
None,
|
||||
/// Precedence of `yield` and `yield from` expressions.
|
||||
Yield,
|
||||
/// Precedence of assignment expressions (`name := expr`).
|
||||
Assign,
|
||||
/// Precedence of starred expressions (`*expr`).
|
||||
Starred,
|
||||
/// Precedence of lambda expressions (`lambda args: expr`).
|
||||
Lambda,
|
||||
/// Precedence of if/else expressions (`expr if cond else expr`).
|
||||
IfElse,
|
||||
/// Precedence of boolean `or` expressions.
|
||||
Or,
|
||||
/// Precedence of boolean `and` expressions.
|
||||
And,
|
||||
/// Precedence of boolean `not` expressions.
|
||||
Not,
|
||||
/// Precedence of comparisons (`<`, `<=`, `>`, `>=`, `!=`, `==`),
|
||||
/// memberships (`in`, `not in`) and identity tests (`is`, `is not`).
|
||||
ComparisonsMembershipIdentity,
|
||||
/// Precedence of bitwise `|` and `^` operators.
|
||||
BitXorOr,
|
||||
/// Precedence of bitwise `&` operator.
|
||||
BitAnd,
|
||||
/// Precedence of left and right shift expressions (`<<`, `>>`).
|
||||
LeftRightShift,
|
||||
/// Precedence of addition and subtraction expressions (`+`, `-`).
|
||||
AddSub,
|
||||
/// Precedence of multiplication (`*`), matrix multiplication (`@`), division (`/`),
|
||||
/// floor division (`//`) and remainder (`%`) expressions.
|
||||
MulDivRemain,
|
||||
/// Precedence of unary positive (`+`), negative (`-`), and bitwise NOT (`~`) expressions.
|
||||
PosNegBitNot,
|
||||
/// Precedence of exponentiation expressions (`**`).
|
||||
Exponent,
|
||||
/// Precedence of `await` expressions.
|
||||
Await,
|
||||
/// Precedence of call expressions (`()`), attribute access (`.`), and subscript (`[]`) expressions.
|
||||
CallAttribute,
|
||||
/// Precedence of atomic expressions (literals, names, containers).
|
||||
Atomic,
|
||||
}
|
||||
|
||||
impl OperatorPrecedence {
|
||||
fn from_expr(expr: &Expr) -> Self {
|
||||
match expr {
|
||||
// Binding or parenthesized expression, list display, dictionary display, set display
|
||||
Expr::Tuple(_)
|
||||
| Expr::Dict(_)
|
||||
| Expr::Set(_)
|
||||
| Expr::ListComp(_)
|
||||
| Expr::List(_)
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_)
|
||||
| Expr::Generator(_)
|
||||
| Expr::Name(_)
|
||||
| Expr::StringLiteral(_)
|
||||
| Expr::BytesLiteral(_)
|
||||
| Expr::NumberLiteral(_)
|
||||
| Expr::BooleanLiteral(_)
|
||||
| Expr::NoneLiteral(_)
|
||||
| Expr::EllipsisLiteral(_)
|
||||
| Expr::FString(_) => Self::Atomic,
|
||||
// Subscription, slicing, call, attribute reference
|
||||
Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_) | Expr::Slice(_) => {
|
||||
Self::CallAttribute
|
||||
}
|
||||
|
||||
// Await expression
|
||||
Expr::Await(_) => Self::Await,
|
||||
|
||||
// Exponentiation **
|
||||
// Handled below along with other binary operators
|
||||
|
||||
// Unary operators: +x, -x, ~x (except boolean not)
|
||||
Expr::UnaryOp(operator) => match operator.op {
|
||||
UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
|
||||
UnaryOp::Not => Self::Not,
|
||||
},
|
||||
|
||||
// Math binary ops
|
||||
Expr::BinOp(binary_operation) => Self::from(binary_operation.op),
|
||||
|
||||
// Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not
|
||||
Expr::Compare(_) => Self::ComparisonsMembershipIdentity,
|
||||
|
||||
// Boolean not
|
||||
// Handled above in unary operators
|
||||
|
||||
// Boolean operations: and, or
|
||||
Expr::BoolOp(bool_op) => Self::from(bool_op.op),
|
||||
|
||||
// Conditional expressions: x if y else z
|
||||
Expr::If(_) => Self::IfElse,
|
||||
|
||||
// Lambda expressions
|
||||
Expr::Lambda(_) => Self::Lambda,
|
||||
|
||||
// Unpacking also omitted in the docs, but has almost the lowest precedence,
|
||||
// except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid
|
||||
// but `[*v := [1,2]] would fail on incorrect syntax because * will associate
|
||||
// `v` before the assignment.
|
||||
Expr::Starred(_) => Self::Starred,
|
||||
|
||||
// Assignment expressions (aka named)
|
||||
Expr::Named(_) => Self::Assign,
|
||||
|
||||
// Although omitted in docs, yield expressions may be used inside an expression
|
||||
// but must be parenthesized. So for our purposes we assume they just have
|
||||
// the lowest "real" precedence.
|
||||
Expr::Yield(_) | Expr::YieldFrom(_) => Self::Yield,
|
||||
|
||||
// Not a real python expression, so treat as lowest as well
|
||||
Expr::IpyEscapeCommand(_) => Self::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Expr> for OperatorPrecedence {
|
||||
fn from(expr: &Expr) -> Self {
|
||||
Self::from_expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Operator> for OperatorPrecedence {
|
||||
fn from(operator: Operator) -> Self {
|
||||
match operator {
|
||||
// Multiplication, matrix multiplication, division, floor division, remainder:
|
||||
// *, @, /, //, %
|
||||
Operator::Mult
|
||||
| Operator::MatMult
|
||||
| Operator::Div
|
||||
| Operator::Mod
|
||||
| Operator::FloorDiv => Self::MulDivRemain,
|
||||
// Addition, subtraction
|
||||
Operator::Add | Operator::Sub => Self::AddSub,
|
||||
// Bitwise shifts: <<, >>
|
||||
Operator::LShift | Operator::RShift => Self::LeftRightShift,
|
||||
// Bitwise operations: &, ^, |
|
||||
Operator::BitAnd => Self::BitAnd,
|
||||
Operator::BitXor | Operator::BitOr => Self::BitXorOr,
|
||||
// Exponentiation **
|
||||
Operator::Pow => Self::Exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoolOp> for OperatorPrecedence {
|
||||
fn from(operator: BoolOp) -> Self {
|
||||
match operator {
|
||||
BoolOp::And => Self::And,
|
||||
BoolOp::Or => Self::Or,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,10 @@ use std::str::FromStr;
|
|||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::{self as ast, Expr, Int, LiteralExpressionRef, UnaryOp};
|
||||
use ruff_python_ast::{self as ast, Expr, Int, LiteralExpressionRef, OperatorPrecedence, UnaryOp};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::pylint::rules::OperatorPrecedence;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
enum LiteralType {
|
||||
|
|
|
@ -5,6 +5,7 @@ pub use expression::*;
|
|||
pub use generated::*;
|
||||
pub use int::*;
|
||||
pub use nodes::*;
|
||||
pub use operator_precedence::*;
|
||||
|
||||
pub mod comparable;
|
||||
pub mod docstrings;
|
||||
|
@ -16,6 +17,7 @@ mod int;
|
|||
pub mod name;
|
||||
mod node;
|
||||
mod nodes;
|
||||
pub mod operator_precedence;
|
||||
pub mod parenthesize;
|
||||
pub mod relocate;
|
||||
pub mod script;
|
||||
|
|
|
@ -18,7 +18,8 @@ use crate::{
|
|||
name::Name,
|
||||
str::{Quote, TripleQuotes},
|
||||
str_prefix::{AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix},
|
||||
ExceptHandler, Expr, FStringElement, LiteralExpressionRef, Pattern, Stmt, TypeParam,
|
||||
ExceptHandler, Expr, ExprRef, FStringElement, LiteralExpressionRef, OperatorPrecedence,
|
||||
Pattern, Stmt, TypeParam,
|
||||
};
|
||||
|
||||
/// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module)
|
||||
|
@ -365,6 +366,17 @@ impl Expr {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`OperatorPrecedence`] of this expression
|
||||
pub fn precedence(&self) -> OperatorPrecedence {
|
||||
OperatorPrecedence::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprRef<'_> {
|
||||
pub fn precedence(&self) -> OperatorPrecedence {
|
||||
OperatorPrecedence::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An AST node used to represent a IPython escape command at the expression level.
|
||||
|
|
176
crates/ruff_python_ast/src/operator_precedence.rs
Normal file
176
crates/ruff_python_ast/src/operator_precedence.rs
Normal file
|
@ -0,0 +1,176 @@
|
|||
use crate::{BoolOp, Expr, ExprRef, Operator, UnaryOp};
|
||||
|
||||
/// Represents the precedence levels for Python expressions.
|
||||
/// Variants at the top have lower precedence and variants at the bottom have
|
||||
/// higher precedence.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum OperatorPrecedence {
|
||||
/// The lowest (virtual) precedence level
|
||||
None,
|
||||
/// Precedence of `yield` and `yield from` expressions.
|
||||
Yield,
|
||||
/// Precedence of assignment expressions (`name := expr`).
|
||||
Assign,
|
||||
/// Precedence of starred expressions (`*expr`).
|
||||
Starred,
|
||||
/// Precedence of lambda expressions (`lambda args: expr`).
|
||||
Lambda,
|
||||
/// Precedence of if/else expressions (`expr if cond else expr`).
|
||||
IfElse,
|
||||
/// Precedence of boolean `or` expressions.
|
||||
Or,
|
||||
/// Precedence of boolean `and` expressions.
|
||||
And,
|
||||
/// Precedence of boolean `not` expressions.
|
||||
Not,
|
||||
/// Precedence of comparisons (`<`, `<=`, `>`, `>=`, `!=`, `==`),
|
||||
/// memberships (`in`, `not in`) and identity tests (`is`, `is not`).
|
||||
ComparisonsMembershipIdentity,
|
||||
/// Precedence of bitwise `|` and `^` operators.
|
||||
BitXorOr,
|
||||
/// Precedence of bitwise `&` operator.
|
||||
BitAnd,
|
||||
/// Precedence of left and right shift expressions (`<<`, `>>`).
|
||||
LeftRightShift,
|
||||
/// Precedence of addition and subtraction expressions (`+`, `-`).
|
||||
AddSub,
|
||||
/// Precedence of multiplication (`*`), matrix multiplication (`@`), division (`/`),
|
||||
/// floor division (`//`) and remainder (`%`) expressions.
|
||||
MulDivRemain,
|
||||
/// Precedence of unary positive (`+`), negative (`-`), and bitwise NOT (`~`) expressions.
|
||||
PosNegBitNot,
|
||||
/// Precedence of exponentiation expressions (`**`).
|
||||
Exponent,
|
||||
/// Precedence of `await` expressions.
|
||||
Await,
|
||||
/// Precedence of call expressions (`()`), attribute access (`.`), and subscript (`[]`) expressions.
|
||||
CallAttribute,
|
||||
/// Precedence of atomic expressions (literals, names, containers).
|
||||
Atomic,
|
||||
}
|
||||
|
||||
impl OperatorPrecedence {
|
||||
pub fn from_expr_ref(expr: &ExprRef) -> Self {
|
||||
match expr {
|
||||
// Binding or parenthesized expression, list display, dictionary display, set display
|
||||
ExprRef::Tuple(_)
|
||||
| ExprRef::Dict(_)
|
||||
| ExprRef::Set(_)
|
||||
| ExprRef::ListComp(_)
|
||||
| ExprRef::List(_)
|
||||
| ExprRef::SetComp(_)
|
||||
| ExprRef::DictComp(_)
|
||||
| ExprRef::Generator(_)
|
||||
| ExprRef::Name(_)
|
||||
| ExprRef::StringLiteral(_)
|
||||
| ExprRef::BytesLiteral(_)
|
||||
| ExprRef::NumberLiteral(_)
|
||||
| ExprRef::BooleanLiteral(_)
|
||||
| ExprRef::NoneLiteral(_)
|
||||
| ExprRef::EllipsisLiteral(_)
|
||||
| ExprRef::FString(_) => Self::Atomic,
|
||||
// Subscription, slicing, call, attribute reference
|
||||
ExprRef::Attribute(_)
|
||||
| ExprRef::Subscript(_)
|
||||
| ExprRef::Call(_)
|
||||
| ExprRef::Slice(_) => Self::CallAttribute,
|
||||
|
||||
// Await expression
|
||||
ExprRef::Await(_) => Self::Await,
|
||||
|
||||
// Exponentiation **
|
||||
// Handled below along with other binary operators
|
||||
|
||||
// Unary operators: +x, -x, ~x (except boolean not)
|
||||
ExprRef::UnaryOp(operator) => match operator.op {
|
||||
UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
|
||||
UnaryOp::Not => Self::Not,
|
||||
},
|
||||
|
||||
// Math binary ops
|
||||
ExprRef::BinOp(binary_operation) => Self::from(binary_operation.op),
|
||||
|
||||
// Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not
|
||||
ExprRef::Compare(_) => Self::ComparisonsMembershipIdentity,
|
||||
|
||||
// Boolean not
|
||||
// Handled above in unary operators
|
||||
|
||||
// Boolean operations: and, or
|
||||
ExprRef::BoolOp(bool_op) => Self::from(bool_op.op),
|
||||
|
||||
// Conditional expressions: x if y else z
|
||||
ExprRef::If(_) => Self::IfElse,
|
||||
|
||||
// Lambda expressions
|
||||
ExprRef::Lambda(_) => Self::Lambda,
|
||||
|
||||
// Unpacking also omitted in the docs, but has almost the lowest precedence,
|
||||
// except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid
|
||||
// but `[*v := [1,2]] would fail on incorrect syntax because * will associate
|
||||
// `v` before the assignment.
|
||||
ExprRef::Starred(_) => Self::Starred,
|
||||
|
||||
// Assignment expressions (aka named)
|
||||
ExprRef::Named(_) => Self::Assign,
|
||||
|
||||
// Although omitted in docs, yield expressions may be used inside an expression
|
||||
// but must be parenthesized. So for our purposes we assume they just have
|
||||
// the lowest "real" precedence.
|
||||
ExprRef::Yield(_) | ExprRef::YieldFrom(_) => Self::Yield,
|
||||
|
||||
// Not a real python expression, so treat as lowest as well
|
||||
ExprRef::IpyEscapeCommand(_) => Self::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_expr(expr: &Expr) -> Self {
|
||||
Self::from(&ExprRef::from(expr))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Expr> for OperatorPrecedence {
|
||||
fn from(expr: &Expr) -> Self {
|
||||
Self::from_expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&ExprRef<'a>> for OperatorPrecedence {
|
||||
fn from(expr_ref: &ExprRef<'a>) -> Self {
|
||||
Self::from_expr_ref(expr_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Operator> for OperatorPrecedence {
|
||||
fn from(operator: Operator) -> Self {
|
||||
match operator {
|
||||
// Multiplication, matrix multiplication, division, floor division, remainder:
|
||||
// *, @, /, //, %
|
||||
Operator::Mult
|
||||
| Operator::MatMult
|
||||
| Operator::Div
|
||||
| Operator::Mod
|
||||
| Operator::FloorDiv => Self::MulDivRemain,
|
||||
// Addition, subtraction
|
||||
Operator::Add | Operator::Sub => Self::AddSub,
|
||||
// Bitwise shifts: <<, >>
|
||||
Operator::LShift | Operator::RShift => Self::LeftRightShift,
|
||||
// Bitwise operations: &, ^, |
|
||||
Operator::BitAnd => Self::BitAnd,
|
||||
Operator::BitXor | Operator::BitOr => Self::BitXorOr,
|
||||
// Exponentiation **
|
||||
Operator::Pow => Self::Exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoolOp> for OperatorPrecedence {
|
||||
fn from(operator: BoolOp) -> Self {
|
||||
match operator {
|
||||
BoolOp::And => Self::And,
|
||||
BoolOp::Or => Self::Or,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue