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:
Dhruv Manilawala 2024-04-18 21:39:30 +05:30 committed by GitHub
parent 6c4d779140
commit b7066e64e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 417 additions and 9 deletions

View file

@ -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: ...

View file

@ -347,8 +347,18 @@ impl<'src> Parser<'src> {
/// [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 {
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();
loop {
@ -2498,7 +2508,7 @@ enum Associativity {
///
/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Copy, Clone)]
enum Precedence {
pub(super) enum Precedence {
/// Precedence for an unknown operator.
Unknown,
/// The initital precedence when parsing an expression.

View file

@ -17,7 +17,7 @@ use crate::parser::{
use crate::token_set::TokenSet;
use crate::{Mode, ParseErrorType, Tok, TokenKind};
use super::expression::{AllowNamedExpression, AllowStarredExpression};
use super::expression::{AllowNamedExpression, AllowStarredExpression, Precedence};
use super::Parenthesized;
/// Tokens that represent compound statements.
@ -2147,15 +2147,31 @@ impl<'src> Parser<'src> {
// expressions.
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) {
// 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))
} 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

View file

@ -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,
},
),
},
),
],
},
),
],
},
)
```