Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 1 addition & 165 deletions crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_python_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
14 changes: 13 additions & 1 deletion crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
Loading
Loading