mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Separate terminator token for f-string elements kind (#11842)
## Summary This PR separates the terminator token for f-string elements depending on the context. A list of f-string element can occur either in a regular f-string or a format spec of an f-string. The terminator token is different depending on that context. ## Test Plan `cargo insta test` and verify the updated snapshots.
This commit is contained in:
parent
93973b96cb
commit
a525b4be3d
5 changed files with 73 additions and 44 deletions
|
@ -18,7 +18,7 @@ use crate::string::{parse_fstring_literal_element, parse_string_literal, StringT
|
||||||
use crate::token_set::TokenSet;
|
use crate::token_set::TokenSet;
|
||||||
use crate::{FStringErrorType, Mode, ParseErrorType, TokenKind};
|
use crate::{FStringErrorType, Mode, ParseErrorType, TokenKind};
|
||||||
|
|
||||||
use super::{Parenthesized, RecoveryContextKind};
|
use super::{FStringElementsKind, Parenthesized, RecoveryContextKind};
|
||||||
|
|
||||||
/// A token set consisting of a newline or end of file.
|
/// A token set consisting of a newline or end of file.
|
||||||
const NEWLINE_EOF_SET: TokenSet = TokenSet::new([TokenKind::Newline, TokenKind::EndOfFile]);
|
const NEWLINE_EOF_SET: TokenSet = TokenSet::new([TokenKind::Newline, TokenKind::EndOfFile]);
|
||||||
|
@ -1307,7 +1307,7 @@ impl<'src> Parser<'src> {
|
||||||
let flags = self.tokens.current_flags().as_any_string_flags();
|
let flags = self.tokens.current_flags().as_any_string_flags();
|
||||||
|
|
||||||
self.bump(TokenKind::FStringStart);
|
self.bump(TokenKind::FStringStart);
|
||||||
let elements = self.parse_fstring_elements(flags);
|
let elements = self.parse_fstring_elements(flags, FStringElementsKind::Regular);
|
||||||
|
|
||||||
self.expect(TokenKind::FStringEnd);
|
self.expect(TokenKind::FStringEnd);
|
||||||
|
|
||||||
|
@ -1323,10 +1323,14 @@ impl<'src> Parser<'src> {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the parser isn't positioned at a `{` or `FStringMiddle` token.
|
/// If the parser isn't positioned at a `{` or `FStringMiddle` token.
|
||||||
fn parse_fstring_elements(&mut self, flags: ast::AnyStringFlags) -> FStringElements {
|
fn parse_fstring_elements(
|
||||||
|
&mut self,
|
||||||
|
flags: ast::AnyStringFlags,
|
||||||
|
kind: FStringElementsKind,
|
||||||
|
) -> FStringElements {
|
||||||
let mut elements = vec![];
|
let mut elements = vec![];
|
||||||
|
|
||||||
self.parse_list(RecoveryContextKind::FStringElements, |parser| {
|
self.parse_list(RecoveryContextKind::FStringElements(kind), |parser| {
|
||||||
let element = match parser.current_token_kind() {
|
let element = match parser.current_token_kind() {
|
||||||
TokenKind::Lbrace => {
|
TokenKind::Lbrace => {
|
||||||
FStringElement::Expression(parser.parse_fstring_expression_element(flags))
|
FStringElement::Expression(parser.parse_fstring_expression_element(flags))
|
||||||
|
@ -1463,7 +1467,7 @@ impl<'src> Parser<'src> {
|
||||||
|
|
||||||
let format_spec = if self.eat(TokenKind::Colon) {
|
let format_spec = if self.eat(TokenKind::Colon) {
|
||||||
let spec_start = self.node_start();
|
let spec_start = self.node_start();
|
||||||
let elements = self.parse_fstring_elements(flags);
|
let elements = self.parse_fstring_elements(flags, FStringElementsKind::FormatSpec);
|
||||||
Some(Box::new(ast::FStringFormatSpec {
|
Some(Box::new(ast::FStringFormatSpec {
|
||||||
range: self.node_range(spec_start),
|
range: self.node_range(spec_start),
|
||||||
elements,
|
elements,
|
||||||
|
|
|
@ -722,6 +722,37 @@ impl WithItemKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
enum FStringElementsKind {
|
||||||
|
/// The regular f-string elements.
|
||||||
|
///
|
||||||
|
/// For example, the `"hello "`, `x`, and `" world"` elements in:
|
||||||
|
/// ```py
|
||||||
|
/// f"hello {x:.2f} world"
|
||||||
|
/// ```
|
||||||
|
Regular,
|
||||||
|
|
||||||
|
/// The f-string elements are part of the format specifier.
|
||||||
|
///
|
||||||
|
/// For example, the `.2f` in:
|
||||||
|
/// ```py
|
||||||
|
/// f"hello {x:.2f} world"
|
||||||
|
/// ```
|
||||||
|
FormatSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FStringElementsKind {
|
||||||
|
const fn list_terminator(self) -> TokenKind {
|
||||||
|
match self {
|
||||||
|
FStringElementsKind::Regular => TokenKind::FStringEnd,
|
||||||
|
// test_ok fstring_format_spec_terminator
|
||||||
|
// f"hello {x:} world"
|
||||||
|
// f"hello {x:.3f} world"
|
||||||
|
FStringElementsKind::FormatSpec => TokenKind::Rbrace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
enum Parenthesized {
|
enum Parenthesized {
|
||||||
/// The elements are parenthesized, e.g., `(a, b)`.
|
/// The elements are parenthesized, e.g., `(a, b)`.
|
||||||
|
@ -819,7 +850,7 @@ enum RecoveryContextKind {
|
||||||
|
|
||||||
/// When parsing a list of f-string elements which are either literal elements
|
/// When parsing a list of f-string elements which are either literal elements
|
||||||
/// or expressions.
|
/// or expressions.
|
||||||
FStringElements,
|
FStringElements(FStringElementsKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecoveryContextKind {
|
impl RecoveryContextKind {
|
||||||
|
@ -954,10 +985,8 @@ impl RecoveryContextKind {
|
||||||
p.at(TokenKind::Colon)
|
p.at(TokenKind::Colon)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RecoveryContextKind::FStringElements => {
|
RecoveryContextKind::FStringElements(kind) => {
|
||||||
// Tokens other than `FStringEnd` and `}` are for better error recovery
|
p.at(kind.list_terminator())
|
||||||
p.at_ts(TokenSet::new([
|
|
||||||
TokenKind::FStringEnd,
|
|
||||||
// test_err unterminated_fstring_newline_recovery
|
// test_err unterminated_fstring_newline_recovery
|
||||||
// f"hello
|
// f"hello
|
||||||
// 1 + 1
|
// 1 + 1
|
||||||
|
@ -967,15 +996,7 @@ impl RecoveryContextKind {
|
||||||
// 3 + 3
|
// 3 + 3
|
||||||
// f"hello {x}
|
// f"hello {x}
|
||||||
// 4 + 4
|
// 4 + 4
|
||||||
TokenKind::Newline,
|
|| p.at(TokenKind::Newline)
|
||||||
// When the parser is parsing f-string elements inside format spec,
|
|
||||||
// the terminator would be `}`.
|
|
||||||
|
|
||||||
// test_ok fstring_format_spec_terminator
|
|
||||||
// f"hello {x:} world"
|
|
||||||
// f"hello {x:.3f} world"
|
|
||||||
TokenKind::Rbrace,
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1017,7 +1038,7 @@ impl RecoveryContextKind {
|
||||||
) || p.at_name_or_soft_keyword()
|
) || p.at_name_or_soft_keyword()
|
||||||
}
|
}
|
||||||
RecoveryContextKind::WithItems(_) => p.at_expr(),
|
RecoveryContextKind::WithItems(_) => p.at_expr(),
|
||||||
RecoveryContextKind::FStringElements => matches!(
|
RecoveryContextKind::FStringElements(_) => matches!(
|
||||||
p.current_token_kind(),
|
p.current_token_kind(),
|
||||||
// Literal element
|
// Literal element
|
||||||
TokenKind::FStringMiddle
|
TokenKind::FStringMiddle
|
||||||
|
@ -1111,9 +1132,14 @@ impl RecoveryContextKind {
|
||||||
"Expected an expression or the end of the with item list".to_string(),
|
"Expected an expression or the end of the with item list".to_string(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
RecoveryContextKind::FStringElements => ParseErrorType::OtherError(
|
RecoveryContextKind::FStringElements(kind) => match kind {
|
||||||
|
FStringElementsKind::Regular => ParseErrorType::OtherError(
|
||||||
"Expected an f-string element or the end of the f-string".to_string(),
|
"Expected an f-string element or the end of the f-string".to_string(),
|
||||||
),
|
),
|
||||||
|
FStringElementsKind::FormatSpec => {
|
||||||
|
ParseErrorType::OtherError("Expected an f-string element or a '}'".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1152,6 +1178,7 @@ bitflags! {
|
||||||
const WITH_ITEMS_PARENTHESIZED_EXPRESSION = 1 << 26;
|
const WITH_ITEMS_PARENTHESIZED_EXPRESSION = 1 << 26;
|
||||||
const WITH_ITEMS_UNPARENTHESIZED = 1 << 28;
|
const WITH_ITEMS_UNPARENTHESIZED = 1 << 28;
|
||||||
const F_STRING_ELEMENTS = 1 << 29;
|
const F_STRING_ELEMENTS = 1 << 29;
|
||||||
|
const F_STRING_ELEMENTS_IN_FORMAT_SPEC = 1 << 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1204,7 +1231,12 @@ impl RecoveryContext {
|
||||||
}
|
}
|
||||||
WithItemKind::Unparenthesized => RecoveryContext::WITH_ITEMS_UNPARENTHESIZED,
|
WithItemKind::Unparenthesized => RecoveryContext::WITH_ITEMS_UNPARENTHESIZED,
|
||||||
},
|
},
|
||||||
RecoveryContextKind::FStringElements => RecoveryContext::F_STRING_ELEMENTS,
|
RecoveryContextKind::FStringElements(kind) => match kind {
|
||||||
|
FStringElementsKind::Regular => RecoveryContext::F_STRING_ELEMENTS,
|
||||||
|
FStringElementsKind::FormatSpec => {
|
||||||
|
RecoveryContext::F_STRING_ELEMENTS_IN_FORMAT_SPEC
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1271,7 +1303,12 @@ impl RecoveryContext {
|
||||||
RecoveryContext::WITH_ITEMS_UNPARENTHESIZED => {
|
RecoveryContext::WITH_ITEMS_UNPARENTHESIZED => {
|
||||||
RecoveryContextKind::WithItems(WithItemKind::Unparenthesized)
|
RecoveryContextKind::WithItems(WithItemKind::Unparenthesized)
|
||||||
}
|
}
|
||||||
RecoveryContext::F_STRING_ELEMENTS => RecoveryContextKind::FStringElements,
|
RecoveryContext::F_STRING_ELEMENTS => {
|
||||||
|
RecoveryContextKind::FStringElements(FStringElementsKind::Regular)
|
||||||
|
}
|
||||||
|
RecoveryContext::F_STRING_ELEMENTS_IN_FORMAT_SPEC => {
|
||||||
|
RecoveryContextKind::FStringElements(FStringElementsKind::FormatSpec)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,15 @@ Module(
|
||||||
body: [
|
body: [
|
||||||
Expr(
|
Expr(
|
||||||
StmtExpr {
|
StmtExpr {
|
||||||
range: 0..14,
|
range: 0..16,
|
||||||
value: FString(
|
value: FString(
|
||||||
ExprFString {
|
ExprFString {
|
||||||
range: 0..14,
|
range: 0..16,
|
||||||
value: FStringValue {
|
value: FStringValue {
|
||||||
inner: Single(
|
inner: Single(
|
||||||
FString(
|
FString(
|
||||||
FString {
|
FString {
|
||||||
range: 0..14,
|
range: 0..16,
|
||||||
elements: [
|
elements: [
|
||||||
Expression(
|
Expression(
|
||||||
FStringExpressionElement {
|
FStringExpressionElement {
|
||||||
|
@ -110,17 +110,5 @@ Module(
|
||||||
|
|
||||||
|
|
|
|
||||||
1 | f"{lambda x: x}"
|
1 | f"{lambda x: x}"
|
||||||
| ^ Syntax Error: Expected FStringEnd, found '}'
|
| ^ Syntax Error: Expected an f-string element or the end of the f-string
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
1 | f"{lambda x: x}"
|
|
||||||
| ^ Syntax Error: Expected a statement
|
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
1 | f"{lambda x: x}"
|
|
||||||
| ^ Syntax Error: Expected a statement
|
|
||||||
|
|
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ Module(
|
||||||
|
|
||||||
|
|
|
|
||||||
1 | f"hello {x:"
|
1 | f"hello {x:"
|
||||||
| ^ Syntax Error: f-string: expecting '}'
|
| ^ Syntax Error: Expected an f-string element or a '}'
|
||||||
2 | f"hello {x:.3f"
|
2 | f"hello {x:.3f"
|
||||||
|
|
|
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ Module(
|
||||||
|
|
|
|
||||||
1 | f"hello {x:"
|
1 | f"hello {x:"
|
||||||
2 | f"hello {x:.3f"
|
2 | f"hello {x:.3f"
|
||||||
| ^ Syntax Error: f-string: expecting '}'
|
| ^ Syntax Error: Expected an f-string element or a '}'
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -335,7 +335,7 @@ Module(
|
||||||
4 | 2 + 2
|
4 | 2 + 2
|
||||||
5 | f"hello {x:
|
5 | f"hello {x:
|
||||||
6 | 3 + 3
|
6 | 3 + 3
|
||||||
| ^ Syntax Error: Expected an f-string element or the end of the f-string
|
| ^ Syntax Error: Expected an f-string element or a '}'
|
||||||
7 | f"hello {x}
|
7 | f"hello {x}
|
||||||
8 | 4 + 4
|
8 | 4 + 4
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue