Skip to content

Commit fa28dc5

Browse files
authored
[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.
1 parent 63dd68e commit fa28dc5

5 files changed

Lines changed: 193 additions & 168 deletions

File tree

crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs

Lines changed: 1 addition & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
22
use ruff_macros::{derive_message_formats, ViolationMetadata};
3-
use ruff_python_ast::{self as ast, BoolOp, Expr, Operator, Stmt, UnaryOp};
3+
use ruff_python_ast::{self as ast, Expr, OperatorPrecedence, Stmt};
44
use ruff_python_semantic::SemanticModel;
55
use ruff_text_size::Ranged;
66

@@ -572,167 +572,3 @@ fn in_dunder_method_definition(semantic: &SemanticModel) -> bool {
572572
func_def.name.starts_with("__") && func_def.name.ends_with("__")
573573
})
574574
}
575-
576-
/// Represents the precedence levels for Python expressions.
577-
/// Variants at the top have lower precedence and variants at the bottom have
578-
/// higher precedence.
579-
///
580-
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
581-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
582-
pub(crate) enum OperatorPrecedence {
583-
/// The lowest (virtual) precedence level
584-
None,
585-
/// Precedence of `yield` and `yield from` expressions.
586-
Yield,
587-
/// Precedence of assignment expressions (`name := expr`).
588-
Assign,
589-
/// Precedence of starred expressions (`*expr`).
590-
Starred,
591-
/// Precedence of lambda expressions (`lambda args: expr`).
592-
Lambda,
593-
/// Precedence of if/else expressions (`expr if cond else expr`).
594-
IfElse,
595-
/// Precedence of boolean `or` expressions.
596-
Or,
597-
/// Precedence of boolean `and` expressions.
598-
And,
599-
/// Precedence of boolean `not` expressions.
600-
Not,
601-
/// Precedence of comparisons (`<`, `<=`, `>`, `>=`, `!=`, `==`),
602-
/// memberships (`in`, `not in`) and identity tests (`is`, `is not`).
603-
ComparisonsMembershipIdentity,
604-
/// Precedence of bitwise `|` and `^` operators.
605-
BitXorOr,
606-
/// Precedence of bitwise `&` operator.
607-
BitAnd,
608-
/// Precedence of left and right shift expressions (`<<`, `>>`).
609-
LeftRightShift,
610-
/// Precedence of addition and subtraction expressions (`+`, `-`).
611-
AddSub,
612-
/// Precedence of multiplication (`*`), matrix multiplication (`@`), division (`/`),
613-
/// floor division (`//`) and remainder (`%`) expressions.
614-
MulDivRemain,
615-
/// Precedence of unary positive (`+`), negative (`-`), and bitwise NOT (`~`) expressions.
616-
PosNegBitNot,
617-
/// Precedence of exponentiation expressions (`**`).
618-
Exponent,
619-
/// Precedence of `await` expressions.
620-
Await,
621-
/// Precedence of call expressions (`()`), attribute access (`.`), and subscript (`[]`) expressions.
622-
CallAttribute,
623-
/// Precedence of atomic expressions (literals, names, containers).
624-
Atomic,
625-
}
626-
627-
impl OperatorPrecedence {
628-
fn from_expr(expr: &Expr) -> Self {
629-
match expr {
630-
// Binding or parenthesized expression, list display, dictionary display, set display
631-
Expr::Tuple(_)
632-
| Expr::Dict(_)
633-
| Expr::Set(_)
634-
| Expr::ListComp(_)
635-
| Expr::List(_)
636-
| Expr::SetComp(_)
637-
| Expr::DictComp(_)
638-
| Expr::Generator(_)
639-
| Expr::Name(_)
640-
| Expr::StringLiteral(_)
641-
| Expr::BytesLiteral(_)
642-
| Expr::NumberLiteral(_)
643-
| Expr::BooleanLiteral(_)
644-
| Expr::NoneLiteral(_)
645-
| Expr::EllipsisLiteral(_)
646-
| Expr::FString(_) => Self::Atomic,
647-
// Subscription, slicing, call, attribute reference
648-
Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_) | Expr::Slice(_) => {
649-
Self::CallAttribute
650-
}
651-
652-
// Await expression
653-
Expr::Await(_) => Self::Await,
654-
655-
// Exponentiation **
656-
// Handled below along with other binary operators
657-
658-
// Unary operators: +x, -x, ~x (except boolean not)
659-
Expr::UnaryOp(operator) => match operator.op {
660-
UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
661-
UnaryOp::Not => Self::Not,
662-
},
663-
664-
// Math binary ops
665-
Expr::BinOp(binary_operation) => Self::from(binary_operation.op),
666-
667-
// Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not
668-
Expr::Compare(_) => Self::ComparisonsMembershipIdentity,
669-
670-
// Boolean not
671-
// Handled above in unary operators
672-
673-
// Boolean operations: and, or
674-
Expr::BoolOp(bool_op) => Self::from(bool_op.op),
675-
676-
// Conditional expressions: x if y else z
677-
Expr::If(_) => Self::IfElse,
678-
679-
// Lambda expressions
680-
Expr::Lambda(_) => Self::Lambda,
681-
682-
// Unpacking also omitted in the docs, but has almost the lowest precedence,
683-
// except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid
684-
// but `[*v := [1,2]] would fail on incorrect syntax because * will associate
685-
// `v` before the assignment.
686-
Expr::Starred(_) => Self::Starred,
687-
688-
// Assignment expressions (aka named)
689-
Expr::Named(_) => Self::Assign,
690-
691-
// Although omitted in docs, yield expressions may be used inside an expression
692-
// but must be parenthesized. So for our purposes we assume they just have
693-
// the lowest "real" precedence.
694-
Expr::Yield(_) | Expr::YieldFrom(_) => Self::Yield,
695-
696-
// Not a real python expression, so treat as lowest as well
697-
Expr::IpyEscapeCommand(_) => Self::None,
698-
}
699-
}
700-
}
701-
702-
impl From<&Expr> for OperatorPrecedence {
703-
fn from(expr: &Expr) -> Self {
704-
Self::from_expr(expr)
705-
}
706-
}
707-
708-
impl From<Operator> for OperatorPrecedence {
709-
fn from(operator: Operator) -> Self {
710-
match operator {
711-
// Multiplication, matrix multiplication, division, floor division, remainder:
712-
// *, @, /, //, %
713-
Operator::Mult
714-
| Operator::MatMult
715-
| Operator::Div
716-
| Operator::Mod
717-
| Operator::FloorDiv => Self::MulDivRemain,
718-
// Addition, subtraction
719-
Operator::Add | Operator::Sub => Self::AddSub,
720-
// Bitwise shifts: <<, >>
721-
Operator::LShift | Operator::RShift => Self::LeftRightShift,
722-
// Bitwise operations: &, ^, |
723-
Operator::BitAnd => Self::BitAnd,
724-
Operator::BitXor | Operator::BitOr => Self::BitXorOr,
725-
// Exponentiation **
726-
Operator::Pow => Self::Exponent,
727-
}
728-
}
729-
}
730-
731-
impl From<BoolOp> for OperatorPrecedence {
732-
fn from(operator: BoolOp) -> Self {
733-
match operator {
734-
BoolOp::And => Self::And,
735-
BoolOp::Or => Self::Or,
736-
}
737-
}
738-
}

