rustpython_ruff_python_parser/parser/expression.rs
1use std::ops::Deref;
2
3use bitflags::bitflags;
4use rustc_hash::{FxBuildHasher, FxHashSet};
5
6use ruff_python_ast::name::Name;
7use ruff_python_ast::token::TokenKind;
8use ruff_python_ast::{
9 self as ast, AnyStringFlags, AtomicNodeIndex, BoolOp, CmpOp, ConversionFlag, Expr, ExprContext,
10 FString, InterpolatedStringElement, InterpolatedStringElements, IpyEscapeKind, Number,
11 Operator, OperatorPrecedence, StringFlags, TString, UnaryOp,
12};
13use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
14
15use crate::error::{FStringKind, StarTupleKind, UnparenthesizedNamedExprKind};
16use crate::parser::progress::ParserProgress;
17use crate::parser::{FunctionKind, Parser, helpers};
18use crate::string::{
19 InterpolatedStringKind, StringType, parse_interpolated_string_literal_element,
20 parse_string_literal,
21};
22use crate::token::TokenValue;
23use crate::token_set::TokenSet;
24use crate::{
25 InterpolatedStringErrorType, Mode, ParseErrorType, UnsupportedSyntaxError,
26 UnsupportedSyntaxErrorKind,
27};
28
29use super::{InterpolatedStringElementsKind, Parenthesized, RecoveryContextKind};
30
31/// A token set consisting of a newline or end of file.
32const NEWLINE_EOF_SET: TokenSet = TokenSet::new([TokenKind::Newline, TokenKind::EndOfFile]);
33
34/// Tokens that represents a literal expression.
35const LITERAL_SET: TokenSet = TokenSet::new([
36 TokenKind::Int,
37 TokenKind::Float,
38 TokenKind::Complex,
39 TokenKind::String,
40 TokenKind::Ellipsis,
41 TokenKind::True,
42 TokenKind::False,
43 TokenKind::None,
44]);
45
46/// Tokens that represents either an expression or the start of one.
47pub(super) const EXPR_SET: TokenSet = TokenSet::new([
48 TokenKind::Name,
49 TokenKind::Minus,
50 TokenKind::Plus,
51 TokenKind::Tilde,
52 TokenKind::Star,
53 TokenKind::DoubleStar,
54 TokenKind::Lpar,
55 TokenKind::Lbrace,
56 TokenKind::Lsqb,
57 TokenKind::Lambda,
58 TokenKind::Await,
59 TokenKind::Not,
60 TokenKind::Yield,
61 TokenKind::FStringStart,
62 TokenKind::TStringStart,
63 TokenKind::IpyEscapeCommand,
64])
65.union(LITERAL_SET);
66
67/// Tokens that can appear after an expression.
68pub(super) const END_EXPR_SET: TokenSet = TokenSet::new([
69 // Ex) `expr` (without a newline)
70 TokenKind::EndOfFile,
71 // Ex) `expr`
72 TokenKind::Newline,
73 // Ex) `expr;`
74 TokenKind::Semi,
75 // Ex) `data[expr:]`
76 // Ex) `def foo() -> expr:`
77 // Ex) `{expr: expr}`
78 TokenKind::Colon,
79 // Ex) `{expr}`
80 TokenKind::Rbrace,
81 // Ex) `[expr]`
82 TokenKind::Rsqb,
83 // Ex) `(expr)`
84 TokenKind::Rpar,
85 // Ex) `expr,`
86 TokenKind::Comma,
87 // Ex)
88 //
89 // if True:
90 // expr
91 // # <- Dedent
92 // x
93 TokenKind::Dedent,
94 // Ex) `expr if expr else expr`
95 TokenKind::If,
96 TokenKind::Else,
97 // Ex) `with expr as target:`
98 // Ex) `except expr as NAME:`
99 TokenKind::As,
100 // Ex) `raise expr from expr`
101 TokenKind::From,
102 // Ex) `[expr for expr in iter]`
103 TokenKind::For,
104 // Ex) `[expr async for expr in iter]`
105 TokenKind::Async,
106 // Ex) `expr in expr`
107 TokenKind::In,
108 // Ex) `name: expr = expr`
109 // Ex) `f"{expr=}"`
110 TokenKind::Equal,
111 // Ex) `f"{expr!s}"`
112 TokenKind::Exclamation,
113]);
114
115/// Tokens that can appear at the end of a sequence.
116const END_SEQUENCE_SET: TokenSet = END_EXPR_SET.remove(TokenKind::Comma);
117
118impl<'src> Parser<'src> {
119 /// Returns `true` if the parser is at a name or keyword (including soft keyword) token.
120 pub(super) fn at_name_or_keyword(&self) -> bool {
121 self.at(TokenKind::Name) || self.current_token_kind().is_keyword()
122 }
123
124 /// Returns `true` if the parser is at a name or soft keyword token.
125 pub(super) fn at_name_or_soft_keyword(&self) -> bool {
126 self.at(TokenKind::Name) || self.at_soft_keyword()
127 }
128
129 /// Returns `true` if the parser is at a soft keyword token.
130 pub(super) fn at_soft_keyword(&self) -> bool {
131 self.current_token_kind().is_soft_keyword()
132 }
133
134 /// Returns `true` if the current token is the start of an expression.
135 pub(super) fn at_expr(&self) -> bool {
136 self.at_ts(EXPR_SET) || self.at_soft_keyword()
137 }
138
139 /// Returns `true` if the current token ends a sequence.
140 pub(super) fn at_sequence_end(&self) -> bool {
141 self.at_ts(END_SEQUENCE_SET)
142 }
143
144 /// Parses every Python expression.
145 ///
146 /// Matches the `expressions` rule in the [Python grammar]. The [`ExpressionContext`] can be
147 /// used to match the `star_expressions` rule.
148 ///
149 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
150 pub(super) fn parse_expression_list(&mut self, context: ExpressionContext) -> ParsedExpr {
151 let start = self.node_start();
152 let parsed_expr = self.parse_conditional_expression_or_higher_impl(context);
153
154 if self.at(TokenKind::Comma) {
155 Expr::Tuple(self.parse_tuple_expression(
156 parsed_expr.expr,
157 start,
158 Parenthesized::No,
159 |p| p.parse_conditional_expression_or_higher_impl(context),
160 ))
161 .into()
162 } else {
163 parsed_expr
164 }
165 }
166
167 /// Parses every Python expression except unparenthesized tuple.
168 ///
169 /// Matches the `named_expression` rule in the [Python grammar]. The [`ExpressionContext`] can
170 /// be used to match the `star_named_expression` rule.
171 ///
172 /// NOTE: If you have expressions separated by commas and want to parse them individually
173 /// instead of as a tuple, as done by [`Parser::parse_expression_list`], use this function.
174 ///
175 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
176 pub(super) fn parse_named_expression_or_higher(
177 &mut self,
178 context: ExpressionContext,
179 ) -> ParsedExpr {
180 let start = self.node_start();
181 let parsed_expr = self.parse_conditional_expression_or_higher_impl(context);
182
183 if self.at(TokenKind::ColonEqual) {
184 Expr::Named(self.parse_named_expression(parsed_expr.expr, start)).into()
185 } else {
186 parsed_expr
187 }
188 }
189
190 /// Parses every Python expression except unparenthesized tuple and named expressions.
191 ///
192 /// Matches the `expression` rule in the [Python grammar].
193 ///
194 /// This uses the default [`ExpressionContext`]. Use
195 /// [`Parser::parse_conditional_expression_or_higher_impl`] if you prefer to pass in the
196 /// context.
197 ///
198 /// NOTE: If you have expressions separated by commas and want to parse them individually
199 /// instead of as a tuple, as done by [`Parser::parse_expression_list`] use this function.
200 ///
201 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
202 pub(super) fn parse_conditional_expression_or_higher(&mut self) -> ParsedExpr {
203 self.parse_conditional_expression_or_higher_impl(ExpressionContext::default())
204 }
205
206 pub(super) fn parse_conditional_expression_or_higher_impl(
207 &mut self,
208 context: ExpressionContext,
209 ) -> ParsedExpr {
210 if self.at(TokenKind::Lambda) {
211 Expr::Lambda(self.parse_lambda_expr()).into()
212 } else {
213 let start = self.node_start();
214 let parsed_expr = self.parse_simple_expression(context);
215
216 if self.at(TokenKind::If) {
217 Expr::If(self.parse_if_expression(parsed_expr.expr, start)).into()
218 } else {
219 parsed_expr
220 }
221 }
222 }
223
224 /// Parses every Python expression except unparenthesized tuples, named expressions,
225 /// and `if` expression.
226 ///
227 /// This is a combination of the `disjunction`, `starred_expression`, `yield_expr`
228 /// and `lambdef` rules of the [Python grammar].
229 ///
230 /// Note that this function parses lambda expression but reports an error as they're not
231 /// allowed in this context. This is done for better error recovery.
232 /// Use [`Parser::parse_conditional_expression_or_higher`] or any methods which calls into the
233 /// specified method to allow parsing lambda expression.
234 ///
235 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
236 fn parse_simple_expression(&mut self, context: ExpressionContext) -> ParsedExpr {
237 self.parse_binary_expression_or_higher(OperatorPrecedence::None, context)
238 }
239
240 /// Parses a binary expression using the [Pratt parsing algorithm].
241 ///
242 /// [Pratt parsing algorithm]: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
243 fn parse_binary_expression_or_higher(
244 &mut self,
245 left_precedence: OperatorPrecedence,
246 context: ExpressionContext,
247 ) -> ParsedExpr {
248 let start = self.node_start();
249 let lhs = self.parse_lhs_expression(left_precedence, context);
250 self.parse_binary_expression_or_higher_recursive(lhs, left_precedence, context, start)
251 }
252
253 pub(super) fn parse_binary_expression_or_higher_recursive(
254 &mut self,
255 mut left: ParsedExpr,
256 left_precedence: OperatorPrecedence,
257 context: ExpressionContext,
258 start: TextSize,
259 ) -> ParsedExpr {
260 let mut progress = ParserProgress::default();
261
262 loop {
263 progress.assert_progressing(self);
264
265 let current_token = self.current_token_kind();
266
267 if matches!(current_token, TokenKind::In) && context.is_in_excluded() {
268 // Omit the `in` keyword when parsing the target expression in a comprehension or
269 // a `for` statement.
270 break;
271 }
272
273 let Some(operator) = BinaryLikeOperator::try_from_tokens(current_token, self.peek())
274 else {
275 // Not an operator.
276 break;
277 };
278
279 let new_precedence = operator.precedence();
280
281 let stop_at_current_operator = if new_precedence.is_right_associative() {
282 new_precedence < left_precedence
283 } else {
284 new_precedence <= left_precedence
285 };
286
287 if stop_at_current_operator {
288 break;
289 }
290
291 left.expr = match operator {
292 BinaryLikeOperator::Boolean(bool_op) => {
293 Expr::BoolOp(self.parse_boolean_expression(left.expr, start, bool_op, context))
294 }
295 BinaryLikeOperator::Comparison(cmp_op) => Expr::Compare(
296 self.parse_comparison_expression(left.expr, start, cmp_op, context),
297 ),
298 BinaryLikeOperator::Binary(bin_op) => {
299 self.bump(TokenKind::from(bin_op));
300
301 let right = self.parse_binary_expression_or_higher(new_precedence, context);
302
303 Expr::BinOp(ast::ExprBinOp {
304 left: Box::new(left.expr),
305 op: bin_op,
306 right: Box::new(right.expr),
307 range: self.node_range(start),
308 node_index: AtomicNodeIndex::NONE,
309 })
310 }
311 };
312 }
313
314 left
315 }
316
317 /// Parses the left-hand side of an expression.
318 ///
319 /// This includes prefix expressions such as unary operators, boolean `not`,
320 /// `await`, `lambda`. It also parses atoms and postfix expressions.
321 ///
322 /// The given [`OperatorPrecedence`] is used to determine if the parsed expression
323 /// is valid in that context. For example, a unary operator is not valid
324 /// in an `await` expression in which case the `left_precedence` would
325 /// be [`OperatorPrecedence::Await`].
326 fn parse_lhs_expression(
327 &mut self,
328 left_precedence: OperatorPrecedence,
329 context: ExpressionContext,
330 ) -> ParsedExpr {
331 let start = self.node_start();
332 let token = self.current_token_kind();
333
334 if let Some(unary_op) = token.as_unary_operator() {
335 let expr = self.parse_unary_expression(unary_op, context);
336
337 if matches!(unary_op, UnaryOp::Not) {
338 if left_precedence > OperatorPrecedence::Not {
339 self.add_error(
340 ParseErrorType::OtherError(
341 "Boolean 'not' expression cannot be used here".to_string(),
342 ),
343 &expr,
344 );
345 }
346 } else {
347 if left_precedence > OperatorPrecedence::PosNegBitNot
348 // > The power operator `**` binds less tightly than an arithmetic
349 // > or bitwise unary operator on its right, that is, 2**-1 is 0.5.
350 //
351 // Reference: https://docs.python.org/3/reference/expressions.html#id21
352 && left_precedence != OperatorPrecedence::Exponent
353 {
354 self.add_error(
355 ParseErrorType::OtherError(format!(
356 "Unary '{unary_op}' expression cannot be used here",
357 )),
358 &expr,
359 );
360 }
361 }
362
363 return Expr::UnaryOp(expr).into();
364 }
365
366 match self.current_token_kind() {
367 TokenKind::Star => {
368 let starred_expr = self.parse_starred_expression(context);
369
370 if left_precedence > OperatorPrecedence::None
371 || !context.is_starred_expression_allowed()
372 {
373 self.add_error(ParseErrorType::InvalidStarredExpressionUsage, &starred_expr);
374 }
375
376 return Expr::Starred(starred_expr).into();
377 }
378 TokenKind::Await => {
379 let await_expr = self.parse_await_expression();
380
381 // `await` expressions cannot be nested
382 if left_precedence >= OperatorPrecedence::Await {
383 self.add_error(
384 ParseErrorType::OtherError(
385 "Await expression cannot be used here".to_string(),
386 ),
387 &await_expr,
388 );
389 }
390
391 return Expr::Await(await_expr).into();
392 }
393 TokenKind::Lambda => {
394 // Lambda expression isn't allowed in this context but we'll still parse it and
395 // report an error for better recovery.
396 let lambda_expr = self.parse_lambda_expr();
397 self.add_error(ParseErrorType::InvalidLambdaExpressionUsage, &lambda_expr);
398 return Expr::Lambda(lambda_expr).into();
399 }
400 TokenKind::Yield => {
401 let expr = self.parse_yield_expression();
402
403 if left_precedence > OperatorPrecedence::None
404 || !context.is_yield_expression_allowed()
405 {
406 self.add_error(ParseErrorType::InvalidYieldExpressionUsage, &expr);
407 }
408
409 return expr.into();
410 }
411 _ => {}
412 }
413
414 let lhs = self.parse_atom();
415
416 ParsedExpr {
417 expr: self.parse_postfix_expression(lhs.expr, start),
418 is_parenthesized: lhs.is_parenthesized,
419 }
420 }
421
422 /// Parses an expression with a minimum precedence of bitwise `or`.
423 ///
424 /// This methods actually parses the expression using the `expression` rule
425 /// of the [Python grammar] and then validates the parsed expression. In a
426 /// sense, it matches the `bitwise_or` rule of the [Python grammar].
427 ///
428 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
429 fn parse_expression_with_bitwise_or_precedence(&mut self) -> ParsedExpr {
430 let parsed_expr = self.parse_conditional_expression_or_higher();
431
432 if parsed_expr.is_parenthesized {
433 // Parentheses resets the precedence, so we don't need to validate it.
434 return parsed_expr;
435 }
436
437 let expr_name = match parsed_expr.expr {
438 Expr::Compare(_) => "Comparison",
439 Expr::BoolOp(_)
440 | Expr::UnaryOp(ast::ExprUnaryOp {
441 op: ast::UnaryOp::Not,
442 ..
443 }) => "Boolean",
444 Expr::If(_) => "Conditional",
445 Expr::Lambda(_) => "Lambda",
446 _ => return parsed_expr,
447 };
448
449 self.add_error(
450 ParseErrorType::OtherError(format!("{expr_name} expression cannot be used here")),
451 &parsed_expr,
452 );
453
454 parsed_expr
455 }
456
457 /// Parses a name.
458 ///
459 /// For an invalid name, the `id` field will be an empty string and the `ctx`
460 /// field will be [`ExprContext::Invalid`].
461 ///
462 /// See: <https://docs.python.org/3/reference/expressions.html#atom-identifiers>
463 pub(super) fn parse_name(&mut self) -> ast::ExprName {
464 let identifier = self.parse_identifier();
465
466 let ctx = if identifier.is_valid() {
467 ExprContext::Load
468 } else {
469 ExprContext::Invalid
470 };
471
472 ast::ExprName {
473 range: identifier.range,
474 id: identifier.id,
475 ctx,
476 node_index: AtomicNodeIndex::NONE,
477 }
478 }
479
480 pub(super) fn parse_missing_name(&mut self) -> ast::ExprName {
481 let identifier = self.parse_missing_identifier();
482
483 ast::ExprName {
484 range: identifier.range,
485 id: identifier.id,
486 ctx: ExprContext::Invalid,
487 node_index: AtomicNodeIndex::NONE,
488 }
489 }
490
491 /// Parses an identifier.
492 ///
493 /// For an invalid identifier, the `id` field will be an empty string.
494 ///
495 /// See: <https://docs.python.org/3/reference/expressions.html#atom-identifiers>
496 pub(super) fn parse_identifier(&mut self) -> ast::Identifier {
497 let range = self.current_token_range();
498
499 if self.at(TokenKind::Name) {
500 let TokenValue::Name(name) = self.bump_value(TokenKind::Name) else {
501 unreachable!();
502 };
503 return ast::Identifier {
504 id: name,
505 range,
506 node_index: AtomicNodeIndex::NONE,
507 };
508 }
509
510 if self.current_token_kind().is_soft_keyword() {
511 let id = Name::new(self.src_text(range));
512 self.bump_soft_keyword_as_name();
513 return ast::Identifier {
514 id,
515 range,
516 node_index: AtomicNodeIndex::NONE,
517 };
518 }
519
520 if self.current_token_kind().is_keyword() {
521 // Non-soft keyword
522 self.add_error(
523 ParseErrorType::OtherError(format!(
524 "Expected an identifier, but found a keyword {} that cannot be used here",
525 self.current_token_kind()
526 )),
527 range,
528 );
529
530 let id = Name::new(self.src_text(range));
531 self.bump_any();
532 ast::Identifier {
533 id,
534 range,
535 node_index: AtomicNodeIndex::NONE,
536 }
537 } else {
538 self.parse_missing_identifier()
539 }
540 }
541
542 fn parse_missing_identifier(&mut self) -> ast::Identifier {
543 self.add_error(
544 ParseErrorType::OtherError("Expected an identifier".into()),
545 self.current_token_range(),
546 );
547
548 ast::Identifier {
549 id: Name::empty(),
550 range: self.missing_node_range(),
551 node_index: AtomicNodeIndex::NONE,
552 }
553 }
554
555 /// Parses an atom.
556 ///
557 /// See: <https://docs.python.org/3/reference/expressions.html#atoms>
558 fn parse_atom(&mut self) -> ParsedExpr {
559 let start = self.node_start();
560
561 let lhs = match self.current_token_kind() {
562 TokenKind::Float => {
563 let TokenValue::Float(value) = self.bump_value(TokenKind::Float) else {
564 unreachable!()
565 };
566
567 Expr::NumberLiteral(ast::ExprNumberLiteral {
568 value: Number::Float(value),
569 range: self.node_range(start),
570 node_index: AtomicNodeIndex::NONE,
571 })
572 }
573 TokenKind::Complex => {
574 let TokenValue::Complex { real, imag } = self.bump_value(TokenKind::Complex) else {
575 unreachable!()
576 };
577 Expr::NumberLiteral(ast::ExprNumberLiteral {
578 value: Number::Complex { real, imag },
579 range: self.node_range(start),
580 node_index: AtomicNodeIndex::NONE,
581 })
582 }
583 TokenKind::Int => {
584 let TokenValue::Int(value) = self.bump_value(TokenKind::Int) else {
585 unreachable!()
586 };
587 Expr::NumberLiteral(ast::ExprNumberLiteral {
588 value: Number::Int(value),
589 range: self.node_range(start),
590 node_index: AtomicNodeIndex::NONE,
591 })
592 }
593 TokenKind::True => {
594 self.bump(TokenKind::True);
595 Expr::BooleanLiteral(ast::ExprBooleanLiteral {
596 value: true,
597 range: self.node_range(start),
598 node_index: AtomicNodeIndex::NONE,
599 })
600 }
601 TokenKind::False => {
602 self.bump(TokenKind::False);
603 Expr::BooleanLiteral(ast::ExprBooleanLiteral {
604 value: false,
605 range: self.node_range(start),
606 node_index: AtomicNodeIndex::NONE,
607 })
608 }
609 TokenKind::None => {
610 self.bump(TokenKind::None);
611 Expr::NoneLiteral(ast::ExprNoneLiteral {
612 range: self.node_range(start),
613 node_index: AtomicNodeIndex::NONE,
614 })
615 }
616 TokenKind::Ellipsis => {
617 self.bump(TokenKind::Ellipsis);
618 Expr::EllipsisLiteral(ast::ExprEllipsisLiteral {
619 range: self.node_range(start),
620 node_index: AtomicNodeIndex::NONE,
621 })
622 }
623 TokenKind::Name => Expr::Name(self.parse_name()),
624 TokenKind::IpyEscapeCommand => {
625 Expr::IpyEscapeCommand(self.parse_ipython_escape_command_expression())
626 }
627 TokenKind::String | TokenKind::FStringStart | TokenKind::TStringStart => {
628 self.parse_strings()
629 }
630 TokenKind::Lpar => {
631 return self.parse_parenthesized_expression();
632 }
633 TokenKind::Lsqb => self.parse_list_like_expression(),
634 TokenKind::Lbrace => self.parse_set_or_dict_like_expression(),
635
636 kind => {
637 if kind.is_keyword() {
638 Expr::Name(self.parse_name())
639 } else {
640 self.add_error(
641 ParseErrorType::ExpectedExpression,
642 self.current_token_range(),
643 );
644 Expr::Name(ast::ExprName {
645 range: self.missing_node_range(),
646 id: Name::empty(),
647 ctx: ExprContext::Invalid,
648 node_index: AtomicNodeIndex::NONE,
649 })
650 }
651 }
652 };
653
654 lhs.into()
655 }
656
657 /// Parses a postfix expression in a loop until there are no postfix expressions left to parse.
658 ///
659 /// For a given left-hand side, a postfix expression can begin with either `(` for a call
660 /// expression, `[` for a subscript expression, or `.` for an attribute expression.
661 ///
662 /// This method does nothing if the current token is not a candidate for a postfix expression.
663 pub(super) fn parse_postfix_expression(&mut self, mut lhs: Expr, start: TextSize) -> Expr {
664 loop {
665 lhs = match self.current_token_kind() {
666 TokenKind::Lpar => Expr::Call(self.parse_call_expression(lhs, start)),
667 TokenKind::Lsqb => Expr::Subscript(self.parse_subscript_expression(lhs, start)),
668 TokenKind::Dot => Expr::Attribute(self.parse_attribute_expression(lhs, start)),
669 _ => break lhs,
670 };
671 }
672 }
673
674 /// Parse a call expression.
675 ///
676 /// The function name is parsed by the caller and passed as `func` along with
677 /// the `start` position of the call expression.
678 ///
679 /// # Panics
680 ///
681 /// If the parser isn't position at a `(` token.
682 ///
683 /// See: <https://docs.python.org/3/reference/expressions.html#calls>
684 pub(super) fn parse_call_expression(&mut self, func: Expr, start: TextSize) -> ast::ExprCall {
685 let arguments = self.parse_arguments();
686
687 ast::ExprCall {
688 func: Box::new(func),
689 arguments,
690 range: self.node_range(start),
691 node_index: AtomicNodeIndex::NONE,
692 }
693 }
694
695 /// Parses an argument list.
696 ///
697 /// # Panics
698 ///
699 /// If the parser isn't positioned at a `(` token.
700 ///
701 /// See: <https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-argument_list>
702 pub(super) fn parse_arguments(&mut self) -> ast::Arguments {
703 let start = self.node_start();
704 self.bump(TokenKind::Lpar);
705
706 let mut args = vec![];
707 let mut keywords = vec![];
708 let mut seen_keyword_argument = false; // foo = 1
709 let mut seen_keyword_unpacking = false; // **foo
710
711 let has_trailing_comma =
712 self.parse_comma_separated_list(RecoveryContextKind::Arguments, |parser| {
713 let argument_start = parser.node_start();
714 if parser.eat(TokenKind::DoubleStar) {
715 let value = parser.parse_conditional_expression_or_higher();
716
717 keywords.push(ast::Keyword {
718 arg: None,
719 value: value.expr,
720 range: parser.node_range(argument_start),
721 node_index: AtomicNodeIndex::NONE,
722 });
723
724 seen_keyword_unpacking = true;
725 } else {
726 let start = parser.node_start();
727 let mut parsed_expr = parser
728 .parse_named_expression_or_higher(ExpressionContext::starred_conditional());
729
730 match parser.current_token_kind() {
731 TokenKind::Async | TokenKind::For => {
732 if parsed_expr.is_unparenthesized_starred_expr() {
733 parser.add_error(
734 ParseErrorType::IterableUnpackingInComprehension,
735 &parsed_expr,
736 );
737 }
738
739 parsed_expr = Expr::Generator(parser.parse_generator_expression(
740 parsed_expr.expr,
741 start,
742 Parenthesized::No,
743 ))
744 .into();
745 }
746 _ => {
747 if seen_keyword_unpacking
748 && parsed_expr.is_unparenthesized_starred_expr()
749 {
750 parser.add_error(
751 ParseErrorType::InvalidArgumentUnpackingOrder,
752 &parsed_expr,
753 );
754 }
755 }
756 }
757
758 let arg_range = parser.node_range(start);
759 if parser.eat(TokenKind::Equal) {
760 seen_keyword_argument = true;
761 let arg = if let ParsedExpr {
762 expr: Expr::Name(ident_expr),
763 is_parenthesized,
764 } = parsed_expr
765 {
766 // test_ok parenthesized_kwarg_py37
767 // # parse_options: {"target-version": "3.7"}
768 // f((a)=1)
769
770 // test_err parenthesized_kwarg_py38
771 // # parse_options: {"target-version": "3.8"}
772 // f((a)=1)
773 // f((a) = 1)
774 // f( ( a ) = 1)
775
776 if is_parenthesized {
777 parser.add_unsupported_syntax_error(
778 UnsupportedSyntaxErrorKind::ParenthesizedKeywordArgumentName,
779 arg_range,
780 );
781 }
782
783 ast::Identifier {
784 id: ident_expr.id,
785 range: ident_expr.range,
786 node_index: AtomicNodeIndex::NONE,
787 }
788 } else {
789 // TODO(dhruvmanila): Parser shouldn't drop the `parsed_expr` if it's
790 // not a name expression. We could add the expression into `args` but
791 // that means the error is a missing comma instead.
792 parser.add_error(
793 ParseErrorType::OtherError("Expected a parameter name".to_string()),
794 &parsed_expr,
795 );
796 ast::Identifier {
797 id: Name::empty(),
798 range: parsed_expr.range(),
799 node_index: AtomicNodeIndex::NONE,
800 }
801 };
802
803 let value = parser.parse_conditional_expression_or_higher();
804
805 keywords.push(ast::Keyword {
806 arg: Some(arg),
807 value: value.expr,
808 range: parser.node_range(argument_start),
809 node_index: AtomicNodeIndex::NONE,
810 });
811 } else {
812 if !parsed_expr.is_unparenthesized_starred_expr() {
813 if seen_keyword_unpacking {
814 parser.add_error(
815 ParseErrorType::PositionalAfterKeywordUnpacking,
816 &parsed_expr,
817 );
818 } else if seen_keyword_argument {
819 parser.add_error(
820 ParseErrorType::PositionalAfterKeywordArgument,
821 &parsed_expr,
822 );
823 }
824 }
825 args.push(parsed_expr.expr);
826 }
827 }
828 });
829
830 self.expect(TokenKind::Rpar);
831
832 let arguments = ast::Arguments {
833 range: self.node_range(start),
834 node_index: AtomicNodeIndex::NONE,
835 args: args.into_boxed_slice(),
836 keywords: keywords.into_boxed_slice(),
837 };
838
839 self.validate_arguments(&arguments, has_trailing_comma);
840
841 arguments
842 }
843
844 /// Parses a subscript expression.
845 ///
846 /// # Panics
847 ///
848 /// If the parser isn't positioned at a `[` token.
849 ///
850 /// See: <https://docs.python.org/3/reference/expressions.html#subscriptions>
851 fn parse_subscript_expression(
852 &mut self,
853 mut value: Expr,
854 start: TextSize,
855 ) -> ast::ExprSubscript {
856 self.bump(TokenKind::Lsqb);
857
858 // To prevent the `value` context from being `Del` within a `del` statement,
859 // we set the context as `Load` here.
860 helpers::set_expr_ctx(&mut value, ExprContext::Load);
861
862 // Slice range doesn't include the `[` token.
863 let slice_start = self.node_start();
864
865 // Create an error when receiving an empty slice to parse, e.g. `x[]`
866 if self.eat(TokenKind::Rsqb) {
867 let slice_range = self.node_range(slice_start);
868 self.add_error(ParseErrorType::EmptySlice, slice_range);
869
870 return ast::ExprSubscript {
871 value: Box::new(value),
872 slice: Box::new(Expr::Name(ast::ExprName {
873 range: slice_range,
874 id: Name::empty(),
875 ctx: ExprContext::Invalid,
876 node_index: AtomicNodeIndex::NONE,
877 })),
878 ctx: ExprContext::Load,
879 range: self.node_range(start),
880 node_index: AtomicNodeIndex::NONE,
881 };
882 }
883
884 let mut slice = self.parse_slice();
885
886 // If there are more than one element in the slice, we need to create a tuple
887 // expression to represent it.
888 if self.eat(TokenKind::Comma) {
889 let mut slices = vec![slice];
890
891 self.parse_comma_separated_list(RecoveryContextKind::Slices, |parser| {
892 slices.push(parser.parse_slice());
893 });
894
895 slice = Expr::Tuple(ast::ExprTuple {
896 elts: slices,
897 ctx: ExprContext::Load,
898 range: self.node_range(slice_start),
899 parenthesized: false,
900 node_index: AtomicNodeIndex::NONE,
901 });
902 } else if slice.is_starred_expr() {
903 // If the only slice element is a starred expression, that is represented
904 // using a tuple expression with a single element. This is the second case
905 // in the `slices` rule in the Python grammar.
906 slice = Expr::Tuple(ast::ExprTuple {
907 elts: vec![slice],
908 ctx: ExprContext::Load,
909 range: self.node_range(slice_start),
910 parenthesized: false,
911 node_index: AtomicNodeIndex::NONE,
912 });
913 }
914
915 self.expect(TokenKind::Rsqb);
916
917 // test_ok star_index_py311
918 // # parse_options: {"target-version": "3.11"}
919 // lst[*index] # simple index
920 // class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
921 // lst[a, *b, c] # different positions
922 // lst[a, b, *c] # different positions
923 // lst[*a, *b] # multiple unpacks
924 // array[3:5, *idxs] # mixed with slices
925
926 // test_err star_index_py310
927 // # parse_options: {"target-version": "3.10"}
928 // lst[*index] # simple index
929 // class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
930 // lst[a, *b, c] # different positions
931 // lst[a, b, *c] # different positions
932 // lst[*a, *b] # multiple unpacks
933 // array[3:5, *idxs] # mixed with slices
934
935 // test_err star_slices
936 // array[*start:*end]
937
938 // test_ok parenthesized_star_index_py310
939 // # parse_options: {"target-version": "3.10"}
940 // out[(*(slice(None) for _ in range(2)), *ind)] = 1
941 if let Expr::Tuple(ast::ExprTuple {
942 elts,
943 parenthesized: false,
944 ..
945 }) = &slice
946 {
947 for elt in elts.iter().filter(|elt| elt.is_starred_expr()) {
948 self.add_unsupported_syntax_error(
949 UnsupportedSyntaxErrorKind::StarExpressionInIndex,
950 elt.range(),
951 );
952 }
953 }
954
955 ast::ExprSubscript {
956 value: Box::new(value),
957 slice: Box::new(slice),
958 ctx: ExprContext::Load,
959 range: self.node_range(start),
960 node_index: AtomicNodeIndex::NONE,
961 }
962 }
963
964 /// Parses a slice expression.
965 ///
966 /// See: <https://docs.python.org/3/reference/expressions.html#slicings>
967 fn parse_slice(&mut self) -> Expr {
968 const UPPER_END_SET: TokenSet =
969 TokenSet::new([TokenKind::Comma, TokenKind::Colon, TokenKind::Rsqb])
970 .union(NEWLINE_EOF_SET);
971 const STEP_END_SET: TokenSet =
972 TokenSet::new([TokenKind::Comma, TokenKind::Rsqb]).union(NEWLINE_EOF_SET);
973
974 // test_err named_expr_slice
975 // # even after 3.9, an unparenthesized named expression is not allowed in a slice
976 // lst[x:=1:-1]
977 // lst[1:x:=1]
978 // lst[1:3:x:=1]
979
980 // test_err named_expr_slice_parse_error
981 // # parse_options: {"target-version": "3.8"}
982 // # before 3.9, only emit the parse error, not the unsupported syntax error
983 // lst[x:=1:-1]
984
985 let start = self.node_start();
986
987 let lower = if self.at_expr() {
988 let lower =
989 self.parse_named_expression_or_higher(ExpressionContext::starred_conditional());
990
991 // This means we're in a subscript.
992 if self.at_ts(NEWLINE_EOF_SET.union([TokenKind::Rsqb, TokenKind::Comma].into())) {
993 // test_ok parenthesized_named_expr_index_py38
994 // # parse_options: {"target-version": "3.8"}
995 // lst[(x:=1)]
996
997 // test_ok unparenthesized_named_expr_index_py39
998 // # parse_options: {"target-version": "3.9"}
999 // lst[x:=1]
1000
1001 // test_err unparenthesized_named_expr_index_py38
1002 // # parse_options: {"target-version": "3.8"}
1003 // lst[x:=1]
1004 if lower.is_unparenthesized_named_expr() {
1005 self.add_unsupported_syntax_error(
1006 UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
1007 UnparenthesizedNamedExprKind::SequenceIndex,
1008 ),
1009 lower.range(),
1010 );
1011 }
1012 return lower.expr;
1013 }
1014
1015 // Now we know we're in a slice.
1016 if !lower.is_parenthesized {
1017 match lower.expr {
1018 Expr::Starred(_) => {
1019 self.add_error(ParseErrorType::InvalidStarredExpressionUsage, &lower);
1020 }
1021 Expr::Named(_) => {
1022 self.add_error(ParseErrorType::UnparenthesizedNamedExpression, &lower);
1023 }
1024 _ => {}
1025 }
1026 }
1027
1028 Some(lower.expr)
1029 } else {
1030 None
1031 };
1032
1033 self.expect(TokenKind::Colon);
1034
1035 let lower = lower.map(Box::new);
1036 let upper = if self.at_ts(UPPER_END_SET) {
1037 None
1038 } else {
1039 Some(Box::new(self.parse_conditional_expression_or_higher().expr))
1040 };
1041
1042 let step = if self.eat(TokenKind::Colon) {
1043 if self.at_ts(STEP_END_SET) {
1044 None
1045 } else {
1046 Some(Box::new(self.parse_conditional_expression_or_higher().expr))
1047 }
1048 } else {
1049 None
1050 };
1051
1052 Expr::Slice(ast::ExprSlice {
1053 range: self.node_range(start),
1054 node_index: AtomicNodeIndex::NONE,
1055 lower,
1056 upper,
1057 step,
1058 })
1059 }
1060
1061 /// Parses a unary expression.
1062 ///
1063 /// This includes the unary arithmetic `+` and `-`, bitwise `~`, and the
1064 /// boolean `not` operators.
1065 ///
1066 /// # Panics
1067 ///
1068 /// If the parser isn't positioned at any of the unary operators.
1069 ///
1070 /// See: <https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations>
1071 pub(super) fn parse_unary_expression(
1072 &mut self,
1073 op: UnaryOp,
1074 context: ExpressionContext,
1075 ) -> ast::ExprUnaryOp {
1076 let start = self.node_start();
1077 self.bump(TokenKind::from(op));
1078
1079 let operand = self.parse_binary_expression_or_higher(OperatorPrecedence::from(op), context);
1080
1081 ast::ExprUnaryOp {
1082 op,
1083 operand: Box::new(operand.expr),
1084 range: self.node_range(start),
1085 node_index: AtomicNodeIndex::NONE,
1086 }
1087 }
1088
1089 /// Parses an attribute expression.
1090 ///
1091 /// # Panics
1092 ///
1093 /// If the parser isn't positioned at a `.` token.
1094 ///
1095 /// See: <https://docs.python.org/3/reference/expressions.html#attribute-references>
1096 pub(super) fn parse_attribute_expression(
1097 &mut self,
1098 value: Expr,
1099 start: TextSize,
1100 ) -> ast::ExprAttribute {
1101 self.bump(TokenKind::Dot);
1102
1103 let attr = self.parse_identifier();
1104
1105 ast::ExprAttribute {
1106 value: Box::new(value),
1107 attr,
1108 ctx: ExprContext::Load,
1109 range: self.node_range(start),
1110 node_index: AtomicNodeIndex::NONE,
1111 }
1112 }
1113
1114 /// Parses a boolean operation expression.
1115 ///
1116 /// Note that the boolean `not` operator is parsed as a unary expression and
1117 /// not as a boolean expression.
1118 ///
1119 /// # Panics
1120 ///
1121 /// If the parser isn't positioned at a `or` or `and` token.
1122 ///
1123 /// See: <https://docs.python.org/3/reference/expressions.html#boolean-operations>
1124 fn parse_boolean_expression(
1125 &mut self,
1126 lhs: Expr,
1127 start: TextSize,
1128 op: BoolOp,
1129 context: ExpressionContext,
1130 ) -> ast::ExprBoolOp {
1131 self.bump(TokenKind::from(op));
1132
1133 let mut values = vec![lhs];
1134 let mut progress = ParserProgress::default();
1135
1136 // Keep adding the expression to `values` until we see a different
1137 // token than `operator_token`.
1138 loop {
1139 progress.assert_progressing(self);
1140
1141 let parsed_expr =
1142 self.parse_binary_expression_or_higher(OperatorPrecedence::from(op), context);
1143 values.push(parsed_expr.expr);
1144
1145 if !self.eat(TokenKind::from(op)) {
1146 break;
1147 }
1148 }
1149
1150 ast::ExprBoolOp {
1151 values,
1152 op,
1153 range: self.node_range(start),
1154 node_index: AtomicNodeIndex::NONE,
1155 }
1156 }
1157
1158 /// Bump the appropriate token(s) for the given comparison operator.
1159 fn bump_cmp_op(&mut self, op: CmpOp) {
1160 let (first, second) = match op {
1161 CmpOp::Eq => (TokenKind::EqEqual, None),
1162 CmpOp::NotEq => (TokenKind::NotEqual, None),
1163 CmpOp::Lt => (TokenKind::Less, None),
1164 CmpOp::LtE => (TokenKind::LessEqual, None),
1165 CmpOp::Gt => (TokenKind::Greater, None),
1166 CmpOp::GtE => (TokenKind::GreaterEqual, None),
1167 CmpOp::Is => (TokenKind::Is, None),
1168 CmpOp::IsNot => (TokenKind::Is, Some(TokenKind::Not)),
1169 CmpOp::In => (TokenKind::In, None),
1170 CmpOp::NotIn => (TokenKind::Not, Some(TokenKind::In)),
1171 };
1172
1173 self.bump(first);
1174 if let Some(second) = second {
1175 self.bump(second);
1176 }
1177 }
1178
1179 /// Parse a comparison expression.
1180 ///
1181 /// This includes the following operators:
1182 /// - Value comparisons: `==`, `!=`, `<`, `<=`, `>`, and `>=`.
1183 /// - Membership tests: `in` and `not in`.
1184 /// - Identity tests: `is` and `is not`.
1185 ///
1186 /// # Panics
1187 ///
1188 /// If the parser isn't positioned at any of the comparison operators.
1189 ///
1190 /// See: <https://docs.python.org/3/reference/expressions.html#comparisons>
1191 fn parse_comparison_expression(
1192 &mut self,
1193 lhs: Expr,
1194 start: TextSize,
1195 op: CmpOp,
1196 context: ExpressionContext,
1197 ) -> ast::ExprCompare {
1198 self.bump_cmp_op(op);
1199
1200 let mut comparators = vec![];
1201 let mut operators = vec![op];
1202
1203 let mut progress = ParserProgress::default();
1204
1205 loop {
1206 progress.assert_progressing(self);
1207
1208 comparators.push(
1209 self.parse_binary_expression_or_higher(
1210 OperatorPrecedence::ComparisonsMembershipIdentity,
1211 context,
1212 )
1213 .expr,
1214 );
1215
1216 let next_token = self.current_token_kind();
1217 if matches!(next_token, TokenKind::In) && context.is_in_excluded() {
1218 break;
1219 }
1220
1221 let Some(next_op) = helpers::token_kind_to_cmp_op([next_token, self.peek()]) else {
1222 break;
1223 };
1224
1225 self.bump_cmp_op(next_op);
1226 operators.push(next_op);
1227 }
1228
1229 ast::ExprCompare {
1230 left: Box::new(lhs),
1231 ops: operators.into_boxed_slice(),
1232 comparators: comparators.into_boxed_slice(),
1233 range: self.node_range(start),
1234 node_index: AtomicNodeIndex::NONE,
1235 }
1236 }
1237
1238 /// Parses all kinds of strings and implicitly concatenated strings.
1239 ///
1240 /// # Panics
1241 ///
1242 /// If the parser isn't positioned at a `String`, `FStringStart`, or `TStringStart` token.
1243 ///
1244 /// See: <https://docs.python.org/3/reference/grammar.html> (Search "strings:")
1245 pub(super) fn parse_strings(&mut self) -> Expr {
1246 const STRING_START_SET: TokenSet = TokenSet::new([
1247 TokenKind::String,
1248 TokenKind::FStringStart,
1249 TokenKind::TStringStart,
1250 ]);
1251
1252 let start = self.node_start();
1253 let mut strings = vec![];
1254
1255 let mut progress = ParserProgress::default();
1256
1257 while self.at_ts(STRING_START_SET) {
1258 progress.assert_progressing(self);
1259
1260 if self.at(TokenKind::String) {
1261 strings.push(self.parse_string_or_byte_literal());
1262 } else if self.at(TokenKind::FStringStart) {
1263 strings.push(StringType::FString(
1264 self.parse_interpolated_string(InterpolatedStringKind::FString)
1265 .into(),
1266 ));
1267 } else if self.at(TokenKind::TStringStart) {
1268 // test_ok template_strings_py314
1269 // # parse_options: {"target-version": "3.14"}
1270 // t"{hey}"
1271 // t'{there}'
1272 // t"""what's
1273 // happening?"""
1274
1275 // test_err template_strings_py313
1276 // # parse_options: {"target-version": "3.13"}
1277 // t"{hey}"
1278 // t'{there}'
1279 // t"""what's
1280 // happening?"""
1281 let string_type = StringType::TString(
1282 self.parse_interpolated_string(InterpolatedStringKind::TString)
1283 .into(),
1284 );
1285 self.add_unsupported_syntax_error(
1286 UnsupportedSyntaxErrorKind::TemplateStrings,
1287 string_type.range(),
1288 );
1289 strings.push(string_type);
1290 }
1291 }
1292
1293 let range = self.node_range(start);
1294
1295 match strings.len() {
1296 // This is not possible as the function was called by matching against a
1297 // `String`, `FStringStart`, or `TStringStart` token.
1298 0 => unreachable!("Expected to parse at least one string"),
1299 // We need a owned value, hence the `pop` here.
1300 1 => match strings.pop().unwrap() {
1301 StringType::Str(string) => Expr::StringLiteral(ast::ExprStringLiteral {
1302 value: ast::StringLiteralValue::single(string),
1303 range,
1304 node_index: AtomicNodeIndex::NONE,
1305 }),
1306 StringType::Bytes(bytes) => Expr::BytesLiteral(ast::ExprBytesLiteral {
1307 value: ast::BytesLiteralValue::single(bytes),
1308 range,
1309 node_index: AtomicNodeIndex::NONE,
1310 }),
1311 StringType::FString(fstring) => Expr::FString(ast::ExprFString {
1312 value: ast::FStringValue::single(fstring),
1313 range,
1314 node_index: AtomicNodeIndex::NONE,
1315 }),
1316 StringType::TString(tstring) => Expr::TString(ast::ExprTString {
1317 value: ast::TStringValue::single(tstring),
1318 range,
1319 node_index: AtomicNodeIndex::NONE,
1320 }),
1321 },
1322 _ => self.handle_implicitly_concatenated_strings(strings, range),
1323 }
1324 }
1325
1326 /// Handles implicitly concatenated strings.
1327 ///
1328 /// # Panics
1329 ///
1330 /// If the length of `strings` is less than 2.
1331 fn handle_implicitly_concatenated_strings(
1332 &mut self,
1333 strings: Vec<StringType>,
1334 range: TextRange,
1335 ) -> Expr {
1336 assert!(strings.len() > 1);
1337
1338 let mut has_fstring = false;
1339 let mut byte_literal_count = 0;
1340 let mut tstring_count = 0;
1341 for string in &strings {
1342 match string {
1343 StringType::FString(_) => has_fstring = true,
1344 StringType::TString(_) => tstring_count += 1,
1345 StringType::Bytes(_) => byte_literal_count += 1,
1346 StringType::Str(_) => {}
1347 }
1348 }
1349 let has_bytes = byte_literal_count > 0;
1350 let has_tstring = tstring_count > 0;
1351
1352 if has_bytes {
1353 if byte_literal_count < strings.len() {
1354 // TODO(dhruvmanila): This is not an ideal recovery because the parser
1355 // replaces the byte literals with an invalid string literal node. Any
1356 // downstream tools can extract the raw bytes from the range.
1357 //
1358 // We could convert the node into a string and mark it as invalid
1359 // and would be clever to mark the type which is fewer in quantity.
1360
1361 // test_err mixed_bytes_and_non_bytes_literals
1362 // 'first' b'second'
1363 // f'first' b'second'
1364 // 'first' f'second' b'third'
1365 self.add_error(
1366 ParseErrorType::OtherError(
1367 "Bytes literal cannot be mixed with non-bytes literals".to_string(),
1368 ),
1369 range,
1370 );
1371 }
1372 // Only construct a byte expression if all the literals are bytes
1373 // otherwise, we'll try either string, t-string, or f-string. This is to retain
1374 // as much information as possible.
1375 else {
1376 let mut values = Vec::with_capacity(strings.len());
1377 for string in strings {
1378 values.push(match string {
1379 StringType::Bytes(value) => value,
1380 _ => unreachable!("Expected `StringType::Bytes`"),
1381 });
1382 }
1383 return Expr::from(ast::ExprBytesLiteral {
1384 value: ast::BytesLiteralValue::concatenated(values),
1385 range,
1386 node_index: AtomicNodeIndex::NONE,
1387 });
1388 }
1389 }
1390
1391 if has_tstring {
1392 if tstring_count < strings.len() {
1393 self.add_error(
1394 ParseErrorType::OtherError(
1395 "cannot mix t-string literals with string or bytes literals".to_string(),
1396 ),
1397 range,
1398 );
1399 }
1400 // Only construct a t-string expression if all the literals are t-strings
1401 // otherwise, we'll try either string or f-string. This is to retain
1402 // as much information as possible.
1403 else {
1404 let mut values = Vec::with_capacity(strings.len());
1405 for string in strings {
1406 values.push(match string {
1407 StringType::TString(value) => value,
1408 _ => unreachable!("Expected `StringType::TString`"),
1409 });
1410 }
1411 return Expr::from(ast::ExprTString {
1412 value: ast::TStringValue::concatenated(values),
1413 range,
1414 node_index: AtomicNodeIndex::NONE,
1415 });
1416 }
1417 }
1418
1419 // TODO(dhruvmanila): Parser drops unterminated strings here as well
1420 // because the lexer doesn't emit them.
1421
1422 // test_err implicitly_concatenated_unterminated_string
1423 // 'hello' 'world
1424 // 1 + 1
1425 // 'hello' f'world {x}
1426 // 2 + 2
1427
1428 // test_err implicitly_concatenated_unterminated_string_multiline
1429 // (
1430 // 'hello'
1431 // f'world {x}
1432 // )
1433 // 1 + 1
1434 // (
1435 // 'first'
1436 // 'second
1437 // f'third'
1438 // )
1439 // 2 + 2
1440
1441 if !has_fstring && !has_tstring {
1442 let mut values = Vec::with_capacity(strings.len());
1443 for string in strings {
1444 values.push(match string {
1445 StringType::Str(value) => value,
1446 _ => ast::StringLiteral::invalid(string.range()),
1447 });
1448 }
1449 return Expr::from(ast::ExprStringLiteral {
1450 value: ast::StringLiteralValue::concatenated(values),
1451 range,
1452 node_index: AtomicNodeIndex::NONE,
1453 });
1454 }
1455
1456 let mut parts = Vec::with_capacity(strings.len());
1457 for string in strings {
1458 match string {
1459 StringType::FString(fstring) => parts.push(ast::FStringPart::FString(fstring)),
1460 StringType::Str(string) => parts.push(ast::FStringPart::Literal(string)),
1461 // Bytes and Template strings are invalid at this point
1462 // and stored as invalid string literal parts in the
1463 // f-string
1464 StringType::TString(tstring) => parts.push(ast::FStringPart::Literal(
1465 ast::StringLiteral::invalid(tstring.range()),
1466 )),
1467 StringType::Bytes(bytes) => parts.push(ast::FStringPart::Literal(
1468 ast::StringLiteral::invalid(bytes.range()),
1469 )),
1470 }
1471 }
1472
1473 Expr::from(ast::ExprFString {
1474 value: ast::FStringValue::concatenated(parts),
1475 range,
1476 node_index: AtomicNodeIndex::NONE,
1477 })
1478 }
1479
1480 /// Parses a single string or byte literal.
1481 ///
1482 /// This does not handle implicitly concatenated strings.
1483 ///
1484 /// # Panics
1485 ///
1486 /// If the parser isn't positioned at a `String` token.
1487 ///
1488 /// See: <https://docs.python.org/3.13/reference/lexical_analysis.html#string-and-bytes-literals>
1489 fn parse_string_or_byte_literal(&mut self) -> StringType {
1490 let range = self.current_token_range();
1491 let flags = self.tokens.current_flags().as_any_string_flags();
1492
1493 let TokenValue::String(value) = self.bump_value(TokenKind::String) else {
1494 unreachable!()
1495 };
1496
1497 match parse_string_literal(value, flags, range) {
1498 Ok(string) => string,
1499 Err(error) => {
1500 let location = error.location();
1501 self.add_error(ParseErrorType::Lexical(error.into_error()), location);
1502
1503 if flags.is_byte_string() {
1504 // test_err invalid_byte_literal
1505 // b'123a𝐁c'
1506 // rb"a𝐁c123"
1507 // b"""123a𝐁c"""
1508 StringType::Bytes(ast::BytesLiteral {
1509 value: Box::new([]),
1510 range,
1511 flags: ast::BytesLiteralFlags::from(flags).with_invalid(),
1512 node_index: AtomicNodeIndex::NONE,
1513 })
1514 } else {
1515 // test_err invalid_string_literal
1516 // 'hello \N{INVALID} world'
1517 // """hello \N{INVALID} world"""
1518 StringType::Str(ast::StringLiteral {
1519 value: "".into(),
1520 range,
1521 flags: ast::StringLiteralFlags::from(flags).with_invalid(),
1522 node_index: AtomicNodeIndex::NONE,
1523 })
1524 }
1525 }
1526 }
1527 }
1528
1529 /// Parses an f/t-string.
1530 ///
1531 /// This does not handle implicitly concatenated strings.
1532 ///
1533 /// # Panics
1534 ///
1535 /// If the parser isn't positioned at an `FStringStart` or
1536 /// `TStringStart` token.
1537 ///
1538 /// See: <https://docs.python.org/3/reference/grammar.html> (Search "fstring:" or "tstring:")
1539 /// See: <https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals>
1540 fn parse_interpolated_string(
1541 &mut self,
1542 kind: InterpolatedStringKind,
1543 ) -> InterpolatedStringData {
1544 let start = self.node_start();
1545 let mut flags = self.tokens.current_flags().as_any_string_flags();
1546
1547 self.bump(kind.start_token());
1548 let elements = self.parse_interpolated_string_elements(
1549 flags,
1550 InterpolatedStringElementsKind::Regular(kind),
1551 kind,
1552 );
1553
1554 if !self.expect(kind.end_token()) {
1555 flags = flags.with_unclosed(true);
1556 }
1557
1558 InterpolatedStringData {
1559 elements,
1560 range: self.node_range(start),
1561 flags,
1562 }
1563 }
1564
1565 /// Check `range` for comment tokens and report an `UnsupportedSyntaxError` for each one found.
1566 fn check_fstring_comments(&mut self, range: TextRange) {
1567 self.unsupported_syntax_errors
1568 .extend(self.tokens.in_range(range).iter().filter_map(|token| {
1569 token.kind().is_comment().then_some(UnsupportedSyntaxError {
1570 kind: UnsupportedSyntaxErrorKind::Pep701FString(FStringKind::Comment),
1571 range: token.range(),
1572 target_version: self.options.target_version,
1573 })
1574 }));
1575 }
1576
1577 /// Parses a list of f/t-string elements.
1578 ///
1579 /// # Panics
1580 ///
1581 /// If the parser isn't positioned at a `{`, `FStringMiddle`,
1582 /// or `TStringMiddle` token.
1583 fn parse_interpolated_string_elements(
1584 &mut self,
1585 flags: ast::AnyStringFlags,
1586 elements_kind: InterpolatedStringElementsKind,
1587 string_kind: InterpolatedStringKind,
1588 ) -> ast::InterpolatedStringElements {
1589 let mut elements = vec![];
1590 let middle_token_kind = string_kind.middle_token();
1591
1592 self.parse_list(
1593 RecoveryContextKind::InterpolatedStringElements(elements_kind),
1594 |parser| {
1595 let element = match parser.current_token_kind() {
1596 TokenKind::Lbrace => ast::InterpolatedStringElement::from(
1597 parser.parse_interpolated_element(flags, string_kind),
1598 ),
1599 tok if tok == middle_token_kind => {
1600 let range = parser.current_token_range();
1601 let TokenValue::InterpolatedStringMiddle(value) =
1602 parser.bump_value(middle_token_kind)
1603 else {
1604 unreachable!()
1605 };
1606 InterpolatedStringElement::Literal(
1607 parse_interpolated_string_literal_element(value, flags, range)
1608 .unwrap_or_else(|lex_error| {
1609 // test_err invalid_fstring_literal_element
1610 // f'hello \N{INVALID} world'
1611 // f"""hello \N{INVALID} world"""
1612 let location = lex_error.location();
1613 parser.add_error(
1614 ParseErrorType::Lexical(lex_error.into_error()),
1615 location,
1616 );
1617 ast::InterpolatedStringLiteralElement {
1618 value: "".into(),
1619 range,
1620 node_index: AtomicNodeIndex::NONE,
1621 }
1622 }),
1623 )
1624 }
1625 // `Invalid` tokens are created when there's a lexical error, so
1626 // we ignore it here to avoid creating unexpected token errors
1627 TokenKind::Unknown => {
1628 parser.bump_any();
1629 return;
1630 }
1631 tok => {
1632 // This should never happen because the list parsing will only
1633 // call this closure for the above token kinds which are the same
1634 // as in the FIRST set.
1635 unreachable!(
1636 "{}: unexpected token `{tok:?}` at {:?}",
1637 string_kind,
1638 parser.current_token_range()
1639 );
1640 }
1641 };
1642 elements.push(element);
1643 },
1644 );
1645
1646 ast::InterpolatedStringElements::from(elements)
1647 }
1648
1649 /// Parses an f/t-string expression element.
1650 ///
1651 /// # Panics
1652 ///
1653 /// If the parser isn't positioned at a `{` token.
1654 fn parse_interpolated_element(
1655 &mut self,
1656 flags: ast::AnyStringFlags,
1657 string_kind: InterpolatedStringKind,
1658 ) -> ast::InterpolatedElement {
1659 let start = self.node_start();
1660 self.bump(TokenKind::Lbrace);
1661
1662 self.tokens
1663 .re_lex_string_token_in_interpolation_element(string_kind);
1664
1665 // test_err f_string_empty_expression
1666 // f"{}"
1667 // f"{ }"
1668
1669 // test_err t_string_empty_expression
1670 // # parse_options: {"target-version": "3.14"}
1671 // t"{}"
1672 // t"{ }"
1673
1674 // test_err f_string_invalid_starred_expr
1675 // # Starred expression inside f-string has a minimum precedence of bitwise or.
1676 // f"{*}"
1677 // f"{*x and y}"
1678 // f"{*yield x}"
1679
1680 // test_err t_string_invalid_starred_expr
1681 // # parse_options: {"target-version": "3.14"}
1682 // # Starred expression inside t-string has a minimum precedence of bitwise or.
1683 // t"{*}"
1684 // t"{*x and y}"
1685 // t"{*yield x}"
1686
1687 let value = self.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or());
1688
1689 if !value.is_parenthesized && value.expr.is_lambda_expr() {
1690 // TODO(dhruvmanila): This requires making some changes in lambda expression
1691 // parsing logic to handle the emitted `FStringMiddle` token in case the
1692 // lambda expression is not parenthesized.
1693
1694 // test_err f_string_lambda_without_parentheses
1695 // f"{lambda x: x}"
1696
1697 // test_err t_string_lambda_without_parentheses
1698 // # parse_options: {"target-version": "3.14"}
1699 // t"{lambda x: x}"
1700 self.add_error(
1701 ParseErrorType::from_interpolated_string_error(
1702 InterpolatedStringErrorType::LambdaWithoutParentheses,
1703 string_kind,
1704 ),
1705 value.range(),
1706 );
1707 }
1708 let debug_text = if self.eat(TokenKind::Equal) {
1709 let leading_range = TextRange::new(start + "{".text_len(), value.start());
1710 let trailing_range = TextRange::new(value.end(), self.current_token_range().start());
1711 Some(ast::DebugText {
1712 leading: self.src_text(leading_range).to_string(),
1713 trailing: self.src_text(trailing_range).to_string(),
1714 })
1715 } else {
1716 None
1717 };
1718
1719 let conversion = if self.eat(TokenKind::Exclamation) {
1720 // Ensure that the `r` is lexed as a `r` name token instead of a raw string
1721 // in `f{abc!r"` (note the missing `}`).
1722 self.tokens.re_lex_raw_string_in_format_spec();
1723
1724 let conversion_flag_range = self.current_token_range();
1725 if self.at(TokenKind::Name) {
1726 // test_err f_string_conversion_follows_exclamation
1727 // f"{x! s}"
1728 // t"{x! s}"
1729 // f"{x! z}"
1730 if self.prev_token_end != conversion_flag_range.start() {
1731 self.add_error(
1732 ParseErrorType::from_interpolated_string_error(
1733 InterpolatedStringErrorType::ConversionFlagNotImmediatelyAfterExclamation,
1734 string_kind,
1735 ),
1736 TextRange::new(self.prev_token_end, conversion_flag_range.start()),
1737 );
1738 }
1739 let TokenValue::Name(name) = self.bump_value(TokenKind::Name) else {
1740 unreachable!();
1741 };
1742 match &*name {
1743 "s" => ConversionFlag::Str,
1744 "r" => ConversionFlag::Repr,
1745 "a" => ConversionFlag::Ascii,
1746 _ => {
1747 // test_err f_string_invalid_conversion_flag_name_tok
1748 // f"{x!z}"
1749
1750 // test_err t_string_invalid_conversion_flag_name_tok
1751 // # parse_options: {"target-version": "3.14"}
1752 // t"{x!z}"
1753 self.add_error(
1754 ParseErrorType::from_interpolated_string_error(
1755 InterpolatedStringErrorType::InvalidConversionFlag,
1756 string_kind,
1757 ),
1758 conversion_flag_range,
1759 );
1760 ConversionFlag::None
1761 }
1762 }
1763 } else {
1764 // test_err f_string_invalid_conversion_flag_other_tok
1765 // f"{x!123}"
1766 // f"{x!'a'}"
1767
1768 // test_err t_string_invalid_conversion_flag_other_tok
1769 // # parse_options: {"target-version": "3.14"}
1770 // t"{x!123}"
1771 // t"{x!'a'}"
1772 self.add_error(
1773 ParseErrorType::from_interpolated_string_error(
1774 InterpolatedStringErrorType::InvalidConversionFlag,
1775 string_kind,
1776 ),
1777 conversion_flag_range,
1778 );
1779 // TODO(dhruvmanila): Avoid dropping this token
1780 self.bump_any();
1781 ConversionFlag::None
1782 }
1783 } else {
1784 ConversionFlag::None
1785 };
1786
1787 let format_spec = if self.eat(TokenKind::Colon) {
1788 let spec_start = self.node_start();
1789 let elements = self.parse_interpolated_string_elements(
1790 flags,
1791 InterpolatedStringElementsKind::FormatSpec(string_kind),
1792 string_kind,
1793 );
1794 Some(Box::new(ast::InterpolatedStringFormatSpec {
1795 range: self.node_range(spec_start),
1796 elements,
1797 node_index: AtomicNodeIndex::NONE,
1798 }))
1799 } else {
1800 None
1801 };
1802
1803 self.tokens
1804 .re_lex_string_token_in_interpolation_element(string_kind);
1805
1806 // We're using `eat` here instead of `expect` to use the f-string specific error type.
1807 if !self.eat(TokenKind::Rbrace) {
1808 // TODO(dhruvmanila): This requires some changes in the lexer. One of them
1809 // would be to emit `FStringEnd`. Currently, the following test cases doesn't
1810 // really work as expected. Refer https://github.com/astral-sh/ruff/pull/10372
1811
1812 // test_err f_string_unclosed_lbrace
1813 // f"{"
1814 // f"{foo!r"
1815 // f"{foo="
1816 // f"{"
1817 // f"""{"""
1818
1819 // test_err t_string_unclosed_lbrace
1820 // # parse_options: {"target-version": "3.14"}
1821 // t"{"
1822 // t"{foo!r"
1823 // t"{foo="
1824 // t"{"
1825 // t"""{"""
1826
1827 // The lexer does emit `FStringEnd` for the following test cases:
1828
1829 // test_err f_string_unclosed_lbrace_in_format_spec
1830 // f"hello {x:"
1831 // f"hello {x:.3f"
1832
1833 // test_err t_string_unclosed_lbrace_in_format_spec
1834 // # parse_options: {"target-version": "3.14"}
1835 // t"hello {x:"
1836 // t"hello {x:.3f"
1837 self.add_error(
1838 ParseErrorType::from_interpolated_string_error(
1839 InterpolatedStringErrorType::UnclosedLbrace,
1840 string_kind,
1841 ),
1842 self.current_token_range(),
1843 );
1844 }
1845
1846 // test_ok pep701_f_string_py312
1847 // # parse_options: {"target-version": "3.12"}
1848 // f'Magic wand: { bag['wand'] }' # nested quotes
1849 // f"{'\n'.join(a)}" # escape sequence
1850 // f'''A complex trick: {
1851 // bag['bag'] # comment
1852 // }'''
1853 // f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}" # arbitrary nesting
1854 // f"{f'''{"nested"} inner'''} outer" # nested (triple) quotes
1855 // f"test {a \
1856 // } more" # line continuation
1857
1858 // test_ok pep750_t_string_py314
1859 // # parse_options: {"target-version": "3.14"}
1860 // t'Magic wand: { bag['wand'] }' # nested quotes
1861 // t"{'\n'.join(a)}" # escape sequence
1862 // t'''A complex trick: {
1863 // bag['bag'] # comment
1864 // }'''
1865 // t"{t"{t"{t"{t"{t"{1+1}"}"}"}"}"}" # arbitrary nesting
1866 // t"{t'''{"nested"} inner'''} outer" # nested (triple) quotes
1867 // t"test {a \
1868 // } more" # line continuation
1869
1870 // test_ok pep701_f_string_py311
1871 // # parse_options: {"target-version": "3.11"}
1872 // f"outer {'# not a comment'}"
1873 // f'outer {x:{"# not a comment"} }'
1874 // f"""{f'''{f'{"# not a comment"}'}'''}"""
1875 // f"""{f'''# before expression {f'# aro{f"#{1+1}#"}und #'}'''} # after expression"""
1876 // f"escape outside of \t {expr}\n"
1877 // f"test\"abcd"
1878 // f"{1:\x64}" # escapes are valid in the format spec
1879 // f"{1:\"d\"}" # this also means that escaped outer quotes are valid
1880
1881 // test_err pep701_f_string_py311
1882 // # parse_options: {"target-version": "3.11"}
1883 // f'Magic wand: { bag['wand'] }' # nested quotes
1884 // f"{'\n'.join(a)}" # escape sequence
1885 // f'''A complex trick: {
1886 // bag['bag'] # comment
1887 // }'''
1888 // f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}" # arbitrary nesting
1889 // f"{f'''{"nested"} inner'''} outer" # nested (triple) quotes
1890 // f"test {a \
1891 // } more" # line continuation
1892 // f"""{f"""{x}"""}""" # mark the whole triple quote
1893 // f"{'\n'.join(['\t', '\v', '\r'])}" # multiple escape sequences, multiple errors
1894
1895 // test_err pep701_nested_interpolation_py311
1896 // # parse_options: {"target-version": "3.11"}
1897 // # nested interpolations also need to be checked
1898 // f'{1: abcd "{'aa'}" }'
1899 // f'{1: abcd "{"\n"}" }'
1900
1901 // test_err nested_quote_in_format_spec_py312
1902 // # parse_options: {"target-version": "3.12"}
1903 // f"{1:""}" # this is a ParseError on all versions
1904
1905 // test_ok non_nested_quote_in_format_spec_py311
1906 // # parse_options: {"target-version": "3.11"}
1907 // f"{1:''}" # but this is okay on all versions
1908 let range = self.node_range(start);
1909
1910 if !self.options.target_version.supports_pep_701()
1911 && matches!(string_kind, InterpolatedStringKind::FString)
1912 {
1913 // We need to check the whole expression range, including any leading or trailing
1914 // debug text, but exclude the format spec, where escapes and escaped, reused quotes
1915 // are allowed.
1916 let range = format_spec
1917 .as_ref()
1918 .map(|format_spec| TextRange::new(range.start(), format_spec.start()))
1919 .unwrap_or(range);
1920
1921 let quote_bytes = flags.quote_str().as_bytes();
1922 let quote_len = flags.quote_len();
1923 for slash_position in memchr::memchr_iter(b'\\', self.source[range].as_bytes()) {
1924 let slash_position = TextSize::try_from(slash_position).unwrap();
1925 self.add_unsupported_syntax_error(
1926 UnsupportedSyntaxErrorKind::Pep701FString(FStringKind::Backslash),
1927 TextRange::at(range.start() + slash_position, '\\'.text_len()),
1928 );
1929 }
1930
1931 if let Some(quote_position) =
1932 memchr::memmem::find(self.source[range].as_bytes(), quote_bytes)
1933 {
1934 let quote_position = TextSize::try_from(quote_position).unwrap();
1935 self.add_unsupported_syntax_error(
1936 UnsupportedSyntaxErrorKind::Pep701FString(FStringKind::NestedQuote),
1937 TextRange::at(range.start() + quote_position, quote_len),
1938 );
1939 }
1940
1941 self.check_fstring_comments(range);
1942 }
1943
1944 ast::InterpolatedElement {
1945 expression: Box::new(value.expr),
1946 debug_text,
1947 conversion,
1948 format_spec,
1949 range,
1950 node_index: AtomicNodeIndex::NONE,
1951 }
1952 }
1953
1954 /// Parses a list or a list comprehension expression.
1955 ///
1956 /// # Panics
1957 ///
1958 /// If the parser isn't positioned at a `[` token.
1959 ///
1960 /// See: <https://docs.python.org/3/reference/expressions.html#list-displays>
1961 fn parse_list_like_expression(&mut self) -> Expr {
1962 let start = self.node_start();
1963
1964 self.bump(TokenKind::Lsqb);
1965
1966 // Nice error message when having a unclosed open bracket `[`
1967 if self.at_ts(NEWLINE_EOF_SET) {
1968 self.add_error(
1969 ParseErrorType::OtherError("missing closing bracket `]`".to_string()),
1970 self.current_token_range(),
1971 );
1972 }
1973
1974 // Return an empty `ListExpr` when finding a `]` right after the `[`
1975 if self.eat(TokenKind::Rsqb) {
1976 return Expr::List(ast::ExprList {
1977 elts: vec![],
1978 ctx: ExprContext::Load,
1979 range: self.node_range(start),
1980 node_index: AtomicNodeIndex::NONE,
1981 });
1982 }
1983
1984 // Parse the first element with a more general rule and limit it later.
1985 let first_element =
1986 self.parse_named_expression_or_higher(ExpressionContext::starred_bitwise_or());
1987
1988 match self.current_token_kind() {
1989 TokenKind::Async | TokenKind::For => {
1990 // Parenthesized starred expression isn't allowed either but that is
1991 // handled by the `parse_parenthesized_expression` method.
1992
1993 // test_ok starred_list_comp_py315
1994 // # parse_options: {"target-version": "3.15"}
1995 // [*x for x in y]
1996 // [*factor.dims for factor in bases]
1997
1998 // test_err starred_list_comp_py314
1999 // # parse_options: {"target-version": "3.14"}
2000 // [*x for x in y]
2001 if first_element.is_unparenthesized_starred_expr() {
2002 self.add_unsupported_syntax_error(
2003 UnsupportedSyntaxErrorKind::IterableUnpackingInListComprehension,
2004 first_element.range(),
2005 );
2006 }
2007
2008 Expr::ListComp(self.parse_list_comprehension_expression(first_element.expr, start))
2009 }
2010 _ => Expr::List(self.parse_list_expression(first_element.expr, start)),
2011 }
2012 }
2013
2014 /// Parses a set, dict, set comprehension, or dict comprehension.
2015 ///
2016 /// # Panics
2017 ///
2018 /// If the parser isn't positioned at a `{` token.
2019 ///
2020 /// See:
2021 /// - <https://docs.python.org/3/reference/expressions.html#set-displays>
2022 /// - <https://docs.python.org/3/reference/expressions.html#dictionary-displays>
2023 /// - <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
2024 fn parse_set_or_dict_like_expression(&mut self) -> Expr {
2025 let start = self.node_start();
2026 self.bump(TokenKind::Lbrace);
2027
2028 // Nice error message when having a unclosed open brace `{`
2029 if self.at_ts(NEWLINE_EOF_SET) {
2030 self.add_error(
2031 ParseErrorType::OtherError("missing closing brace `}`".to_string()),
2032 self.current_token_range(),
2033 );
2034 }
2035
2036 // Return an empty `DictExpr` when finding a `}` right after the `{`
2037 if self.eat(TokenKind::Rbrace) {
2038 return Expr::Dict(ast::ExprDict {
2039 items: vec![],
2040 range: self.node_range(start),
2041 node_index: AtomicNodeIndex::NONE,
2042 });
2043 }
2044
2045 if self.eat(TokenKind::DoubleStar) {
2046 // Handle dictionary unpacking. Here, the grammar is `'**' bitwise_or`
2047 // which requires limiting the expression.
2048 let value = self.parse_expression_with_bitwise_or_precedence();
2049
2050 return Expr::Dict(self.parse_dictionary_expression(None, value.expr, start));
2051 }
2052
2053 // For dictionary expressions, the key uses the `expression` rule while for
2054 // set expressions, the element uses the `star_expression` rule. So, use the
2055 // one that is more general and limit it later.
2056 let key_or_element =
2057 self.parse_named_expression_or_higher(ExpressionContext::starred_bitwise_or());
2058
2059 match self.current_token_kind() {
2060 TokenKind::Async | TokenKind::For => {
2061 if key_or_element.is_unparenthesized_starred_expr() {
2062 self.add_error(
2063 ParseErrorType::IterableUnpackingInComprehension,
2064 &key_or_element,
2065 );
2066 } else if key_or_element.is_unparenthesized_named_expr() {
2067 // test_ok parenthesized_named_expr_py38
2068 // # parse_options: {"target-version": "3.8"}
2069 // {(x := 1), 2, 3}
2070 // {(last := x) for x in range(3)}
2071
2072 // test_ok unparenthesized_named_expr_py39
2073 // # parse_options: {"target-version": "3.9"}
2074 // {x := 1, 2, 3}
2075 // {last := x for x in range(3)}
2076
2077 // test_err unparenthesized_named_expr_set_comp_py38
2078 // # parse_options: {"target-version": "3.8"}
2079 // {last := x for x in range(3)}
2080 self.add_unsupported_syntax_error(
2081 UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
2082 UnparenthesizedNamedExprKind::SetComprehension,
2083 ),
2084 key_or_element.range(),
2085 );
2086 }
2087
2088 Expr::SetComp(self.parse_set_comprehension_expression(key_or_element.expr, start))
2089 }
2090 TokenKind::Colon => {
2091 // Now, we know that it's either a dictionary expression or a dictionary comprehension.
2092 // In either case, the key is limited to an `expression`.
2093 if !key_or_element.is_parenthesized {
2094 match key_or_element.expr {
2095 Expr::Starred(_) => self.add_error(
2096 ParseErrorType::InvalidStarredExpressionUsage,
2097 &key_or_element.expr,
2098 ),
2099 Expr::Named(_) => self.add_error(
2100 ParseErrorType::UnparenthesizedNamedExpression,
2101 &key_or_element,
2102 ),
2103 _ => {}
2104 }
2105 }
2106
2107 self.bump(TokenKind::Colon);
2108 let value = self.parse_conditional_expression_or_higher();
2109
2110 if matches!(self.current_token_kind(), TokenKind::Async | TokenKind::For) {
2111 Expr::DictComp(self.parse_dictionary_comprehension_expression(
2112 key_or_element.expr,
2113 value.expr,
2114 start,
2115 ))
2116 } else {
2117 Expr::Dict(self.parse_dictionary_expression(
2118 Some(key_or_element.expr),
2119 value.expr,
2120 start,
2121 ))
2122 }
2123 }
2124 _ => Expr::Set(self.parse_set_expression(key_or_element, start)),
2125 }
2126 }
2127
2128 /// Parses an expression in parentheses, a tuple expression, or a generator expression.
2129 ///
2130 /// Matches the `(tuple | group | genexp)` rule in the [Python grammar].
2131 ///
2132 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
2133 fn parse_parenthesized_expression(&mut self) -> ParsedExpr {
2134 let start = self.node_start();
2135 self.bump(TokenKind::Lpar);
2136
2137 // Nice error message when having a unclosed open parenthesis `(`
2138 if self.at_ts(NEWLINE_EOF_SET) {
2139 let range = self.current_token_range();
2140 self.add_error(
2141 ParseErrorType::OtherError("missing closing parenthesis `)`".to_string()),
2142 range,
2143 );
2144 }
2145
2146 // Return an empty `TupleExpr` when finding a `)` right after the `(`
2147 if self.eat(TokenKind::Rpar) {
2148 return Expr::Tuple(ast::ExprTuple {
2149 elts: vec![],
2150 ctx: ExprContext::Load,
2151 range: self.node_range(start),
2152 node_index: AtomicNodeIndex::NONE,
2153 parenthesized: true,
2154 })
2155 .into();
2156 }
2157
2158 // Use the more general rule of the three to parse the first element
2159 // and limit it later.
2160 let mut parsed_expr =
2161 self.parse_named_expression_or_higher(ExpressionContext::yield_or_starred_bitwise_or());
2162
2163 match self.current_token_kind() {
2164 TokenKind::Comma => {
2165 // grammar: `tuple`
2166 let tuple =
2167 self.parse_tuple_expression(parsed_expr.expr, start, Parenthesized::Yes, |p| {
2168 p.parse_named_expression_or_higher(ExpressionContext::starred_bitwise_or())
2169 });
2170
2171 ParsedExpr {
2172 expr: tuple.into(),
2173 is_parenthesized: false,
2174 }
2175 }
2176 TokenKind::Async | TokenKind::For => {
2177 // grammar: `genexp`
2178 if parsed_expr.is_unparenthesized_starred_expr() {
2179 self.add_error(
2180 ParseErrorType::IterableUnpackingInComprehension,
2181 &parsed_expr,
2182 );
2183 }
2184
2185 let generator = Expr::Generator(self.parse_generator_expression(
2186 parsed_expr.expr,
2187 start,
2188 Parenthesized::Yes,
2189 ));
2190
2191 ParsedExpr {
2192 expr: generator,
2193 is_parenthesized: false,
2194 }
2195 }
2196 _ => {
2197 // grammar: `group`
2198 if parsed_expr.expr.is_starred_expr() {
2199 self.add_error(ParseErrorType::InvalidStarredExpressionUsage, &parsed_expr);
2200 }
2201
2202 self.expect(TokenKind::Rpar);
2203
2204 parsed_expr.is_parenthesized = true;
2205 parsed_expr
2206 }
2207 }
2208 }
2209
2210 /// Parses multiple items separated by a comma into a tuple expression.
2211 ///
2212 /// Uses the `parse_func` to parse each item in the tuple.
2213 pub(super) fn parse_tuple_expression(
2214 &mut self,
2215 first_element: Expr,
2216 start: TextSize,
2217 parenthesized: Parenthesized,
2218 mut parse_func: impl FnMut(&mut Parser<'src>) -> ParsedExpr,
2219 ) -> ast::ExprTuple {
2220 // TODO(dhruvmanila): Can we remove `parse_func` and use `parenthesized` to
2221 // determine the parsing function?
2222
2223 if !self.at_sequence_end() {
2224 self.expect(TokenKind::Comma);
2225 }
2226
2227 let mut elts = vec![first_element];
2228
2229 self.parse_comma_separated_list(RecoveryContextKind::TupleElements(parenthesized), |p| {
2230 elts.push(parse_func(p).expr);
2231 });
2232
2233 if parenthesized.is_yes() {
2234 self.expect(TokenKind::Rpar);
2235 }
2236
2237 ast::ExprTuple {
2238 elts,
2239 ctx: ExprContext::Load,
2240 range: self.node_range(start),
2241 node_index: AtomicNodeIndex::NONE,
2242 parenthesized: parenthesized.is_yes(),
2243 }
2244 }
2245
2246 /// Parses a list expression.
2247 ///
2248 /// See: <https://docs.python.org/3/reference/expressions.html#list-displays>
2249 fn parse_list_expression(&mut self, first_element: Expr, start: TextSize) -> ast::ExprList {
2250 if !self.at_sequence_end() {
2251 self.expect(TokenKind::Comma);
2252 }
2253
2254 let mut elts = vec![first_element];
2255
2256 self.parse_comma_separated_list(RecoveryContextKind::ListElements, |parser| {
2257 elts.push(
2258 parser
2259 .parse_named_expression_or_higher(ExpressionContext::starred_bitwise_or())
2260 .expr,
2261 );
2262 });
2263
2264 self.expect(TokenKind::Rsqb);
2265
2266 ast::ExprList {
2267 elts,
2268 ctx: ExprContext::Load,
2269 range: self.node_range(start),
2270 node_index: AtomicNodeIndex::NONE,
2271 }
2272 }
2273
2274 /// Parses a set expression.
2275 ///
2276 /// See: <https://docs.python.org/3/reference/expressions.html#set-displays>
2277 fn parse_set_expression(&mut self, first_element: ParsedExpr, start: TextSize) -> ast::ExprSet {
2278 if !self.at_sequence_end() {
2279 self.expect(TokenKind::Comma);
2280 }
2281
2282 // test_err unparenthesized_named_expr_set_literal_py38
2283 // # parse_options: {"target-version": "3.8"}
2284 // {x := 1, 2, 3}
2285 // {1, x := 2, 3}
2286 // {1, 2, x := 3}
2287
2288 if first_element.is_unparenthesized_named_expr() {
2289 self.add_unsupported_syntax_error(
2290 UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
2291 UnparenthesizedNamedExprKind::SetLiteral,
2292 ),
2293 first_element.range(),
2294 );
2295 }
2296
2297 let mut elts = vec![first_element.expr];
2298
2299 self.parse_comma_separated_list(RecoveryContextKind::SetElements, |parser| {
2300 let parsed_expr =
2301 parser.parse_named_expression_or_higher(ExpressionContext::starred_bitwise_or());
2302
2303 if parsed_expr.is_unparenthesized_named_expr() {
2304 parser.add_unsupported_syntax_error(
2305 UnsupportedSyntaxErrorKind::UnparenthesizedNamedExpr(
2306 UnparenthesizedNamedExprKind::SetLiteral,
2307 ),
2308 parsed_expr.range(),
2309 );
2310 }
2311
2312 elts.push(parsed_expr.expr);
2313 });
2314
2315 self.expect(TokenKind::Rbrace);
2316
2317 ast::ExprSet {
2318 range: self.node_range(start),
2319 node_index: AtomicNodeIndex::NONE,
2320 elts,
2321 }
2322 }
2323
2324 /// Parses a dictionary expression.
2325 ///
2326 /// See: <https://docs.python.org/3/reference/expressions.html#dictionary-displays>
2327 fn parse_dictionary_expression(
2328 &mut self,
2329 key: Option<Expr>,
2330 value: Expr,
2331 start: TextSize,
2332 ) -> ast::ExprDict {
2333 if !self.at_sequence_end() {
2334 self.expect(TokenKind::Comma);
2335 }
2336
2337 let mut items = vec![ast::DictItem { key, value }];
2338
2339 self.parse_comma_separated_list(RecoveryContextKind::DictElements, |parser| {
2340 if parser.eat(TokenKind::DoubleStar) {
2341 // Handle dictionary unpacking. Here, the grammar is `'**' bitwise_or`
2342 // which requires limiting the expression.
2343 items.push(ast::DictItem {
2344 key: None,
2345 value: parser.parse_expression_with_bitwise_or_precedence().expr,
2346 });
2347 } else {
2348 let key = parser.parse_conditional_expression_or_higher().expr;
2349 parser.expect(TokenKind::Colon);
2350
2351 items.push(ast::DictItem {
2352 key: Some(key),
2353 value: parser.parse_conditional_expression_or_higher().expr,
2354 });
2355 }
2356 });
2357
2358 self.expect(TokenKind::Rbrace);
2359
2360 ast::ExprDict {
2361 range: self.node_range(start),
2362 node_index: AtomicNodeIndex::NONE,
2363 items,
2364 }
2365 }
2366
2367 /// Parses a list of comprehension generators.
2368 ///
2369 /// These are the `for` and `async for` clauses in a comprehension, optionally
2370 /// followed by `if` clauses.
2371 ///
2372 /// See: <https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-comp_for>
2373 fn parse_generators(&mut self) -> Vec<ast::Comprehension> {
2374 const GENERATOR_SET: TokenSet = TokenSet::new([TokenKind::For, TokenKind::Async]);
2375
2376 let mut generators = vec![];
2377 let mut progress = ParserProgress::default();
2378
2379 while self.at_ts(GENERATOR_SET) {
2380 progress.assert_progressing(self);
2381 generators.push(self.parse_comprehension());
2382 }
2383
2384 generators
2385 }
2386
2387 /// Parses a comprehension.
2388 ///
2389 /// # Panics
2390 ///
2391 /// If the parser isn't positioned at an `async` or `for` token.
2392 ///
2393 /// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
2394 fn parse_comprehension(&mut self) -> ast::Comprehension {
2395 let start = self.node_start();
2396
2397 let is_async = self.eat(TokenKind::Async);
2398
2399 if is_async {
2400 // test_err comprehension_missing_for_after_async
2401 // (async)
2402 // (x async x in iter)
2403 self.expect(TokenKind::For);
2404 } else {
2405 self.bump(TokenKind::For);
2406 }
2407
2408 let mut target =
2409 self.parse_expression_list(ExpressionContext::starred_conditional().with_in_excluded());
2410
2411 helpers::set_expr_ctx(&mut target.expr, ExprContext::Store);
2412 self.validate_assignment_target(&target.expr);
2413
2414 self.expect(TokenKind::In);
2415 let iter = self.parse_simple_expression(ExpressionContext::default());
2416
2417 let mut ifs = vec![];
2418 let mut progress = ParserProgress::default();
2419
2420 while self.eat(TokenKind::If) {
2421 progress.assert_progressing(self);
2422
2423 let parsed_expr = self.parse_simple_expression(ExpressionContext::default());
2424
2425 ifs.push(parsed_expr.expr);
2426 }
2427
2428 ast::Comprehension {
2429 range: self.node_range(start),
2430 node_index: AtomicNodeIndex::NONE,
2431 target: target.expr,
2432 iter: iter.expr,
2433 ifs,
2434 is_async,
2435 }
2436 }
2437
2438 /// Parses a generator expression.
2439 ///
2440 /// The given `start` offset is the start of either the opening parenthesis if the generator is
2441 /// parenthesized or the first token of the expression.
2442 ///
2443 /// See: <https://docs.python.org/3/reference/expressions.html#generator-expressions>
2444 pub(super) fn parse_generator_expression(
2445 &mut self,
2446 element: Expr,
2447 start: TextSize,
2448 parenthesized: Parenthesized,
2449 ) -> ast::ExprGenerator {
2450 let generators = self.parse_generators();
2451
2452 if parenthesized.is_yes() {
2453 self.expect(TokenKind::Rpar);
2454 }
2455
2456 ast::ExprGenerator {
2457 elt: Box::new(element),
2458 generators,
2459 range: self.node_range(start),
2460 node_index: AtomicNodeIndex::NONE,
2461 parenthesized: parenthesized.is_yes(),
2462 }
2463 }
2464
2465 /// Parses a list comprehension expression.
2466 ///
2467 /// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
2468 fn parse_list_comprehension_expression(
2469 &mut self,
2470 element: Expr,
2471 start: TextSize,
2472 ) -> ast::ExprListComp {
2473 let generators = self.parse_generators();
2474
2475 self.expect(TokenKind::Rsqb);
2476
2477 ast::ExprListComp {
2478 elt: Box::new(element),
2479 generators,
2480 range: self.node_range(start),
2481 node_index: AtomicNodeIndex::NONE,
2482 }
2483 }
2484
2485 /// Parses a dictionary comprehension expression.
2486 ///
2487 /// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
2488 fn parse_dictionary_comprehension_expression(
2489 &mut self,
2490 key: Expr,
2491 value: Expr,
2492 start: TextSize,
2493 ) -> ast::ExprDictComp {
2494 let generators = self.parse_generators();
2495
2496 self.expect(TokenKind::Rbrace);
2497
2498 ast::ExprDictComp {
2499 key: Box::new(key),
2500 value: Box::new(value),
2501 generators,
2502 range: self.node_range(start),
2503 node_index: AtomicNodeIndex::NONE,
2504 }
2505 }
2506
2507 /// Parses a set comprehension expression.
2508 ///
2509 /// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
2510 fn parse_set_comprehension_expression(
2511 &mut self,
2512 element: Expr,
2513 start: TextSize,
2514 ) -> ast::ExprSetComp {
2515 let generators = self.parse_generators();
2516
2517 self.expect(TokenKind::Rbrace);
2518
2519 ast::ExprSetComp {
2520 elt: Box::new(element),
2521 generators,
2522 range: self.node_range(start),
2523 node_index: AtomicNodeIndex::NONE,
2524 }
2525 }
2526
2527 /// Parses a starred expression with the given precedence.
2528 ///
2529 /// The expression is parsed with the highest precedence. If the precedence
2530 /// of the parsed expression is lower than the given precedence, an error
2531 /// is reported.
2532 ///
2533 /// For example, if the given precedence is [`StarredExpressionPrecedence::BitOr`],
2534 /// the comparison expression is not allowed.
2535 ///
2536 /// Refer to the [Python grammar] for more information.
2537 ///
2538 /// # Panics
2539 ///
2540 /// If the parser isn't positioned at a `*` token.
2541 ///
2542 /// [Python grammar]: https://docs.python.org/3/reference/grammar.html
2543 fn parse_starred_expression(&mut self, context: ExpressionContext) -> ast::ExprStarred {
2544 let start = self.node_start();
2545 self.bump(TokenKind::Star);
2546
2547 let parsed_expr = match context.starred_expression_precedence() {
2548 StarredExpressionPrecedence::Conditional => {
2549 self.parse_conditional_expression_or_higher_impl(context)
2550 }
2551 StarredExpressionPrecedence::BitwiseOr => {
2552 self.parse_expression_with_bitwise_or_precedence()
2553 }
2554 };
2555
2556 ast::ExprStarred {
2557 value: Box::new(parsed_expr.expr),
2558 ctx: ExprContext::Load,
2559 range: self.node_range(start),
2560 node_index: AtomicNodeIndex::NONE,
2561 }
2562 }
2563
2564 /// Parses an `await` expression.
2565 ///
2566 /// # Panics
2567 ///
2568 /// If the parser isn't positioned at an `await` token.
2569 ///
2570 /// See: <https://docs.python.org/3/reference/expressions.html#await-expression>
2571 fn parse_await_expression(&mut self) -> ast::ExprAwait {
2572 let start = self.node_start();
2573 self.bump(TokenKind::Await);
2574
2575 let parsed_expr = self.parse_binary_expression_or_higher(
2576 OperatorPrecedence::Await,
2577 ExpressionContext::default(),
2578 );
2579
2580 ast::ExprAwait {
2581 value: Box::new(parsed_expr.expr),
2582 range: self.node_range(start),
2583 node_index: AtomicNodeIndex::NONE,
2584 }
2585 }
2586
2587 /// Parses a `yield` expression.
2588 ///
2589 /// # Panics
2590 ///
2591 /// If the parser isn't positioned at a `yield` token.
2592 ///
2593 /// See: <https://docs.python.org/3/reference/expressions.html#yield-expressions>
2594 fn parse_yield_expression(&mut self) -> Expr {
2595 let start = self.node_start();
2596 self.bump(TokenKind::Yield);
2597
2598 if self.eat(TokenKind::From) {
2599 return self.parse_yield_from_expression(start);
2600 }
2601
2602 let value = self.at_expr().then(|| {
2603 let parsed_expr = self.parse_expression_list(ExpressionContext::starred_bitwise_or());
2604
2605 // test_ok iter_unpack_yield_py37
2606 // # parse_options: {"target-version": "3.7"}
2607 // rest = (4, 5, 6)
2608 // def g(): yield (1, 2, 3, *rest)
2609
2610 // test_ok iter_unpack_yield_py38
2611 // # parse_options: {"target-version": "3.8"}
2612 // rest = (4, 5, 6)
2613 // def g(): yield 1, 2, 3, *rest
2614 // def h(): yield 1, (yield 2, *rest), 3
2615
2616 // test_err iter_unpack_yield_py37
2617 // # parse_options: {"target-version": "3.7"}
2618 // rest = (4, 5, 6)
2619 // def g(): yield 1, 2, 3, *rest
2620 // def h(): yield 1, (yield 2, *rest), 3
2621 self.check_tuple_unpacking(
2622 &parsed_expr,
2623 UnsupportedSyntaxErrorKind::StarTuple(StarTupleKind::Yield),
2624 );
2625
2626 Box::new(parsed_expr.expr)
2627 });
2628
2629 Expr::Yield(ast::ExprYield {
2630 value,
2631 range: self.node_range(start),
2632 node_index: AtomicNodeIndex::NONE,
2633 })
2634 }
2635
2636 /// Parses a `yield from` expression.
2637 ///
2638 /// This method should not be used directly. Use [`Parser::parse_yield_expression`]
2639 /// even when parsing a `yield from` expression.
2640 ///
2641 /// See: <https://docs.python.org/3/reference/expressions.html#yield-expressions>
2642 fn parse_yield_from_expression(&mut self, start: TextSize) -> Expr {
2643 // Grammar:
2644 // 'yield' 'from' expression
2645 //
2646 // Here, a tuple expression isn't allowed without the parentheses. But, we
2647 // allow it here to report better error message.
2648 //
2649 // Now, this also solves another problem. Take the following example:
2650 //
2651 // ```python
2652 // yield from x, y
2653 // ```
2654 //
2655 // If we didn't use the `parse_expression_list` method here, the parser
2656 // would have stopped at the comma. Then, the outer expression would
2657 // have been a tuple expression with two elements: `yield from x` and `y`.
2658 let expr = self
2659 .parse_expression_list(ExpressionContext::default())
2660 .expr;
2661
2662 match &expr {
2663 Expr::Tuple(tuple) if !tuple.parenthesized => {
2664 self.add_error(ParseErrorType::UnparenthesizedTupleExpression, &expr);
2665 }
2666 _ => {}
2667 }
2668
2669 Expr::YieldFrom(ast::ExprYieldFrom {
2670 value: Box::new(expr),
2671 range: self.node_range(start),
2672 node_index: AtomicNodeIndex::NONE,
2673 })
2674 }
2675
2676 /// Parses a named expression (`:=`).
2677 ///
2678 /// # Panics
2679 ///
2680 /// If the parser isn't positioned at a `:=` token.
2681 ///
2682 /// See: <https://docs.python.org/3/reference/expressions.html#assignment-expressions>
2683 pub(super) fn parse_named_expression(
2684 &mut self,
2685 mut target: Expr,
2686 start: TextSize,
2687 ) -> ast::ExprNamed {
2688 self.bump(TokenKind::ColonEqual);
2689
2690 if !target.is_name_expr() {
2691 self.add_error(ParseErrorType::InvalidNamedAssignmentTarget, target.range());
2692 }
2693 helpers::set_expr_ctx(&mut target, ExprContext::Store);
2694
2695 let value = self.parse_conditional_expression_or_higher();
2696
2697 let range = self.node_range(start);
2698
2699 // test_err walrus_py37
2700 // # parse_options: { "target-version": "3.7" }
2701 // (x := 1)
2702
2703 // test_ok walrus_py38
2704 // # parse_options: { "target-version": "3.8" }
2705 // (x := 1)
2706
2707 self.add_unsupported_syntax_error(UnsupportedSyntaxErrorKind::Walrus, range);
2708
2709 ast::ExprNamed {
2710 target: Box::new(target),
2711 value: Box::new(value.expr),
2712 range,
2713 node_index: AtomicNodeIndex::NONE,
2714 }
2715 }
2716
2717 /// Parses a lambda expression.
2718 ///
2719 /// # Panics
2720 ///
2721 /// If the parser isn't positioned at a `lambda` token.
2722 ///
2723 /// See: <https://docs.python.org/3/reference/expressions.html#lambda>
2724 fn parse_lambda_expr(&mut self) -> ast::ExprLambda {
2725 let start = self.node_start();
2726 self.bump(TokenKind::Lambda);
2727
2728 let parameters = if self.at(TokenKind::Colon) {
2729 // test_ok lambda_with_no_parameters
2730 // lambda: 1
2731 None
2732 } else {
2733 Some(Box::new(self.parse_parameters(FunctionKind::Lambda)))
2734 };
2735
2736 self.expect(TokenKind::Colon);
2737
2738 // test_ok lambda_with_valid_body
2739 // lambda x: x
2740 // lambda x: x if True else y
2741 // lambda x: await x
2742 // lambda x: lambda y: x + y
2743 // lambda x: (yield x) # Parenthesized `yield` is fine
2744 // lambda x: x, *y
2745
2746 // test_err lambda_body_with_starred_expr
2747 // lambda x: *y
2748 // lambda x: *y,
2749 // lambda x: *y, z
2750 // lambda x: *y and z
2751
2752 // test_err lambda_body_with_yield_expr
2753 // lambda x: yield y
2754 // lambda x: yield from y
2755 let body = self.parse_conditional_expression_or_higher();
2756
2757 ast::ExprLambda {
2758 body: Box::new(body.expr),
2759 parameters,
2760 range: self.node_range(start),
2761 node_index: AtomicNodeIndex::NONE,
2762 }
2763 }
2764
2765 /// Parses an `if` expression.
2766 ///
2767 /// # Panics
2768 ///
2769 /// If the parser isn't positioned at an `if` token.
2770 ///
2771 /// See: <https://docs.python.org/3/reference/expressions.html#conditional-expressions>
2772 pub(super) fn parse_if_expression(&mut self, body: Expr, start: TextSize) -> ast::ExprIf {
2773 self.bump(TokenKind::If);
2774
2775 let test = self.parse_simple_expression(ExpressionContext::default());
2776
2777 self.expect(TokenKind::Else);
2778
2779 let orelse = self.parse_conditional_expression_or_higher();
2780
2781 ast::ExprIf {
2782 body: Box::new(body),
2783 test: Box::new(test.expr),
2784 orelse: Box::new(orelse.expr),
2785 range: self.node_range(start),
2786 node_index: AtomicNodeIndex::NONE,
2787 }
2788 }
2789
2790 /// Parses an IPython escape command at the expression level.
2791 ///
2792 /// # Panics
2793 ///
2794 /// If the parser isn't positioned at a `IpyEscapeCommand` token.
2795 /// If the escape command kind is not `%` or `!`.
2796 fn parse_ipython_escape_command_expression(&mut self) -> ast::ExprIpyEscapeCommand {
2797 let start = self.node_start();
2798
2799 let TokenValue::IpyEscapeCommand { value, kind } =
2800 self.bump_value(TokenKind::IpyEscapeCommand)
2801 else {
2802 unreachable!()
2803 };
2804
2805 if !matches!(kind, IpyEscapeKind::Magic | IpyEscapeKind::Shell) {
2806 // This should never occur as the lexer won't allow it.
2807 unreachable!("IPython escape command expression is only allowed for % and !");
2808 }
2809
2810 let command = ast::ExprIpyEscapeCommand {
2811 range: self.node_range(start),
2812 node_index: AtomicNodeIndex::NONE,
2813 kind,
2814 value,
2815 };
2816
2817 if self.options.mode != Mode::Ipython {
2818 self.add_error(ParseErrorType::UnexpectedIpythonEscapeCommand, &command);
2819 }
2820
2821 command
2822 }
2823
2824 /// Performs the following validations on the function call arguments:
2825 /// 1. There aren't any duplicate keyword argument
2826 /// 2. If there are more than one argument (positional or keyword) or a single argument with a
2827 /// trailing comma, all generator expressions present should be parenthesized.
2828 fn validate_arguments(&mut self, arguments: &ast::Arguments, has_trailing_comma: bool) {
2829 let mut all_arg_names =
2830 FxHashSet::with_capacity_and_hasher(arguments.keywords.len(), FxBuildHasher);
2831
2832 for (name, range) in arguments
2833 .keywords
2834 .iter()
2835 .filter_map(|argument| argument.arg.as_ref().map(|arg| (arg, argument.range)))
2836 {
2837 let arg_name = name.as_str();
2838 if !all_arg_names.insert(arg_name) {
2839 self.add_error(
2840 ParseErrorType::DuplicateKeywordArgumentError(arg_name.to_string()),
2841 range,
2842 );
2843 }
2844 }
2845
2846 if has_trailing_comma || arguments.len() > 1 {
2847 for arg in &*arguments.args {
2848 if let Some(ast::ExprGenerator {
2849 range,
2850 parenthesized: false,
2851 ..
2852 }) = arg.as_generator_expr()
2853 {
2854 // test_ok args_unparenthesized_generator
2855 // zip((x for x in range(10)), (y for y in range(10)))
2856 // sum(x for x in range(10))
2857 // sum((x for x in range(10)),)
2858
2859 // test_err args_unparenthesized_generator
2860 // sum(x for x in range(10), 5)
2861 // total(1, 2, x for x in range(5), 6)
2862 // sum(x for x in range(10),)
2863 self.add_error(ParseErrorType::UnparenthesizedGeneratorExpression, range);
2864 }
2865 }
2866 }
2867 }
2868}
2869
2870#[derive(Debug)]
2871pub(super) struct ParsedExpr {
2872 pub(super) expr: Expr,
2873 pub(super) is_parenthesized: bool,
2874}
2875
2876impl ParsedExpr {
2877 #[inline]
2878 pub(super) const fn is_unparenthesized_starred_expr(&self) -> bool {
2879 !self.is_parenthesized && self.expr.is_starred_expr()
2880 }
2881
2882 #[inline]
2883 pub(super) const fn is_unparenthesized_named_expr(&self) -> bool {
2884 !self.is_parenthesized && self.expr.is_named_expr()
2885 }
2886}
2887
2888impl From<Expr> for ParsedExpr {
2889 #[inline]
2890 fn from(expr: Expr) -> Self {
2891 ParsedExpr {
2892 expr,
2893 is_parenthesized: false,
2894 }
2895 }
2896}
2897
2898impl Deref for ParsedExpr {
2899 type Target = Expr;
2900
2901 fn deref(&self) -> &Self::Target {
2902 &self.expr
2903 }
2904}
2905
2906impl Ranged for ParsedExpr {
2907 #[inline]
2908 fn range(&self) -> TextRange {
2909 self.expr.range()
2910 }
2911}
2912
2913#[derive(Debug)]
2914enum BinaryLikeOperator {
2915 Boolean(BoolOp),
2916 Comparison(CmpOp),
2917 Binary(Operator),
2918}
2919
2920impl BinaryLikeOperator {
2921 /// Attempts to convert the two tokens into the corresponding binary-like operator. Returns
2922 /// [None] if it's not a binary-like operator.
2923 fn try_from_tokens(current: TokenKind, next: TokenKind) -> Option<BinaryLikeOperator> {
2924 if let Some(bool_op) = current.as_bool_operator() {
2925 Some(BinaryLikeOperator::Boolean(bool_op))
2926 } else if let Some(bin_op) = current.as_binary_operator() {
2927 Some(BinaryLikeOperator::Binary(bin_op))
2928 } else {
2929 helpers::token_kind_to_cmp_op([current, next]).map(BinaryLikeOperator::Comparison)
2930 }
2931 }
2932
2933 /// Returns the [`OperatorPrecedence`] for the given operator token or [None] if the token
2934 /// isn't an operator token.
2935 fn precedence(&self) -> OperatorPrecedence {
2936 match self {
2937 BinaryLikeOperator::Boolean(bool_op) => OperatorPrecedence::from(*bool_op),
2938 BinaryLikeOperator::Comparison(_) => OperatorPrecedence::ComparisonsMembershipIdentity,
2939 BinaryLikeOperator::Binary(bin_op) => OperatorPrecedence::from(*bin_op),
2940 }
2941 }
2942}
2943
2944/// Represents the precedence used for parsing the value part of a starred expression.
2945#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2946pub(super) enum StarredExpressionPrecedence {
2947 /// Matches `'*' bitwise_or` which is part of the `star_expression` rule in the
2948 /// [Python grammar](https://docs.python.org/3/reference/grammar.html).
2949 BitwiseOr,
2950
2951 /// Matches `'*' expression` which is part of the `starred_expression` rule in the
2952 /// [Python grammar](https://docs.python.org/3/reference/grammar.html).
2953 Conditional,
2954}
2955
2956/// Represents the expression parsing context.
2957#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
2958pub(super) struct ExpressionContext(ExpressionContextFlags);
2959
2960bitflags! {
2961 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
2962 struct ExpressionContextFlags: u8 {
2963 /// This flag is set when the `in` keyword should be excluded from a comparison expression.
2964 /// It is to avoid ambiguity in `for ... in ...` statements.
2965 const EXCLUDE_IN = 1 << 0;
2966
2967 /// This flag is set when a starred expression should be allowed. This doesn't affect the
2968 /// parsing of a starred expression as it will be parsed nevertheless. But, if it is not
2969 /// allowed, an error is reported.
2970 const ALLOW_STARRED_EXPRESSION = 1 << 1;
2971
2972 /// This flag is set when the value of a starred expression should be limited to bitwise OR
2973 /// precedence. Matches the `* bitwise_or` grammar rule if set.
2974 const STARRED_BITWISE_OR_PRECEDENCE = 1 << 2;
2975
2976 /// This flag is set when a yield expression should be allowed. This doesn't affect the
2977 /// parsing of a yield expression as it will be parsed nevertheless. But, if it is not
2978 /// allowed, an error is reported.
2979 const ALLOW_YIELD_EXPRESSION = 1 << 3;
2980 }
2981}
2982
2983impl ExpressionContext {
2984 /// Create a new context allowing starred expression at conditional precedence.
2985 pub(super) fn starred_conditional() -> Self {
2986 ExpressionContext::default()
2987 .with_starred_expression_allowed(StarredExpressionPrecedence::Conditional)
2988 }
2989
2990 /// Create a new context allowing starred expression at bitwise OR precedence.
2991 pub(super) fn starred_bitwise_or() -> Self {
2992 ExpressionContext::default()
2993 .with_starred_expression_allowed(StarredExpressionPrecedence::BitwiseOr)
2994 }
2995
2996 /// Create a new context allowing starred expression at bitwise OR precedence or yield
2997 /// expression.
2998 pub(super) fn yield_or_starred_bitwise_or() -> Self {
2999 ExpressionContext::starred_bitwise_or().with_yield_expression_allowed()
3000 }
3001
3002 /// Returns a new [`ExpressionContext`] which allows starred expression with the given
3003 /// precedence.
3004 fn with_starred_expression_allowed(self, precedence: StarredExpressionPrecedence) -> Self {
3005 let mut flags = self.0 | ExpressionContextFlags::ALLOW_STARRED_EXPRESSION;
3006 match precedence {
3007 StarredExpressionPrecedence::BitwiseOr => {
3008 flags |= ExpressionContextFlags::STARRED_BITWISE_OR_PRECEDENCE;
3009 }
3010 StarredExpressionPrecedence::Conditional => {
3011 flags -= ExpressionContextFlags::STARRED_BITWISE_OR_PRECEDENCE;
3012 }
3013 }
3014 ExpressionContext(flags)
3015 }
3016
3017 /// Returns a new [`ExpressionContext`] which allows yield expression.
3018 fn with_yield_expression_allowed(self) -> Self {
3019 ExpressionContext(self.0 | ExpressionContextFlags::ALLOW_YIELD_EXPRESSION)
3020 }
3021
3022 /// Returns a new [`ExpressionContext`] which excludes `in` as part of a comparison expression.
3023 pub(super) fn with_in_excluded(self) -> Self {
3024 ExpressionContext(self.0 | ExpressionContextFlags::EXCLUDE_IN)
3025 }
3026
3027 /// Returns `true` if the `in` keyword should be excluded from a comparison expression.
3028 const fn is_in_excluded(self) -> bool {
3029 self.0.contains(ExpressionContextFlags::EXCLUDE_IN)
3030 }
3031
3032 /// Returns `true` if starred expressions are allowed.
3033 const fn is_starred_expression_allowed(self) -> bool {
3034 self.0
3035 .contains(ExpressionContextFlags::ALLOW_STARRED_EXPRESSION)
3036 }
3037
3038 /// Returns `true` if yield expressions are allowed.
3039 const fn is_yield_expression_allowed(self) -> bool {
3040 self.0
3041 .contains(ExpressionContextFlags::ALLOW_YIELD_EXPRESSION)
3042 }
3043
3044 /// Returns the [`StarredExpressionPrecedence`] for the context, regardless of whether starred
3045 /// expressions are allowed or not.
3046 const fn starred_expression_precedence(self) -> StarredExpressionPrecedence {
3047 if self
3048 .0
3049 .contains(ExpressionContextFlags::STARRED_BITWISE_OR_PRECEDENCE)
3050 {
3051 StarredExpressionPrecedence::BitwiseOr
3052 } else {
3053 StarredExpressionPrecedence::Conditional
3054 }
3055 }
3056}
3057
3058#[derive(Debug)]
3059struct InterpolatedStringData {
3060 elements: InterpolatedStringElements,
3061 range: TextRange,
3062 flags: AnyStringFlags,
3063}
3064
3065impl From<InterpolatedStringData> for FString {
3066 fn from(value: InterpolatedStringData) -> Self {
3067 Self {
3068 elements: value.elements,
3069 range: value.range,
3070 flags: value.flags.into(),
3071 node_index: AtomicNodeIndex::NONE,
3072 }
3073 }
3074}
3075
3076impl From<InterpolatedStringData> for TString {
3077 fn from(value: InterpolatedStringData) -> Self {
3078 Self {
3079 elements: value.elements,
3080 range: value.range,
3081 flags: value.flags.into(),
3082 node_index: AtomicNodeIndex::NONE,
3083 }
3084 }
3085}