Compare commits

...

4 Commits

Author SHA1 Message Date
Dhruv Manilawala
f7b34848c3 Refactor comparison expression parsing 2024-04-21 12:40:41 +05:30
Dhruv Manilawala
c0fc84fd78 Refactor boolean expression parsing 2024-04-21 11:53:15 +05:30
Dhruv Manilawala
060141b1de Make associativity a property of precedence 2024-04-20 23:55:20 +05:30
Dhruv Manilawala
b04cb9f92a Add ExpressionContext for expression parsing
This commit adds a new `ExpressionContext` struct which is used in
expression parsing.

This solves the following problem:
1. Allowing starred expression with different precedence
2. Allowing yield expression in certain context
3. Remove ambiguity with `in` keyword when parsing a `for ... in`
   statement
2024-04-20 15:25:13 +05:30
24 changed files with 1413 additions and 1079 deletions

1
Cargo.lock generated
View File

@@ -2180,7 +2180,6 @@ dependencies = [
"anyhow",
"bitflags 2.5.0",
"bstr",
"drop_bomb",
"insta",
"is-macro",
"itertools 0.12.1",

View File

@@ -18,7 +18,6 @@ ruff_text_size = { path = "../ruff_text_size" }
anyhow = { workspace = true }
bitflags = { workspace = true }
drop_bomb = { workspace = true }
bstr = { workspace = true }
is-macro = { workspace = true }
itertools = { workspace = true }

View File

@@ -1 +0,0 @@
for d(x in y) in target: ...

View File

@@ -3,4 +3,5 @@ for "a" in x: ...
for *x and y in z: ...
for *x | y in z: ...
for await x in z: ...
for yield x in y: ...
for [x, 1, y, *["a"]] in z: ...

View File

@@ -0,0 +1,6 @@
for x not in y in z: ...
for x == y in z: ...
for x or y in z: ...
for -x in y: ...
for not x in y: ...
for x | y in z: ...

View File

@@ -1,3 +1,4 @@
for d(x in y) in target: ...
for (x in y)() in iter: ...
for (x in y) in iter: ...
for (x in y, z) in iter: ...

View File

@@ -1 +0,0 @@
for d[x in y] in target: ...

View File

@@ -1,2 +1,3 @@
for d[x in y] in target: ...
for (x in y)[0] in iter: ...
for (x in y).attr in iter: ...

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,4 @@
use ruff_python_ast::{self as ast, CmpOp, Expr, ExprContext};
use crate::TokenKind;
use ruff_python_ast::{self as ast, Expr, ExprContext};
/// Set the `ctx` for `Expr::Id`, `Expr::Attribute`, `Expr::Subscript`, `Expr::Starred`,
/// `Expr::Tuple` and `Expr::List`. If `expr` is either `Expr::Tuple` or `Expr::List`,
@@ -26,20 +24,3 @@ pub(super) fn set_expr_ctx(expr: &mut Expr, new_ctx: ExprContext) {
_ => {}
}
}
/// Converts a [`TokenKind`] array of size 2 to its correspondent [`CmpOp`].
pub(super) fn token_kind_to_cmp_op(kind: [TokenKind; 2]) -> Result<CmpOp, ()> {
Ok(match kind {
[TokenKind::Is, TokenKind::Not] => CmpOp::IsNot,
[TokenKind::Is, _] => CmpOp::Is,
[TokenKind::Not, TokenKind::In] => CmpOp::NotIn,
[TokenKind::In, _] => CmpOp::In,
[TokenKind::EqEqual, _] => CmpOp::Eq,
[TokenKind::NotEqual, _] => CmpOp::NotEq,
[TokenKind::Less, _] => CmpOp::Lt,
[TokenKind::LessEqual, _] => CmpOp::LtE,
[TokenKind::Greater, _] => CmpOp::Gt,
[TokenKind::GreaterEqual, _] => CmpOp::GtE,
_ => return Err(()),
})
}

View File

