mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 15:47:41 +00:00
Consider binary expr for parenthesized with items parsing (#11012)
## Summary This PR fixes the bug in with items parsing where it would fail to recognize that the parenthesized expression is part of a large binary expression. ## Test Plan Add test cases and verified the snapshots.
This commit is contained in:
parent
6c4d779140
commit
b7066e64e7
4 changed files with 417 additions and 9 deletions
|
@ -0,0 +1,10 @@
|
||||||
|
# It doesn't matter what's inside the parentheses, these tests need to make sure
|
||||||
|
# all binary expressions parses correctly.
|
||||||
|
with (a) and b: ...
|
||||||
|
with (a) is not b: ...
|
||||||
|
# Make sure precedence works
|
||||||
|
with (a) or b and c: ...
|
||||||
|
with (a) and b or c: ...
|
||||||
|
with (a | b) << c | d: ...
|
||||||
|
# Postfix should still be parsed first
|
||||||
|
with (a)[0] + b * c: ...
|
|
@ -347,8 +347,18 @@ impl<'src> Parser<'src> {
|
||||||
/// [Pratt parsing algorithm]: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
|
/// [Pratt parsing algorithm]: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
|
||||||
fn parse_expression_with_precedence(&mut self, previous_precedence: Precedence) -> ParsedExpr {
|
fn parse_expression_with_precedence(&mut self, previous_precedence: Precedence) -> ParsedExpr {
|
||||||
let start = self.node_start();
|
let start = self.node_start();
|
||||||
let mut lhs = self.parse_lhs_expression(previous_precedence);
|
let lhs = self.parse_lhs_expression(previous_precedence);
|
||||||
|
self.parse_expression_with_precedence_recursive(lhs, previous_precedence, start)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses an expression with binding power of at least `previous_precedence` given the
|
||||||
|
/// left-hand side expression.
|
||||||
|
pub(super) fn parse_expression_with_precedence_recursive(
|
||||||
|
&mut self,
|
||||||
|
mut lhs: ParsedExpr,
|
||||||
|
previous_precedence: Precedence,
|
||||||
|
start: TextSize,
|
||||||
|
) -> ParsedExpr {
|
||||||
let mut progress = ParserProgress::default();
|
let mut progress = ParserProgress::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -2498,7 +2508,7 @@ enum Associativity {
|
||||||
///
|
///
|
||||||
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
|
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
|
||||||
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Copy, Clone)]
|
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Copy, Clone)]
|
||||||
enum Precedence {
|
pub(super) enum Precedence {
|
||||||
/// Precedence for an unknown operator.
|
/// Precedence for an unknown operator.
|
||||||
Unknown,
|
Unknown,
|
||||||
/// The initital precedence when parsing an expression.
|
/// The initital precedence when parsing an expression.
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::parser::{
|
||||||
use crate::token_set::TokenSet;
|
use crate::token_set::TokenSet;
|
||||||
use crate::{Mode, ParseErrorType, Tok, TokenKind};
|
use crate::{Mode, ParseErrorType, Tok, TokenKind};
|
||||||
|
|
||||||
use super::expression::{AllowNamedExpression, AllowStarredExpression};
|
use super::expression::{AllowNamedExpression, AllowStarredExpression, Precedence};
|
||||||
use super::Parenthesized;
|
use super::Parenthesized;
|
||||||
|
|
||||||
/// Tokens that represent compound statements.
|
/// Tokens that represent compound statements.
|
||||||
|
@ -2147,15 +2147,31 @@ impl<'src> Parser<'src> {
|
||||||
// expressions.
|
// expressions.
|
||||||
lhs = self.parse_postfix_expression(lhs, start);
|
lhs = self.parse_postfix_expression(lhs, start);
|
||||||
|
|
||||||
// test_ok ambiguous_lpar_with_items_if_expr
|
|
||||||
// with (x) if True else y: ...
|
|
||||||
// with (x for x in iter) if True else y: ...
|
|
||||||
// with (x async for x in iter) if True else y: ...
|
|
||||||
// with (x)[0] if True else y: ...
|
|
||||||
let context_expr = if self.at(TokenKind::If) {
|
let context_expr = if self.at(TokenKind::If) {
|
||||||
|
// test_ok ambiguous_lpar_with_items_if_expr
|
||||||
|
// with (x) if True else y: ...
|
||||||
|
// with (x for x in iter) if True else y: ...
|
||||||
|
// with (x async for x in iter) if True else y: ...
|
||||||
|
// with (x)[0] if True else y: ...
|
||||||
Expr::If(self.parse_if_expression(lhs, start))
|
Expr::If(self.parse_if_expression(lhs, start))
|
||||||
} else {
|
} else {
|
||||||
lhs
|
// test_ok ambiguous_lpar_with_items_binary_expr
|
||||||
|
// # It doesn't matter what's inside the parentheses, these tests need to make sure
|
||||||
|
// # all binary expressions parses correctly.
|
||||||
|
// with (a) and b: ...
|
||||||
|
// with (a) is not b: ...
|
||||||
|
// # Make sure precedence works
|
||||||
|
// with (a) or b and c: ...
|
||||||
|
// with (a) and b or c: ...
|
||||||
|
// with (a | b) << c | d: ...
|
||||||
|
// # Postfix should still be parsed first
|
||||||
|
// with (a)[0] + b * c: ...
|
||||||
|
self.parse_expression_with_precedence_recursive(
|
||||||
|
lhs.into(),
|
||||||
|
Precedence::Initial,
|
||||||
|
start,
|
||||||
|
)
|
||||||
|
.expr
|
||||||
};
|
};
|
||||||
|
|
||||||
let optional_vars = self
|
let optional_vars = self
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/ambiguous_lpar_with_items_binary_expr.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..337,
|
||||||
|
body: [
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 124..143,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 129..138,
|
||||||
|
context_expr: BoolOp(
|
||||||
|
ExprBoolOp {
|
||||||
|
range: 129..138,
|
||||||
|
op: And,
|
||||||
|
values: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 130..131,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 137..138,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 140..143,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 140..143,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 144..166,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 149..161,
|
||||||
|
context_expr: Compare(
|
||||||
|
ExprCompare {
|
||||||
|
range: 149..161,
|
||||||
|
left: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 150..151,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ops: [
|
||||||
|
IsNot,
|
||||||
|
],
|
||||||
|
comparators: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 160..161,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 163..166,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 163..166,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 196..220,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 201..215,
|
||||||
|
context_expr: BoolOp(
|
||||||
|
ExprBoolOp {
|
||||||
|
range: 201..215,
|
||||||
|
op: Or,
|
||||||
|
values: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 202..203,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BoolOp(
|
||||||
|
ExprBoolOp {
|
||||||
|
range: 208..215,
|
||||||
|
op: And,
|
||||||
|
values: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 208..209,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 214..215,
|
||||||
|
id: "c",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 217..220,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 217..220,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 221..245,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 226..240,
|
||||||
|
context_expr: BoolOp(
|
||||||
|
ExprBoolOp {
|
||||||
|
range: 226..240,
|
||||||
|
op: Or,
|
||||||
|
values: [
|
||||||
|
BoolOp(
|
||||||
|
ExprBoolOp {
|
||||||
|
range: 226..235,
|
||||||
|
op: And,
|
||||||
|
values: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 227..228,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 234..235,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 239..240,
|
||||||
|
id: "c",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 242..245,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 242..245,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 246..272,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 251..267,
|
||||||
|
context_expr: BinOp(
|
||||||
|
ExprBinOp {
|
||||||
|
range: 251..267,
|
||||||
|
left: BinOp(
|
||||||
|
ExprBinOp {
|
||||||
|
range: 251..263,
|
||||||
|
left: BinOp(
|
||||||
|
ExprBinOp {
|
||||||
|
range: 252..257,
|
||||||
|
left: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 252..253,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
op: BitOr,
|
||||||
|
right: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 256..257,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
op: LShift,
|
||||||
|
right: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 262..263,
|
||||||
|
id: "c",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
op: BitOr,
|
||||||
|
right: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 266..267,
|
||||||
|
id: "d",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 269..272,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 269..272,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 312..336,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 317..331,
|
||||||
|
context_expr: BinOp(
|
||||||
|
ExprBinOp {
|
||||||
|
range: 317..331,
|
||||||
|
left: Subscript(
|
||||||
|
ExprSubscript {
|
||||||
|
range: 317..323,
|
||||||
|
value: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 318..319,
|
||||||
|
id: "a",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
slice: NumberLiteral(
|
||||||
|
ExprNumberLiteral {
|
||||||
|
range: 321..322,
|
||||||
|
value: Int(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
op: Add,
|
||||||
|
right: BinOp(
|
||||||
|
ExprBinOp {
|
||||||
|
range: 326..331,
|
||||||
|
left: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 326..327,
|
||||||
|
id: "b",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
op: Mult,
|
||||||
|
right: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 330..331,
|
||||||
|
id: "c",
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 333..336,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 333..336,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue