fix the ranges of constants inside f-strings (#33)

This commit is contained in:
David Szotten 2023-07-25 18:17:06 +01:00 committed by GitHub
parent 5ef4ccd632
commit 13196fc500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 231 additions and 137 deletions

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..14,
range: 2..13,
value: Str(
"Hello world",
),

View File

@ -80,7 +80,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 62..81,
range: 64..71,
value: Str(
"caught ",
),
@ -89,7 +89,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 62..81,
range: 71..80,
value: Call(
ExprCall {
range: 72..79,
@ -167,7 +167,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 114..133,
range: 116..123,
value: Str(
"caught ",
),
@ -176,7 +176,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 114..133,
range: 123..132,
value: Call(
ExprCall {
range: 124..131,

View File

@ -184,7 +184,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 133..179,
range: 135..142,
value: Str(
"caught ",
),
@ -193,7 +193,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 133..179,
range: 142..151,
value: Call(
ExprCall {
range: 143..150,
@ -222,7 +222,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 133..179,
range: 151..164,
value: Str(
" with nested ",
),
@ -231,7 +231,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 133..179,
range: 164..178,
value: Attribute(
ExprAttribute {
range: 165..177,
@ -304,7 +304,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 213..259,
range: 215..222,
value: Str(
"caught ",
),
@ -313,7 +313,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 213..259,
range: 222..231,
value: Call(
ExprCall {
range: 223..230,
@ -342,7 +342,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 213..259,
range: 231..244,
value: Str(
" with nested ",
),
@ -351,7 +351,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 213..259,
range: 244..258,
value: Attribute(
ExprAttribute {
range: 245..257,

View File

@ -0,0 +1,73 @@
---
source: parser/src/string.rs
expression: parse_ast
---
[
Expr(
StmtExpr {
range: 0..22,
value: JoinedStr(
ExprJoinedStr {
range: 0..22,
values: [
Constant(
ExprConstant {
range: 2..5,
value: Str(
"aaa",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 5..10,
value: Name(
ExprName {
range: 6..9,
id: "bbb",
ctx: Load,
},
),
conversion: None,
format_spec: None,
},
),
Constant(
ExprConstant {
range: 10..13,
value: Str(
"ccc",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 13..18,
value: Name(
ExprName {
range: 14..17,
id: "ddd",
ctx: Load,
},
),
conversion: None,
format_spec: None,
},
),
Constant(
ExprConstant {
range: 18..21,
value: Str(
"eee",
),
kind: None,
},
),
],
},
),
},
),
]

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..8,
range: 2..4,
value: Str(
"\\",
),
@ -21,7 +21,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..8,
range: 4..7,
value: Name(
ExprName {
range: 5..6,

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..8,
range: 2..4,
value: Str(
"\n",
),
@ -21,7 +21,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..8,
range: 4..7,
value: Name(
ExprName {
range: 5..6,

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..9,
range: 3..5,
value: Str(
"\\\n",
),
@ -21,7 +21,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..9,
range: 5..8,
value: Name(
ExprName {
range: 6..7,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
"user=",
),
@ -14,7 +14,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
"",
),
@ -23,7 +23,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..10,
range: 2..9,
value: Name(
ExprName {
range: 3..7,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
Constant(
ExprConstant {
range: 0..38,
range: 2..6,
value: Str(
"mix ",
),
@ -14,7 +14,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..38,
range: 6..13,
value: Str(
"user=",
),
@ -23,7 +23,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..38,
range: 6..13,
value: Str(
"",
),
@ -32,7 +32,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..38,
range: 6..13,
value: Name(
ExprName {
range: 7..11,
@ -46,7 +46,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..38,
range: 13..28,
value: Str(
" with text and ",
),
@ -55,7 +55,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..38,
range: 28..37,
value: Str(
"second=",
),
@ -64,7 +64,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..38,
range: 28..37,
value: Str(
"",
),
@ -73,7 +73,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..38,
range: 28..37,
value: Name(
ExprName {
range: 29..35,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
Constant(
ExprConstant {
range: 0..14,
range: 2..13,
value: Str(
"user=",
),
@ -14,7 +14,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..14,
range: 2..13,
value: Str(
"",
),
@ -23,7 +23,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..14,
range: 2..13,
value: Name(
ExprName {
range: 3..7,
@ -35,11 +35,11 @@ expression: parse_ast
format_spec: Some(
JoinedStr(
ExprJoinedStr {
range: 0..14,
range: 9..12,
values: [
Constant(
ExprConstant {
range: 0..14,
range: 9..12,
value: Str(
">10",
),

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..11,
range: 4..5,
value: Str(
"\n",
),
@ -21,7 +21,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..11,
range: 5..8,
value: Name(
ExprName {
range: 6..7,

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..17,
range: 1..16,
value: Str(
"Hello world",
),

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..17,
range: 1..16,
value: Str(
"Hello world",
),

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..22,
range: 1..16,
value: Str(
"Hello world",
),
@ -21,7 +21,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 9..22,
range: 16..21,
value: Constant(
ExprConstant {
range: 17..20,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..18,
range: 2..5,
value: Name(
ExprName {
range: 3..4,
@ -19,7 +19,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..18,
range: 5..10,
value: Name(
ExprName {
range: 7..8,
@ -33,7 +33,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..18,
range: 10..17,
value: Str(
"{foo}",
),

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..13,
range: 2..12,
value: Compare(
ExprCompare {
range: 3..11,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..15,
range: 2..14,
value: Name(
ExprName {
range: 3..6,
@ -17,11 +17,11 @@ expression: parse_ast
format_spec: Some(
JoinedStr(
ExprJoinedStr {
range: 0..15,
range: 7..13,
values: [
FormattedValue(
ExprFormattedValue {
range: 0..15,
range: 7..13,
value: Name(
ExprName {
range: 8..12,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..11,
range: 2..10,
value: Compare(
ExprCompare {
range: 3..9,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..13,
range: 2..12,
value: Name(
ExprName {
range: 3..6,
@ -17,11 +17,11 @@ expression: parse_ast
format_spec: Some(
JoinedStr(
ExprJoinedStr {
range: 0..13,
range: 7..11,
values: [
Constant(
ExprConstant {
range: 0..13,
range: 7..11,
value: Str(
"spec",
),

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
"x =",
),
@ -14,7 +14,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
"",
),
@ -23,7 +23,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..10,
range: 2..9,
value: Name(
ExprName {
range: 3..4,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
"x=",
),
@ -14,7 +14,7 @@ expression: parse_ast
),
Constant(
ExprConstant {
range: 0..10,
range: 2..9,
value: Str(
" ",
),
@ -23,7 +23,7 @@ expression: parse_ast
),
FormattedValue(
ExprFormattedValue {
range: 0..10,
range: 2..9,
value: Name(
ExprName {
range: 3..4,

View File

@ -5,7 +5,7 @@ expression: parse_ast
[
FormattedValue(
ExprFormattedValue {
range: 0..10,
range: 2..9,
value: Yield(
ExprYield {
range: 3..8,

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..18,
range: 2..17,
value: Str(
"Hello world",
),

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
Constant(
ExprConstant {
range: 0..22,
range: 2..21,
value: Str(
"Hello world!",
),

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
FormattedValue(
ExprFormattedValue {
range: 0..7,
range: 3..6,
value: Name(
ExprName {
range: 4..5,

View File

@ -12,7 +12,7 @@ expression: parse_ast
values: [
FormattedValue(
ExprFormattedValue {
range: 0..11,
range: 5..8,
value: Name(
ExprName {
range: 6..7,

View File

@ -11,6 +11,7 @@ use crate::{
token::{StringKind, Tok},
};
use itertools::Itertools;
use rustpython_ast::Ranged;
use rustpython_parser_core::{
text_size::{TextLen, TextSize},
ConversionFlag,
@ -20,21 +21,13 @@ use rustpython_parser_core::{
const MAX_UNICODE_NAME: usize = 88;
struct StringParser<'a> {
chars: std::iter::Peekable<std::str::Chars<'a>>,
chars: std::str::Chars<'a>,
kind: StringKind,
start: TextSize,
end: TextSize,
location: TextSize,
}
impl<'a> StringParser<'a> {
fn new(
source: &'a str,
kind: StringKind,
triple_quoted: bool,
start: TextSize,
end: TextSize,
) -> Self {
fn new(source: &'a str, kind: StringKind, triple_quoted: bool, start: TextSize) -> Self {
let offset = kind.prefix_len()
+ if triple_quoted {
TextSize::from(3)
@ -42,10 +35,8 @@ impl<'a> StringParser<'a> {
TextSize::from(1)
};
Self {
chars: source.chars().peekable(),
chars: source.chars(),
kind,
start,
end,
location: start + offset,
}
}
@ -58,8 +49,15 @@ impl<'a> StringParser<'a> {
}
#[inline]
fn peek(&mut self) -> Option<&char> {
self.chars.peek()
fn peek(&mut self) -> Option<char> {
self.chars.clone().next()
}
#[inline]
fn peek2(&mut self) -> Option<char> {
let mut chars = self.chars.clone();
chars.next();
chars.next()
}
#[inline]
@ -68,12 +66,13 @@ impl<'a> StringParser<'a> {
}
#[inline]
fn expr(&self, node: Expr) -> Expr {
node
fn range(&self, start_location: TextSize) -> TextRange {
TextRange::new(start_location, self.location)
}
fn range(&self) -> TextRange {
TextRange::new(self.start, self.end)
#[inline]
fn expr(&self, node: Expr) -> Expr {
node
}
fn parse_unicode_literal(&mut self, literal_number: usize) -> Result<char, LexicalError> {
@ -191,18 +190,20 @@ impl<'a> StringParser<'a> {
let mut conversion = ConversionFlag::None;
let mut self_documenting = false;
let mut trailing_seq = String::new();
let location = self.get_pos();
let start_location = self.get_pos();
assert_eq!(self.next_char(), Some('{'));
while let Some(ch) = self.next_char() {
match ch {
// can be integrated better with the remaining code, but as a starting point ok
// in general I would do here a tokenizing of the fstrings to omit this peeking.
'!' | '=' | '>' | '<' if self.peek() == Some(&'=') => {
'!' | '=' | '>' | '<' if self.peek() == Some('=') => {
expression.push(ch);
expression.push('=');
self.next_char();
}
'!' if delimiters.is_empty() && self.peek() != Some(&'=') => {
'!' if delimiters.is_empty() && self.peek() != Some('=') => {
if expression.trim().is_empty() {
return Err(FStringError::new(EmptyExpression, self.get_pos()).into());
}
@ -231,18 +232,19 @@ impl<'a> StringParser<'a> {
// match a python 3.8 self documenting expression
// format '{' PYTHON_EXPRESSION '=' FORMAT_SPECIFIER? '}'
'=' if self.peek() != Some(&'=') && delimiters.is_empty() => {
'=' if self.peek() != Some('=') && delimiters.is_empty() => {
self_documenting = true;
}
':' if delimiters.is_empty() => {
let start_location = self.get_pos();
let parsed_spec = self.parse_spec(nested)?;
spec = Some(Box::new(
self.expr(
ast::ExprJoinedStr {
values: parsed_spec,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -313,26 +315,30 @@ impl<'a> StringParser<'a> {
vec![self.expr(
ast::ExprFormattedValue {
value: Box::new(
parse_fstring_expr(&expression, location).map_err(|e| {
FStringError::new(
InvalidExpression(Box::new(e.error)),
location,
)
})?,
parse_fstring_expr(&expression, start_location).map_err(
|e| {
FStringError::new(
InvalidExpression(Box::new(e.error)),
start_location,
)
},
)?,
),
conversion,
format_spec: spec,
range: self.range(),
range: self.range(start_location),
}
.into(),
)]
} else {
// TODO: range is wrong but `self_documenting` needs revisiting beyond
// ranges: https://github.com/astral-sh/ruff/issues/5970
vec![
self.expr(
ast::ExprConstant {
value: Constant::Str(expression.to_owned() + "="),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -340,19 +346,21 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: trailing_seq.into(),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
self.expr(
ast::ExprFormattedValue {
value: Box::new(
parse_fstring_expr(&expression, location).map_err(|e| {
FStringError::new(
InvalidExpression(Box::new(e.error)),
location,
)
})?,
parse_fstring_expr(&expression, start_location).map_err(
|e| {
FStringError::new(
InvalidExpression(Box::new(e.error)),
start_location,
)
},
)?,
),
conversion: if conversion == ConversionFlag::None
&& spec.is_none()
@ -362,7 +370,7 @@ impl<'a> StringParser<'a> {
conversion
},
format_spec: spec,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -401,7 +409,8 @@ impl<'a> StringParser<'a> {
fn parse_spec(&mut self, nested: u8) -> Result<Vec<Expr>, LexicalError> {
let mut spec_constructor = Vec::new();
let mut constant_piece = String::new();
while let Some(&next) = self.peek() {
let mut start_location = self.get_pos();
while let Some(next) = self.peek() {
match next {
'{' => {
if !constant_piece.is_empty() {
@ -410,7 +419,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: constant_piece.drain(..).collect::<String>().into(),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -418,6 +427,7 @@ impl<'a> StringParser<'a> {
}
let parsed_expr = self.parse_fstring(nested + 1)?;
spec_constructor.extend(parsed_expr);
start_location = self.get_pos();
continue;
}
'}' => {
@ -435,7 +445,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: constant_piece.drain(..).collect::<String>().into(),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -452,15 +462,16 @@ impl<'a> StringParser<'a> {
}
let mut content = String::new();
let mut start_location = self.get_pos();
let mut values = vec![];
while let Some(&ch) = self.peek() {
while let Some(ch) = self.peek() {
match ch {
'{' => {
self.next_char();
if nested == 0 {
match self.peek() {
match self.peek2() {
Some('{') => {
self.next_char();
self.next_char();
content.push('{');
continue;
@ -477,7 +488,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: content.drain(..).collect::<String>().into(),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -486,6 +497,7 @@ impl<'a> StringParser<'a> {
let parsed_values = self.parse_formatted_value(nested)?;
values.extend(parsed_values);
start_location = self.get_pos();
}
'}' => {
if nested > 0 {
@ -516,7 +528,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: content.into(),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
),
@ -528,6 +540,7 @@ impl<'a> StringParser<'a> {
fn parse_bytes(&mut self) -> Result<Expr, LexicalError> {
let mut content = String::new();
let start_location = self.get_pos();
while let Some(ch) = self.next_char() {
match ch {
'\\' if !self.kind.is_raw() => {
@ -551,7 +564,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: Constant::Bytes(content.chars().map(|c| c as u8).collect()),
kind: None,
range: self.range(),
range: self.range(start_location),
}
.into(),
))
@ -559,6 +572,7 @@ impl<'a> StringParser<'a> {
fn parse_string(&mut self) -> Result<Expr, LexicalError> {
let mut content = String::new();
let start_location = self.get_pos();
while let Some(ch) = self.next_char() {
match ch {
'\\' if !self.kind.is_raw() => {
@ -571,7 +585,7 @@ impl<'a> StringParser<'a> {
ast::ExprConstant {
value: Constant::Str(content),
kind: self.kind.is_unicode().then(|| "u".to_string()),
range: self.range(),
range: self.range(start_location),
}
.into(),
))
@ -590,8 +604,7 @@ impl<'a> StringParser<'a> {
fn parse_fstring_expr(source: &str, location: TextSize) -> Result<Expr, ParseError> {
let fstring_body = format!("({source})");
let start = location - TextSize::from(1);
ast::Expr::parse_starts_at(&fstring_body, "<fstring>", start)
ast::Expr::parse_starts_at(&fstring_body, "<fstring>", location)
}
fn parse_string(
@ -599,9 +612,8 @@ fn parse_string(
kind: StringKind,
triple_quoted: bool,
start: TextSize,
end: TextSize,
) -> Result<Vec<Expr>, LexicalError> {
StringParser::new(source, kind, triple_quoted, start, end).parse()
StringParser::new(source, kind, triple_quoted, start).parse()
}
pub(crate) fn parse_strings(
@ -631,8 +643,8 @@ pub(crate) fn parse_strings(
if has_bytes {
let mut content: Vec<u8> = vec![];
for (start, (source, kind, triple_quoted), end) in values {
for value in parse_string(&source, kind, triple_quoted, start, end)? {
for (start, (source, kind, triple_quoted), _) in values {
for value in parse_string(&source, kind, triple_quoted, start)? {
match value {
Expr::Constant(ast::ExprConstant {
value: Constant::Bytes(value),
@ -652,8 +664,8 @@ pub(crate) fn parse_strings(
if !has_fstring {
let mut content: Vec<String> = vec![];
for (start, (source, kind, triple_quoted), end) in values {
for value in parse_string(&source, kind, triple_quoted, start, end)? {
for (start, (source, kind, triple_quoted), _) in values {
for value in parse_string(&source, kind, triple_quoted, start)? {
match value {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
@ -674,34 +686,43 @@ pub(crate) fn parse_strings(
// De-duplicate adjacent constants.
let mut deduped: Vec<Expr> = vec![];
let mut current: Vec<String> = vec![];
let mut current_start = initial_start;
let mut current_end = last_end;
let take_current = |current: &mut Vec<String>| -> Expr {
let take_current = |current: &mut Vec<String>, start, end| -> Expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(current.drain(..).join("")),
kind: initial_kind.clone(),
range: TextRange::new(initial_start, last_end),
range: TextRange::new(start, end),
})
};
for (start, (source, kind, triple_quoted), end) in values {
for value in parse_string(&source, kind, triple_quoted, start, end)? {
for (start, (source, kind, triple_quoted), _) in values {
for value in parse_string(&source, kind, triple_quoted, start)? {
let value_range = value.range();
match value {
Expr::FormattedValue { .. } => {
if !current.is_empty() {
deduped.push(take_current(&mut current));
deduped.push(take_current(&mut current, current_start, current_end));
}
deduped.push(value)
}
Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
value: Constant::Str(inner),
..
}) => current.push(value),
}) => {
if current.is_empty() {
current_start = value_range.start();
}
current_end = value_range.end();
current.push(inner);
}
_ => unreachable!("Unexpected non-string expression."),
}
}
}
if !current.is_empty() {
deduped.push(take_current(&mut current));
deduped.push(take_current(&mut current, current_start, current_end));
}
Ok(Expr::JoinedStr(ast::ExprJoinedStr {
@ -818,14 +839,7 @@ mod tests {
use crate::{ast, Parse};
fn parse_fstring(source: &str) -> Result<Vec<Expr>, LexicalError> {
StringParser::new(
source,
StringKind::FString,
false,
TextSize::default(),
TextSize::default() + source.text_len() + TextSize::from(3), // 3 for prefix and quotes
)
.parse()
StringParser::new(source, StringKind::FString, false, TextSize::default()).parse()
}
#[test]
@ -1069,6 +1083,13 @@ mod tests {
insta::assert_debug_snapshot!(parse_ast);
}
#[test]
fn test_fstring_constant_range() {
let source = r#"f"aaa{bbb}ccc{ddd}eee""#;
let parse_ast = ast::Suite::parse(source, "<test>").unwrap();
insta::assert_debug_snapshot!(parse_ast);
}
#[test]
fn test_fstring_unescaped_newline() {
let source = r#"f"""