Avoid moving back the lexer for triple-quoted fstring (#11939)

## Summary

This PR avoids moving back the lexer for a triple-quoted f-string during
the re-lexing phase.

The reason this is a problem is that for a triple-quoted f-string the
newlines are part of the f-string itself, specifically they'll be part
of the `FStringMiddle` token. So, if we moved the lexer back, there
would be a `Newline` token whose range would be in between an
`FStringMiddle` token. This creates a panic in downstream usage.

fixes: #11937 

## Test Plan

Add test cases and validate the snapshots.
This commit is contained in:
Dhruv Manilawala 2024-06-20 16:27:36 +05:30 committed by GitHub
parent 22733cb7c7
commit ed948eaefb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 380 additions and 159 deletions

View file

@ -139,64 +139,74 @@ Module(
),
Expr(
StmtExpr {
range: 24..29,
range: 24..37,
value: FString(
ExprFString {
range: 24..29,
range: 24..37,
value: FStringValue {
inner: Single(
FString(
FString {
range: 24..29,
elements: [
Expression(
FStringExpressionElement {
range: 26..27,
expression: Name(
ExprName {
range: 27..27,
id: "",
ctx: Invalid,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: false,
inner: Concatenated(
[
FString(
FString {
range: 24..29,
elements: [
Expression(
FStringExpressionElement {
range: 26..27,
expression: Name(
ExprName {
range: 27..27,
id: "",
ctx: Invalid,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: false,
},
},
},
),
),
FString(
FString {
range: 29..37,
elements: [
Expression(
FStringExpressionElement {
range: 33..34,
expression: Name(
ExprName {
range: 34..34,
id: "",
ctx: Invalid,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: true,
},
},
),
],
),
},
},
),
},
),
Expr(
StmtExpr {
range: 33..38,
value: Set(
ExprSet {
range: 33..38,
elts: [
Name(
ExprName {
range: 34..34,
id: "",
ctx: Invalid,
},
),
],
},
),
},
),
],
},
)
@ -319,12 +329,10 @@ Module(
|
2 | f"{foo!r"
3 | f"{foo="
4 | f"{"
| _____^
5 | | f"""{"""
| |_^ Syntax Error: Expected FStringEnd, found FStringMiddle
3 | f"{foo="
4 | f"{"
5 | f"""{"""
| ^^^^ Syntax Error: Expected FStringEnd, found FStringStart
|
@ -332,15 +340,7 @@ Module(
3 | f"{foo="
4 | f"{"
5 | f"""{"""
| ^^^ Syntax Error: Expected a statement
|
|
3 | f"{foo="
4 | f"{"
5 | f"""{"""
|______^
| ^^^ Syntax Error: Expected an expression
|

View file

@ -7,7 +7,7 @@ input_file: crates/ruff_python_parser/resources/invalid/re_lex_logical_token.py
```
Module(
ModModule {
range: 0..1129,
range: 0..979,
body: [
If(
StmtIf {
@ -670,53 +670,6 @@ Module(
],
},
),
Expr(
StmtExpr {
range: 1097..1109,
value: FString(
ExprFString {
range: 1097..1109,
value: FStringValue {
inner: Single(
FString(
FString {
range: 1097..1109,
elements: [
Literal(
FStringLiteralElement {
range: 1101..1107,
value: "hello ",
},
),
Expression(
FStringExpressionElement {
range: 1107..1109,
expression: Name(
ExprName {
range: 1108..1109,
id: "x",
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: true,
},
},
),
),
},
},
),
},
),
],
},
)
@ -878,45 +831,8 @@ Module(
|
60 | # There are trailing whitespace before the newline character but those whitespaces are
61 | # part of the comment token
62 | f"""hello {x # comment
| Syntax Error: Expected a statement
63 | y = 1
|
|
60 | # There are trailing whitespace before the newline character but those whitespaces are
61 | # part of the comment token
62 | f"""hello {x # comment
| ___________________________^
63 | | y = 1
| |_____^ Syntax Error: f-string: unterminated triple-quoted string
|
|
61 | # part of the comment token
62 | f"""hello {x # comment
63 | y = 1
| ^ Syntax Error: f-string: expecting '}'
|
|
60 | # There are trailing whitespace before the newline character but those whitespaces are
61 | # part of the comment token
62 | f"""hello {x # comment
| ___________________________^
63 | | y = 1
| |_____^ Syntax Error: Expected FStringEnd, found Unknown
|
|
61 | # part of the comment token
62 | f"""hello {x # comment
63 | y = 1
| Syntax Error: Expected a statement
55 | if call(f"hello
56 | def bar():
57 | pass
| Syntax Error: Expected a statement
|

View file

@ -0,0 +1,96 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/invalid/re_lexing/triple_quoted_fstring_1.py
---
## AST
```
Module(
ModModule {
range: 0..198,
body: [
Expr(
StmtExpr {
range: 166..178,
value: FString(
ExprFString {
range: 166..178,
value: FStringValue {
inner: Single(
FString(
FString {
range: 166..178,
elements: [
Literal(
FStringLiteralElement {
range: 170..176,
value: "hello ",
},
),
Expression(
FStringExpressionElement {
range: 176..178,
expression: Name(
ExprName {
range: 177..178,
id: "x",
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
),
],
flags: FStringFlags {
quote_style: Double,
prefix: Regular,
triple_quoted: true,
},
},
),
),
},
},
),
},
),
],
},
)
```
## Errors
|
3 | # https://github.com/astral-sh/ruff/issues/11929
4 |
5 | f"""hello {x # comment
| ___________________________^
6 | | y = 1
| |_____^ Syntax Error: f-string: unterminated triple-quoted string
|
|
5 | f"""hello {x # comment
6 | y = 1
| ^ Syntax Error: f-string: expecting '}'
|
|
3 | # https://github.com/astral-sh/ruff/issues/11929
4 |
5 | f"""hello {x # comment
| ___________________________^
6 | | y = 1
| |_____^ Syntax Error: Expected FStringEnd, found Unknown
|
|
5 | f"""hello {x # comment
6 | y = 1
| Syntax Error: Expected a statement
|

View file

@ -0,0 +1,75 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/invalid/re_lexing/triple_quoted_fstring_2.py
---
## AST
```
Module(
ModModule {
range: 0..183,
body: [
Expr(
StmtExpr {
range: 167..183,
value: FString(
ExprFString {
range: 167..183,
value: FStringValue {
inner: Single(
FString(
FString {
range: 167..183,
elements: [
Expression(
FStringExpressionElement {
range: 171..180,
expression: Name(
ExprName {
range: 172..175,
id: "foo",
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: Some(
FStringFormatSpec {
range: 176..180,
elements: [
Literal(
FStringLiteralElement {
range: 176..180,
value: ".3f\n",
},
),
],
},
),
},
),
],
flags: FStringFlags {
quote_style: Single,
prefix: Regular,
triple_quoted: true,
},
},
),
),
},
},
),
},
),
],
},
)
```
## Errors
|
5 | f'''{foo:.3f
6 | '''
| ^^^ Syntax Error: f-string: expecting '}'
|

View file

@ -0,0 +1,110 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/invalid/re_lexing/triple_quoted_fstring_3.py
---
## AST
```
Module(
ModModule {
range: 0..262,
body: [
If(
StmtIf {
range: 231..262,
test: Call(
ExprCall {
range: 234..253,
func: Name(
ExprName {
range: 234..238,
id: "call",
ctx: Load,
},
),
arguments: Arguments {
range: 238..253,
args: [
FString(
ExprFString {
range: 239..253,
value: FStringValue {
inner: Single(
FString(
FString {
range: 239..253,
elements: [
Expression(
FStringExpressionElement {
range: 243..250,
expression: Name(
ExprName {
range: 244..245,
id: "x",
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: Some(
FStringFormatSpec {
range: 246..250,
elements: [
Literal(
FStringLiteralElement {
range: 246..250,
value: ".3f\n",
},
),
],
},
),
},
),
],
flags: FStringFlags {
quote_style: Single,
prefix: Regular,
triple_quoted: true,
},
},
),
),
},
},
),
],
keywords: [],
},
},
),
body: [
Pass(
StmtPass {
range: 258..262,
},
),
],
elif_else_clauses: [],
},
),
],
},
)
```
## Errors
|
5 | if call(f'''{x:.3f
6 | '''
| ^^^ Syntax Error: f-string: expecting '}'
7 | pass
|
|
5 | if call(f'''{x:.3f
6 | '''
| ^ Syntax Error: Expected ')', found newline
7 | pass
|