Start working on using a bumpalo for the AST
This commit is contained in:
@@ -64,11 +64,11 @@
|
||||
//! [parsing]: https://en.wikipedia.org/wiki/Parsing
|
||||
//! [lexer]: crate::lexer
|
||||
|
||||
use std::iter::FusedIterator;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use crate::error::{FStringErrorType, ParseError, ParseErrorType};
|
||||
pub use crate::token::{Token, TokenKind};
|
||||
use ruff_allocator::Allocator;
|
||||
use std::iter::FusedIterator;
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::parser::Parser;
|
||||
|
||||
@@ -107,8 +107,11 @@ pub mod typing;
|
||||
/// let module = parse_module(source);
|
||||
/// assert!(module.is_ok());
|
||||
/// ```
|
||||
pub fn parse_module(source: &str) -> Result<Parsed<ModModule>, ParseError> {
|
||||
Parser::new(source, Mode::Module)
|
||||
pub fn parse_module<'ast>(
|
||||
source: &str,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<Parsed<ModModule<'ast>>, ParseError> {
|
||||
Parser::new(source, Mode::Module, allocator)
|
||||
.parse()
|
||||
.try_into_module()
|
||||
.unwrap()
|
||||
@@ -130,8 +133,11 @@ pub fn parse_module(source: &str) -> Result<Parsed<ModModule>, ParseError> {
|
||||
/// let expr = parse_expression("1 + 2");
|
||||
/// assert!(expr.is_ok());
|
||||
/// ```
|
||||
pub fn parse_expression(source: &str) -> Result<Parsed<ModExpression>, ParseError> {
|
||||
Parser::new(source, Mode::Expression)
|
||||
pub fn parse_expression<'a>(
|
||||
source: &str,
|
||||
allocator: &'a Allocator,
|
||||
) -> Result<Parsed<ModExpression<'a>>, ParseError> {
|
||||
Parser::new(source, Mode::Expression, allocator)
|
||||
.parse()
|
||||
.try_into_expression()
|
||||
.unwrap()
|
||||
@@ -154,12 +160,13 @@ pub fn parse_expression(source: &str) -> Result<Parsed<ModExpression>, ParseErro
|
||||
/// let parsed = parse_expression_range("11 + 22 + 33", TextRange::new(TextSize::new(5), TextSize::new(7)));
|
||||
/// assert!(parsed.is_ok());
|
||||
/// ```
|
||||
pub fn parse_expression_range(
|
||||
pub fn parse_expression_range<'ast>(
|
||||
source: &str,
|
||||
range: TextRange,
|
||||
) -> Result<Parsed<ModExpression>, ParseError> {
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<Parsed<ModExpression<'ast>>, ParseError> {
|
||||
let source = &source[..range.end().to_usize()];
|
||||
Parser::new_starts_at(source, Mode::Expression, range.start())
|
||||
Parser::new_starts_at(source, Mode::Expression, range.start(), allocator)
|
||||
.parse()
|
||||
.try_into_expression()
|
||||
.unwrap()
|
||||
@@ -212,29 +219,41 @@ pub fn parse_expression_range(
|
||||
/// let parsed = parse(source, Mode::Ipython);
|
||||
/// assert!(parsed.is_ok());
|
||||
/// ```
|
||||
pub fn parse(source: &str, mode: Mode) -> Result<Parsed<Mod>, ParseError> {
|
||||
parse_unchecked(source, mode).into_result()
|
||||
pub fn parse<'ast>(
|
||||
source: &str,
|
||||
mode: Mode,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<Parsed<Mod<'ast>>, ParseError> {
|
||||
parse_unchecked(source, mode, allocator).into_result()
|
||||
}
|
||||
|
||||
/// Parse the given Python source code using the specified [`Mode`].
|
||||
///
|
||||
/// This is same as the [`parse`] function except that it doesn't check for any [`ParseError`]
|
||||
/// and returns the [`Parsed`] as is.
|
||||
pub fn parse_unchecked(source: &str, mode: Mode) -> Parsed<Mod> {
|
||||
Parser::new(source, mode).parse()
|
||||
pub fn parse_unchecked<'ast>(
|
||||
source: &str,
|
||||
mode: Mode,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Parsed<Mod<'ast>> {
|
||||
Parser::new(source, mode, allocator).parse()
|
||||
}
|
||||
|
||||
/// Parse the given Python source code using the specified [`PySourceType`].
|
||||
pub fn parse_unchecked_source(source: &str, source_type: PySourceType) -> Parsed<ModModule> {
|
||||
pub fn parse_unchecked_source<'ast>(
|
||||
source: &str,
|
||||
source_type: PySourceType,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Parsed<ModModule<'ast>> {
|
||||
// SAFETY: Safe because `PySourceType` always parses to a `ModModule`
|
||||
Parser::new(source, source_type.as_mode())
|
||||
Parser::new(source, source_type.as_mode(), allocator)
|
||||
.parse()
|
||||
.try_into_module()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Represents the parsed source code.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Parsed<T> {
|
||||
syntax: T,
|
||||
tokens: Tokens,
|
||||
@@ -293,7 +312,16 @@ impl<T> Parsed<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsed<Mod> {
|
||||
impl<T> PartialEq for Parsed<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.syntax == other.syntax && self.tokens == other.tokens && self.errors == other.errors
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Parsed<Mod<'ast>> {
|
||||
/// Attempts to convert the [`Parsed<Mod>`] into a [`Parsed<ModModule>`].
|
||||
///
|
||||
/// This method checks if the `syntax` field of the output is a [`Mod::Module`]. If it is, the
|
||||
@@ -301,7 +329,7 @@ impl Parsed<Mod> {
|
||||
/// returns [`None`].
|
||||
///
|
||||
/// [`Some(Parsed<ModModule>)`]: Some
|
||||
pub fn try_into_module(self) -> Option<Parsed<ModModule>> {
|
||||
pub fn try_into_module(self) -> Option<Parsed<ModModule<'ast>>> {
|
||||
match self.syntax {
|
||||
Mod::Module(module) => Some(Parsed {
|
||||
syntax: module,
|
||||
@@ -319,7 +347,7 @@ impl Parsed<Mod> {
|
||||
/// Otherwise, it returns [`None`].
|
||||
///
|
||||
/// [`Some(Parsed<ModExpression>)`]: Some
|
||||
pub fn try_into_expression(self) -> Option<Parsed<ModExpression>> {
|
||||
pub fn try_into_expression(self) -> Option<Parsed<ModExpression<'ast>>> {
|
||||
match self.syntax {
|
||||
Mod::Module(_) => None,
|
||||
Mod::Expression(expression) => Some(Parsed {
|
||||
@@ -331,32 +359,32 @@ impl Parsed<Mod> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsed<ModModule> {
|
||||
impl<'ast> Parsed<ModModule<'ast>> {
|
||||
/// Returns the module body contained in this parsed output as a [`Suite`].
|
||||
pub fn suite(&self) -> &Suite {
|
||||
pub fn suite(&self) -> &Suite<'ast> {
|
||||
&self.syntax.body
|
||||
}
|
||||
|
||||
/// Consumes the [`Parsed`] output and returns the module body as a [`Suite`].
|
||||
pub fn into_suite(self) -> Suite {
|
||||
pub fn into_suite(self) -> Suite<'ast> {
|
||||
self.syntax.body
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsed<ModExpression> {
|
||||
impl<'ast> Parsed<ModExpression<'ast>> {
|
||||
/// Returns the expression contained in this parsed output.
|
||||
pub fn expr(&self) -> &Expr {
|
||||
pub fn expr(&self) -> &Expr<'ast> {
|
||||
&self.syntax.body
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the expression contained in this parsed output.
|
||||
pub fn expr_mut(&mut self) -> &mut Expr {
|
||||
pub fn expr_mut(&mut self) -> &mut Expr<'ast> {
|
||||
&mut self.syntax.body
|
||||
}
|
||||
|
||||
/// Consumes the [`Parsed`] output and returns the contained [`Expr`].
|
||||
pub fn into_expr(self) -> Expr {
|
||||
*self.syntax.body
|
||||
pub fn into_expr(self) -> Expr<'ast> {
|
||||
self.syntax.body
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ pub(super) const END_EXPR_SET: TokenSet = TokenSet::new([
|
||||
/// Tokens that can appear at the end of a sequence.
|
||||
const END_SEQUENCE_SET: TokenSet = END_EXPR_SET.remove(TokenKind::Comma);
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
impl<'src, 'ast> Parser<'src, 'ast> {
|
||||
/// Returns `true` if the parser is at a name or keyword (including soft keyword) token.
|
||||
pub(super) fn at_name_or_keyword(&self) -> bool {
|
||||
self.at(TokenKind::Name) || self.current_token_kind().is_keyword()
|
||||
@@ -138,7 +138,7 @@ impl<'src> Parser<'src> {
|
||||
/// used to match the `star_expressions` rule.
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
pub(super) fn parse_expression_list(&mut self, context: ExpressionContext) -> ParsedExpr {
|
||||
pub(super) fn parse_expression_list(&mut self, context: ExpressionContext) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
let parsed_expr = self.parse_conditional_expression_or_higher_impl(context);
|
||||
|
||||
@@ -167,7 +167,7 @@ impl<'src> Parser<'src> {
|
||||
pub(super) fn parse_named_expression_or_higher(
|
||||
&mut self,
|
||||
context: ExpressionContext,
|
||||
) -> ParsedExpr {
|
||||
) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
let parsed_expr = self.parse_conditional_expression_or_higher_impl(context);
|
||||
|
||||
@@ -190,14 +190,14 @@ impl<'src> Parser<'src> {
|
||||
/// instead of as a tuple, as done by [`Parser::parse_expression_list`] use this function.
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
pub(super) fn parse_conditional_expression_or_higher(&mut self) -> ParsedExpr {
|
||||
pub(super) fn parse_conditional_expression_or_higher(&mut self) -> ParsedExpr<'ast> {
|
||||
self.parse_conditional_expression_or_higher_impl(ExpressionContext::default())
|
||||
}
|
||||
|
||||
pub(super) fn parse_conditional_expression_or_higher_impl(
|
||||
&mut self,
|
||||
context: ExpressionContext,
|
||||
) -> ParsedExpr {
|
||||
) -> ParsedExpr<'ast> {
|
||||
if self.at(TokenKind::Lambda) {
|
||||
Expr::Lambda(self.parse_lambda_expr()).into()
|
||||
} else {
|
||||
@@ -224,7 +224,7 @@ impl<'src> Parser<'src> {
|
||||
/// specified method to allow parsing lambda expression.
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
fn parse_simple_expression(&mut self, context: ExpressionContext) -> ParsedExpr {
|
||||
fn parse_simple_expression(&mut self, context: ExpressionContext) -> ParsedExpr<'ast> {
|
||||
self.parse_binary_expression_or_higher(OperatorPrecedence::Initial, context)
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
left_precedence: OperatorPrecedence,
|
||||
context: ExpressionContext,
|
||||
) -> ParsedExpr {
|
||||
) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
let lhs = self.parse_lhs_expression(left_precedence, context);
|
||||
self.parse_binary_expression_or_higher_recursive(lhs, left_precedence, context, start)
|
||||
@@ -243,11 +243,11 @@ impl<'src> Parser<'src> {
|
||||
|
||||
pub(super) fn parse_binary_expression_or_higher_recursive(
|
||||
&mut self,
|
||||
mut left: ParsedExpr,
|
||||
mut left: ParsedExpr<'ast>,
|
||||
left_precedence: OperatorPrecedence,
|
||||
context: ExpressionContext,
|
||||
start: TextSize,
|
||||
) -> ParsedExpr {
|
||||
) -> ParsedExpr<'ast> {
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
loop {
|
||||
@@ -292,9 +292,9 @@ impl<'src> Parser<'src> {
|
||||
let right = self.parse_binary_expression_or_higher(new_precedence, context);
|
||||
|
||||
Expr::BinOp(ast::ExprBinOp {
|
||||
left: Box::new(left.expr),
|
||||
left: self.alloc_box(left.expr),
|
||||
op: bin_op,
|
||||
right: Box::new(right.expr),
|
||||
right: self.alloc_box(right.expr),
|
||||
range: self.node_range(start),
|
||||
})
|
||||
}
|
||||
@@ -317,7 +317,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
left_precedence: OperatorPrecedence,
|
||||
context: ExpressionContext,
|
||||
) -> ParsedExpr {
|
||||
) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
let token = self.current_token_kind();
|
||||
|
||||
@@ -416,7 +416,7 @@ impl<'src> Parser<'src> {
|
||||
/// sense, it matches the `bitwise_or` rule of the [Python grammar].
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
fn parse_expression_with_bitwise_or_precedence(&mut self) -> ParsedExpr {
|
||||
fn parse_expression_with_bitwise_or_precedence(&mut self) -> ParsedExpr<'ast> {
|
||||
let parsed_expr = self.parse_conditional_expression_or_higher();
|
||||
|
||||
if parsed_expr.is_parenthesized {
|
||||
@@ -450,7 +450,7 @@ impl<'src> Parser<'src> {
|
||||
/// field will be [`ExprContext::Invalid`].
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#atom-identifiers>
|
||||
pub(super) fn parse_name(&mut self) -> ast::ExprName {
|
||||
pub(super) fn parse_name(&mut self) -> ast::ExprName<'ast> {
|
||||
let identifier = self.parse_identifier();
|
||||
|
||||
let ctx = if identifier.is_valid() {
|
||||
@@ -471,20 +471,26 @@ impl<'src> Parser<'src> {
|
||||
/// For an invalid identifier, the `id` field will be an empty string.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#atom-identifiers>
|
||||
pub(super) fn parse_identifier(&mut self) -> ast::Identifier {
|
||||
pub(super) fn parse_identifier(&mut self) -> ast::Identifier<'ast> {
|
||||
let range = self.current_token_range();
|
||||
|
||||
if self.at(TokenKind::Name) {
|
||||
let TokenValue::Name(name) = self.bump_value(TokenKind::Name) else {
|
||||
unreachable!();
|
||||
};
|
||||
return ast::Identifier { id: name, range };
|
||||
return ast::Identifier {
|
||||
id: self.allocator.alloc_str(&name),
|
||||
range,
|
||||
};
|
||||
}
|
||||
|
||||
if self.current_token_kind().is_soft_keyword() {
|
||||
let id = Name::new(self.src_text(range));
|
||||
self.bump_soft_keyword_as_name();
|
||||
return ast::Identifier { id, range };
|
||||
return ast::Identifier {
|
||||
id: self.allocator.alloc_str(&id),
|
||||
range,
|
||||
};
|
||||
}
|
||||
|
||||
if self.current_token_kind().is_keyword() {
|
||||
@@ -497,7 +503,7 @@ impl<'src> Parser<'src> {
|
||||
range,
|
||||
);
|
||||
|
||||
let id = Name::new(self.src_text(range));
|
||||
let id = self.alloc_str(self.src_text(range));
|
||||
self.bump_any();
|
||||
ast::Identifier { id, range }
|
||||
} else {
|
||||
@@ -507,7 +513,7 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
|
||||
ast::Identifier {
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
range: self.missing_node_range(),
|
||||
}
|
||||
}
|
||||
@@ -516,7 +522,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses an atom.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#atoms>
|
||||
fn parse_atom(&mut self) -> ParsedExpr {
|
||||
fn parse_atom(&mut self) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let lhs = match self.current_token_kind() {
|
||||
@@ -595,7 +601,7 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
Expr::Name(ast::ExprName {
|
||||
range: self.missing_node_range(),
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
ctx: ExprContext::Invalid,
|
||||
})
|
||||
}
|
||||
@@ -611,7 +617,11 @@ impl<'src> Parser<'src> {
|
||||
/// expression, `[` for a subscript expression, or `.` for an attribute expression.
|
||||
///
|
||||
/// This method does nothing if the current token is not a candidate for a postfix expression.
|
||||
pub(super) fn parse_postfix_expression(&mut self, mut lhs: Expr, start: TextSize) -> Expr {
|
||||
pub(super) fn parse_postfix_expression(
|
||||
&mut self,
|
||||
mut lhs: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> Expr<'ast> {
|
||||
loop {
|
||||
lhs = match self.current_token_kind() {
|
||||
TokenKind::Lpar => Expr::Call(self.parse_call_expression(lhs, start)),
|
||||
@@ -632,11 +642,11 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't position at a `(` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#calls>
|
||||
fn parse_call_expression(&mut self, func: Expr, start: TextSize) -> ast::ExprCall {
|
||||
fn parse_call_expression(&mut self, func: Expr<'ast>, start: TextSize) -> ast::ExprCall<'ast> {
|
||||
let arguments = self.parse_arguments();
|
||||
|
||||
ast::ExprCall {
|
||||
func: Box::new(func),
|
||||
func: self.alloc_box(func),
|
||||
arguments,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -649,12 +659,12 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `(` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-argument_list>
|
||||
pub(super) fn parse_arguments(&mut self) -> ast::Arguments {
|
||||
pub(super) fn parse_arguments(&mut self) -> ast::Arguments<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lpar);
|
||||
|
||||
let mut args = vec![];
|
||||
let mut keywords = vec![];
|
||||
let mut args = ruff_allocator::Vec::new_in(&self.allocator);
|
||||
let mut keywords = ruff_allocator::Vec::new_in(&self.allocator);
|
||||
let mut seen_keyword_argument = false; // foo = 1
|
||||
let mut seen_keyword_unpacking = false; // **foo
|
||||
|
||||
@@ -717,7 +727,7 @@ impl<'src> Parser<'src> {
|
||||
&parsed_expr,
|
||||
);
|
||||
ast::Identifier {
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
range: parsed_expr.range(),
|
||||
}
|
||||
};
|
||||
@@ -752,8 +762,8 @@ impl<'src> Parser<'src> {
|
||||
|
||||
let arguments = ast::Arguments {
|
||||
range: self.node_range(start),
|
||||
args: args.into_boxed_slice(),
|
||||
keywords: keywords.into_boxed_slice(),
|
||||
args: args.into_bump_slice_mut(),
|
||||
keywords: keywords.into_bump_slice_mut(),
|
||||
};
|
||||
|
||||
self.validate_arguments(&arguments);
|
||||
@@ -770,9 +780,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#subscriptions>
|
||||
fn parse_subscript_expression(
|
||||
&mut self,
|
||||
mut value: Expr,
|
||||
mut value: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprSubscript {
|
||||
) -> ast::ExprSubscript<'ast> {
|
||||
self.bump(TokenKind::Lsqb);
|
||||
|
||||
// To prevent the `value` context from being `Del` within a `del` statement,
|
||||
@@ -788,10 +798,10 @@ impl<'src> Parser<'src> {
|
||||
self.add_error(ParseErrorType::EmptySlice, slice_range);
|
||||
|
||||
return ast::ExprSubscript {
|
||||
value: Box::new(value),
|
||||
slice: Box::new(Expr::Name(ast::ExprName {
|
||||
value: self.alloc_box(value),
|
||||
slice: self.alloc_box(Expr::Name(ast::ExprName {
|
||||
range: slice_range,
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
ctx: ExprContext::Invalid,
|
||||
})),
|
||||
ctx: ExprContext::Load,
|
||||
@@ -831,8 +841,8 @@ impl<'src> Parser<'src> {
|
||||
self.expect(TokenKind::Rsqb);
|
||||
|
||||
ast::ExprSubscript {
|
||||
value: Box::new(value),
|
||||
slice: Box::new(slice),
|
||||
value: self.alloc_box(value),
|
||||
slice: self.alloc_box(slice),
|
||||
ctx: ExprContext::Load,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -841,7 +851,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a slice expression.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#slicings>
|
||||
fn parse_slice(&mut self) -> Expr {
|
||||
fn parse_slice(&mut self) -> Expr<'ast> {
|
||||
const UPPER_END_SET: TokenSet =
|
||||
TokenSet::new([TokenKind::Comma, TokenKind::Colon, TokenKind::Rsqb])
|
||||
.union(NEWLINE_EOF_SET);
|
||||
@@ -876,18 +886,20 @@ impl<'src> Parser<'src> {
|
||||
|
||||
self.expect(TokenKind::Colon);
|
||||
|
||||
let lower = lower.map(Box::new);
|
||||
let lower = lower.map(|lower| self.alloc_box(lower));
|
||||
let upper = if self.at_ts(UPPER_END_SET) {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
};
|
||||
|
||||
let step = if self.eat(TokenKind::Colon) {
|
||||
if self.at_ts(STEP_END_SET) {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -915,7 +927,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
op: UnaryOp,
|
||||
context: ExpressionContext,
|
||||
) -> ast::ExprUnaryOp {
|
||||
) -> ast::ExprUnaryOp<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::from(op));
|
||||
|
||||
@@ -923,7 +935,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
ast::ExprUnaryOp {
|
||||
op,
|
||||
operand: Box::new(operand.expr),
|
||||
operand: self.alloc_box(operand.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -937,15 +949,15 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#attribute-references>
|
||||
pub(super) fn parse_attribute_expression(
|
||||
&mut self,
|
||||
value: Expr,
|
||||
value: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprAttribute {
|
||||
) -> ast::ExprAttribute<'ast> {
|
||||
self.bump(TokenKind::Dot);
|
||||
|
||||
let attr = self.parse_identifier();
|
||||
|
||||
ast::ExprAttribute {
|
||||
value: Box::new(value),
|
||||
value: self.alloc_box(value),
|
||||
attr,
|
||||
ctx: ExprContext::Load,
|
||||
range: self.node_range(start),
|
||||
@@ -964,11 +976,11 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#boolean-operations>
|
||||
fn parse_boolean_expression(
|
||||
&mut self,
|
||||
lhs: Expr,
|
||||
lhs: Expr<'ast>,
|
||||
start: TextSize,
|
||||
op: BoolOp,
|
||||
context: ExpressionContext,
|
||||
) -> ast::ExprBoolOp {
|
||||
) -> ast::ExprBoolOp<'ast> {
|
||||
self.bump(TokenKind::from(op));
|
||||
|
||||
let mut values = vec![lhs];
|
||||
@@ -1030,15 +1042,16 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#comparisons>
|
||||
fn parse_comparison_expression(
|
||||
&mut self,
|
||||
lhs: Expr,
|
||||
lhs: Expr<'ast>,
|
||||
start: TextSize,
|
||||
op: CmpOp,
|
||||
context: ExpressionContext,
|
||||
) -> ast::ExprCompare {
|
||||
) -> ast::ExprCompare<'ast> {
|
||||
self.bump_cmp_op(op);
|
||||
|
||||
let mut comparators = vec![];
|
||||
let mut operators = vec![op];
|
||||
let mut comparators = ruff_allocator::Vec::new_in(&self.allocator);
|
||||
let mut operators = ruff_allocator::Vec::new_in(&self.allocator);
|
||||
operators.push(op);
|
||||
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
@@ -1067,9 +1080,9 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
ast::ExprCompare {
|
||||
left: Box::new(lhs),
|
||||
ops: operators.into_boxed_slice(),
|
||||
comparators: comparators.into_boxed_slice(),
|
||||
left: self.alloc_box(lhs),
|
||||
ops: operators.into_bump_slice_mut(),
|
||||
comparators: comparators.into_bump_slice_mut(),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -1081,7 +1094,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `String` or `FStringStart` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/grammar.html> (Search "strings:")
|
||||
pub(super) fn parse_strings(&mut self) -> Expr {
|
||||
pub(super) fn parse_strings(&mut self) -> Expr<'ast> {
|
||||
const STRING_START_SET: TokenSet =
|
||||
TokenSet::new([TokenKind::String, TokenKind::FStringStart]);
|
||||
|
||||
@@ -1132,9 +1145,9 @@ impl<'src> Parser<'src> {
|
||||
/// If the length of `strings` is less than 2.
|
||||
fn handle_implicitly_concatenated_strings(
|
||||
&mut self,
|
||||
strings: Vec<StringType>,
|
||||
strings: Vec<StringType<'ast>>,
|
||||
range: TextRange,
|
||||
) -> Expr {
|
||||
) -> Expr<'ast> {
|
||||
assert!(strings.len() > 1);
|
||||
|
||||
let mut has_fstring = false;
|
||||
@@ -1251,7 +1264,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `String` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3.13/reference/lexical_analysis.html#string-and-bytes-literals>
|
||||
fn parse_string_or_byte_literal(&mut self) -> StringType {
|
||||
fn parse_string_or_byte_literal(&mut self) -> StringType<'ast> {
|
||||
let range = self.current_token_range();
|
||||
let flags = self.tokens.current_flags().as_any_string_flags();
|
||||
|
||||
@@ -1259,7 +1272,7 @@ impl<'src> Parser<'src> {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
match parse_string_literal(value, flags, range) {
|
||||
match parse_string_literal(&value, flags, range, self.allocator) {
|
||||
Ok(string) => string,
|
||||
Err(error) => {
|
||||
let location = error.location();
|
||||
@@ -1271,7 +1284,7 @@ impl<'src> Parser<'src> {
|
||||
// rb"a𝐁c123"
|
||||
// b"""123a𝐁c"""
|
||||
StringType::Bytes(ast::BytesLiteral {
|
||||
value: Box::new([]),
|
||||
value: &[],
|
||||
range,
|
||||
flags: ast::BytesLiteralFlags::from(flags).with_invalid(),
|
||||
})
|
||||
@@ -1299,7 +1312,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/grammar.html> (Search "fstring:")
|
||||
/// See: <https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals>
|
||||
fn parse_fstring(&mut self) -> ast::FString {
|
||||
fn parse_fstring(&mut self) -> ast::FString<'ast> {
|
||||
let start = self.node_start();
|
||||
let flags = self.tokens.current_flags().as_any_string_flags();
|
||||
|
||||
@@ -1324,7 +1337,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
flags: ast::AnyStringFlags,
|
||||
kind: FStringElementsKind,
|
||||
) -> FStringElements {
|
||||
) -> FStringElements<'ast> {
|
||||
let mut elements = vec![];
|
||||
|
||||
self.parse_list(RecoveryContextKind::FStringElements(kind), |parser| {
|
||||
@@ -1340,8 +1353,8 @@ impl<'src> Parser<'src> {
|
||||
unreachable!()
|
||||
};
|
||||
FStringElement::Literal(
|
||||
parse_fstring_literal_element(value, flags, range).unwrap_or_else(
|
||||
|lex_error| {
|
||||
parse_fstring_literal_element(&value, flags, range, self.allocator)
|
||||
.unwrap_or_else(|lex_error| {
|
||||
// test_err invalid_fstring_literal_element
|
||||
// f'hello \N{INVALID} world'
|
||||
// f"""hello \N{INVALID} world"""
|
||||
@@ -1354,8 +1367,7 @@ impl<'src> Parser<'src> {
|
||||
value: "".into(),
|
||||
range,
|
||||
}
|
||||
},
|
||||
),
|
||||
}),
|
||||
)
|
||||
}
|
||||
// `Invalid` tokens are created when there's a lexical error, so
|
||||
@@ -1388,7 +1400,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_fstring_expression_element(
|
||||
&mut self,
|
||||
flags: ast::AnyStringFlags,
|
||||
) -> ast::FStringExpressionElement {
|
||||
) -> ast::FStringExpressionElement<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lbrace);
|
||||
|
||||
@@ -1465,7 +1477,7 @@ impl<'src> Parser<'src> {
|
||||
let format_spec = if self.eat(TokenKind::Colon) {
|
||||
let spec_start = self.node_start();
|
||||
let elements = self.parse_fstring_elements(flags, FStringElementsKind::FormatSpec);
|
||||
Some(Box::new(ast::FStringFormatSpec {
|
||||
Some(self.alloc_box(ast::FStringFormatSpec {
|
||||
range: self.node_range(spec_start),
|
||||
elements,
|
||||
}))
|
||||
@@ -1498,7 +1510,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
ast::FStringExpressionElement {
|
||||
expression: Box::new(value.expr),
|
||||
expression: self.alloc_box(value.expr),
|
||||
debug_text,
|
||||
conversion,
|
||||
format_spec,
|
||||
@@ -1513,7 +1525,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `[` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#list-displays>
|
||||
fn parse_list_like_expression(&mut self) -> Expr {
|
||||
fn parse_list_like_expression(&mut self) -> Expr<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
self.bump(TokenKind::Lsqb);
|
||||
@@ -1566,7 +1578,7 @@ impl<'src> Parser<'src> {
|
||||
/// - <https://docs.python.org/3/reference/expressions.html#set-displays>
|
||||
/// - <https://docs.python.org/3/reference/expressions.html#dictionary-displays>
|
||||
/// - <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
|
||||
fn parse_set_or_dict_like_expression(&mut self) -> Expr {
|
||||
fn parse_set_or_dict_like_expression(&mut self) -> Expr<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lbrace);
|
||||
|
||||
@@ -1654,7 +1666,7 @@ impl<'src> Parser<'src> {
|
||||
/// Matches the `(tuple | group | genexp)` rule in the [Python grammar].
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
fn parse_parenthesized_expression(&mut self) -> ParsedExpr {
|
||||
fn parse_parenthesized_expression(&mut self) -> ParsedExpr<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lpar);
|
||||
|
||||
@@ -1735,11 +1747,11 @@ impl<'src> Parser<'src> {
|
||||
/// Uses the `parse_func` to parse each item in the tuple.
|
||||
pub(super) fn parse_tuple_expression(
|
||||
&mut self,
|
||||
first_element: Expr,
|
||||
first_element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
parenthesized: Parenthesized,
|
||||
mut parse_func: impl FnMut(&mut Parser<'src>) -> ParsedExpr,
|
||||
) -> ast::ExprTuple {
|
||||
mut parse_func: impl FnMut(&mut Parser<'src, 'ast>) -> ParsedExpr<'ast>,
|
||||
) -> ast::ExprTuple<'ast> {
|
||||
// TODO(dhruvmanila): Can we remove `parse_func` and use `parenthesized` to
|
||||
// determine the parsing function?
|
||||
|
||||
@@ -1768,7 +1780,11 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a list expression.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#list-displays>
|
||||
fn parse_list_expression(&mut self, first_element: Expr, start: TextSize) -> ast::ExprList {
|
||||
fn parse_list_expression(
|
||||
&mut self,
|
||||
first_element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprList<'ast> {
|
||||
if !self.at_sequence_end() {
|
||||
self.expect(TokenKind::Comma);
|
||||
}
|
||||
@@ -1795,7 +1811,11 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a set expression.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#set-displays>
|
||||
fn parse_set_expression(&mut self, first_element: Expr, start: TextSize) -> ast::ExprSet {
|
||||
fn parse_set_expression(
|
||||
&mut self,
|
||||
first_element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprSet<'ast> {
|
||||
if !self.at_sequence_end() {
|
||||
self.expect(TokenKind::Comma);
|
||||
}
|
||||
@@ -1823,10 +1843,10 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#dictionary-displays>
|
||||
fn parse_dictionary_expression(
|
||||
&mut self,
|
||||
key: Option<Expr>,
|
||||
value: Expr,
|
||||
key: Option<Expr<'ast>>,
|
||||
value: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprDict {
|
||||
) -> ast::ExprDict<'ast> {
|
||||
if !self.at_sequence_end() {
|
||||
self.expect(TokenKind::Comma);
|
||||
}
|
||||
@@ -1866,7 +1886,7 @@ impl<'src> Parser<'src> {
|
||||
/// followed by `if` clauses.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-comp_for>
|
||||
fn parse_generators(&mut self) -> Vec<ast::Comprehension> {
|
||||
fn parse_generators(&mut self) -> Vec<ast::Comprehension<'ast>> {
|
||||
const GENERATOR_SET: TokenSet = TokenSet::new([TokenKind::For, TokenKind::Async]);
|
||||
|
||||
let mut generators = vec![];
|
||||
@@ -1887,7 +1907,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `async` or `for` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
|
||||
fn parse_comprehension(&mut self) -> ast::Comprehension {
|
||||
fn parse_comprehension(&mut self) -> ast::Comprehension<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let is_async = self.eat(TokenKind::Async);
|
||||
@@ -1938,10 +1958,10 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#generator-expressions>
|
||||
pub(super) fn parse_generator_expression(
|
||||
&mut self,
|
||||
element: Expr,
|
||||
element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
parenthesized: Parenthesized,
|
||||
) -> ast::ExprGenerator {
|
||||
) -> ast::ExprGenerator<'ast> {
|
||||
let generators = self.parse_generators();
|
||||
|
||||
if parenthesized.is_yes() {
|
||||
@@ -1949,7 +1969,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
ast::ExprGenerator {
|
||||
elt: Box::new(element),
|
||||
elt: self.alloc_box(element),
|
||||
generators,
|
||||
range: self.node_range(start),
|
||||
parenthesized: parenthesized.is_yes(),
|
||||
@@ -1961,15 +1981,15 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
|
||||
fn parse_list_comprehension_expression(
|
||||
&mut self,
|
||||
element: Expr,
|
||||
element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprListComp {
|
||||
) -> ast::ExprListComp<'ast> {
|
||||
let generators = self.parse_generators();
|
||||
|
||||
self.expect(TokenKind::Rsqb);
|
||||
|
||||
ast::ExprListComp {
|
||||
elt: Box::new(element),
|
||||
elt: self.alloc_box(element),
|
||||
generators,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -1980,17 +2000,17 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
|
||||
fn parse_dictionary_comprehension_expression(
|
||||
&mut self,
|
||||
key: Expr,
|
||||
value: Expr,
|
||||
key: Expr<'ast>,
|
||||
value: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprDictComp {
|
||||
) -> ast::ExprDictComp<'ast> {
|
||||
let generators = self.parse_generators();
|
||||
|
||||
self.expect(TokenKind::Rbrace);
|
||||
|
||||
ast::ExprDictComp {
|
||||
key: Box::new(key),
|
||||
value: Box::new(value),
|
||||
key: self.alloc_box(key),
|
||||
value: self.alloc_box(value),
|
||||
generators,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -2001,15 +2021,15 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries>
|
||||
fn parse_set_comprehension_expression(
|
||||
&mut self,
|
||||
element: Expr,
|
||||
element: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprSetComp {
|
||||
) -> ast::ExprSetComp<'ast> {
|
||||
let generators = self.parse_generators();
|
||||
|
||||
self.expect(TokenKind::Rbrace);
|
||||
|
||||
ast::ExprSetComp {
|
||||
elt: Box::new(element),
|
||||
elt: self.alloc_box(element),
|
||||
generators,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -2031,7 +2051,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `*` token.
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
fn parse_starred_expression(&mut self, context: ExpressionContext) -> ast::ExprStarred {
|
||||
fn parse_starred_expression(&mut self, context: ExpressionContext) -> ast::ExprStarred<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Star);
|
||||
|
||||
@@ -2045,7 +2065,7 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
ast::ExprStarred {
|
||||
value: Box::new(parsed_expr.expr),
|
||||
value: self.alloc_box(parsed_expr.expr),
|
||||
ctx: ExprContext::Load,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -2058,7 +2078,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `await` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#await-expression>
|
||||
fn parse_await_expression(&mut self) -> ast::ExprAwait {
|
||||
fn parse_await_expression(&mut self) -> ast::ExprAwait<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Await);
|
||||
|
||||
@@ -2068,7 +2088,7 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
|
||||
ast::ExprAwait {
|
||||
value: Box::new(parsed_expr.expr),
|
||||
value: self.alloc_box(parsed_expr.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -2080,7 +2100,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `yield` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#yield-expressions>
|
||||
fn parse_yield_expression(&mut self) -> Expr {
|
||||
fn parse_yield_expression(&mut self) -> Expr<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Yield);
|
||||
|
||||
@@ -2089,10 +2109,10 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
let value = self.at_expr().then(|| {
|
||||
Box::new(
|
||||
self.parse_expression_list(ExpressionContext::starred_bitwise_or())
|
||||
.expr,
|
||||
)
|
||||
let expression = self
|
||||
.parse_expression_list(ExpressionContext::starred_bitwise_or())
|
||||
.expr;
|
||||
self.alloc_box(expression)
|
||||
});
|
||||
|
||||
Expr::Yield(ast::ExprYield {
|
||||
@@ -2107,7 +2127,7 @@ impl<'src> Parser<'src> {
|
||||
/// even when parsing a `yield from` expression.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#yield-expressions>
|
||||
fn parse_yield_from_expression(&mut self, start: TextSize) -> Expr {
|
||||
fn parse_yield_from_expression(&mut self, start: TextSize) -> Expr<'ast> {
|
||||
// Grammar:
|
||||
// 'yield' 'from' expression
|
||||
//
|
||||
@@ -2135,7 +2155,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
Expr::YieldFrom(ast::ExprYieldFrom {
|
||||
value: Box::new(expr),
|
||||
value: self.alloc_box(expr),
|
||||
range: self.node_range(start),
|
||||
})
|
||||
}
|
||||
@@ -2149,9 +2169,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#assignment-expressions>
|
||||
pub(super) fn parse_named_expression(
|
||||
&mut self,
|
||||
mut target: Expr,
|
||||
mut target: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprNamed {
|
||||
) -> ast::ExprNamed<'ast> {
|
||||
self.bump(TokenKind::ColonEqual);
|
||||
|
||||
if !target.is_name_expr() {
|
||||
@@ -2162,8 +2182,8 @@ impl<'src> Parser<'src> {
|
||||
let value = self.parse_conditional_expression_or_higher();
|
||||
|
||||
ast::ExprNamed {
|
||||
target: Box::new(target),
|
||||
value: Box::new(value.expr),
|
||||
target: self.alloc_box(target),
|
||||
value: self.alloc_box(value.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -2175,7 +2195,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `lambda` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#lambda>
|
||||
fn parse_lambda_expr(&mut self) -> ast::ExprLambda {
|
||||
fn parse_lambda_expr(&mut self) -> ast::ExprLambda<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lambda);
|
||||
|
||||
@@ -2184,7 +2204,8 @@ impl<'src> Parser<'src> {
|
||||
// lambda: 1
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(self.parse_parameters(FunctionKind::Lambda)))
|
||||
let parameters = self.parse_parameters(FunctionKind::Lambda);
|
||||
Some(self.alloc_box(parameters))
|
||||
};
|
||||
|
||||
self.expect(TokenKind::Colon);
|
||||
@@ -2209,7 +2230,7 @@ impl<'src> Parser<'src> {
|
||||
let body = self.parse_conditional_expression_or_higher();
|
||||
|
||||
ast::ExprLambda {
|
||||
body: Box::new(body.expr),
|
||||
body: self.alloc_box(body.expr),
|
||||
parameters,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -2222,7 +2243,11 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `if` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/expressions.html#conditional-expressions>
|
||||
pub(super) fn parse_if_expression(&mut self, body: Expr, start: TextSize) -> ast::ExprIf {
|
||||
pub(super) fn parse_if_expression(
|
||||
&mut self,
|
||||
body: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::ExprIf<'ast> {
|
||||
self.bump(TokenKind::If);
|
||||
|
||||
let test = self.parse_simple_expression(ExpressionContext::default());
|
||||
@@ -2232,9 +2257,9 @@ impl<'src> Parser<'src> {
|
||||
let orelse = self.parse_conditional_expression_or_higher();
|
||||
|
||||
ast::ExprIf {
|
||||
body: Box::new(body),
|
||||
test: Box::new(test.expr),
|
||||
orelse: Box::new(orelse.expr),
|
||||
body: self.alloc_box(body),
|
||||
test: self.alloc_box(test.expr),
|
||||
orelse: self.alloc_box(orelse.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -2245,7 +2270,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// If the parser isn't positioned at a `IpyEscapeCommand` token.
|
||||
/// If the escape command kind is not `%` or `!`.
|
||||
fn parse_ipython_escape_command_expression(&mut self) -> ast::ExprIpyEscapeCommand {
|
||||
fn parse_ipython_escape_command_expression(&mut self) -> ast::ExprIpyEscapeCommand<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let TokenValue::IpyEscapeCommand { value, kind } =
|
||||
@@ -2262,7 +2287,7 @@ impl<'src> Parser<'src> {
|
||||
let command = ast::ExprIpyEscapeCommand {
|
||||
range: self.node_range(start),
|
||||
kind,
|
||||
value,
|
||||
value: self.alloc_str(&value),
|
||||
};
|
||||
|
||||
if self.mode != Mode::Ipython {
|
||||
@@ -2276,7 +2301,7 @@ impl<'src> Parser<'src> {
|
||||
/// 1. There aren't any duplicate keyword argument
|
||||
/// 2. If there are more than one argument (positional or keyword), all generator expressions
|
||||
/// present should be parenthesized.
|
||||
fn validate_arguments(&mut self, arguments: &ast::Arguments) {
|
||||
fn validate_arguments(&mut self, arguments: &ast::Arguments<'ast>) {
|
||||
let mut all_arg_names =
|
||||
FxHashSet::with_capacity_and_hasher(arguments.keywords.len(), FxBuildHasher);
|
||||
|
||||
@@ -2316,21 +2341,21 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ParsedExpr {
|
||||
pub(super) expr: Expr,
|
||||
pub(super) struct ParsedExpr<'ast> {
|
||||
pub(super) expr: Expr<'ast>,
|
||||
pub(super) is_parenthesized: bool,
|
||||
}
|
||||
|
||||
impl ParsedExpr {
|
||||
impl ParsedExpr<'_> {
|
||||
#[inline]
|
||||
pub(super) const fn is_unparenthesized_starred_expr(&self) -> bool {
|
||||
!self.is_parenthesized && self.expr.is_starred_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expr> for ParsedExpr {
|
||||
impl<'ast> From<Expr<'ast>> for ParsedExpr<'ast> {
|
||||
#[inline]
|
||||
fn from(expr: Expr) -> Self {
|
||||
fn from(expr: Expr<'ast>) -> Self {
|
||||
ParsedExpr {
|
||||
expr,
|
||||
is_parenthesized: false,
|
||||
@@ -2338,15 +2363,15 @@ impl From<Expr> for ParsedExpr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ParsedExpr {
|
||||
type Target = Expr;
|
||||
impl<'ast> Deref for ParsedExpr<'ast> {
|
||||
type Target = Expr<'ast>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.expr
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for ParsedExpr {
|
||||
impl Ranged for ParsedExpr<'_> {
|
||||
#[inline]
|
||||
fn range(&self) -> TextRange {
|
||||
self.expr.range()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use ruff_allocator::Allocator;
|
||||
use ruff_python_ast::{Mod, ModExpression, ModModule};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
@@ -23,7 +23,7 @@ mod statement;
|
||||
mod tests;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Parser<'src> {
|
||||
pub(crate) struct Parser<'src, 'ast> {
|
||||
source: &'src str,
|
||||
|
||||
/// Token source for the parser that skips over any non-trivia token.
|
||||
@@ -47,16 +47,23 @@ pub(crate) struct Parser<'src> {
|
||||
|
||||
/// The start offset in the source code from which to start parsing at.
|
||||
start_offset: TextSize,
|
||||
|
||||
allocator: &'ast Allocator,
|
||||
}
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
impl<'src, 'ast> Parser<'src, 'ast> {
|
||||
/// Create a new parser for the given source code.
|
||||
pub(crate) fn new(source: &'src str, mode: Mode) -> Self {
|
||||
Parser::new_starts_at(source, mode, TextSize::new(0))
|
||||
pub(crate) fn new(source: &'src str, mode: Mode, allocator: &'ast Allocator) -> Self {
|
||||
Parser::new_starts_at(source, mode, TextSize::new(0), allocator)
|
||||
}
|
||||
|
||||
/// Create a new parser for the given source code which starts parsing at the given offset.
|
||||
pub(crate) fn new_starts_at(source: &'src str, mode: Mode, start_offset: TextSize) -> Self {
|
||||
pub(crate) fn new_starts_at(
|
||||
source: &'src str,
|
||||
mode: Mode,
|
||||
start_offset: TextSize,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Self {
|
||||
let tokens = TokenSource::from_source(source, mode, start_offset);
|
||||
|
||||
Parser {
|
||||
@@ -68,11 +75,12 @@ impl<'src> Parser<'src> {
|
||||
prev_token_end: TextSize::new(0),
|
||||
start_offset,
|
||||
current_token_id: TokenId::default(),
|
||||
allocator,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the [`Parser`] and returns the parsed [`Parsed`].
|
||||
pub(crate) fn parse(mut self) -> Parsed<Mod> {
|
||||
pub(crate) fn parse(mut self) -> Parsed<Mod<'ast>> {
|
||||
let syntax = match self.mode {
|
||||
Mode::Expression => Mod::Expression(self.parse_single_expression()),
|
||||
Mode::Module | Mode::Ipython => Mod::Module(self.parse_module()),
|
||||
@@ -89,7 +97,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// After parsing a single expression, an error is reported and all remaining tokens are
|
||||
/// dropped by the parser.
|
||||
fn parse_single_expression(&mut self) -> ModExpression {
|
||||
fn parse_single_expression(&mut self) -> ModExpression<'ast> {
|
||||
let start = self.node_start();
|
||||
let parsed_expr = self.parse_expression_list(ExpressionContext::default());
|
||||
|
||||
@@ -116,7 +124,7 @@ impl<'src> Parser<'src> {
|
||||
self.bump(TokenKind::EndOfFile);
|
||||
|
||||
ModExpression {
|
||||
body: Box::new(parsed_expr.expr),
|
||||
body: parsed_expr.expr,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -124,7 +132,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a Python module.
|
||||
///
|
||||
/// This is to be used for [`Mode::Module`] and [`Mode::Ipython`].
|
||||
fn parse_module(&mut self) -> ModModule {
|
||||
fn parse_module(&mut self) -> ModModule<'ast> {
|
||||
let body = self.parse_list_into_vec(
|
||||
RecoveryContextKind::ModuleStatements,
|
||||
Parser::parse_statement,
|
||||
@@ -138,6 +146,21 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_box<T>(&self, value: T) -> ruff_allocator::Box<'ast, T> {
|
||||
ruff_allocator::Box::new_in(value, &self.allocator)
|
||||
}
|
||||
|
||||
fn alloc<T>(&self, value: T) -> &'ast mut T {
|
||||
self.allocator.alloc(value)
|
||||
}
|
||||
|
||||
fn alloc_str<T>(&self, value: T) -> &'ast mut str
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
self.allocator.alloc_str(value.as_ref())
|
||||
}
|
||||
|
||||
fn finish(self, syntax: Mod) -> Parsed<Mod> {
|
||||
assert_eq!(
|
||||
self.current_token_kind(),
|
||||
@@ -444,7 +467,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_list_into_vec<T>(
|
||||
&mut self,
|
||||
recovery_context_kind: RecoveryContextKind,
|
||||
parse_element: impl Fn(&mut Parser<'src>) -> T,
|
||||
parse_element: impl Fn(&mut Parser<'src, 'ast>) -> T,
|
||||
) -> Vec<T> {
|
||||
let mut elements = Vec::new();
|
||||
self.parse_list(recovery_context_kind, |p| elements.push(parse_element(p)));
|
||||
@@ -461,7 +484,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_list(
|
||||
&mut self,
|
||||
recovery_context_kind: RecoveryContextKind,
|
||||
mut parse_element: impl FnMut(&mut Parser<'src>),
|
||||
mut parse_element: impl FnMut(&mut Parser<'src, 'ast>),
|
||||
) {
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
@@ -503,7 +526,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_comma_separated_list_into_vec<T>(
|
||||
&mut self,
|
||||
recovery_context_kind: RecoveryContextKind,
|
||||
parse_element: impl Fn(&mut Parser<'src>) -> T,
|
||||
parse_element: impl Fn(&mut Parser<'src, 'ast>) -> T,
|
||||
) -> Vec<T> {
|
||||
let mut elements = Vec::new();
|
||||
self.parse_comma_separated_list(recovery_context_kind, |p| elements.push(parse_element(p)));
|
||||
@@ -520,7 +543,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_comma_separated_list(
|
||||
&mut self,
|
||||
recovery_context_kind: RecoveryContextKind,
|
||||
mut parse_element: impl FnMut(&mut Parser<'src>),
|
||||
mut parse_element: impl FnMut(&mut Parser<'src, 'ast>),
|
||||
) {
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext, Number, Operator, Pattern, Singleton};
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
@@ -49,7 +48,7 @@ const MAPPING_PATTERN_START_SET: TokenSet = TokenSet::new([
|
||||
])
|
||||
.union(LITERAL_PATTERN_START_SET);
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
impl<'src, 'ast> Parser<'src, 'ast> {
|
||||
/// Returns `true` if the current token is a valid start of a pattern.
|
||||
pub(super) fn at_pattern_start(&self) -> bool {
|
||||
self.at_ts(PATTERN_START_SET) || self.at_soft_keyword()
|
||||
@@ -63,7 +62,7 @@ impl<'src> Parser<'src> {
|
||||
/// Entry point to start parsing a pattern.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-patterns>
|
||||
pub(super) fn parse_match_patterns(&mut self) -> Pattern {
|
||||
pub(super) fn parse_match_patterns(&mut self) -> Pattern<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
// We don't yet know if it's a sequence pattern or a single pattern, so
|
||||
@@ -84,7 +83,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses an `or_pattern` or an `as_pattern`.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-pattern>
|
||||
fn parse_match_pattern(&mut self, allow_star_pattern: AllowStarPattern) -> Pattern {
|
||||
fn parse_match_pattern(&mut self, allow_star_pattern: AllowStarPattern) -> Pattern<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
// We don't yet know if it's an or pattern or an as pattern, so use whatever
|
||||
@@ -124,7 +123,7 @@ impl<'src> Parser<'src> {
|
||||
lhs = Pattern::MatchAs(ast::PatternMatchAs {
|
||||
range: self.node_range(start),
|
||||
name: Some(ident),
|
||||
pattern: Some(Box::new(lhs)),
|
||||
pattern: Some(self.alloc_box(lhs)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a pattern.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-closed_pattern>
|
||||
fn parse_match_pattern_lhs(&mut self, allow_star_pattern: AllowStarPattern) -> Pattern {
|
||||
fn parse_match_pattern_lhs(&mut self, allow_star_pattern: AllowStarPattern) -> Pattern<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let mut lhs = match self.current_token_kind() {
|
||||
@@ -171,7 +170,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `{` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#mapping-patterns>
|
||||
fn parse_match_pattern_mapping(&mut self) -> ast::PatternMatchMapping {
|
||||
fn parse_match_pattern_mapping(&mut self) -> ast::PatternMatchMapping<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lbrace);
|
||||
|
||||
@@ -199,7 +198,7 @@ impl<'src> Parser<'src> {
|
||||
rest = Some(identifier);
|
||||
} else {
|
||||
let key = match parser.parse_match_pattern_lhs(AllowStarPattern::No) {
|
||||
Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => *value,
|
||||
Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => value.into_inner(),
|
||||
Pattern::MatchSingleton(ast::PatternMatchSingleton { value, range }) => {
|
||||
match value {
|
||||
Singleton::None => Expr::NoneLiteral(ast::ExprNoneLiteral { range }),
|
||||
@@ -217,7 +216,7 @@ impl<'src> Parser<'src> {
|
||||
ParseErrorType::OtherError("Invalid mapping pattern key".to_string()),
|
||||
&pattern,
|
||||
);
|
||||
recovery::pattern_to_expr(pattern)
|
||||
recovery::pattern_to_expr(pattern, &parser.allocator)
|
||||
}
|
||||
};
|
||||
keys.push(key);
|
||||
@@ -254,7 +253,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `*` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-star_pattern>
|
||||
fn parse_match_pattern_star(&mut self) -> ast::PatternMatchStar {
|
||||
fn parse_match_pattern_star(&mut self) -> ast::PatternMatchStar<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Star);
|
||||
|
||||
@@ -277,7 +276,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `(` or `[` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#sequence-patterns>
|
||||
fn parse_parenthesized_or_sequence_pattern(&mut self) -> Pattern {
|
||||
fn parse_parenthesized_or_sequence_pattern(&mut self) -> Pattern<'ast> {
|
||||
let start = self.node_start();
|
||||
let parentheses = if self.eat(TokenKind::Lpar) {
|
||||
SequenceMatchPatternParentheses::Tuple
|
||||
@@ -333,10 +332,10 @@ impl<'src> Parser<'src> {
|
||||
/// [open sequence pattern]: https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-open_sequence_pattern
|
||||
fn parse_sequence_match_pattern(
|
||||
&mut self,
|
||||
first_element: Pattern,
|
||||
first_element: Pattern<'ast>,
|
||||
start: TextSize,
|
||||
parentheses: Option<SequenceMatchPatternParentheses>,
|
||||
) -> ast::PatternMatchSequence {
|
||||
) -> ast::PatternMatchSequence<'ast> {
|
||||
if parentheses.is_some_and(|parentheses| {
|
||||
self.at(parentheses.closing_kind()) || self.peek() == parentheses.closing_kind()
|
||||
}) {
|
||||
@@ -366,7 +365,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a literal pattern.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-literal_pattern>
|
||||
fn parse_match_pattern_literal(&mut self) -> Pattern {
|
||||
fn parse_match_pattern_literal(&mut self) -> Pattern<'ast> {
|
||||
let start = self.node_start();
|
||||
match self.current_token_kind() {
|
||||
TokenKind::None => {
|
||||
@@ -394,7 +393,7 @@ impl<'src> Parser<'src> {
|
||||
let str = self.parse_strings();
|
||||
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(str),
|
||||
value: self.alloc_box(str),
|
||||
range: self.node_range(start),
|
||||
})
|
||||
}
|
||||
@@ -405,7 +404,7 @@ impl<'src> Parser<'src> {
|
||||
let range = self.node_range(start);
|
||||
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: self.alloc_box(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: Number::Complex { real, imag },
|
||||
range,
|
||||
})),
|
||||
@@ -419,7 +418,7 @@ impl<'src> Parser<'src> {
|
||||
let range = self.node_range(start);
|
||||
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: self.alloc_box(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: Number::Int(value),
|
||||
range,
|
||||
})),
|
||||
@@ -433,7 +432,7 @@ impl<'src> Parser<'src> {
|
||||
let range = self.node_range(start);
|
||||
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: self.alloc_box(Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: Number::Float(value),
|
||||
range,
|
||||
})),
|
||||
@@ -462,7 +461,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
return Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(Expr::UnaryOp(unary_expr)),
|
||||
value: self.alloc_box(Expr::UnaryOp(unary_expr)),
|
||||
range: self.node_range(start),
|
||||
});
|
||||
}
|
||||
@@ -481,7 +480,7 @@ impl<'src> Parser<'src> {
|
||||
let attribute = self.parse_attr_expr_for_match_pattern(id, start);
|
||||
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
value: Box::new(attribute),
|
||||
value: self.alloc_box(attribute),
|
||||
range: self.node_range(start),
|
||||
})
|
||||
} else {
|
||||
@@ -511,12 +510,12 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
let invalid_node = Expr::Name(ast::ExprName {
|
||||
range: self.missing_node_range(),
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
ctx: ExprContext::Invalid,
|
||||
});
|
||||
Pattern::MatchValue(ast::PatternMatchValue {
|
||||
range: invalid_node.range(),
|
||||
value: Box::new(invalid_node),
|
||||
value: self.alloc_box(invalid_node),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -533,9 +532,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#literal-patterns>
|
||||
fn parse_complex_literal_pattern(
|
||||
&mut self,
|
||||
lhs: Pattern,
|
||||
lhs: Pattern<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::PatternMatchValue {
|
||||
) -> ast::PatternMatchValue<'ast> {
|
||||
let operator = if self.eat(TokenKind::Plus) {
|
||||
Operator::Add
|
||||
} else {
|
||||
@@ -550,7 +549,7 @@ impl<'src> Parser<'src> {
|
||||
lhs.value
|
||||
} else {
|
||||
self.add_error(ParseErrorType::ExpectedRealNumber, &lhs);
|
||||
Box::new(recovery::pattern_to_expr(lhs))
|
||||
self.alloc_box(recovery::pattern_to_expr(lhs, &self.allocator))
|
||||
};
|
||||
|
||||
let rhs_pattern = self.parse_match_pattern_lhs(AllowStarPattern::No);
|
||||
@@ -561,13 +560,13 @@ impl<'src> Parser<'src> {
|
||||
rhs.value
|
||||
} else {
|
||||
self.add_error(ParseErrorType::ExpectedImaginaryNumber, &rhs_pattern);
|
||||
Box::new(recovery::pattern_to_expr(rhs_pattern))
|
||||
self.alloc_box(recovery::pattern_to_expr(rhs_pattern, &self.allocator))
|
||||
};
|
||||
|
||||
let range = self.node_range(start);
|
||||
|
||||
ast::PatternMatchValue {
|
||||
value: Box::new(Expr::BinOp(ast::ExprBinOp {
|
||||
value: self.alloc_box(Expr::BinOp(ast::ExprBinOp {
|
||||
left: lhs_value,
|
||||
op: operator,
|
||||
right: rhs_value,
|
||||
@@ -578,7 +577,11 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
/// Parses an attribute expression until the current token is not a `.`.
|
||||
fn parse_attr_expr_for_match_pattern(&mut self, mut lhs: Expr, start: TextSize) -> Expr {
|
||||
fn parse_attr_expr_for_match_pattern(
|
||||
&mut self,
|
||||
mut lhs: Expr<'ast>,
|
||||
start: TextSize,
|
||||
) -> Expr<'ast> {
|
||||
while self.current_token_kind() == TokenKind::Dot {
|
||||
lhs = Expr::Attribute(self.parse_attribute_expression(lhs, start));
|
||||
}
|
||||
@@ -597,9 +600,9 @@ impl<'src> Parser<'src> {
|
||||
/// [pattern arguments]: https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-pattern_arguments
|
||||
fn parse_match_pattern_class(
|
||||
&mut self,
|
||||
cls: Pattern,
|
||||
cls: Pattern<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::PatternMatchClass {
|
||||
) -> ast::PatternMatchClass<'ast> {
|
||||
let arguments_start = self.node_start();
|
||||
|
||||
let cls = match cls {
|
||||
@@ -609,15 +612,15 @@ impl<'src> Parser<'src> {
|
||||
..
|
||||
}) => {
|
||||
if ident.is_valid() {
|
||||
Box::new(Expr::Name(ast::ExprName {
|
||||
self.alloc_box(Expr::Name(ast::ExprName {
|
||||
range: ident.range(),
|
||||
id: ident.id,
|
||||
ctx: ExprContext::Load,
|
||||
}))
|
||||
} else {
|
||||
Box::new(Expr::Name(ast::ExprName {
|
||||
self.alloc_box(Expr::Name(ast::ExprName {
|
||||
range: ident.range(),
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
ctx: ExprContext::Invalid,
|
||||
}))
|
||||
}
|
||||
@@ -632,7 +635,7 @@ impl<'src> Parser<'src> {
|
||||
ParseErrorType::OtherError("Invalid value for a class pattern".to_string()),
|
||||
&pattern,
|
||||
);
|
||||
Box::new(recovery::pattern_to_expr(pattern))
|
||||
self.alloc_box(recovery::pattern_to_expr(pattern, &self.allocator))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -668,7 +671,7 @@ impl<'src> Parser<'src> {
|
||||
&pattern,
|
||||
);
|
||||
ast::Identifier {
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
range: parser.missing_node_range(),
|
||||
}
|
||||
};
|
||||
@@ -724,7 +727,7 @@ impl AllowStarPattern {
|
||||
|
||||
/// Returns `true` if the given expression is a real number literal or a unary
|
||||
/// addition or subtraction of a real number literal.
|
||||
const fn is_real_number(expr: &Expr) -> bool {
|
||||
fn is_real_number(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: ast::Number::Int(_) | ast::Number::Float(_),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_allocator::Allocator;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext, Pattern};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
@@ -25,7 +25,10 @@ use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
/// This function returns an invalid [`ast::ExprName`] if the given pattern is a [`Pattern::MatchAs`]
|
||||
/// with both the pattern and name present. This is because it cannot be converted to an expression
|
||||
/// without dropping one of them as there's no way to represent `x as y` as a valid expression.
|
||||
pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
pub(super) fn pattern_to_expr<'ast>(
|
||||
pattern: Pattern<'ast>,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Expr<'ast> {
|
||||
match pattern {
|
||||
Pattern::MatchSingleton(ast::PatternMatchSingleton { range, value }) => match value {
|
||||
ast::Singleton::True => {
|
||||
@@ -37,11 +40,14 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
}),
|
||||
ast::Singleton::None => Expr::NoneLiteral(ast::ExprNoneLiteral { range }),
|
||||
},
|
||||
Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => *value,
|
||||
Pattern::MatchValue(ast::PatternMatchValue { value, .. }) => value.into_inner(),
|
||||
// We don't know which kind of sequence this is: `case [1, 2]:` or `case (1, 2):`.
|
||||
Pattern::MatchSequence(ast::PatternMatchSequence { range, patterns }) => {
|
||||
Expr::List(ast::ExprList {
|
||||
elts: patterns.into_iter().map(pattern_to_expr).collect(),
|
||||
elts: patterns
|
||||
.into_iter()
|
||||
.map(|pattern| pattern_to_expr(pattern, allocator))
|
||||
.collect(),
|
||||
ctx: ExprContext::Store,
|
||||
range,
|
||||
})
|
||||
@@ -57,7 +63,7 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
.zip(patterns)
|
||||
.map(|(key, pattern)| ast::DictItem {
|
||||
key: Some(key),
|
||||
value: pattern_to_expr(pattern),
|
||||
value: pattern_to_expr(pattern, allocator),
|
||||
})
|
||||
.collect();
|
||||
if let Some(rest) = rest {
|
||||
@@ -79,41 +85,46 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
func: cls,
|
||||
arguments: ast::Arguments {
|
||||
range: arguments.range,
|
||||
args: arguments
|
||||
.patterns
|
||||
.into_iter()
|
||||
.map(pattern_to_expr)
|
||||
.collect(),
|
||||
keywords: arguments
|
||||
.keywords
|
||||
.into_iter()
|
||||
.map(|keyword_pattern| ast::Keyword {
|
||||
args: allocator.alloc_slice_fill_iter(
|
||||
arguments
|
||||
.patterns
|
||||
.into_iter()
|
||||
.map(|pattern| pattern_to_expr(pattern, allocator)),
|
||||
),
|
||||
keywords: allocator.alloc_slice_fill_iter(arguments.keywords.into_iter().map(
|
||||
|keyword_pattern| ast::Keyword {
|
||||
range: keyword_pattern.range,
|
||||
arg: Some(keyword_pattern.attr),
|
||||
value: pattern_to_expr(keyword_pattern.pattern),
|
||||
})
|
||||
.collect(),
|
||||
value: pattern_to_expr(keyword_pattern.pattern, allocator),
|
||||
},
|
||||
)),
|
||||
},
|
||||
}),
|
||||
Pattern::MatchStar(ast::PatternMatchStar { range, name }) => {
|
||||
if let Some(name) = name {
|
||||
Expr::Starred(ast::ExprStarred {
|
||||
range,
|
||||
value: Box::new(Expr::Name(ast::ExprName {
|
||||
range: name.range,
|
||||
id: name.id,
|
||||
ctx: ExprContext::Store,
|
||||
})),
|
||||
value: ruff_allocator::Box::new_in(
|
||||
Expr::Name(ast::ExprName {
|
||||
range: name.range,
|
||||
id: name.id,
|
||||
ctx: ExprContext::Store,
|
||||
}),
|
||||
allocator,
|
||||
),
|
||||
ctx: ExprContext::Store,
|
||||
})
|
||||
} else {
|
||||
Expr::Starred(ast::ExprStarred {
|
||||
range,
|
||||
value: Box::new(Expr::Name(ast::ExprName {
|
||||
range: TextRange::new(range.end() - "_".text_len(), range.end()),
|
||||
id: Name::new_static("_"),
|
||||
ctx: ExprContext::Store,
|
||||
})),
|
||||
value: ruff_allocator::Box::new_in(
|
||||
Expr::Name(ast::ExprName {
|
||||
range: TextRange::new(range.end() - "_".text_len(), range.end()),
|
||||
id: "_",
|
||||
ctx: ExprContext::Store,
|
||||
}),
|
||||
allocator,
|
||||
),
|
||||
ctx: ExprContext::Store,
|
||||
})
|
||||
}
|
||||
@@ -125,10 +136,10 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
}) => match (pattern, name) {
|
||||
(Some(_), Some(_)) => Expr::Name(ast::ExprName {
|
||||
range,
|
||||
id: Name::empty(),
|
||||
id: "",
|
||||
ctx: ExprContext::Invalid,
|
||||
}),
|
||||
(Some(pattern), None) => pattern_to_expr(*pattern),
|
||||
(Some(pattern), None) => pattern_to_expr(pattern.into_inner(), allocator),
|
||||
(None, Some(name)) => Expr::Name(ast::ExprName {
|
||||
range: name.range,
|
||||
id: name.id,
|
||||
@@ -136,16 +147,16 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
}),
|
||||
(None, None) => Expr::Name(ast::ExprName {
|
||||
range,
|
||||
id: Name::new_static("_"),
|
||||
id: "_",
|
||||
ctx: ExprContext::Store,
|
||||
}),
|
||||
},
|
||||
Pattern::MatchOr(ast::PatternMatchOr { patterns, .. }) => {
|
||||
let to_bin_expr = |left: Pattern, right: Pattern| ast::ExprBinOp {
|
||||
let to_bin_expr = |left: Pattern<'ast>, right: Pattern<'ast>| ast::ExprBinOp {
|
||||
range: TextRange::new(left.start(), right.end()),
|
||||
left: Box::new(pattern_to_expr(left)),
|
||||
left: ruff_allocator::Box::new_in(pattern_to_expr(left, allocator), allocator),
|
||||
op: ast::Operator::BitOr,
|
||||
right: Box::new(pattern_to_expr(right)),
|
||||
right: ruff_allocator::Box::new_in(pattern_to_expr(right, allocator), allocator),
|
||||
};
|
||||
|
||||
let mut iter = patterns.into_iter();
|
||||
@@ -155,9 +166,12 @@ pub(super) fn pattern_to_expr(pattern: Pattern) -> Expr {
|
||||
Expr::BinOp(iter.fold(to_bin_expr(left, right), |expr_bin_op, pattern| {
|
||||
ast::ExprBinOp {
|
||||
range: TextRange::new(expr_bin_op.start(), pattern.end()),
|
||||
left: Box::new(Expr::BinOp(expr_bin_op)),
|
||||
left: ruff_allocator::Box::new_in(Expr::BinOp(expr_bin_op), allocator),
|
||||
op: ast::Operator::BitOr,
|
||||
right: Box::new(pattern_to_expr(pattern)),
|
||||
right: ruff_allocator::Box::new_in(
|
||||
pattern_to_expr(pattern, allocator),
|
||||
allocator,
|
||||
),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/parser/tests.rs
|
||||
assertion_line: 56
|
||||
expression: parsed.expr()
|
||||
---
|
||||
Name(
|
||||
ExprName {
|
||||
range: 0..5,
|
||||
id: "first",
|
||||
ctx: Load,
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,400 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/parser/tests.rs
|
||||
assertion_line: 144
|
||||
expression: parsed.syntax()
|
||||
---
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..929,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 21..42,
|
||||
value: BinOp(
|
||||
ExprBinOp {
|
||||
range: 27..40,
|
||||
left: Name(
|
||||
ExprName {
|
||||
range: 27..28,
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
op: Mod,
|
||||
right: Name(
|
||||
ExprName {
|
||||
range: 39..40,
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 66..73,
|
||||
kind: Help2,
|
||||
value: "a.foo",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 74..80,
|
||||
kind: Help,
|
||||
value: "a.foo",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 81..88,
|
||||
kind: Help,
|
||||
value: "a.foo",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 89..100,
|
||||
kind: Help2,
|
||||
value: "a.foo()",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 115..128,
|
||||
kind: Magic,
|
||||
value: "timeit a = b",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 129..147,
|
||||
kind: Magic,
|
||||
value: "timeit foo(b) % 3",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 148..176,
|
||||
kind: Magic,
|
||||
value: "alias showPath pwd && ls -a",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 177..205,
|
||||
kind: Magic,
|
||||
value: "timeit a = foo(b); b = 2",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 206..226,
|
||||
kind: Magic,
|
||||
value: "matplotlib --inline",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 227..253,
|
||||
kind: Magic,
|
||||
value: "matplotlib --inline",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 277..309,
|
||||
kind: Shell,
|
||||
value: "pwd && ls -a | sed 's/^/\\ /'",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 310..347,
|
||||
kind: Shell,
|
||||
value: "pwd && ls -a | sed 's/^/\\\\ /'",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 348..393,
|
||||
kind: ShCap,
|
||||
value: "cd /Users/foo/Library/Application\\ Support/",
|
||||
},
|
||||
),
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 566..626,
|
||||
is_async: false,
|
||||
decorator_list: [],
|
||||
name: Identifier {
|
||||
id: "foo",
|
||||
range: 570..573,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 573..575,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Return(
|
||||
StmtReturn {
|
||||
range: 581..626,
|
||||
value: Some(
|
||||
Compare(
|
||||
ExprCompare {
|
||||
range: 598..620,
|
||||
left: Name(
|
||||
ExprName {
|
||||
range: 598..599,
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
ops: [
|
||||
NotEq,
|
||||
],
|
||||
comparators: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 619..620,
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 656..664,
|
||||
kind: Paren,
|
||||
value: "foo 1 2",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 665..673,
|
||||
kind: Quote2,
|
||||
value: "foo 1 2",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 674..682,
|
||||
kind: Quote,
|
||||
value: "foo 1 2",
|
||||
},
|
||||
),
|
||||
For(
|
||||
StmtFor {
|
||||
range: 711..737,
|
||||
is_async: false,
|
||||
target: Name(
|
||||
ExprName {
|
||||
range: 715..716,
|
||||
id: "a",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
iter: Call(
|
||||
ExprCall {
|
||||
range: 720..728,
|
||||
func: Name(
|
||||
ExprName {
|
||||
range: 720..725,
|
||||
id: "range",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
arguments: Arguments {
|
||||
range: 725..728,
|
||||
args: [
|
||||
NumberLiteral(
|
||||
ExprNumberLiteral {
|
||||
range: 726..727,
|
||||
value: Int(
|
||||
5,
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
body: [
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 734..737,
|
||||
kind: Shell,
|
||||
value: "ls",
|
||||
},
|
||||
),
|
||||
],
|
||||
orelse: [],
|
||||
},
|
||||
),
|
||||
Assign(
|
||||
StmtAssign {
|
||||
range: 739..748,
|
||||
targets: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 739..741,
|
||||
id: "p1",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
],
|
||||
value: IpyEscapeCommand(
|
||||
ExprIpyEscapeCommand {
|
||||
range: 744..748,
|
||||
kind: Shell,
|
||||
value: "pwd",
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
AnnAssign(
|
||||
StmtAnnAssign {
|
||||
range: 749..763,
|
||||
target: Name(
|
||||
ExprName {
|
||||
range: 749..751,
|
||||
id: "p2",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
annotation: Name(
|
||||
ExprName {
|
||||
range: 753..756,
|
||||
id: "str",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
value: Some(
|
||||
IpyEscapeCommand(
|
||||
ExprIpyEscapeCommand {
|
||||
range: 759..763,
|
||||
kind: Shell,
|
||||
value: "pwd",
|
||||
},
|
||||
),
|
||||
),
|
||||
simple: true,
|
||||
},
|
||||
),
|
||||
Assign(
|
||||
StmtAssign {
|
||||
range: 764..784,
|
||||
targets: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 764..767,
|
||||
id: "foo",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
],
|
||||
value: IpyEscapeCommand(
|
||||
ExprIpyEscapeCommand {
|
||||
range: 770..784,
|
||||
kind: Magic,
|
||||
value: "foo bar",
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 786..791,
|
||||
kind: Magic,
|
||||
value: " foo",
|
||||
},
|
||||
),
|
||||
Assign(
|
||||
StmtAssign {
|
||||
range: 792..813,
|
||||
targets: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 792..795,
|
||||
id: "foo",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
],
|
||||
value: IpyEscapeCommand(
|
||||
ExprIpyEscapeCommand {
|
||||
range: 798..813,
|
||||
kind: Magic,
|
||||
value: "foo # comment",
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 838..842,
|
||||
kind: Help,
|
||||
value: "foo",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 843..852,
|
||||
kind: Help2,
|
||||
value: "foo.bar",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 853..865,
|
||||
kind: Help,
|
||||
value: "foo.bar.baz",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 866..874,
|
||||
kind: Help2,
|
||||
value: "foo[0]",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 875..885,
|
||||
kind: Help,
|
||||
value: "foo[0][1]",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 886..905,
|
||||
kind: Help2,
|
||||
value: "foo.bar[0].baz[1]",
|
||||
},
|
||||
),
|
||||
IpyEscapeCommand(
|
||||
StmtIpyEscapeCommand {
|
||||
range: 906..929,
|
||||
kind: Help2,
|
||||
value: "foo.bar[0].baz[2].egg",
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/parser/tests.rs
|
||||
assertion_line: 66
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Assign(
|
||||
StmtAssign {
|
||||
range: 0..37,
|
||||
targets: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 0..1,
|
||||
id: "x",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
],
|
||||
value: StringLiteral(
|
||||
ExprStringLiteral {
|
||||
range: 4..37,
|
||||
value: StringLiteralValue {
|
||||
inner: Single(
|
||||
StringLiteral {
|
||||
range: 4..37,
|
||||
value: "\u{8}another cool trick",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Double,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,9 +1,7 @@
|
||||
use compact_str::CompactString;
|
||||
use std::fmt::Display;
|
||||
|
||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_ast::{
|
||||
self as ast, ExceptHandler, Expr, ExprContext, IpyEscapeKind, Operator, Stmt, WithItem,
|
||||
};
|
||||
@@ -77,7 +75,7 @@ const AUGMENTED_ASSIGN_SET: TokenSet = TokenSet::new([
|
||||
TokenKind::RightShiftEqual,
|
||||
]);
|
||||
|
||||
impl<'src> Parser<'src> {
|
||||
impl<'src, 'ast> Parser<'src, 'ast> {
|
||||
/// Returns `true` if the current token is the start of a compound statement.
|
||||
pub(super) fn at_compound_stmt(&self) -> bool {
|
||||
self.at_ts(COMPOUND_STMT_SET)
|
||||
@@ -109,7 +107,7 @@ impl<'src> Parser<'src> {
|
||||
/// See:
|
||||
/// - <https://docs.python.org/3/reference/compound_stmts.html>
|
||||
/// - <https://docs.python.org/3/reference/simple_stmts.html>
|
||||
pub(super) fn parse_statement(&mut self) -> Stmt {
|
||||
pub(super) fn parse_statement(&mut self) -> Stmt<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
match self.current_token_kind() {
|
||||
@@ -150,7 +148,7 @@ impl<'src> Parser<'src> {
|
||||
/// This statement must be terminated by a newline or semicolon.
|
||||
///
|
||||
/// Use [`Parser::parse_simple_statements`] to parse a sequence of simple statements.
|
||||
fn parse_single_simple_statement(&mut self) -> Stmt {
|
||||
fn parse_single_simple_statement(&mut self) -> Stmt<'ast> {
|
||||
let stmt = self.parse_simple_statement();
|
||||
|
||||
// The order of the token is important here.
|
||||
@@ -189,7 +187,7 @@ impl<'src> Parser<'src> {
|
||||
/// Matches the `simple_stmts` rule in the [Python grammar].
|
||||
///
|
||||
/// [Python grammar]: https://docs.python.org/3/reference/grammar.html
|
||||
fn parse_simple_statements(&mut self) -> Vec<Stmt> {
|
||||
fn parse_simple_statements(&mut self) -> Vec<Stmt<'ast>> {
|
||||
let mut stmts = vec![];
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
@@ -259,7 +257,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a simple statement.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html>
|
||||
fn parse_simple_statement(&mut self) -> Stmt {
|
||||
fn parse_simple_statement(&mut self) -> Stmt<'ast> {
|
||||
match self.current_token_kind() {
|
||||
TokenKind::Return => Stmt::Return(self.parse_return_statement()),
|
||||
TokenKind::Import => Stmt::Import(self.parse_import_statement()),
|
||||
@@ -311,7 +309,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
Stmt::Expr(ast::StmtExpr {
|
||||
range: self.node_range(start),
|
||||
value: Box::new(parsed_expr.expr),
|
||||
value: self.alloc_box(parsed_expr.expr),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -325,7 +323,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `del` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-del_stmt>
|
||||
fn parse_delete_statement(&mut self) -> ast::StmtDelete {
|
||||
fn parse_delete_statement(&mut self) -> ast::StmtDelete<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Del);
|
||||
|
||||
@@ -377,7 +375,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `return` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-return_stmt>
|
||||
fn parse_return_statement(&mut self) -> ast::StmtReturn {
|
||||
fn parse_return_statement(&mut self) -> ast::StmtReturn<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Return);
|
||||
|
||||
@@ -388,10 +386,10 @@ impl<'src> Parser<'src> {
|
||||
// return x := 1
|
||||
// return *x and y
|
||||
let value = self.at_expr().then(|| {
|
||||
Box::new(
|
||||
self.parse_expression_list(ExpressionContext::starred_bitwise_or())
|
||||
.expr,
|
||||
)
|
||||
let value = self
|
||||
.parse_expression_list(ExpressionContext::starred_bitwise_or())
|
||||
.expr;
|
||||
self.alloc_box(value)
|
||||
});
|
||||
|
||||
ast::StmtReturn {
|
||||
@@ -407,7 +405,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `raise` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-raise_stmt>
|
||||
fn parse_raise_statement(&mut self) -> ast::StmtRaise {
|
||||
fn parse_raise_statement(&mut self) -> ast::StmtRaise<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Raise);
|
||||
|
||||
@@ -432,7 +430,7 @@ impl<'src> Parser<'src> {
|
||||
self.add_error(ParseErrorType::UnparenthesizedTupleExpression, &exc);
|
||||
}
|
||||
|
||||
Some(Box::new(exc.expr))
|
||||
Some(self.alloc_box(exc.expr))
|
||||
};
|
||||
|
||||
let cause = (exc.is_some() && self.eat(TokenKind::From)).then(|| {
|
||||
@@ -453,7 +451,7 @@ impl<'src> Parser<'src> {
|
||||
self.add_error(ParseErrorType::UnparenthesizedTupleExpression, &cause);
|
||||
}
|
||||
|
||||
Box::new(cause.expr)
|
||||
self.alloc_box(cause.expr)
|
||||
});
|
||||
|
||||
ast::StmtRaise {
|
||||
@@ -470,7 +468,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `import` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#the-import-statement>
|
||||
fn parse_import_statement(&mut self) -> ast::StmtImport {
|
||||
fn parse_import_statement(&mut self) -> ast::StmtImport<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Import);
|
||||
|
||||
@@ -510,7 +508,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `from` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-import_stmt>
|
||||
fn parse_from_import_statement(&mut self) -> ast::StmtImportFrom {
|
||||
fn parse_from_import_statement(&mut self) -> ast::StmtImportFrom<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::From);
|
||||
|
||||
@@ -619,15 +617,12 @@ impl<'src> Parser<'src> {
|
||||
/// See:
|
||||
/// - <https://docs.python.org/3/reference/simple_stmts.html#the-import-statement>
|
||||
/// - <https://docs.python.org/3/library/ast.html#ast.alias>
|
||||
fn parse_alias(&mut self, style: ImportStyle) -> ast::Alias {
|
||||
fn parse_alias(&mut self, style: ImportStyle) -> ast::Alias<'ast> {
|
||||
let start = self.node_start();
|
||||
if self.eat(TokenKind::Star) {
|
||||
let range = self.node_range(start);
|
||||
return ast::Alias {
|
||||
name: ast::Identifier {
|
||||
id: Name::new_static("*"),
|
||||
range,
|
||||
},
|
||||
name: ast::Identifier { id: "*", range },
|
||||
asname: None,
|
||||
range,
|
||||
};
|
||||
@@ -668,10 +663,11 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a dotted name.
|
||||
///
|
||||
/// A dotted name is a sequence of identifiers separated by a single dot.
|
||||
fn parse_dotted_name(&mut self) -> ast::Identifier {
|
||||
fn parse_dotted_name(&mut self) -> ast::Identifier<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let mut dotted_name: CompactString = self.parse_identifier().id.into();
|
||||
let id = self.parse_identifier().id;
|
||||
let mut dotted_name = ruff_allocator::String::from_str_in(id, &self.allocator);
|
||||
let mut progress = ParserProgress::default();
|
||||
|
||||
while self.eat(TokenKind::Dot) {
|
||||
@@ -688,7 +684,7 @@ impl<'src> Parser<'src> {
|
||||
// import a.b.c
|
||||
// import a . b . c
|
||||
ast::Identifier {
|
||||
id: Name::from(dotted_name),
|
||||
id: dotted_name.into_bump_str(),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -745,7 +741,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `assert` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement>
|
||||
fn parse_assert_statement(&mut self) -> ast::StmtAssert {
|
||||
fn parse_assert_statement(&mut self) -> ast::StmtAssert<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Assert);
|
||||
|
||||
@@ -766,7 +762,8 @@ impl<'src> Parser<'src> {
|
||||
// assert False, assert x
|
||||
// assert False, yield x
|
||||
// assert False, x := 1
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err assert_empty_msg
|
||||
// assert x,
|
||||
@@ -781,7 +778,7 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
ast::StmtAssert {
|
||||
test: Box::new(test.expr),
|
||||
test: self.alloc_box(test.expr),
|
||||
msg,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
@@ -794,7 +791,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `global` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-global_stmt>
|
||||
fn parse_global_statement(&mut self) -> ast::StmtGlobal {
|
||||
fn parse_global_statement(&mut self) -> ast::StmtGlobal<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Global);
|
||||
|
||||
@@ -832,7 +829,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `nonlocal` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-nonlocal_stmt>
|
||||
fn parse_nonlocal_statement(&mut self) -> ast::StmtNonlocal {
|
||||
fn parse_nonlocal_statement(&mut self) -> ast::StmtNonlocal<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Nonlocal);
|
||||
|
||||
@@ -873,7 +870,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `type` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#the-type-statement>
|
||||
fn parse_type_alias_statement(&mut self) -> ast::StmtTypeAlias {
|
||||
fn parse_type_alias_statement(&mut self) -> ast::StmtTypeAlias<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Type);
|
||||
|
||||
@@ -897,9 +894,9 @@ impl<'src> Parser<'src> {
|
||||
let value = self.parse_conditional_expression_or_higher();
|
||||
|
||||
ast::StmtTypeAlias {
|
||||
name: Box::new(name),
|
||||
name: self.alloc_box(name),
|
||||
type_params,
|
||||
value: Box::new(value.expr),
|
||||
value: self.alloc_box(value.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -909,7 +906,7 @@ impl<'src> Parser<'src> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at an `IpyEscapeCommand` token.
|
||||
fn parse_ipython_escape_command_statement(&mut self) -> ast::StmtIpyEscapeCommand {
|
||||
fn parse_ipython_escape_command_statement(&mut self) -> ast::StmtIpyEscapeCommand<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let TokenValue::IpyEscapeCommand { value, kind } =
|
||||
@@ -923,7 +920,11 @@ impl<'src> Parser<'src> {
|
||||
self.add_error(ParseErrorType::UnexpectedIpythonEscapeCommand, range);
|
||||
}
|
||||
|
||||
ast::StmtIpyEscapeCommand { range, kind, value }
|
||||
ast::StmtIpyEscapeCommand {
|
||||
range,
|
||||
kind,
|
||||
value: self.alloc_str(&value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an IPython help end escape command at the statement level.
|
||||
@@ -933,15 +934,19 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `?` token.
|
||||
fn parse_ipython_help_end_escape_command_statement(
|
||||
&mut self,
|
||||
parsed_expr: &ParsedExpr,
|
||||
) -> ast::StmtIpyEscapeCommand {
|
||||
parsed_expr: &ParsedExpr<'ast>,
|
||||
) -> ast::StmtIpyEscapeCommand<'ast> {
|
||||
// We are permissive than the original implementation because we would allow whitespace
|
||||
// between the expression and the suffix while the IPython implementation doesn't allow it.
|
||||
// For example, `foo ?` would be valid in our case but invalid for IPython.
|
||||
fn unparse_expr(parser: &mut Parser, expr: &Expr, buffer: &mut String) {
|
||||
fn unparse_expr<'ast>(
|
||||
parser: &mut Parser<'_, 'ast>,
|
||||
expr: &Expr<'ast>,
|
||||
buffer: &mut ruff_allocator::String,
|
||||
) {
|
||||
match expr {
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
buffer.push_str(id.as_str());
|
||||
buffer.push_str(id);
|
||||
}
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
unparse_expr(parser, value, buffer);
|
||||
@@ -1012,11 +1017,11 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
}
|
||||
|
||||
let mut value = String::new();
|
||||
let mut value = ruff_allocator::String::new_in(&self.allocator);
|
||||
unparse_expr(self, &parsed_expr.expr, &mut value);
|
||||
|
||||
ast::StmtIpyEscapeCommand {
|
||||
value: value.into_boxed_str(),
|
||||
value: value.into_bump_str(),
|
||||
kind,
|
||||
range: self.node_range(parsed_expr.start()),
|
||||
}
|
||||
@@ -1029,7 +1034,11 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `=` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#assignment-statements>
|
||||
fn parse_assign_statement(&mut self, target: ParsedExpr, start: TextSize) -> ast::StmtAssign {
|
||||
fn parse_assign_statement(
|
||||
&mut self,
|
||||
target: ParsedExpr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::StmtAssign<'ast> {
|
||||
self.bump(TokenKind::Equal);
|
||||
|
||||
let mut targets = vec![target.expr];
|
||||
@@ -1084,7 +1093,7 @@ impl<'src> Parser<'src> {
|
||||
|
||||
ast::StmtAssign {
|
||||
targets,
|
||||
value: Box::new(value.expr),
|
||||
value: self.alloc_box(value.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -1098,9 +1107,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#annotated-assignment-statements>
|
||||
fn parse_annotated_assignment_statement(
|
||||
&mut self,
|
||||
mut target: ParsedExpr,
|
||||
mut target: ParsedExpr<'ast>,
|
||||
start: TextSize,
|
||||
) -> ast::StmtAnnAssign {
|
||||
) -> ast::StmtAnnAssign<'ast> {
|
||||
self.bump(TokenKind::Colon);
|
||||
|
||||
// test_err ann_assign_stmt_invalid_target
|
||||
@@ -1142,10 +1151,10 @@ impl<'src> Parser<'src> {
|
||||
// x: Any = *a and b
|
||||
// x: Any = x := 1
|
||||
// x: list = [x, *a | b, *a or b]
|
||||
Some(Box::new(
|
||||
self.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or())
|
||||
.expr,
|
||||
))
|
||||
let expression = self
|
||||
.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or())
|
||||
.expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err ann_assign_stmt_missing_rhs
|
||||
// x: int =
|
||||
@@ -1160,8 +1169,8 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
ast::StmtAnnAssign {
|
||||
target: Box::new(target.expr),
|
||||
annotation: Box::new(annotation.expr),
|
||||
target: self.alloc_box(target.expr),
|
||||
annotation: self.alloc_box(annotation.expr),
|
||||
value,
|
||||
simple,
|
||||
range: self.node_range(start),
|
||||
@@ -1177,10 +1186,10 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements>
|
||||
fn parse_augmented_assignment_statement(
|
||||
&mut self,
|
||||
mut target: ParsedExpr,
|
||||
mut target: ParsedExpr<'ast>,
|
||||
op: Operator,
|
||||
start: TextSize,
|
||||
) -> ast::StmtAugAssign {
|
||||
) -> ast::StmtAugAssign<'ast> {
|
||||
// Consume the operator
|
||||
self.bump_ts(AUGMENTED_ASSIGN_SET);
|
||||
|
||||
@@ -1215,9 +1224,9 @@ impl<'src> Parser<'src> {
|
||||
let value = self.parse_expression_list(ExpressionContext::yield_or_starred_bitwise_or());
|
||||
|
||||
ast::StmtAugAssign {
|
||||
target: Box::new(target.expr),
|
||||
target: self.alloc_box(target.expr),
|
||||
op,
|
||||
value: Box::new(value.expr),
|
||||
value: self.alloc_box(value.expr),
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
@@ -1229,7 +1238,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at an `if` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-if-statement>
|
||||
fn parse_if_statement(&mut self) -> ast::StmtIf {
|
||||
fn parse_if_statement(&mut self) -> ast::StmtIf<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::If);
|
||||
|
||||
@@ -1270,7 +1279,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
ast::StmtIf {
|
||||
test: Box::new(test.expr),
|
||||
test: self.alloc_box(test.expr),
|
||||
body,
|
||||
elif_else_clauses,
|
||||
range: self.node_range(start),
|
||||
@@ -1282,7 +1291,7 @@ impl<'src> Parser<'src> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at an `elif` or `else` token.
|
||||
fn parse_elif_or_else_clause(&mut self, kind: ElifOrElse) -> ast::ElifElseClause {
|
||||
fn parse_elif_or_else_clause(&mut self, kind: ElifOrElse) -> ast::ElifElseClause<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(kind.as_token_kind());
|
||||
|
||||
@@ -1327,7 +1336,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `try` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-try-statement>
|
||||
fn parse_try_statement(&mut self) -> ast::StmtTry {
|
||||
fn parse_try_statement(&mut self) -> ast::StmtTry<'ast> {
|
||||
let try_start = self.node_start();
|
||||
self.bump(TokenKind::Try);
|
||||
self.expect(TokenKind::Colon);
|
||||
@@ -1436,7 +1445,7 @@ impl<'src> Parser<'src> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at an `except` token.
|
||||
fn parse_except_clause(&mut self) -> (ExceptHandler, ExceptClauseKind) {
|
||||
fn parse_except_clause(&mut self) -> (ExceptHandler<'ast>, ExceptClauseKind) {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Except);
|
||||
|
||||
@@ -1484,7 +1493,7 @@ impl<'src> Parser<'src> {
|
||||
&parsed_expr,
|
||||
);
|
||||
}
|
||||
Some(Box::new(parsed_expr.expr))
|
||||
Some(self.alloc_box(parsed_expr.expr))
|
||||
} else {
|
||||
if block_kind.is_star() || self.at(TokenKind::As) {
|
||||
// test_err except_stmt_missing_exception
|
||||
@@ -1566,7 +1575,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `for` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-for-statement>
|
||||
fn parse_for_statement(&mut self, start: TextSize) -> ast::StmtFor {
|
||||
fn parse_for_statement(&mut self, start: TextSize) -> ast::StmtFor<'ast> {
|
||||
self.bump(TokenKind::For);
|
||||
|
||||
// test_err for_stmt_missing_target
|
||||
@@ -1634,8 +1643,8 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
ast::StmtFor {
|
||||
target: Box::new(target.expr),
|
||||
iter: Box::new(iter.expr),
|
||||
target: self.alloc_box(target.expr),
|
||||
iter: self.alloc_box(iter.expr),
|
||||
is_async: false,
|
||||
body,
|
||||
orelse,
|
||||
@@ -1650,7 +1659,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `while` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-while-statement>
|
||||
fn parse_while_statement(&mut self) -> ast::StmtWhile {
|
||||
fn parse_while_statement(&mut self) -> ast::StmtWhile<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::While);
|
||||
|
||||
@@ -1683,7 +1692,7 @@ impl<'src> Parser<'src> {
|
||||
};
|
||||
|
||||
ast::StmtWhile {
|
||||
test: Box::new(test.expr),
|
||||
test: self.alloc_box(test.expr),
|
||||
body,
|
||||
orelse,
|
||||
range: self.node_range(start),
|
||||
@@ -1704,9 +1713,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#function-definitions>
|
||||
fn parse_function_definition(
|
||||
&mut self,
|
||||
decorator_list: Vec<ast::Decorator>,
|
||||
decorator_list: Vec<ast::Decorator<'ast>>,
|
||||
start: TextSize,
|
||||
) -> ast::StmtFunctionDef {
|
||||
) -> ast::StmtFunctionDef<'ast> {
|
||||
self.bump(TokenKind::Def);
|
||||
|
||||
// test_err function_def_missing_identifier
|
||||
@@ -1770,7 +1779,7 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
}
|
||||
|
||||
Some(Box::new(returns.expr))
|
||||
Some(self.alloc_box(returns.expr))
|
||||
} else {
|
||||
// test_err function_def_missing_return_type
|
||||
// def foo() -> : ...
|
||||
@@ -1795,8 +1804,8 @@ impl<'src> Parser<'src> {
|
||||
|
||||
ast::StmtFunctionDef {
|
||||
name,
|
||||
type_params: type_params.map(Box::new),
|
||||
parameters: Box::new(parameters),
|
||||
type_params: type_params.map(|params| self.alloc_box(params)),
|
||||
parameters: self.alloc_box(parameters),
|
||||
body,
|
||||
decorator_list,
|
||||
is_async: false,
|
||||
@@ -1817,9 +1826,9 @@ impl<'src> Parser<'src> {
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-classdef>
|
||||
fn parse_class_definition(
|
||||
&mut self,
|
||||
decorator_list: Vec<ast::Decorator>,
|
||||
decorator_list: Vec<ast::Decorator<'ast>>,
|
||||
start: TextSize,
|
||||
) -> ast::StmtClassDef {
|
||||
) -> ast::StmtClassDef<'ast> {
|
||||
self.bump(TokenKind::Class);
|
||||
|
||||
// test_err class_def_missing_name
|
||||
@@ -1837,9 +1846,10 @@ impl<'src> Parser<'src> {
|
||||
// test_ok class_def_arguments
|
||||
// class Foo: ...
|
||||
// class Foo(): ...
|
||||
let arguments = self
|
||||
.at(TokenKind::Lpar)
|
||||
.then(|| Box::new(self.parse_arguments()));
|
||||
let arguments = self.at(TokenKind::Lpar).then(|| {
|
||||
let arguments = self.parse_arguments();
|
||||
self.alloc_box(arguments)
|
||||
});
|
||||
|
||||
self.expect(TokenKind::Colon);
|
||||
|
||||
@@ -1853,7 +1863,7 @@ impl<'src> Parser<'src> {
|
||||
range: self.node_range(start),
|
||||
decorator_list,
|
||||
name,
|
||||
type_params: type_params.map(Box::new),
|
||||
type_params: type_params.map(|params| self.alloc_box(params)),
|
||||
arguments,
|
||||
body,
|
||||
}
|
||||
@@ -1869,7 +1879,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `with` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-with-statement>
|
||||
fn parse_with_statement(&mut self, start: TextSize) -> ast::StmtWith {
|
||||
fn parse_with_statement(&mut self, start: TextSize) -> ast::StmtWith<'ast> {
|
||||
self.bump(TokenKind::With);
|
||||
|
||||
let items = self.parse_with_items();
|
||||
@@ -1888,7 +1898,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a list of with items.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-with-statement>
|
||||
fn parse_with_items(&mut self) -> Vec<WithItem> {
|
||||
fn parse_with_items(&mut self) -> Vec<WithItem<'ast>> {
|
||||
if !self.at_expr() {
|
||||
self.add_error(
|
||||
ParseErrorType::OtherError(
|
||||
@@ -1959,7 +1969,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `(` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-with_stmt_contents>
|
||||
fn try_parse_parenthesized_with_items(&mut self) -> Option<Vec<WithItem>> {
|
||||
fn try_parse_parenthesized_with_items(&mut self) -> Option<Vec<WithItem<'ast>>> {
|
||||
let checkpoint = self.checkpoint();
|
||||
|
||||
// We'll start with the assumption that the with items are parenthesized.
|
||||
@@ -2067,7 +2077,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a single `with` item.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-with_item>
|
||||
fn parse_with_item(&mut self, state: WithItemParsingState) -> ParsedWithItem {
|
||||
fn parse_with_item(&mut self, state: WithItemParsingState) -> ParsedWithItem<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
// The grammar for the context expression of a with item depends on the state
|
||||
@@ -2099,9 +2109,10 @@ impl<'src> Parser<'src> {
|
||||
WithItemParsingState::Regular => self.parse_conditional_expression_or_higher(),
|
||||
};
|
||||
|
||||
let optional_vars = self
|
||||
.at(TokenKind::As)
|
||||
.then(|| Box::new(self.parse_with_item_optional_vars().expr));
|
||||
let optional_vars = self.at(TokenKind::As).then(|| {
|
||||
let vars = self.parse_with_item_optional_vars().expr;
|
||||
self.alloc_box(vars)
|
||||
});
|
||||
|
||||
ParsedWithItem {
|
||||
is_parenthesized: context_expr.is_parenthesized,
|
||||
@@ -2118,7 +2129,7 @@ impl<'src> Parser<'src> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at an `as` token.
|
||||
fn parse_with_item_optional_vars(&mut self) -> ParsedExpr {
|
||||
fn parse_with_item_optional_vars(&mut self) -> ParsedExpr<'ast> {
|
||||
self.bump(TokenKind::As);
|
||||
|
||||
let mut target = self
|
||||
@@ -2158,7 +2169,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `match` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-match-statement>
|
||||
fn try_parse_match_statement(&mut self) -> Option<ast::StmtMatch> {
|
||||
fn try_parse_match_statement(&mut self) -> Option<ast::StmtMatch<'ast>> {
|
||||
let checkpoint = self.checkpoint();
|
||||
|
||||
let start = self.node_start();
|
||||
@@ -2174,7 +2185,7 @@ impl<'src> Parser<'src> {
|
||||
let cases = self.parse_match_body();
|
||||
|
||||
Some(ast::StmtMatch {
|
||||
subject: Box::new(subject),
|
||||
subject: self.alloc_box(subject),
|
||||
cases,
|
||||
range: self.node_range(start),
|
||||
})
|
||||
@@ -2196,7 +2207,7 @@ impl<'src> Parser<'src> {
|
||||
let cases = self.parse_match_body();
|
||||
|
||||
Some(ast::StmtMatch {
|
||||
subject: Box::new(subject),
|
||||
subject: self.alloc_box(subject),
|
||||
cases,
|
||||
range: self.node_range(start),
|
||||
})
|
||||
@@ -2217,7 +2228,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `match` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#the-match-statement>
|
||||
fn parse_match_statement(&mut self) -> ast::StmtMatch {
|
||||
fn parse_match_statement(&mut self) -> ast::StmtMatch<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Match);
|
||||
|
||||
@@ -2227,14 +2238,14 @@ impl<'src> Parser<'src> {
|
||||
let cases = self.parse_match_body();
|
||||
|
||||
ast::StmtMatch {
|
||||
subject: Box::new(subject),
|
||||
subject: self.alloc_box(subject),
|
||||
cases,
|
||||
range: self.node_range(start),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the subject expression for a `match` statement.
|
||||
fn parse_match_subject_expression(&mut self) -> Expr {
|
||||
fn parse_match_subject_expression(&mut self) -> Expr<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
// Subject expression grammar is:
|
||||
@@ -2288,7 +2299,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// This method expects that the parser is positioned at a `Newline` token. If not, it adds a
|
||||
/// syntax error and continues parsing.
|
||||
fn parse_match_body(&mut self) -> Vec<ast::MatchCase> {
|
||||
fn parse_match_body(&mut self) -> Vec<ast::MatchCase<'ast>> {
|
||||
// test_err match_stmt_no_newline_before_case
|
||||
// match foo: case _: ...
|
||||
self.expect(TokenKind::Newline);
|
||||
@@ -2315,7 +2326,7 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
/// Parses a list of match case blocks.
|
||||
fn parse_match_case_blocks(&mut self) -> Vec<ast::MatchCase> {
|
||||
fn parse_match_case_blocks(&mut self) -> Vec<ast::MatchCase<'ast>> {
|
||||
let mut cases = vec![];
|
||||
|
||||
if !self.at(TokenKind::Case) {
|
||||
@@ -2349,7 +2360,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `case` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-case_block>
|
||||
fn parse_match_case(&mut self) -> ast::MatchCase {
|
||||
fn parse_match_case(&mut self) -> ast::MatchCase<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Case);
|
||||
|
||||
@@ -2377,10 +2388,10 @@ impl<'src> Parser<'src> {
|
||||
// case y if (*a): ...
|
||||
// match x:
|
||||
// case y if yield x: ...
|
||||
Some(Box::new(
|
||||
self.parse_named_expression_or_higher(ExpressionContext::default())
|
||||
.expr,
|
||||
))
|
||||
let guard = self
|
||||
.parse_named_expression_or_higher(ExpressionContext::default())
|
||||
.expr;
|
||||
Some(self.alloc_box(guard))
|
||||
} else {
|
||||
// test_err match_stmt_missing_guard_expr
|
||||
// match x:
|
||||
@@ -2420,7 +2431,7 @@ impl<'src> Parser<'src> {
|
||||
/// - <https://docs.python.org/3/reference/compound_stmts.html#the-async-with-statement>
|
||||
/// - <https://docs.python.org/3/reference/compound_stmts.html#the-async-for-statement>
|
||||
/// - <https://docs.python.org/3/reference/compound_stmts.html#coroutine-function-definition>
|
||||
fn parse_async_statement(&mut self) -> Stmt {
|
||||
fn parse_async_statement(&mut self) -> Stmt<'ast> {
|
||||
let async_start = self.node_start();
|
||||
self.bump(TokenKind::Async);
|
||||
|
||||
@@ -2469,7 +2480,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a decorator list followed by a class, function or async function definition.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorators>
|
||||
fn parse_decorators(&mut self) -> Stmt {
|
||||
fn parse_decorators(&mut self) -> Stmt<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
let mut decorators = vec![];
|
||||
@@ -2558,7 +2569,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// This could either be a single statement that's on the same line as the
|
||||
/// clause header or an indented block.
|
||||
fn parse_body(&mut self, parent_clause: Clause) -> Vec<Stmt> {
|
||||
fn parse_body(&mut self, parent_clause: Clause) -> Vec<Stmt<'ast>> {
|
||||
// Note: The test cases in this method chooses a clause at random to test
|
||||
// the error logic.
|
||||
|
||||
@@ -2604,7 +2615,7 @@ impl<'src> Parser<'src> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the parser isn't positioned at an `Indent` token.
|
||||
fn parse_block(&mut self) -> Vec<Stmt> {
|
||||
fn parse_block(&mut self) -> Vec<Stmt<'ast>> {
|
||||
self.bump(TokenKind::Indent);
|
||||
|
||||
let statements =
|
||||
@@ -2630,7 +2641,7 @@ impl<'src> Parser<'src> {
|
||||
start: TextSize,
|
||||
function_kind: FunctionKind,
|
||||
allow_star_annotation: AllowStarAnnotation,
|
||||
) -> ast::Parameter {
|
||||
) -> ast::Parameter<'ast> {
|
||||
let name = self.parse_identifier();
|
||||
|
||||
// Annotations are only allowed for function definition. For lambda expression,
|
||||
@@ -2668,7 +2679,7 @@ impl<'src> Parser<'src> {
|
||||
self.parse_conditional_expression_or_higher()
|
||||
}
|
||||
};
|
||||
Some(Box::new(parsed_expr.expr))
|
||||
Some(self.alloc_box(parsed_expr.expr))
|
||||
} else {
|
||||
// test_err param_missing_annotation
|
||||
// def foo(x:): ...
|
||||
@@ -2702,7 +2713,7 @@ impl<'src> Parser<'src> {
|
||||
&mut self,
|
||||
start: TextSize,
|
||||
function_kind: FunctionKind,
|
||||
) -> ast::ParameterWithDefault {
|
||||
) -> ast::ParameterWithDefault<'ast> {
|
||||
let parameter = self.parse_parameter(start, function_kind, AllowStarAnnotation::No);
|
||||
|
||||
let default = if self.eat(TokenKind::Equal) {
|
||||
@@ -2717,7 +2728,8 @@ impl<'src> Parser<'src> {
|
||||
// def foo(x=*int): ...
|
||||
// def foo(x=(*int)): ...
|
||||
// def foo(x=yield y): ...
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err param_missing_default
|
||||
// def foo(x=): ...
|
||||
@@ -2742,7 +2754,10 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a parameter list for the given function kind.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-parameter_list>
|
||||
pub(super) fn parse_parameters(&mut self, function_kind: FunctionKind) -> ast::Parameters {
|
||||
pub(super) fn parse_parameters(
|
||||
&mut self,
|
||||
function_kind: FunctionKind,
|
||||
) -> ast::Parameters<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
if matches!(function_kind, FunctionKind::FunctionDef) {
|
||||
@@ -2813,7 +2828,7 @@ impl<'src> Parser<'src> {
|
||||
// TODO(dhruvmanila): The AST doesn't allow multiple `vararg`, so let's
|
||||
// choose to keep the first one so that the parameters remain in preorder.
|
||||
if parameters.vararg.is_none() {
|
||||
parameters.vararg = Some(Box::new(param));
|
||||
parameters.vararg = Some(parser.alloc_box(param));
|
||||
}
|
||||
|
||||
last_keyword_only_separator_range = None;
|
||||
@@ -2887,7 +2902,7 @@ impl<'src> Parser<'src> {
|
||||
);
|
||||
}
|
||||
|
||||
parameters.kwarg = Some(Box::new(param));
|
||||
parameters.kwarg = Some(parser.alloc_box(param));
|
||||
last_keyword_only_separator_range = None;
|
||||
}
|
||||
TokenKind::Slash => {
|
||||
@@ -3009,7 +3024,7 @@ impl<'src> Parser<'src> {
|
||||
/// type parameter list, return `None`.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists>
|
||||
fn try_parse_type_params(&mut self) -> Option<ast::TypeParams> {
|
||||
fn try_parse_type_params(&mut self) -> Option<ast::TypeParams<'ast>> {
|
||||
self.at(TokenKind::Lsqb).then(|| self.parse_type_params())
|
||||
}
|
||||
|
||||
@@ -3020,7 +3035,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the parser isn't positioned at a `[` token.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#type-parameter-lists>
|
||||
fn parse_type_params(&mut self) -> ast::TypeParams {
|
||||
fn parse_type_params(&mut self) -> ast::TypeParams<'ast> {
|
||||
let start = self.node_start();
|
||||
self.bump(TokenKind::Lsqb);
|
||||
|
||||
@@ -3048,7 +3063,7 @@ impl<'src> Parser<'src> {
|
||||
/// Parses a type parameter.
|
||||
///
|
||||
/// See: <https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-type_param>
|
||||
fn parse_type_param(&mut self) -> ast::TypeParam {
|
||||
fn parse_type_param(&mut self) -> ast::TypeParam<'ast> {
|
||||
let start = self.node_start();
|
||||
|
||||
// TODO(dhruvmanila): CPython throws an error if `TypeVarTuple` or `ParamSpec`
|
||||
@@ -3078,12 +3093,12 @@ impl<'src> Parser<'src> {
|
||||
// type X[*Ts = yield x] = int
|
||||
// type X[*Ts = yield from x] = int
|
||||
// type X[*Ts = x := int] = int
|
||||
Some(Box::new(
|
||||
self.parse_conditional_expression_or_higher_impl(
|
||||
let expression = self
|
||||
.parse_conditional_expression_or_higher_impl(
|
||||
ExpressionContext::starred_bitwise_or(),
|
||||
)
|
||||
.expr,
|
||||
))
|
||||
.expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err type_param_type_var_tuple_missing_default
|
||||
// type X[*Ts =] = int
|
||||
@@ -3122,7 +3137,8 @@ impl<'src> Parser<'src> {
|
||||
// type X[**P = yield from x] = int
|
||||
// type X[**P = x := int] = int
|
||||
// type X[**P = *int] = int
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err type_param_param_spec_missing_default
|
||||
// type X[**P =] = int
|
||||
@@ -3160,7 +3176,8 @@ impl<'src> Parser<'src> {
|
||||
// type X[T: yield x] = int
|
||||
// type X[T: yield from x] = int
|
||||
// type X[T: x := int] = int
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err type_param_missing_bound
|
||||
// type X[T: ] = int
|
||||
@@ -3184,7 +3201,8 @@ impl<'src> Parser<'src> {
|
||||
// type X[T = yield from x] = int
|
||||
// type X[T = x := int] = int
|
||||
// type X[T: int = *int] = int
|
||||
Some(Box::new(self.parse_conditional_expression_or_higher().expr))
|
||||
let expression = self.parse_conditional_expression_or_higher().expr;
|
||||
Some(self.alloc_box(expression))
|
||||
} else {
|
||||
// test_err type_param_type_var_missing_default
|
||||
// type X[T =] = int
|
||||
@@ -3215,7 +3233,7 @@ impl<'src> Parser<'src> {
|
||||
/// If it's a starred expression, then validate the value of the starred expression.
|
||||
///
|
||||
/// Report an error for each invalid assignment expression found.
|
||||
pub(super) fn validate_assignment_target(&mut self, expr: &Expr) {
|
||||
pub(super) fn validate_assignment_target(&mut self, expr: &Expr<'ast>) {
|
||||
match expr {
|
||||
Expr::Starred(ast::ExprStarred { value, .. }) => self.validate_assignment_target(value),
|
||||
Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
@@ -3232,7 +3250,7 @@ impl<'src> Parser<'src> {
|
||||
///
|
||||
/// Unlike [`Parser::validate_assignment_target`], starred, list and tuple
|
||||
/// expressions aren't allowed here.
|
||||
fn validate_annotated_assignment_target(&mut self, expr: &Expr) {
|
||||
fn validate_annotated_assignment_target(&mut self, expr: &Expr<'ast>) {
|
||||
match expr {
|
||||
Expr::List(_) => self.add_error(
|
||||
ParseErrorType::OtherError(
|
||||
@@ -3256,7 +3274,7 @@ impl<'src> Parser<'src> {
|
||||
/// If the expression is a list or tuple, then validate each element in the list.
|
||||
///
|
||||
/// See: <https://github.com/python/cpython/blob/d864b0094f9875c5613cbb0b7f7f3ca8f1c6b606/Parser/action_helpers.c#L1150-L1180>
|
||||
fn validate_delete_target(&mut self, expr: &Expr) {
|
||||
fn validate_delete_target(&mut self, expr: &Expr<'ast>) {
|
||||
match expr {
|
||||
Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
for expr in elts {
|
||||
@@ -3271,7 +3289,7 @@ impl<'src> Parser<'src> {
|
||||
/// Validate that the given parameters doesn't have any duplicate names.
|
||||
///
|
||||
/// Report errors for all the duplicate names found.
|
||||
fn validate_parameters(&mut self, parameters: &ast::Parameters) {
|
||||
fn validate_parameters(&mut self, parameters: &ast::Parameters<'ast>) {
|
||||
let mut all_arg_names =
|
||||
FxHashSet::with_capacity_and_hasher(parameters.len(), FxBuildHasher);
|
||||
|
||||
@@ -3431,7 +3449,7 @@ impl<'src> Parser<'src> {
|
||||
fn parse_clauses<T>(
|
||||
&mut self,
|
||||
clause: Clause,
|
||||
mut parse_clause: impl FnMut(&mut Parser<'src>) -> T,
|
||||
mut parse_clause: impl FnMut(&mut Parser<'src, 'ast>) -> T,
|
||||
) -> Vec<T> {
|
||||
let mut clauses = Vec::new();
|
||||
let mut progress = ParserProgress::default();
|
||||
@@ -3544,9 +3562,9 @@ enum WithItemParsingState {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParsedWithItem {
|
||||
struct ParsedWithItem<'ast> {
|
||||
/// The contained with item.
|
||||
item: WithItem,
|
||||
item: WithItem<'ast>,
|
||||
/// If the context expression of the item is parenthesized.
|
||||
is_parenthesized: bool,
|
||||
}
|
||||
|
||||
@@ -1,51 +1,57 @@
|
||||
use crate::{parse, parse_expression, parse_module, Mode};
|
||||
use ruff_allocator::Allocator;
|
||||
|
||||
#[test]
|
||||
fn test_modes() {
|
||||
let allocator = Allocator::new();
|
||||
let source = "a[0][1][2][3][4]";
|
||||
|
||||
assert!(parse(source, Mode::Expression).is_ok());
|
||||
assert!(parse(source, Mode::Module).is_ok());
|
||||
assert!(parse(source, Mode::Expression, &allocator).is_ok());
|
||||
assert!(parse(source, Mode::Module, &allocator).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_mode_invalid_syntax1() {
|
||||
let allocator = Allocator::new();
|
||||
let source = "first second";
|
||||
let error = parse_expression(source).unwrap_err();
|
||||
let error = parse_expression(source, &allocator).unwrap_err();
|
||||
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_mode_invalid_syntax2() {
|
||||
let allocator = Allocator::new();
|
||||
let source = r"first
|
||||
|
||||
second
|
||||
";
|
||||
let error = parse_expression(source).unwrap_err();
|
||||
let error = parse_expression(source, &allocator).unwrap_err();
|
||||
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_mode_invalid_syntax3() {
|
||||
let allocator = Allocator::new();
|
||||
let source = r"first
|
||||
|
||||
second
|
||||
|
||||
third
|
||||
";
|
||||
let error = parse_expression(source).unwrap_err();
|
||||
let error = parse_expression(source, &allocator).unwrap_err();
|
||||
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_mode_valid_syntax() {
|
||||
let allocator = Allocator::new();
|
||||
let source = "first
|
||||
|
||||
";
|
||||
let parsed = parse_expression(source).unwrap();
|
||||
let parsed = parse_expression(source, &allocator).unwrap();
|
||||
|
||||
insta::assert_debug_snapshot!(parsed.expr());
|
||||
}
|
||||
@@ -53,14 +59,16 @@ fn test_expr_mode_valid_syntax() {
|
||||
#[test]
|
||||
fn test_unicode_aliases() {
|
||||
// https://github.com/RustPython/RustPython/issues/4566
|
||||
let allocator = Allocator::new();
|
||||
let source = r#"x = "\N{BACKSPACE}another cool trick""#;
|
||||
let suite = parse_module(source).unwrap().into_suite();
|
||||
let suite = parse_module(source, &allocator).unwrap().into_suite();
|
||||
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ipython_escape_commands() {
|
||||
let allocator = Allocator::new();
|
||||
let parsed = parse(
|
||||
r"
|
||||
# Normal Python code
|
||||
@@ -130,6 +138,7 @@ foo.bar[0].baz[2].egg??
|
||||
"
|
||||
.trim(),
|
||||
Mode::Ipython,
|
||||
&allocator,
|
||||
)
|
||||
.unwrap();
|
||||
insta::assert_debug_snapshot!(parsed.syntax());
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 869
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Assign(
|
||||
StmtAssign {
|
||||
range: 0..16,
|
||||
targets: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 0..4,
|
||||
id: "bold",
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
],
|
||||
value: StringLiteral(
|
||||
ExprStringLiteral {
|
||||
range: 7..16,
|
||||
value: StringLiteralValue {
|
||||
inner: Single(
|
||||
StringLiteral {
|
||||
range: 7..16,
|
||||
value: "\u{3}8[1m",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Single,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 802
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..22,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..22,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..22,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 2..5,
|
||||
value: "aaa",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 5..10,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 6..9,
|
||||
id: "bbb",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 10..13,
|
||||
value: "ccc",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 13..18,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 14..17,
|
||||
id: "ddd",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 18..21,
|
||||
value: "eee",
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 819
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..8,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..8,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..8,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 2..4,
|
||||
value: "\\",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 4..7,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 5..6,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 794
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..8,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..8,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..8,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 2..4,
|
||||
value: "\n",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 4..7,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 5..6,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 844
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..9,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..9,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..9,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 3..5,
|
||||
value: "\\\n",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 5..8,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 6..7,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Raw {
|
||||
uppercase_r: false,
|
||||
},
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 569
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..10,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..10,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..10,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..9,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..7,
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: "=",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 577
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..38,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..38,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..38,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 2..6,
|
||||
value: "mix ",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 6..13,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 7..11,
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: "=",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 13..28,
|
||||
value: " with text and ",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 28..37,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 29..35,
|
||||
id: "second",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: "=",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 585
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..14,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..14,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..14,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..13,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..7,
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: "=",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
FStringFormatSpec {
|
||||
range: 9..12,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 9..12,
|
||||
value: ">10",
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 811
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..11,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..11,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..11,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 4..5,
|
||||
value: "\n",
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 5..8,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 6..7,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 537
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..18,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..18,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..18,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..5,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..4,
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 5..10,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 7..8,
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 10..17,
|
||||
value: "{foo}",
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 860
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..16,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..16,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..16,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..15,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..6,
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
FStringFormatSpec {
|
||||
range: 7..14,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 7..14,
|
||||
expression: StringLiteral(
|
||||
ExprStringLiteral {
|
||||
range: 8..13,
|
||||
value: StringLiteralValue {
|
||||
inner: Concatenated(
|
||||
ConcatenatedStringLiteral {
|
||||
strings: [
|
||||
StringLiteral {
|
||||
range: 8..10,
|
||||
value: "",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Single,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
StringLiteral {
|
||||
range: 11..13,
|
||||
value: "",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Single,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
value: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 545
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..15,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..15,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..15,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..14,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..6,
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
FStringFormatSpec {
|
||||
range: 7..13,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 7..13,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 8..12,
|
||||
id: "spec",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 852
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..13,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..13,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..13,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..12,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..6,
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
FStringFormatSpec {
|
||||
range: 7..11,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 7..11,
|
||||
expression: StringLiteral(
|
||||
ExprStringLiteral {
|
||||
range: 8..10,
|
||||
value: StringLiteralValue {
|
||||
inner: Single(
|
||||
StringLiteral {
|
||||
range: 8..10,
|
||||
value: "",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Single,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 553
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..13,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..13,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..13,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..12,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..6,
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
FStringFormatSpec {
|
||||
range: 7..11,
|
||||
elements: [
|
||||
Literal(
|
||||
FStringLiteralElement {
|
||||
range: 7..11,
|
||||
value: "spec",
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 639
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..10,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..10,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..10,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..9,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..4,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: " =",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 647
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..10,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..10,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..10,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 2..9,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 3..4,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: Some(
|
||||
DebugText {
|
||||
leading: "",
|
||||
trailing: "= ",
|
||||
},
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Regular,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 827
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..7,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..7,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..7,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 3..6,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 4..5,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Raw {
|
||||
uppercase_r: false,
|
||||
},
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source: crates/ruff_python_parser/src/string.rs
|
||||
assertion_line: 835
|
||||
expression: suite
|
||||
---
|
||||
[
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 0..11,
|
||||
value: FString(
|
||||
ExprFString {
|
||||
range: 0..11,
|
||||
value: FStringValue {
|
||||
inner: Single(
|
||||
FString(
|
||||
FString {
|
||||
range: 0..11,
|
||||
elements: [
|
||||
Expression(
|
||||
FStringExpressionElement {
|
||||
range: 5..8,
|
||||
expression: Name(
|
||||
ExprName {
|
||||
range: 6..7,
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
debug_text: None,
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
),
|
||||
],
|
||||
flags: FStringFlags {
|
||||
quote_style: Double,
|
||||
prefix: Raw {
|
||||
uppercase_r: false,
|
||||
},
|
||||
triple_quoted: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,20 +1,20 @@
|
||||
//! Parsing of string literals, bytes literals, and implicit string concatenation.
|
||||
|
||||
use bstr::ByteSlice;
|
||||
|
||||
use ruff_allocator::Allocator;
|
||||
use ruff_python_ast::{self as ast, AnyStringFlags, Expr, StringFlags};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::error::{LexicalError, LexicalErrorType};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum StringType {
|
||||
Str(ast::StringLiteral),
|
||||
Bytes(ast::BytesLiteral),
|
||||
FString(ast::FString),
|
||||
pub(crate) enum StringType<'ast> {
|
||||
Str(ast::StringLiteral<'ast>),
|
||||
Bytes(ast::BytesLiteral<'ast>),
|
||||
FString(ast::FString<'ast>),
|
||||
}
|
||||
|
||||
impl Ranged for StringType {
|
||||
impl Ranged for StringType<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
match self {
|
||||
Self::Str(node) => node.range(),
|
||||
@@ -24,8 +24,8 @@ impl Ranged for StringType {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StringType> for Expr {
|
||||
fn from(string: StringType) -> Self {
|
||||
impl<'ast> From<StringType<'ast>> for Expr<'ast> {
|
||||
fn from(string: StringType<'ast>) -> Self {
|
||||
match string {
|
||||
StringType::Str(node) => Expr::from(node),
|
||||
StringType::Bytes(node) => Expr::from(node),
|
||||
@@ -39,9 +39,9 @@ enum EscapedChar {
|
||||
Escape(char),
|
||||
}
|
||||
|
||||
struct StringParser {
|
||||
struct StringParser<'source, 'ast> {
|
||||
/// The raw content of the string e.g., the `foo` part in `"foo"`.
|
||||
source: Box<str>,
|
||||
source: &'source str,
|
||||
/// Current position of the parser in the source.
|
||||
cursor: usize,
|
||||
/// Flags that can be used to query information about the string.
|
||||
@@ -50,21 +50,29 @@ struct StringParser {
|
||||
offset: TextSize,
|
||||
/// The range of the string literal.
|
||||
range: TextRange,
|
||||
allocator: &'ast Allocator,
|
||||
}
|
||||
|
||||
impl StringParser {
|
||||
fn new(source: Box<str>, flags: AnyStringFlags, offset: TextSize, range: TextRange) -> Self {
|
||||
impl<'source, 'ast> StringParser<'source, 'ast> {
|
||||
fn new(
|
||||
source: &'source str,
|
||||
flags: AnyStringFlags,
|
||||
offset: TextSize,
|
||||
range: TextRange,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Self {
|
||||
Self {
|
||||
source,
|
||||
cursor: 0,
|
||||
flags,
|
||||
offset,
|
||||
range,
|
||||
allocator,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_bytes(&mut self, bytes: usize) -> &str {
|
||||
fn skip_bytes(&mut self, bytes: usize) -> &'source str {
|
||||
let skipped_str = &self.source[self.cursor..self.cursor + bytes];
|
||||
self.cursor += bytes;
|
||||
skipped_str
|
||||
@@ -232,16 +240,16 @@ impl StringParser {
|
||||
Ok(Some(EscapedChar::Literal(new_char)))
|
||||
}
|
||||
|
||||
fn parse_fstring_middle(mut self) -> Result<ast::FStringLiteralElement, LexicalError> {
|
||||
fn parse_fstring_middle(mut self) -> Result<ast::FStringLiteralElement<'ast>, LexicalError> {
|
||||
// Fast-path: if the f-string doesn't contain any escape sequences, return the literal.
|
||||
let Some(mut index) = memchr::memchr3(b'{', b'}', b'\\', self.source.as_bytes()) else {
|
||||
return Ok(ast::FStringLiteralElement {
|
||||
value: self.source,
|
||||
value: self.allocator.alloc_str(&self.source),
|
||||
range: self.range,
|
||||
});
|
||||
};
|
||||
|
||||
let mut value = String::with_capacity(self.source.len());
|
||||
let mut value = ruff_allocator::String::with_capacity_in(self.source.len(), self.allocator);
|
||||
loop {
|
||||
// Add the characters before the escape sequence (or curly brace) to the string.
|
||||
let before_with_slash_or_brace = self.skip_bytes(index + 1);
|
||||
@@ -313,12 +321,12 @@ impl StringParser {
|
||||
}
|
||||
|
||||
Ok(ast::FStringLiteralElement {
|
||||
value: value.into_boxed_str(),
|
||||
value: value.into_bump_str(),
|
||||
range: self.range,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_bytes(mut self) -> Result<StringType, LexicalError> {
|
||||
fn parse_bytes(mut self) -> Result<StringType<'ast>, LexicalError> {
|
||||
if let Some(index) = self.source.as_bytes().find_non_ascii_byte() {
|
||||
let ch = self.source.chars().nth(index).unwrap();
|
||||
return Err(LexicalError::new(
|
||||
@@ -333,7 +341,7 @@ impl StringParser {
|
||||
if self.flags.is_raw_string() {
|
||||
// For raw strings, no escaping is necessary.
|
||||
return Ok(StringType::Bytes(ast::BytesLiteral {
|
||||
value: self.source.into_boxed_bytes(),
|
||||
value: self.allocator.alloc_str(self.source).as_bytes(),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}));
|
||||
@@ -342,14 +350,14 @@ impl StringParser {
|
||||
let Some(mut escape) = memchr::memchr(b'\\', self.source.as_bytes()) else {
|
||||
// If the string doesn't contain any escape sequences, return the owned string.
|
||||
return Ok(StringType::Bytes(ast::BytesLiteral {
|
||||
value: self.source.into_boxed_bytes(),
|
||||
value: self.allocator.alloc_str(self.source).as_bytes(),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}));
|
||||
};
|
||||
|
||||
// If the string contains escape sequences, we need to parse them.
|
||||
let mut value = Vec::with_capacity(self.source.len());
|
||||
let mut value = ruff_allocator::Vec::with_capacity_in(self.source.len(), self.allocator);
|
||||
loop {
|
||||
// Add the characters before the escape sequence to the string.
|
||||
let before_with_slash = self.skip_bytes(escape + 1);
|
||||
@@ -379,17 +387,17 @@ impl StringParser {
|
||||
}
|
||||
|
||||
Ok(StringType::Bytes(ast::BytesLiteral {
|
||||
value: value.into_boxed_slice(),
|
||||
value: value.into_bump_slice(),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_string(mut self) -> Result<StringType, LexicalError> {
|
||||
fn parse_string(mut self) -> Result<StringType<'ast>, LexicalError> {
|
||||
if self.flags.is_raw_string() {
|
||||
// For raw strings, no escaping is necessary.
|
||||
return Ok(StringType::Str(ast::StringLiteral {
|
||||
value: self.source,
|
||||
value: self.allocator.alloc_str(self.source),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}));
|
||||
@@ -398,14 +406,14 @@ impl StringParser {
|
||||
let Some(mut escape) = memchr::memchr(b'\\', self.source.as_bytes()) else {
|
||||
// If the string doesn't contain any escape sequences, return the owned string.
|
||||
return Ok(StringType::Str(ast::StringLiteral {
|
||||
value: self.source,
|
||||
value: self.allocator.alloc_str(self.source),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}));
|
||||
};
|
||||
|
||||
// If the string contains escape sequences, we need to parse them.
|
||||
let mut value = String::with_capacity(self.source.len());
|
||||
let mut value = ruff_allocator::String::with_capacity_in(self.source.len(), self.allocator);
|
||||
|
||||
loop {
|
||||
// Add the characters before the escape sequence to the string.
|
||||
@@ -435,13 +443,13 @@ impl StringParser {
|
||||
}
|
||||
|
||||
Ok(StringType::Str(ast::StringLiteral {
|
||||
value: value.into_boxed_str(),
|
||||
value: value.into_bump_str(),
|
||||
range: self.range,
|
||||
flags: self.flags.into(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse(self) -> Result<StringType, LexicalError> {
|
||||
fn parse(self) -> Result<StringType<'ast>, LexicalError> {
|
||||
if self.flags.is_byte_string() {
|
||||
self.parse_bytes()
|
||||
} else {
|
||||
@@ -450,25 +458,35 @@ impl StringParser {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_string_literal(
|
||||
source: Box<str>,
|
||||
pub(crate) fn parse_string_literal<'source, 'ast>(
|
||||
source: &'source str,
|
||||
flags: AnyStringFlags,
|
||||
range: TextRange,
|
||||
) -> Result<StringType, LexicalError> {
|
||||
StringParser::new(source, flags, range.start() + flags.opener_len(), range).parse()
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<StringType<'ast>, LexicalError> {
|
||||
StringParser::new(
|
||||
source,
|
||||
flags,
|
||||
range.start() + flags.opener_len(),
|
||||
range,
|
||||
allocator,
|
||||
)
|
||||
.parse()
|
||||
}
|
||||
|
||||
// TODO(dhruvmanila): Move this to the new parser
|
||||
pub(crate) fn parse_fstring_literal_element(
|
||||
source: Box<str>,
|
||||
pub(crate) fn parse_fstring_literal_element<'ast>(
|
||||
source: &str,
|
||||
flags: AnyStringFlags,
|
||||
range: TextRange,
|
||||
) -> Result<ast::FStringLiteralElement, LexicalError> {
|
||||
StringParser::new(source, flags, range.start(), range).parse_fstring_middle()
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<ast::FStringLiteralElement<'ast>, LexicalError> {
|
||||
StringParser::new(source, flags, range.start(), range, allocator).parse_fstring_middle()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruff_allocator::Allocator;
|
||||
use ruff_python_ast::Suite;
|
||||
|
||||
use crate::error::LexicalErrorType;
|
||||
@@ -478,84 +496,98 @@ mod tests {
|
||||
const MAC_EOL: &str = "\r";
|
||||
const UNIX_EOL: &str = "\n";
|
||||
|
||||
fn parse_suite(source: &str) -> Result<Suite, ParseError> {
|
||||
parse_module(source).map(Parsed::into_suite)
|
||||
fn parse_suite<'ast>(
|
||||
source: &str,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<Suite<'ast>, ParseError> {
|
||||
parse_module(source, allocator).map(Parsed::into_suite)
|
||||
}
|
||||
|
||||
fn string_parser_escaped_eol(eol: &str) -> Suite {
|
||||
fn string_parser_escaped_eol<'ast>(eol: &str, allocator: &'ast Allocator) -> Suite<'ast> {
|
||||
let source = format!(r"'text \{eol}more text'");
|
||||
parse_suite(&source).unwrap()
|
||||
parse_suite(&source, &allocator).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_parser_escaped_unix_eol() {
|
||||
let suite = string_parser_escaped_eol(UNIX_EOL);
|
||||
let allocator = Allocator::new();
|
||||
let suite = string_parser_escaped_eol(UNIX_EOL, &allocator);
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_parser_escaped_mac_eol() {
|
||||
let suite = string_parser_escaped_eol(MAC_EOL);
|
||||
let allocator = Allocator::new();
|
||||
let suite = string_parser_escaped_eol(MAC_EOL, &allocator);
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_parser_escaped_windows_eol() {
|
||||
let suite = string_parser_escaped_eol(WINDOWS_EOL);
|
||||
let allocator = Allocator::new();
|
||||
let suite = string_parser_escaped_eol(WINDOWS_EOL, &allocator);
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring() {
|
||||
let source = r#"f"{a}{ b }{{foo}}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_nested_spec() {
|
||||
let source = r#"f"{foo:{spec}}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_not_nested_spec() {
|
||||
let source = r#"f"{foo:spec}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty_fstring() {
|
||||
let source = r#"f"""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_self_documenting_base() {
|
||||
let source = r#"f"{user=}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_self_documenting_base_more() {
|
||||
let source = r#"f"mix {user=} with text and {second=}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_self_documenting_format() {
|
||||
let source = r#"f"{user=:>10}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
fn parse_fstring_error(source: &str) -> FStringErrorType {
|
||||
parse_suite(source)
|
||||
let allocator = Allocator::new();
|
||||
parse_suite(source, &allocator)
|
||||
.map_err(|e| match e.error {
|
||||
ParseErrorType::Lexical(LexicalErrorType::FStringError(e)) => e,
|
||||
ParseErrorType::FStringError(e) => e,
|
||||
@@ -579,111 +611,127 @@ mod tests {
|
||||
// error appears after the unexpected `FStringMiddle` token, which is between the
|
||||
// `:` and the `{`.
|
||||
// assert_eq!(parse_fstring_error("f'{lambda x: {x}}'"), LambdaWithoutParentheses);
|
||||
assert!(parse_suite(r#"f"{class}""#).is_err());
|
||||
let allocator = Allocator::new();
|
||||
assert!(parse_suite(r#"f"{class}""#, &allocator).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_not_equals() {
|
||||
let source = r#"f"{1 != 2}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_equals() {
|
||||
let source = r#"f"{42 == 42}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_self_doc_prec_space() {
|
||||
let source = r#"f"{x =}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_self_doc_trailing_space() {
|
||||
let source = r#"f"{x= }""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_yield_expr() {
|
||||
let source = r#"f"{yield}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_string_concat() {
|
||||
let source = "'Hello ' 'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_u_string_concat_1() {
|
||||
let source = "'Hello ' u'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_u_string_concat_2() {
|
||||
let source = "u'Hello ' 'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_f_string_concat_1() {
|
||||
let source = "'Hello ' f'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_f_string_concat_2() {
|
||||
let source = "'Hello ' f'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_f_string_concat_3() {
|
||||
let source = "'Hello ' f'world{\"!\"}'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_f_string_concat_4() {
|
||||
let source = "'Hello ' f'world{\"!\"}' 'again!'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_u_f_string_concat_1() {
|
||||
let source = "u'Hello ' f'world'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_u_f_string_concat_2() {
|
||||
let source = "u'Hello ' f'world' '!'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_string_triple_quotes_with_kind() {
|
||||
let source = "u'''Hello, world!'''";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -691,7 +739,8 @@ mod tests {
|
||||
fn test_single_quoted_byte() {
|
||||
// single quote
|
||||
let source = r##"b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'"##;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -699,7 +748,8 @@ mod tests {
|
||||
fn test_double_quoted_byte() {
|
||||
// double quote
|
||||
let source = r##"b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff""##;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -707,42 +757,48 @@ mod tests {
|
||||
fn test_escape_char_in_byte_literal() {
|
||||
// backslash does not escape
|
||||
let source = r#"b"omkmok\Xaa""#; // spell-checker:ignore omkmok
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_byte_literal_1() {
|
||||
let source = r"rb'\x1z'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_byte_literal_2() {
|
||||
let source = r"rb'\\'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape_octet() {
|
||||
let source = r"b'\43a\4\1234'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_escaped_newline() {
|
||||
let source = r#"f"\n{x}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_constant_range() {
|
||||
let source = r#"f"aaa{bbb}ccc{ddd}eee""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -750,28 +806,32 @@ mod tests {
|
||||
fn test_fstring_unescaped_newline() {
|
||||
let source = r#"f"""
|
||||
{x}""""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_escaped_character() {
|
||||
let source = r#"f"\\{x}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_fstring() {
|
||||
let source = r#"rf"{x}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triple_quoted_raw_fstring() {
|
||||
let source = r#"rf"""{x}""""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -779,21 +839,24 @@ mod tests {
|
||||
fn test_fstring_line_continuation() {
|
||||
let source = r#"rf"\
|
||||
{x}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_nested_string_spec() {
|
||||
let source = r#"f"{foo:{''}}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_nested_concatenation_string_spec() {
|
||||
let source = r#"f"{foo:{'' ''}}""#;
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
@@ -801,42 +864,48 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dont_panic_on_8_in_octal_escape() {
|
||||
let source = r"bold = '\038[1m'";
|
||||
let suite = parse_suite(source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_unicode_literal() {
|
||||
let source = r"'\x1ó34'";
|
||||
let error = parse_suite(source).unwrap_err();
|
||||
let allocator = Allocator::new();
|
||||
let error = parse_suite(source, &allocator).unwrap_err();
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_missing_unicode_lbrace_error() {
|
||||
let source = r"'\N '";
|
||||
let error = parse_suite(source).unwrap_err();
|
||||
let allocator = Allocator::new();
|
||||
let error = parse_suite(source, &allocator).unwrap_err();
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_missing_unicode_rbrace_error() {
|
||||
let source = r"'\N{SPACE'";
|
||||
let error = parse_suite(source).unwrap_err();
|
||||
let allocator = Allocator::new();
|
||||
let error = parse_suite(source, &allocator).unwrap_err();
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_unicode_name_error() {
|
||||
let source = r"'\N{INVALID}'";
|
||||
let error = parse_suite(source).unwrap_err();
|
||||
let allocator = Allocator::new();
|
||||
let error = parse_suite(source, &allocator).unwrap_err();
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_byte_literal_error() {
|
||||
let source = r"b'123a𝐁c'";
|
||||
let error = parse_suite(source).unwrap_err();
|
||||
let allocator = Allocator::new();
|
||||
let error = parse_suite(source, &allocator).unwrap_err();
|
||||
insta::assert_debug_snapshot!(error);
|
||||
}
|
||||
|
||||
@@ -846,7 +915,8 @@ mod tests {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let source = format!(r#""\N{{{0}}}""#, $alias);
|
||||
let suite = parse_suite(&source).unwrap();
|
||||
let allocator = Allocator::new();
|
||||
let suite = parse_suite(&source, &allocator).unwrap();
|
||||
insta::assert_debug_snapshot!(suite);
|
||||
}
|
||||
)*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! This module takes care of parsing a type annotation.
|
||||
|
||||
use ruff_allocator::Allocator;
|
||||
use ruff_python_ast::relocate::relocate_expr;
|
||||
use ruff_python_ast::str::raw_contents;
|
||||
use ruff_python_ast::{ExprStringLiteral, ModExpression, StringFlags, StringLiteral};
|
||||
@@ -31,10 +32,11 @@ impl AnnotationKind {
|
||||
|
||||
/// Parses the given string expression node as a type annotation. The given `source` is the entire
|
||||
/// source code.
|
||||
pub fn parse_type_annotation(
|
||||
string_expr: &ExprStringLiteral,
|
||||
source: &str,
|
||||
) -> Result<(Parsed<ModExpression>, AnnotationKind), ParseError> {
|
||||
pub fn parse_type_annotation<'a>(
|
||||
string_expr: &ExprStringLiteral<'a>,
|
||||
source: &'a str,
|
||||
allocator: &'a Allocator,
|
||||
) -> Result<(Parsed<ModExpression<'a>>, AnnotationKind), ParseError> {
|
||||
let expr_text = &source[string_expr.range()];
|
||||
|
||||
if let [string_literal] = string_expr.value.as_slice() {
|
||||
@@ -43,22 +45,23 @@ pub fn parse_type_annotation(
|
||||
if raw_contents(expr_text)
|
||||
.is_some_and(|raw_contents| raw_contents == string_literal.as_str())
|
||||
{
|
||||
parse_simple_type_annotation(string_literal, source)
|
||||
parse_simple_type_annotation(string_literal, source, allocator)
|
||||
} else {
|
||||
// The raw contents of the string doesn't match the parsed content. This could be the
|
||||
// case for annotations that contain escaped quotes.
|
||||
parse_complex_type_annotation(string_expr)
|
||||
parse_complex_type_annotation(string_expr, allocator)
|
||||
}
|
||||
} else {
|
||||
// String is implicitly concatenated.
|
||||
parse_complex_type_annotation(string_expr)
|
||||
parse_complex_type_annotation(string_expr, allocator)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_simple_type_annotation(
|
||||
string_literal: &StringLiteral,
|
||||
source: &str,
|
||||
) -> Result<(Parsed<ModExpression>, AnnotationKind), ParseError> {
|
||||
fn parse_simple_type_annotation<'a>(
|
||||
string_literal: &StringLiteral<'a>,
|
||||
source: &'a str,
|
||||
allocator: &'a Allocator,
|
||||
) -> Result<(Parsed<ModExpression<'a>>, AnnotationKind), ParseError> {
|
||||
Ok((
|
||||
parse_expression_range(
|
||||
source,
|
||||
@@ -66,15 +69,17 @@ fn parse_simple_type_annotation(
|
||||
.range()
|
||||
.add_start(string_literal.flags.opener_len())
|
||||
.sub_end(string_literal.flags.closer_len()),
|
||||
allocator,
|
||||
)?,
|
||||
AnnotationKind::Simple,
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_complex_type_annotation(
|
||||
string_expr: &ExprStringLiteral,
|
||||
) -> Result<(Parsed<ModExpression>, AnnotationKind), ParseError> {
|
||||
let mut parsed = parse_expression(string_expr.value.to_str())?;
|
||||
fn parse_complex_type_annotation<'ast>(
|
||||
string_expr: &ExprStringLiteral<'_>,
|
||||
allocator: &'ast Allocator,
|
||||
) -> Result<(Parsed<ModExpression<'ast>>, AnnotationKind), ParseError> {
|
||||
let mut parsed = parse_expression(string_expr.value.to_str(), allocator)?;
|
||||
relocate_expr(parsed.expr_mut(), string_expr.range());
|
||||
Ok((parsed, AnnotationKind::Complex))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user