Skip to content

Commit 1889a5e

Browse files
authored
[syntax-errors] Raise unsupported syntax error for template strings prior to Python 3.14 (astral-sh#18664)
Closes astral-sh#18662 One question is whether we would like the range to exclude the quotes?
1 parent 793ff9b commit 1889a5e

File tree

5 files changed

+459
-2
lines changed

5 files changed

+459
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# parse_options: {"target-version": "3.13"}
2+
t"{hey}"
3+
t'{there}'
4+
t"""what's
5+
happening?"""
6+
"implicitly"t"concatenated"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# parse_options: {"target-version": "3.14"}
2+
t"{hey}"
3+
t'{there}'
4+
t"""what's
5+
happening?"""
6+
"implicitly"t"concatenated"

crates/ruff_python_parser/src/parser/expression.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,10 +1250,30 @@ impl<'src> Parser<'src> {
12501250
.into(),
12511251
));
12521252
} else if self.at(TokenKind::TStringStart) {
1253-
strings.push(StringType::TString(
1253+
// test_ok template_strings_py314
1254+
// # parse_options: {"target-version": "3.14"}
1255+
// t"{hey}"
1256+
// t'{there}'
1257+
// t"""what's
1258+
// happening?"""
1259+
// "implicitly"t"concatenated"
1260+
1261+
// test_err template_strings_py313
1262+
// # parse_options: {"target-version": "3.13"}
1263+
// t"{hey}"
1264+
// t'{there}'
1265+
// t"""what's
1266+
// happening?"""
1267+
// "implicitly"t"concatenated"
1268+
let string_type = StringType::TString(
12541269
self.parse_interpolated_string(InterpolatedStringKind::TString)
12551270
.into(),
1256-
));
1271+
);
1272+
self.add_unsupported_syntax_error(
1273+
UnsupportedSyntaxErrorKind::TemplateStrings,
1274+
string_type.range(),
1275+
);
1276+
strings.push(string_type);
12571277
}
12581278
}
12591279

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/err/template_strings_py313.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
node_index: AtomicNodeIndex(..),
11+
range: 0..117,
12+
body: [
13+
Expr(
14+
StmtExpr {
15+
node_index: AtomicNodeIndex(..),
16+
range: 44..52,
17+
value: TString(
18+
ExprTString {
19+
node_index: AtomicNodeIndex(..),
20+
range: 44..52,
21+
value: TStringValue {
22+
inner: Single(
23+
TString(
24+
TString {
25+
range: 44..52,
26+
node_index: AtomicNodeIndex(..),
27+
elements: [
28+
Interpolation(
29+
InterpolatedElement {
30+
range: 46..51,
31+
node_index: AtomicNodeIndex(..),
32+
expression: Name(
33+
ExprName {
34+
node_index: AtomicNodeIndex(..),
35+
range: 47..50,
36+
id: Name("hey"),
37+
ctx: Load,
38+
},
39+
),
40+
debug_text: None,
41+
conversion: None,
42+
format_spec: None,
43+
},
44+
),
45+
],
46+
flags: TStringFlags {
47+
quote_style: Double,
48+
prefix: Regular,
49+
triple_quoted: false,
50+
},
51+
},
52+
),
53+
),
54+
},
55+
},
56+
),
57+
},
58+
),
59+
Expr(
60+
StmtExpr {
61+
node_index: AtomicNodeIndex(..),
62+
range: 53..63,
63+
value: TString(
64+
ExprTString {
65+
node_index: AtomicNodeIndex(..),
66+
range: 53..63,
67+
value: TStringValue {
68+
inner: Single(
69+
TString(
70+
TString {
71+
range: 53..63,
72+
node_index: AtomicNodeIndex(..),
73+
elements: [
74+
Interpolation(
75+
InterpolatedElement {
76+
range: 55..62,
77+
node_index: AtomicNodeIndex(..),
78+
expression: Name(
79+
ExprName {
80+
node_index: AtomicNodeIndex(..),
81+
range: 56..61,
82+
id: Name("there"),
83+
ctx: Load,
84+
},
85+
),
86+
debug_text: None,
87+
conversion: None,
88+
format_spec: None,
89+
},
90+
),
91+
],
92+
flags: TStringFlags {
93+
quote_style: Single,
94+
prefix: Regular,
95+
triple_quoted: false,
96+
},
97+
},
98+
),
99+
),
100+
},
101+
},
102+
),
103+
},
104+
),
105+
Expr(
106+
StmtExpr {
107+
node_index: AtomicNodeIndex(..),
108+
range: 64..88,
109+
value: TString(
110+
ExprTString {
111+
node_index: AtomicNodeIndex(..),
112+
range: 64..88,
113+
value: TStringValue {
114+
inner: Single(
115+
TString(
116+
TString {
117+
range: 64..88,
118+
node_index: AtomicNodeIndex(..),
119+
elements: [
120+
Literal(
121+
InterpolatedStringLiteralElement {
122+
range: 68..85,
123+
node_index: AtomicNodeIndex(..),
124+
value: "what's\nhappening?",
125+
},
126+
),
127+
],
128+
flags: TStringFlags {
129+
quote_style: Double,
130+
prefix: Regular,
131+
triple_quoted: true,
132+
},
133+
},
134+
),
135+
),
136+
},
137+
},
138+
),
139+
},
140+
),
141+
Expr(
142+
StmtExpr {
143+
node_index: AtomicNodeIndex(..),
144+
range: 89..116,
145+
value: TString(
146+
ExprTString {
147+
node_index: AtomicNodeIndex(..),
148+
range: 89..116,
149+
value: TStringValue {
150+
inner: Concatenated(
151+
[
152+
Literal(
153+
StringLiteral {
154+
range: 89..101,
155+
node_index: AtomicNodeIndex(..),
156+
value: "implicitly",
157+
flags: StringLiteralFlags {
158+
quote_style: Double,
159+
prefix: Empty,
160+
triple_quoted: false,
161+
},
162+
},
163+
),
164+
TString(
165+
TString {
166+
range: 101..116,
167+
node_index: AtomicNodeIndex(..),
168+
elements: [
169+
Literal(
170+
InterpolatedStringLiteralElement {
171+
range: 103..115,
172+
node_index: AtomicNodeIndex(..),
173+
value: "concatenated",
174+
},
175+
),
176+
],
177+
flags: TStringFlags {
178+
quote_style: Double,
179+
prefix: Regular,
180+
triple_quoted: false,
181+
},
182+
},
183+
),
184+
],
185+
),
186+
},
187+
},
188+
),
189+
},
190+
),
191+
],
192+
},
193+
)
194+
```
195+
## Unsupported Syntax Errors
196+
197+
|
198+
1 | # parse_options: {"target-version": "3.13"}
199+
2 | t"{hey}"
200+
| ^^^^^^^^ Syntax Error: Cannot use t-strings on Python 3.13 (syntax was added in Python 3.14)
201+
3 | t'{there}'
202+
4 | t"""what's
203+
|
204+
205+
206+
|
207+
1 | # parse_options: {"target-version": "3.13"}
208+
2 | t"{hey}"
209+
3 | t'{there}'
210+
| ^^^^^^^^^^ Syntax Error: Cannot use t-strings on Python 3.13 (syntax was added in Python 3.14)
211+
4 | t"""what's
212+
5 | happening?"""
213+
|
214+
215+
216+
|
217+
2 | t"{hey}"
218+
3 | t'{there}'
219+
4 | / t"""what's
220+
5 | | happening?"""
221+
| |_____________^ Syntax Error: Cannot use t-strings on Python 3.13 (syntax was added in Python 3.14)
222+
6 | "implicitly"t"concatenated"
223+
|
224+
225+
226+
|
227+
4 | t"""what's
228+
5 | happening?"""
229+
6 | "implicitly"t"concatenated"
230+
| ^^^^^^^^^^^^^^^ Syntax Error: Cannot use t-strings on Python 3.13 (syntax was added in Python 3.14)
231+
|

0 commit comments

Comments
 (0)