crates/ruff_linter/src/rules/pyupgrade/rules/native_literals.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ use std::str::FromStr;
33

44
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
55
use ruff_macros::{derive_message_formats, ViolationMetadata};
6-
use ruff_python_ast::{self as ast, Expr, Int, LiteralExpressionRef, UnaryOp};
6+
use ruff_python_ast::{self as ast, Expr, Int, LiteralExpressionRef, OperatorPrecedence, UnaryOp};
77
use ruff_text_size::{Ranged, TextRange};
88

99
use crate::checkers::ast::Checker;
10-
use crate::rules::pylint::rules::OperatorPrecedence;
1110

1211
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
1312
enum LiteralType {

crates/ruff_python_ast/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub use expression::*;
55
pub use generated::*;
66
pub use int::*;
77
pub use nodes::*;
8+
pub use operator_precedence::*;
89

910
pub mod comparable;
1011
pub mod docstrings;
@@ -16,6 +17,7 @@ mod int;
1617
pub mod name;
1718
mod node;
1819
mod nodes;
20+
pub mod operator_precedence;
1921
pub mod parenthesize;
2022
pub mod relocate;
2123
pub mod script;

crates/ruff_python_ast/src/nodes.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use crate::{
1818
name::Name,
1919
str::{Quote, TripleQuotes},
2020
str_prefix::{AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix},
21-
ExceptHandler, Expr, FStringElement, LiteralExpressionRef, Pattern, Stmt, TypeParam,
21+
ExceptHandler, Expr, ExprRef, FStringElement, LiteralExpressionRef, OperatorPrecedence,
22+
Pattern, Stmt, TypeParam,
2223
};
2324

2425
/// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module)
@@ -365,6 +366,17 @@ impl Expr {
365366
_ => None,
366367
}
367368
}
369+
370+
/// Return the [`OperatorPrecedence`] of this expression
371+
pub fn precedence(&self) -> OperatorPrecedence {
372+
OperatorPrecedence::from(self)
373+
}
374+
}
375+
376+
impl ExprRef<'_> {
377+
pub fn precedence(&self) -> OperatorPrecedence {
378+
OperatorPrecedence::from(self)
379+
}
368380
}
369381

370382
/// An AST node used to represent a IPython escape command at the expression level.

0 commit comments

Comments
 (0)