@@ -1,7 +1,6 @@
use std::cmp::Ordering;
use bitflags::bitflags;
use drop_bomb::DebugDropBomb;
use ast::Mod;
use ruff_python_ast as ast;
@@ -16,7 +15,7 @@ use crate::{
Mode, ParseError, ParseErrorType, Tok, TokenKind,
};
use self::expression::AllowStarredExpression;
use self::expression::ExpressionContext;
mod expression;
mod helpers;
@@ -77,13 +76,6 @@ pub(crate) struct Parser<'src> {
/// Stores all the syntax errors found during the parsing.
errors: Vec<ParseError>,
/// This tracks the current expression or statement being parsed.
///
/// The `ctx` is also used to create custom error messages and forbid certain
/// expressions or statements of being parsed. The `ctx` should be empty after
/// an expression or statement is done parsing.
ctx: ParserCtxFlags,
/// Specify the mode in which the code will be parsed.
mode: Mode,
@@ -123,7 +115,6 @@ impl<'src> Parser<'src> {
mode,
source,
errors: Vec::new(),
ctx: ParserCtxFlags::empty(),
tokens,
recovery_context: RecoveryContext::empty(),
last_token_end: tokens_range.start(),
@@ -136,7 +127,7 @@ impl<'src> Parser<'src> {
pub(crate) fn parse_program(mut self) -> Program {
let ast = if self.mode == Mode::Expression {
let start = self.node_start();
let parsed_expr = self.parse_expression_list(AllowStarredExpression::No);
let parsed_expr = self.parse_expression_list(ExpressionContext::default());
// All of the remaining newlines are actually going to be non-logical newlines.
self.eat(TokenKind::Newline);
@@ -185,9 +176,6 @@ impl<'src> Parser<'src> {
}
fn finish(self) -> Vec<ParseError> {
// After parsing, the `ctx` and `ctx_stack` should be empty.
// If it's not, you probably forgot to call `clear_ctx` somewhere.
assert_eq!(self.ctx, ParserCtxFlags::empty());
assert_eq!(
self.current_token_kind(),
TokenKind::EndOfFile,
@@ -232,29 +220,6 @@ impl<'src> Parser<'src> {
merged
}
#[inline]
#[must_use]
fn set_ctx(&mut self, ctx: ParserCtxFlags) -> SavedParserContext {
SavedParserContext {
flags: std::mem::replace(&mut self.ctx, ctx),
bomb: DebugDropBomb::new(
"You must restore the old parser context explicit by calling `restore_ctx`",
),
}
}
#[inline]
fn restore_ctx(&mut self, current: ParserCtxFlags, mut saved_context: SavedParserContext) {
assert_eq!(self.ctx, current);
saved_context.bomb.defuse();
self.ctx = saved_context.flags;
}
#[inline]
fn has_ctx(&self, ctx: ParserCtxFlags) -> bool {
self.ctx.intersects(ctx)
}
/// Returns the start position for a node that starts at the current token.
fn node_start(&self) -> TextSize {
self.current_token_range().start()
@@ -675,13 +640,6 @@ impl SequenceMatchPatternParentheses {
}
}
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
struct ParserCtxFlags: u8 {
const FOR_TARGET = 1 << 2;
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum FunctionKind {
/// A lambda expression, e.g., `lambda x: x`
@@ -1327,9 +1285,3 @@ impl RecoveryContext {
})
}
}
#[derive(Debug)]
struct SavedParserContext {
flags: ParserCtxFlags,
bomb: DebugDropBomb,
}

View File

@@ -6,6 +6,8 @@ use crate::parser::{recovery, Parser, RecoveryContextKind, SequenceMatchPatternP
use crate::token_set::TokenSet;
use crate::{ParseErrorType, Tok, TokenKind};
use super::expression::ExpressionContext;
/// The set of tokens that can start a literal pattern.
const LITERAL_PATTERN_START_SET: TokenSet = TokenSet::new([
TokenKind::None,
@@ -483,7 +485,7 @@ impl<'src> Parser<'src> {
TokenKind::Int | TokenKind::Float | TokenKind::Complex
) =>
{
let unary_expr = self.parse_unary_expression();
let unary_expr = self.parse_unary_expression(ExpressionContext::default());
if unary_expr.op.is_u_add() {
self.add_error(

View File

@@ -11,13 +11,12 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::parser::expression::{GeneratorExpressionInParentheses, ParsedExpr, EXPR_SET};
use crate::parser::progress::ParserProgress;
use crate::parser::{
helpers, FunctionKind, Parser, ParserCtxFlags, RecoveryContext, RecoveryContextKind,
WithItemKind,
helpers, FunctionKind, Parser, RecoveryContext, RecoveryContextKind, WithItemKind,
};
use crate::token_set::TokenSet;
use crate::{Mode, ParseErrorType, Tok, TokenKind};
use super::expression::{AllowNamedExpression, AllowStarredExpression, Precedence};
use super::expression::{ExpressionContext, OperatorPrecedence, StarredExpressionPrecedence};
use super::Parenthesized;
/// Tokens that represent compound statements.
@@ -261,8 +260,11 @@ impl<'src> Parser<'src> {
let start = self.node_start();
// simple_stmt: `... | yield_stmt | star_expressions | ...`
let parsed_expr =
self.parse_yield_expression_or_else(Parser::parse_star_expression_list);
let parsed_expr = self.parse_expression_list(
ExpressionContext::default()
.with_yield_expression_allowed()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
);
if self.at(TokenKind::Equal) {
Stmt::Assign(self.parse_assign_statement(parsed_expr, start))
@@ -309,8 +311,10 @@ impl<'src> Parser<'src> {
|parser| {
// Allow starred expression to raise a better error message for
// an invalid delete target later.
let mut target =
parser.parse_conditional_expression_or_higher(AllowStarredExpression::Yes);
let mut target = parser.parse_conditional_expression_or_higher(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::Conditional),
);
helpers::set_expr_ctx(&mut target.expr, ExprContext::Del);
// test_err invalid_del_target
@@ -356,9 +360,15 @@ impl<'src> Parser<'src> {
// return yield from x
// return x := 1
// return *x and y
let value = self
.at_expr()
.then(|| Box::new(self.parse_star_expression_list().expr));
let value = self.at_expr().then(|| {
Box::new(
self.parse_expression_list(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
)
.expr,
)
});
ast::StmtReturn {
range: self.node_range(start),
@@ -384,7 +394,7 @@ impl<'src> Parser<'src> {
// raise *x
// raise yield x
// raise x := 1
let exc = self.parse_expression_list(AllowStarredExpression::No);
let exc = self.parse_expression_list(ExpressionContext::default());
if let Some(ast::ExprTuple {
parenthesized: false,
@@ -406,7 +416,7 @@ impl<'src> Parser<'src> {
// raise x from *y
// raise x from yield y
// raise x from y := 1
let cause = self.parse_expression_list(AllowStarredExpression::No);
let cause = self.parse_expression_list(ExpressionContext::default());
if let Some(ast::ExprTuple {
parenthesized: false,
@@ -714,7 +724,7 @@ impl<'src> Parser<'src> {
// assert assert x
// assert yield x
// assert x := 1
let test = self.parse_conditional_expression_or_higher(AllowStarredExpression::No);
let test = self.parse_conditional_expression_or_higher(ExpressionContext::default());
let msg = if self.eat(TokenKind::Comma) {
if self.at_expr() {
@@ -724,7 +734,7 @@ impl<'src> Parser<'src> {
// assert False, yield x
// assert False, x := 1
Some(Box::new(
self.parse_conditional_expression_or_higher(AllowStarredExpression::No)
self.parse_conditional_expression_or_higher(ExpressionContext::default())
.expr,
))
} else {
@@ -854,7 +864,7 @@ impl<'src> Parser<'src> {
// type x = yield y
// type x = yield from y
// type x = x := 1
let value = self.parse_conditional_expression_or_higher(AllowStarredExpression::No);
let value = self.parse_conditional_expression_or_higher(ExpressionContext::default());
ast::StmtTypeAlias {
name: Box::new(name),
@@ -1014,15 +1024,18 @@ impl<'src> Parser<'src> {
// x = *lambda x: x
// x = x := 1
let mut value = self.parse_yield_expression_or_else(Parser::parse_star_expression_list);
let context = ExpressionContext::default()
.with_yield_expression_allowed()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr);
let mut value = self.parse_expression_list(context);
if self.at(TokenKind::Equal) {
// This path is only taken when there are more than one assignment targets.
self.parse_list(RecoveryContextKind::AssignmentTargets, |parser| {
parser.bump(TokenKind::Equal);
let mut parsed_expr =
parser.parse_yield_expression_or_else(Parser::parse_star_expression_list);
let mut parsed_expr = parser.parse_expression_list(context);
std::mem::swap(&mut value, &mut parsed_expr);
@@ -1089,10 +1102,12 @@ impl<'src> Parser<'src> {
// x: yield from b = 1
// x: y := int = 1
let context = ExpressionContext::default();
// test_err ann_assign_stmt_type_alias_annotation
// a: type X = int
// lambda: type X = int
let annotation = self.parse_conditional_expression_or_higher(AllowStarredExpression::No);
let annotation = self.parse_conditional_expression_or_higher(context);
let value = if self.eat(TokenKind::Equal) {
if self.at_expr() {
@@ -1101,8 +1116,14 @@ impl<'src> Parser<'src> {
// x: Any = x := 1
// x: list = [x, *a | b, *a or b]
Some(Box::new(
self.parse_yield_expression_or_else(Parser::parse_star_expression_list)
.expr,
self.parse_expression_list(
ExpressionContext::default()
.with_yield_expression_allowed()
.with_starred_expression_allowed(
StarredExpressionPrecedence::BitwiseOr,
),
)
.expr,
))
} else {
// test_err ann_assign_stmt_missing_rhs
@@ -1170,7 +1191,11 @@ impl<'src> Parser<'src> {
// x += *yield from x
// x += *lambda x: x
// x += y := 1
let value = self.parse_yield_expression_or_else(Parser::parse_star_expression_list);
let value = self.parse_expression_list(
ExpressionContext::default()
.with_yield_expression_allowed()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
);
ast::StmtAugAssign {
target: Box::new(target.expr),
@@ -1198,7 +1223,7 @@ impl<'src> Parser<'src> {
// test_err if_stmt_missing_test
// if : ...
let test = self.parse_named_expression_or_higher(AllowStarredExpression::No);
let test = self.parse_named_expression_or_higher(ExpressionContext::default());
// test_err if_stmt_missing_colon
// if x
@@ -1253,7 +1278,7 @@ impl<'src> Parser<'src> {
// elif yield x:
// pass
Some(
self.parse_named_expression_or_higher(AllowStarredExpression::No)
self.parse_named_expression_or_higher(ExpressionContext::default())
.expr,
)
} else {
@@ -1414,7 +1439,7 @@ impl<'src> Parser<'src> {
// pass
// except* *x:
// pass
let parsed_expr = self.parse_expression_list(AllowStarredExpression::No);
let parsed_expr = self.parse_expression_list(ExpressionContext::default());
if matches!(
parsed_expr.expr,
Expr::Tuple(ast::ExprTuple {
@@ -1522,22 +1547,34 @@ impl<'src> Parser<'src> {
fn parse_for_statement(&mut self, start: TextSize) -> ast::StmtFor {
self.bump(TokenKind::For);
// This is to avoid the ambiguity of the `in` token which is used in
// both the `for` statement and the comparison expression. For example:
//
// ```python
// for x in y:
// # ^^^^^^
// # This is not a comparison expression
// pass
// ```
let saved_context = self.set_ctx(ParserCtxFlags::FOR_TARGET);
// test_err for_stmt_missing_target
// for in x: ...
let mut target = self.parse_expression_list(AllowStarredExpression::Yes);
self.restore_ctx(ParserCtxFlags::FOR_TARGET, saved_context);
// test_ok for_in_target_valid_expr
// for d[x in y] in target: ...
// for (x in y)[0] in iter: ...
// for (x in y).attr in iter: ...
// test_err for_stmt_invalid_target_in_keyword
// for d(x in y) in target: ...
// for (x in y)() in iter: ...
// for (x in y) in iter: ...
// for (x in y, z) in iter: ...
// for [x in y, z] in iter: ...
// for {x in y, z} in iter: ...
// test_err for_stmt_invalid_target_binary_expr
// for x not in y in z: ...
// for x == y in z: ...
// for x or y in z: ...
// for -x in y: ...
// for not x in y: ...
// for x | y in z: ...
let mut target = self.parse_expression_list(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::Conditional)
.with_in_not_included(),
);
helpers::set_expr_ctx(&mut target.expr, ExprContext::Store);
@@ -1547,6 +1584,7 @@ impl<'src> Parser<'src> {
// for *x and y in z: ...
// for *x | y in z: ...
// for await x in z: ...
// for yield x in y: ...
// for [x, 1, y, *["a"]] in z: ...
self.validate_assignment_target(&target.expr);
@@ -1563,7 +1601,10 @@ impl<'src> Parser<'src> {
// for x in *a and b: ...
// for x in yield a: ...
// for target in x := 1: ...
let iter = self.parse_star_expression_list();
let iter = self.parse_expression_list(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
);
self.expect(TokenKind::Colon);
@@ -1607,7 +1648,7 @@ impl<'src> Parser<'src> {
// while yield x: ...
// while a, b: ...
// while a := 1, b: ...
let test = self.parse_named_expression_or_higher(AllowStarredExpression::No);
let test = self.parse_named_expression_or_higher(ExpressionContext::default());
// test_err while_stmt_missing_colon
// while (
@@ -1689,7 +1730,7 @@ impl<'src> Parser<'src> {
// def foo() -> *int: ...
// def foo() -> (*int): ...
// def foo() -> yield x: ...
let returns = self.parse_expression_list(AllowStarredExpression::No);
let returns = self.parse_expression_list(ExpressionContext::default());
if matches!(
returns.expr,
@@ -2162,9 +2203,10 @@ impl<'src> Parser<'src> {
// with (a | b) << c | d: ...
// # Postfix should still be parsed first
// with (a)[0] + b * c: ...
self.parse_expression_with_precedence_recursive(
self.parse_binary_expression_or_higher_recursive(
lhs.into(),
Precedence::Initial,
OperatorPrecedence::Initial,
ExpressionContext::default(),
start,
)
.expr
@@ -2215,9 +2257,11 @@ impl<'src> Parser<'src> {
//
// Thus, we can conclude that the grammar used should be:
// (yield_expr | star_named_expression)
let parsed_expr = self.parse_yield_expression_or_else(|p| {
p.parse_star_expression_or_higher(AllowNamedExpression::Yes)
});
let parsed_expr = self.parse_named_expression_or_higher(
ExpressionContext::default()
.with_yield_expression_allowed()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
);
if matches!(self.current_token_kind(), TokenKind::Async | TokenKind::For) {
if parsed_expr.is_unparenthesized_starred_expr() {
@@ -2279,7 +2323,7 @@ impl<'src> Parser<'src> {
} else {
// If it's not in an ambiguous state, then the grammar of the with item
// should be used which is `expression`.
self.parse_conditional_expression_or_higher(AllowStarredExpression::No)
self.parse_conditional_expression_or_higher(ExpressionContext::default())
};
let optional_vars = self
@@ -2305,7 +2349,10 @@ impl<'src> Parser<'src> {
fn parse_with_item_optional_vars(&mut self) -> ParsedExpr {
self.bump(TokenKind::As);
let mut target = self.parse_conditional_expression_or_higher(AllowStarredExpression::Yes);
let mut target = self.parse_conditional_expression_or_higher(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::Conditional),
);
// This has the same semantics as an assignment target.
self.validate_assignment_target(&target.expr);
@@ -2336,7 +2383,10 @@ impl<'src> Parser<'src> {
//
// First try with `star_named_expression`, then if there's no comma,
// we'll restrict it to `named_expression`.
let subject = self.parse_star_expression_or_higher(AllowNamedExpression::Yes);
let subject = self.parse_named_expression_or_higher(
ExpressionContext::default()
.with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr),
);
// test_ok match_stmt_subject_expr
// match x := 1:
@@ -2360,7 +2410,11 @@ impl<'src> Parser<'src> {
let subject = if self.at(TokenKind::Comma) {
let tuple =
self.parse_tuple_expression(subject.expr, subject_start, Parenthesized::No, |p| {
p.parse_star_expression_or_higher(AllowNamedExpression::Yes)
p.parse_named_expression_or_higher(
ExpressionContext::default().with_starred_expression_allowed(
StarredExpressionPrecedence::BitwiseOr,
),
)
});
Expr::Tuple(tuple).into()
@@ -2470,7 +2524,7 @@ impl<'src> Parser<'src> {
// match x:
// case y if yield x: ...
Some(Box::new(
self.parse_named_expression_or_higher(AllowStarredExpression::No)
self.parse_named_expression_or_higher(ExpressionContext::default())
.expr,
))
} else {
@@ -2588,7 +2642,7 @@ impl<'src> Parser<'src> {
// @yield x
// @yield from x
// def foo(): ...
let parsed_expr = self.parse_named_expression_or_higher(AllowStarredExpression::No);
let parsed_expr = self.parse_named_expression_or_higher(ExpressionContext::default());
decorators.push(ast::Decorator {
expression: parsed_expr.expr,
@@ -2744,7 +2798,11 @@ impl<'src> Parser<'src> {
// def foo(*args: *int or str): ...
// def foo(*args: *yield x): ...
// # def foo(*args: **int): ...
self.parse_star_expression_or_higher(AllowNamedExpression::No)
self.parse_conditional_expression_or_higher(
ExpressionContext::default().with_starred_expression_allowed(
StarredExpressionPrecedence::BitwiseOr,
),
)
}
AllowStarAnnotation::No => {
// test_ok param_with_annotation
@@ -2757,7 +2815,7 @@ impl<'src> Parser<'src> {
// def foo(arg: *int): ...
// def foo(arg: yield int): ...
// def foo(arg: x := int): ...
self.parse_conditional_expression_or_higher(AllowStarredExpression::No)
self.parse_conditional_expression_or_higher(ExpressionContext::default())
}
};
Some(Box::new(parsed_expr.expr))
@@ -2810,7 +2868,7 @@ impl<'src> Parser<'src> {
// def foo(x=(*int)): ...
// def foo(x=yield y): ...
Some(Box::new(
self.parse_conditional_expression_or_higher(AllowStarredExpression::No)
self.parse_conditional_expression_or_higher(ExpressionContext::default())
.expr,
))
} else {
@@ -3177,7 +3235,7 @@ impl<'src> Parser<'src> {
// type X[T: yield from x] = int
// type X[T: x := int] = int
Some(Box::new(
self.parse_conditional_expression_or_higher(AllowStarredExpression::No)
self.parse_conditional_expression_or_higher(ExpressionContext::default())
.expr,
))
} else {

View File

@@ -5,7 +5,7 @@
//!
//! [CPython source]: https://github.com/python/cpython/blob/dfc2e065a2e71011017077e549cd2f9bf4944c54/Include/internal/pycore_token.h;
use ruff_python_ast::{AnyStringKind, BoolOp, Int, IpyEscapeKind, Operator, UnaryOp};
use ruff_python_ast::{AnyStringKind, BoolOp, CmpOp, Int, IpyEscapeKind, Operator, UnaryOp};
use std::fmt;
use crate::Mode;
@@ -699,47 +699,70 @@ impl TokenKind {
matches!(self, TokenKind::Match | TokenKind::Case)
}
/// Returns `true` if the current token is a comparison operator. This function requires the
/// next token operators containing two tokens such as `not in` and `is not`.
#[inline]
pub const fn is_compare_operator(&self) -> bool {
matches!(
self,
TokenKind::Not
| TokenKind::In
| TokenKind::Is
| TokenKind::EqEqual
| TokenKind::NotEqual
| TokenKind::Less
| TokenKind::LessEqual
| TokenKind::Greater
| TokenKind::GreaterEqual
)
pub const fn is_compare_operator(&self, next: TokenKind) -> bool {
self.as_compare_operator(next).is_some()
}
/// Returns the [`CmpOp`] that corresponds to this token kind, if it is a comparison operator,
/// otherwise return [None]. This function requires the next token operators containing two
/// tokens such as `not in` and `is not`.
#[inline]
pub const fn as_compare_operator(&self, next: TokenKind) -> Option<CmpOp> {
Some(match self {
TokenKind::In => CmpOp::In,
TokenKind::Not if matches!(next, TokenKind::In) => CmpOp::NotIn,
TokenKind::Is if matches!(next, TokenKind::Not) => CmpOp::IsNot,
TokenKind::Is => CmpOp::Is,
TokenKind::EqEqual => CmpOp::Eq,
TokenKind::NotEqual => CmpOp::NotEq,
TokenKind::Less => CmpOp::Lt,
TokenKind::LessEqual => CmpOp::LtE,
TokenKind::Greater => CmpOp::Gt,
TokenKind::GreaterEqual => CmpOp::GtE,
_ => return None,
})
}
/// Returns `true` if the current token is a boolean operator.
#[inline]
pub const fn is_bool_operator(&self) -> bool {
matches!(self, TokenKind::And | TokenKind::Or)
self.as_bool_operator().is_some()
}
/// Returns the [`BoolOp`] that corresponds to this token kind, if it is a boolean operator,
/// otherwise return [None].
#[inline]
pub const fn as_bool_operator(&self) -> Option<BoolOp> {
Some(match self {
TokenKind::And => BoolOp::And,
TokenKind::Or => BoolOp::Or,
_ => return None,
})
}
/// Returns the [`Operator`] that corresponds to this token kind, if it is
/// an augmented assignment operator, or [`None`] otherwise.
#[inline]
pub const fn as_augmented_assign_operator(&self) -> Option<Operator> {
match self {
TokenKind::PlusEqual => Some(Operator::Add),
TokenKind::MinusEqual => Some(Operator::Sub),
TokenKind::StarEqual => Some(Operator::Mult),
TokenKind::AtEqual => Some(Operator::MatMult),
TokenKind::DoubleStarEqual => Some(Operator::Pow),
TokenKind::SlashEqual => Some(Operator::Div),
TokenKind::DoubleSlashEqual => Some(Operator::FloorDiv),
TokenKind::PercentEqual => Some(Operator::Mod),
TokenKind::AmperEqual => Some(Operator::BitAnd),
TokenKind::VbarEqual => Some(Operator::BitOr),
TokenKind::CircumflexEqual => Some(Operator::BitXor),
TokenKind::LeftShiftEqual => Some(Operator::LShift),
TokenKind::RightShiftEqual => Some(Operator::RShift),
_ => None,
}
Some(match self {
TokenKind::PlusEqual => Operator::Add,
TokenKind::MinusEqual => Operator::Sub,
TokenKind::StarEqual => Operator::Mult,
TokenKind::AtEqual => Operator::MatMult,
TokenKind::DoubleStarEqual => Operator::Pow,
TokenKind::SlashEqual => Operator::Div,
TokenKind::DoubleSlashEqual => Operator::FloorDiv,
TokenKind::PercentEqual => Operator::Mod,
TokenKind::AmperEqual => Operator::BitAnd,
TokenKind::VbarEqual => Operator::BitOr,
TokenKind::CircumflexEqual => Operator::BitXor,
TokenKind::LeftShiftEqual => Operator::LShift,
TokenKind::RightShiftEqual => Operator::RShift,
_ => return None,
})
}
pub const fn from_token(token: &Tok) -> Self {
@@ -888,18 +911,6 @@ impl TryFrom<TokenKind> for Operator {
}
}
impl TryFrom<TokenKind> for BoolOp {
type Error = ();
fn try_from(value: TokenKind) -> Result<Self, Self::Error> {
Ok(match value {
TokenKind::And => BoolOp::And,
TokenKind::Or => BoolOp::Or,
_ => return Err(()),
})
}
}
impl TryFrom<&Tok> for UnaryOp {
type Error = String;
@@ -922,6 +933,16 @@ impl TryFrom<TokenKind> for UnaryOp {
}
}
impl From<BoolOp> for TokenKind {
#[inline]
fn from(op: BoolOp) -> Self {
match op {
BoolOp::And => TokenKind::And,
BoolOp::Or => TokenKind::Or,
}
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match self {

View File

@@ -346,24 +346,29 @@ Module(
ExprList {
range: 187..199,
elts: [
Starred(
ExprStarred {
range: 188..190,
value: Name(
ExprName {
range: 189..190,
id: "x",
ctx: Load,
Named(
ExprNamed {
range: 188..195,
target: Starred(
ExprStarred {
range: 188..190,
value: Name(
ExprName {
range: 189..190,
id: "x",
ctx: Store,
},
),
ctx: Store,
},
),
ctx: Load,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 194..195,
value: Int(
2,
value: NumberLiteral(
ExprNumberLiteral {
range: 194..195,
value: Int(
2,
),
},
),
},
),
@@ -458,5 +463,5 @@ Module(
8 | [*x if True else y, z]
9 | [*lambda x: x, z]
10 | [*x := 2, z]
| ^^ Syntax Error: Expected ',', found ':='
| ^^ Syntax Error: Assignment expression target must be an identifier
|

View File

@@ -84,30 +84,30 @@ Module(
),
Expr(
StmtExpr {
range: 81..84,
value: Starred(
ExprStarred {
range: 82..84,
value: Name(
ExprName {
range: 83..84,
id: "x",
ctx: Load,
range: 81..90,
value: Named(
ExprNamed {
range: 82..89,
target: Starred(
ExprStarred {
range: 82..84,
value: Name(
ExprName {
range: 83..84,
id: "x",
ctx: Store,
},
),
ctx: Store,
},
),
ctx: Load,
},
),
},
),
Expr(
StmtExpr {
range: 88..89,
value: NumberLiteral(
ExprNumberLiteral {
range: 88..89,
value: Int(
1,
value: NumberLiteral(
ExprNumberLiteral {
range: 88..89,
value: Int(
1,
),
},
),
},
),
@@ -198,34 +198,7 @@ Module(
3 | (x.y := 1)
4 | (x[y] := 1)
5 | (*x := 1)
| ^^ Syntax Error: Starred expression cannot be used here
6 | ([x, y] := [1, 2])
|
|
3 | (x.y := 1)
4 | (x[y] := 1)
5 | (*x := 1)
| ^^ Syntax Error: Expected ')', found ':='
6 | ([x, y] := [1, 2])
|
|
3 | (x.y := 1)
4 | (x[y] := 1)
5 | (*x := 1)
| ^ Syntax Error: Expected a statement
6 | ([x, y] := [1, 2])
|
|
3 | (x.y := 1)
4 | (x[y] := 1)
5 | (*x := 1)
| ^ Syntax Error: Expected a statement
| ^^ Syntax Error: Assignment expression target must be an identifier
6 | ([x, y] := [1, 2])
|

View File

@@ -491,34 +491,34 @@ Module(
),
Expr(
StmtExpr {
range: 323..326,
value: Starred(
ExprStarred {
range: 324..326,
value: Name(
ExprName {
range: 325..326,
id: "x",
ctx: Load,
},
),
ctx: Load,
},
),
},
),
Expr(
StmtExpr {
range: 330..343,
range: 323..344,
value: Tuple(
ExprTuple {
range: 330..343,
range: 323..344,
elts: [
NumberLiteral(
ExprNumberLiteral {
range: 330..331,
value: Int(
2,
Named(
ExprNamed {
range: 324..331,
target: Starred(
ExprStarred {
range: 324..326,
value: Name(
ExprName {
range: 325..326,
id: "x",
ctx: Store,
},
),
ctx: Store,
},
),
value: NumberLiteral(
ExprNumberLiteral {
range: 330..331,
value: Int(
2,
),
},
),
},
),
@@ -529,30 +529,35 @@ Module(
ctx: Load,
},
),
Starred(
ExprStarred {
range: 336..338,
value: Name(
ExprName {
range: 337..338,
id: "x",
ctx: Load,
Named(
ExprNamed {
range: 336..343,
target: Starred(
ExprStarred {
range: 336..338,
value: Name(
ExprName {
range: 337..338,
id: "x",
ctx: Store,
},
),
ctx: Store,
},
),
ctx: Load,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 342..343,
value: Int(
2,
value: NumberLiteral(
ExprNumberLiteral {
range: 342..343,
value: Int(
2,
),
},
),
},
),
],
ctx: Load,
parenthesized: false,
parenthesized: true,
},
),
},
@@ -1231,7 +1236,7 @@ Module(
8 | (*x if True else y, z, *x if True else y)
9 | (*lambda x: x, z, *lambda x: x)
10 | (*x := 2, z, *x := 2)
| ^^ Syntax Error: Starred expression cannot be used here
| ^^ Syntax Error: Assignment expression target must be an identifier
|
@@ -1239,34 +1244,7 @@ Module(
8 | (*x if True else y, z, *x if True else y)
9 | (*lambda x: x, z, *lambda x: x)
10 | (*x := 2, z, *x := 2)
| ^^ Syntax Error: Expected ')', found ':='
|
|
8 | (*x if True else y, z, *x if True else y)
9 | (*lambda x: x, z, *lambda x: x)
10 | (*x := 2, z, *x := 2)
| ^^ Syntax Error: Expected ',', found ':='
|
|
8 | (*x if True else y, z, *x if True else y)
9 | (*lambda x: x, z, *lambda x: x)
10 | (*x := 2, z, *x := 2)
| ^ Syntax Error: Expected a statement
|
|
8 | (*x if True else y, z, *x if True else y)
9 | (*lambda x: x, z, *lambda x: x)
10 | (*x := 2, z, *x := 2)
| ^ Syntax Error: Expected a statement
11 |
12 |
13 | # Non-parenthesized
| ^^ Syntax Error: Assignment expression target must be an identifier
|

View File

@@ -339,24 +339,29 @@ Module(
ExprSet {
range: 186..198,
elts: [
Starred(
ExprStarred {
range: 187..189,
value: Name(
ExprName {
range: 188..189,
id: "x",
ctx: Load,
Named(
ExprNamed {
range: 187..194,
target: Starred(
ExprStarred {
range: 187..189,
value: Name(
ExprName {
range: 188..189,
id: "x",
ctx: Store,
},
),
ctx: Store,
},
),
ctx: Load,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 193..194,
value: Int(
2,
value: NumberLiteral(
ExprNumberLiteral {
range: 193..194,
value: Int(
2,
),
},
),
},
),
@@ -450,5 +455,5 @@ Module(
8 | {*x if True else y, z}
9 | {*lambda x: x, z}
10 | {*x := 2, z}
| ^^ Syntax Error: Expected ',', found ':='
| ^^ Syntax Error: Assignment expression target must be an identifier
|

View File

@@ -1,89 +0,0 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/for_in_target_postfix_expr.py
---
## AST
```
Module(
ModModule {
range: 0..29,
body: [
For(
StmtFor {
range: 0..28,
is_async: false,
target: Call(
ExprCall {
range: 4..13,
func: Name(
ExprName {
range: 4..5,
id: "d",
ctx: Load,
},
),
arguments: Arguments {
range: 5..13,
args: [
Compare(
ExprCompare {
range: 6..12,
left: Name(
ExprName {
range: 6..7,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 11..12,
id: "y",
ctx: Load,
},
),
],
},
),
],
keywords: [],
},
},
),
iter: Name(
ExprName {
range: 17..23,
id: "target",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 25..28,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 25..28,
},
),
},
),
],
orelse: [],
},
),
],
},
)
```
## Errors
|
1 | for d(x in y) in target: ...
| ^^^^^^^^^ Syntax Error: Invalid assignment target
|

View File

@@ -7,7 +7,7 @@ input_file: crates/ruff_python_parser/resources/inline/err/for_stmt_invalid_targ
```
Module(
ModModule {
range: 0..132,
range: 0..154,
body: [
For(
StmtFor {
@@ -233,22 +233,79 @@ Module(
),
For(
StmtFor {
range: 100..131,
range: 100..121,
is_async: false,
target: Yield(
ExprYield {
range: 104..116,
value: Some(
Compare(
ExprCompare {
range: 110..116,
left: Name(
ExprName {
range: 110..111,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 115..116,
id: "y",
ctx: Load,
},
),
],
},
),
),
},
),
iter: Name(
ExprName {
range: 116..116,
id: "",
ctx: Invalid,
},
),
body: [
Expr(
StmtExpr {
range: 118..121,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 118..121,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 122..153,
is_async: false,
target: List(
ExprList {
range: 104..121,
range: 126..143,
elts: [
Name(
ExprName {
range: 105..106,
range: 127..128,
id: "x",
ctx: Store,
},
),
NumberLiteral(
ExprNumberLiteral {
range: 108..109,
range: 130..131,
value: Int(
1,
),
@@ -256,25 +313,25 @@ Module(
),
Name(
ExprName {
range: 111..112,
range: 133..134,
id: "y",
ctx: Store,
},
),
Starred(
ExprStarred {
range: 114..120,
range: 136..142,
value: List(
ExprList {
range: 115..120,
range: 137..142,
elts: [
StringLiteral(
ExprStringLiteral {
range: 116..119,
range: 138..141,
value: StringLiteralValue {
inner: Single(
StringLiteral {
range: 116..119,
range: 138..141,
value: "a",
flags: StringLiteralFlags {
quote_style: Double,
@@ -299,7 +356,7 @@ Module(
),
iter: Name(
ExprName {
range: 125..126,
range: 147..148,
id: "z",
ctx: Load,
},
@@ -307,10 +364,10 @@ Module(
body: [
Expr(
StmtExpr {
range: 128..131,
range: 150..153,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 128..131,
range: 150..153,
},
),
},
@@ -358,7 +415,7 @@ Module(
4 | for *x | y in z: ...
| ^^^^^ Syntax Error: Invalid assignment target
5 | for await x in z: ...
6 | for [x, 1, y, *["a"]] in z: ...
6 | for yield x in y: ...
|
@@ -367,21 +424,40 @@ Module(
4 | for *x | y in z: ...
5 | for await x in z: ...
| ^^^^^^^ Syntax Error: Invalid assignment target
6 | for [x, 1, y, *["a"]] in z: ...
6 | for yield x in y: ...
7 | for [x, 1, y, *["a"]] in z: ...
|
|
4 | for *x | y in z: ...
5 | for await x in z: ...
6 | for [x, 1, y, *["a"]] in z: ...
6 | for yield x in y: ...
| ^^^^^^^^^^^^ Syntax Error: Yield expression cannot be used here
7 | for [x, 1, y, *["a"]] in z: ...
|
|
4 | for *x | y in z: ...
5 | for await x in z: ...
6 | for yield x in y: ...
| ^ Syntax Error: Expected 'in', found ':'
7 | for [x, 1, y, *["a"]] in z: ...
|
|
5 | for await x in z: ...
6 | for yield x in y: ...
7 | for [x, 1, y, *["a"]] in z: ...
| ^ Syntax Error: Invalid assignment target
|
|
4 | for *x | y in z: ...
5 | for await x in z: ...
6 | for [x, 1, y, *["a"]] in z: ...
6 | for yield x in y: ...
7 | for [x, 1, y, *["a"]] in z: ...
| ^^^ Syntax Error: Invalid assignment target
|

View File

@@ -0,0 +1,341 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/for_stmt_invalid_target_binary_expr.py
---
## AST
```
Module(
ModModule {
range: 0..124,
body: [
For(
StmtFor {
range: 0..24,
is_async: false,
target: Compare(
ExprCompare {
range: 4..14,
left: Name(
ExprName {
range: 4..5,
id: "x",
ctx: Load,
},
),
ops: [
NotIn,
],
comparators: [
Name(
ExprName {
range: 13..14,
id: "y",
ctx: Load,
},
),
],
},
),
iter: Name(
ExprName {
range: 18..19,
id: "z",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 21..24,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 21..24,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 25..45,
is_async: false,
target: Compare(
ExprCompare {
range: 29..35,
left: Name(
ExprName {
range: 29..30,
id: "x",
ctx: Load,
},
),
ops: [
Eq,
],
comparators: [
Name(
ExprName {
range: 34..35,
id: "y",
ctx: Load,
},
),
],
},
),
iter: Name(
ExprName {
range: 39..40,
id: "z",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 42..45,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 42..45,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 46..66,
is_async: false,
target: BoolOp(
ExprBoolOp {
range: 50..56,
op: Or,
values: [
Name(
ExprName {
range: 50..51,
id: "x",
ctx: Load,
},
),
Name(
ExprName {
range: 55..56,
id: "y",
ctx: Load,
},
),
],
},
),
iter: Name(
ExprName {
range: 60..61,
id: "z",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 63..66,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 63..66,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 67..83,
is_async: false,
target: UnaryOp(
ExprUnaryOp {
range: 71..73,
op: USub,
operand: Name(
ExprName {
range: 72..73,
id: "x",
ctx: Store,
},
),
},
),
iter: Name(
ExprName {
range: 77..78,
id: "y",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 80..83,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 80..83,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 84..103,
is_async: false,
target: UnaryOp(
ExprUnaryOp {
range: 88..93,
op: Not,
operand: Name(
ExprName {
range: 92..93,
id: "x",
ctx: Store,
},
),
},
),
iter: Name(
ExprName {
range: 97..98,
id: "y",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 100..103,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 100..103,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 104..123,
is_async: false,
target: BinOp(
ExprBinOp {
range: 108..113,
left: Name(
ExprName {
range: 108..109,
id: "x",
ctx: Load,
},
),
op: BitOr,
right: Name(
ExprName {
range: 112..113,
id: "y",
ctx: Load,
},
),
},
),
iter: Name(
ExprName {
range: 117..118,
id: "z",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 120..123,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 120..123,
},
),
},
),
],
orelse: [],
},
),
],
},
)
```
## Errors
|
1 | for x not in y in z: ...
| ^^^^^^^^^^ Syntax Error: Invalid assignment target
2 | for x == y in z: ...
3 | for x or y in z: ...
|
|
1 | for x not in y in z: ...
2 | for x == y in z: ...
| ^^^^^^ Syntax Error: Invalid assignment target
3 | for x or y in z: ...
4 | for -x in y: ...
|
|
1 | for x not in y in z: ...
2 | for x == y in z: ...
3 | for x or y in z: ...
| ^^^^^^ Syntax Error: Invalid assignment target
4 | for -x in y: ...
5 | for not x in y: ...
|
|
2 | for x == y in z: ...
3 | for x or y in z: ...
4 | for -x in y: ...
| ^^ Syntax Error: Invalid assignment target
5 | for not x in y: ...
6 | for x | y in z: ...
|
|
3 | for x or y in z: ...
4 | for -x in y: ...
5 | for not x in y: ...
| ^^^^^ Syntax Error: Invalid assignment target
6 | for x | y in z: ...
|
|
4 | for -x in y: ...
5 | for not x in y: ...
6 | for x | y in z: ...
| ^^^^^ Syntax Error: Invalid assignment target
|

View File

@@ -1,27 +1,95 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/parenthesized_compare_expr_in_for.py
input_file: crates/ruff_python_parser/resources/inline/err/for_stmt_invalid_target_in_keyword.py
---
## AST
```
Module(
ModModule {
range: 0..141,
range: 0..170,
body: [
For(
StmtFor {
range: 0..27,
range: 0..28,
is_async: false,
target: Call(
ExprCall {
range: 4..14,
range: 4..13,
func: Name(
ExprName {
range: 4..5,
id: "d",
ctx: Load,
},
),
arguments: Arguments {
range: 5..13,
args: [
Compare(
ExprCompare {
range: 6..12,
left: Name(
ExprName {
range: 6..7,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 11..12,
id: "y",
ctx: Load,
},
),
],
},
),
],
keywords: [],
},
},
),
iter: Name(
ExprName {
range: 17..23,
id: "target",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 25..28,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 25..28,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 29..56,
is_async: false,
target: Call(
ExprCall {
range: 33..43,
func: Compare(
ExprCompare {
range: 5..11,
range: 34..40,
left: Name(
ExprName {
range: 5..6,
range: 34..35,
id: "x",
ctx: Load,
},
@@ -32,7 +100,7 @@ Module(
comparators: [
Name(
ExprName {
range: 10..11,
range: 39..40,
id: "y",
ctx: Load,
},
@@ -41,7 +109,7 @@ Module(
},
),
arguments: Arguments {
range: 12..14,
range: 41..43,
args: [],
keywords: [],
},
@@ -49,7 +117,7 @@ Module(
),
iter: Name(
ExprName {
range: 18..22,
range: 47..51,
id: "iter",
ctx: Load,
},
@@ -57,10 +125,10 @@ Module(
body: [
Expr(
StmtExpr {
range: 24..27,
range: 53..56,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 24..27,
range: 53..56,
},
),
},
@@ -71,14 +139,14 @@ Module(
),
For(
StmtFor {
range: 28..53,
range: 57..82,
is_async: false,
target: Compare(
ExprCompare {
range: 33..39,
range: 62..68,
left: Name(
ExprName {
range: 33..34,
range: 62..63,
id: "x",
ctx: Load,
},
@@ -89,7 +157,7 @@ Module(
comparators: [
Name(
ExprName {
range: 38..39,
range: 67..68,
id: "y",
ctx: Load,
},
@@ -97,72 +165,6 @@ Module(
],
},
),
iter: Name(
ExprName {
range: 44..48,
id: "iter",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 50..53,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 50..53,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 54..82,
is_async: false,
target: Tuple(
ExprTuple {
range: 58..69,
elts: [
Compare(
ExprCompare {
range: 59..65,
left: Name(
ExprName {
range: 59..60,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 64..65,
id: "y",
ctx: Load,
},
),
],
},
),
Name(
ExprName {
range: 67..68,
id: "z",
ctx: Store,
},
),
],
ctx: Store,
parenthesized: true,
},
),
iter: Name(
ExprName {
range: 73..77,
@@ -189,8 +191,8 @@ Module(
StmtFor {
range: 83..111,
is_async: false,
target: List(
ExprList {
target: Tuple(
ExprTuple {
range: 87..98,
elts: [
Compare(
@@ -226,6 +228,7 @@ Module(
),
],
ctx: Store,
parenthesized: true,
},
),
iter: Name(
@@ -254,8 +257,8 @@ Module(
StmtFor {
range: 112..140,
is_async: false,
target: Set(
ExprSet {
target: List(
ExprList {
range: 116..127,
elts: [
Compare(
@@ -286,10 +289,11 @@ Module(
ExprName {
range: 125..126,
id: "z",
ctx: Load,
ctx: Store,
},
),
],
ctx: Store,
},
),
iter: Name(
@@ -314,6 +318,70 @@ Module(
orelse: [],
},
),
For(
StmtFor {
range: 141..169,
is_async: false,
target: Set(
ExprSet {
range: 145..156,
elts: [
Compare(
ExprCompare {
range: 146..152,
left: Name(
ExprName {
range: 146..147,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 151..152,
id: "y",
ctx: Load,
},
),
],
},
),
Name(
ExprName {
range: 154..155,
id: "z",
ctx: Load,
},
),
],
},
),
iter: Name(
ExprName {
range: 160..164,
id: "iter",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 166..169,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 166..169,
},
),
},
),
],
orelse: [],
},
),
],
},
)
@@ -321,44 +389,54 @@ Module(
## Errors
|
1 | for (x in y)() in iter: ...
1 | for d(x in y) in target: ...
| ^^^^^^^^^ Syntax Error: Invalid assignment target
2 | for (x in y)() in iter: ...
3 | for (x in y) in iter: ...
|
|
1 | for d(x in y) in target: ...
2 | for (x in y)() in iter: ...
| ^^^^^^^^^^ Syntax Error: Invalid assignment target
2 | for (x in y) in iter: ...
3 | for (x in y, z) in iter: ...
3 | for (x in y) in iter: ...
4 | for (x in y, z) in iter: ...
|
|
1 | for (x in y)() in iter: ...
2 | for (x in y) in iter: ...
1 | for d(x in y) in target: ...
2 | for (x in y)() in iter: ...
3 | for (x in y) in iter: ...
| ^^^^^^ Syntax Error: Invalid assignment target
3 | for (x in y, z) in iter: ...
4 | for [x in y, z] in iter: ...
4 | for (x in y, z) in iter: ...
5 | for [x in y, z] in iter: ...
|
|
1 | for (x in y)() in iter: ...
2 | for (x in y) in iter: ...
3 | for (x in y, z) in iter: ...
2 | for (x in y)() in iter: ...
3 | for (x in y) in iter: ...
4 | for (x in y, z) in iter: ...
| ^^^^^^ Syntax Error: Invalid assignment target
4 | for [x in y, z] in iter: ...
5 | for {x in y, z} in iter: ...
5 | for [x in y, z] in iter: ...
6 | for {x in y, z} in iter: ...
|
|
2 | for (x in y) in iter: ...
3 | for (x in y, z) in iter: ...
4 | for [x in y, z] in iter: ...
3 | for (x in y) in iter: ...
4 | for (x in y, z) in iter: ...
5 | for [x in y, z] in iter: ...
| ^^^^^^ Syntax Error: Invalid assignment target
5 | for {x in y, z} in iter: ...
6 | for {x in y, z} in iter: ...
|
|
3 | for (x in y, z) in iter: ...
4 | for [x in y, z] in iter: ...
5 | for {x in y, z} in iter: ...
4 | for (x in y, z) in iter: ...
5 | for [x in y, z] in iter: ...
6 | for {x in y, z} in iter: ...
| ^^^^^^^^^^^ Syntax Error: Invalid assignment target
|

View File

@@ -1,78 +0,0 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/for_in_target_postfix_expr.py
---
## AST
```
Module(
ModModule {
range: 0..29,
body: [
For(
StmtFor {
range: 0..28,
is_async: false,
target: Subscript(
ExprSubscript {
range: 4..13,
value: Name(
ExprName {
range: 4..5,
id: "d",
ctx: Load,
},
),
slice: Compare(
ExprCompare {
range: 6..12,
left: Name(
ExprName {
range: 6..7,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 11..12,
id: "y",
ctx: Load,
},
),
],
},
),
ctx: Store,
},
),
iter: Name(
ExprName {
range: 17..23,
id: "target",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 25..28,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 25..28,
},
),
},
),
],
orelse: [],
},
),
],
},
)
```

View File

@@ -1,13 +1,13 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/parenthesized_compare_expr_in_for.py
input_file: crates/ruff_python_parser/resources/inline/ok/for_in_target_valid_expr.py
---
## AST
```
Module(
ModModule {
range: 0..60,
range: 0..89,
body: [
For(
StmtFor {
@@ -15,13 +15,20 @@ Module(
is_async: false,
target: Subscript(
ExprSubscript {
range: 4..15,
value: Compare(
range: 4..13,
value: Name(
ExprName {
range: 4..5,
id: "d",
ctx: Load,
},
),
slice: Compare(
ExprCompare {
range: 5..11,
range: 6..12,
left: Name(
ExprName {
range: 5..6,
range: 6..7,
id: "x",
ctx: Load,
},
@@ -32,7 +39,7 @@ Module(
comparators: [
Name(
ExprName {
range: 10..11,
range: 11..12,
id: "y",
ctx: Load,
},
@@ -40,21 +47,13 @@ Module(
],
},
),
slice: NumberLiteral(
ExprNumberLiteral {
range: 13..14,
value: Int(
0,
),
},
),
ctx: Store,
},
),
iter: Name(
ExprName {
range: 19..23,
id: "iter",
range: 17..23,
id: "target",
ctx: Load,
},
),
@@ -75,11 +74,11 @@ Module(
),
For(
StmtFor {
range: 29..59,
range: 29..57,
is_async: false,
target: Attribute(
ExprAttribute {
range: 33..46,
target: Subscript(
ExprSubscript {
range: 33..44,
value: Compare(
ExprCompare {
range: 34..40,
@@ -104,16 +103,20 @@ Module(
],
},
),
attr: Identifier {
id: "attr",
range: 42..46,
},
slice: NumberLiteral(
ExprNumberLiteral {
range: 42..43,
value: Int(
0,
),
},
),
ctx: Store,
},
),
iter: Name(
ExprName {
range: 50..54,
range: 48..52,
id: "iter",
ctx: Load,
},
@@ -121,10 +124,70 @@ Module(
body: [
Expr(
StmtExpr {
range: 56..59,
range: 54..57,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 56..59,
range: 54..57,
},
),
},
),
],
orelse: [],
},
),
For(
StmtFor {
range: 58..88,
is_async: false,
target: Attribute(
ExprAttribute {
range: 62..75,
value: Compare(
ExprCompare {
range: 63..69,
left: Name(
ExprName {
range: 63..64,
id: "x",
ctx: Load,
},
),
ops: [
In,
],
comparators: [
Name(
ExprName {
range: 68..69,
id: "y",
ctx: Load,
},
),
],
},
),
attr: Identifier {
id: "attr",
range: 71..75,
},
ctx: Store,
},
),
iter: Name(
ExprName {
range: 79..83,
id: "iter",
ctx: Load,
},
),
body: [
Expr(
StmtExpr {
range: 85..88,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 85..88,
},
),
},