mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
Do not consider f-strings with escaped newlines as multiline (#14624)
## Summary This PR fixes a bug in the f-string formatting to not consider the escaped newlines for `is_multiline`. This is done by checking if the f-string is triple-quoted or not similar to normal string literals. This is not required to be gated behind preview because the logic change for `is_multiline` was added in https://github.com/astral-sh/ruff/pull/14454. ## Test Plan Add a test case which formats differently on `main`: https://play.ruff.rs/ea3c55c2-f0fe-474e-b6b8-e3365e0ede5e
This commit is contained in:
parent
4cd2b9926e
commit
f96fa6b0e2
5 changed files with 124 additions and 5 deletions
|
@ -424,3 +424,10 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
||||||
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
||||||
yyyyyyyyyyyyyy + zzzzzzzzzzz
|
yyyyyyyyyyyyyy + zzzzzzzzzzz
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# This is not a multiline f-string, but the expression is too long so it should be
|
||||||
|
# wrapped in parentheses.
|
||||||
|
f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
|
aaaaaaaaaaa = f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
|
|
|
@ -326,3 +326,24 @@ assert False, +"Implicit concatenated string" "uses {} layout on {} format".form
|
||||||
"a" f'{1=: "abcd \'\'}'
|
"a" f'{1=: "abcd \'\'}'
|
||||||
f'{1=: "abcd \'\'}' "a"
|
f'{1=: "abcd \'\'}' "a"
|
||||||
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
||||||
|
|
||||||
|
# These strings contains escaped newline characters and should be joined, they are
|
||||||
|
# not multiline strings.
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
b"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" b"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 1
|
||||||
|
(f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd") # comment 2
|
||||||
|
(
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" # comment 3
|
||||||
|
"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 4
|
||||||
|
)
|
||||||
|
|
|
@ -113,10 +113,12 @@ impl StringLikeExtensions for ast::StringLike<'_> {
|
||||||
fn contains_line_break_or_comments(
|
fn contains_line_break_or_comments(
|
||||||
elements: &ast::FStringElements,
|
elements: &ast::FStringElements,
|
||||||
context: &PyFormatContext,
|
context: &PyFormatContext,
|
||||||
|
is_triple_quoted: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
elements.iter().any(|element| match element {
|
elements.iter().any(|element| match element {
|
||||||
ast::FStringElement::Literal(literal) => {
|
ast::FStringElement::Literal(literal) => {
|
||||||
context.source().contains_line_break(literal.range())
|
is_triple_quoted
|
||||||
|
&& context.source().contains_line_break(literal.range())
|
||||||
}
|
}
|
||||||
ast::FStringElement::Expression(expression) => {
|
ast::FStringElement::Expression(expression) => {
|
||||||
// Expressions containing comments can't be joined.
|
// Expressions containing comments can't be joined.
|
||||||
|
@ -132,7 +134,11 @@ impl StringLikeExtensions for ast::StringLike<'_> {
|
||||||
// ```
|
// ```
|
||||||
context.comments().contains_comments(expression.into())
|
context.comments().contains_comments(expression.into())
|
||||||
|| expression.format_spec.as_deref().is_some_and(|spec| {
|
|| expression.format_spec.as_deref().is_some_and(|spec| {
|
||||||
contains_line_break_or_comments(&spec.elements, context)
|
contains_line_break_or_comments(
|
||||||
|
&spec.elements,
|
||||||
|
context,
|
||||||
|
is_triple_quoted,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|| expression.debug_text.as_ref().is_some_and(|debug_text| {
|
|| expression.debug_text.as_ref().is_some_and(|debug_text| {
|
||||||
memchr2(b'\n', b'\r', debug_text.leading.as_bytes()).is_some()
|
memchr2(b'\n', b'\r', debug_text.leading.as_bytes()).is_some()
|
||||||
|
@ -144,7 +150,11 @@ impl StringLikeExtensions for ast::StringLike<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_f_string_formatting_enabled(context) {
|
if is_f_string_formatting_enabled(context) {
|
||||||
contains_line_break_or_comments(&f_string.elements, context)
|
contains_line_break_or_comments(
|
||||||
|
&f_string.elements,
|
||||||
|
context,
|
||||||
|
f_string.flags.is_triple_quoted(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.source().contains_line_break(f_string.range())
|
context.source().contains_line_break(f_string.range())
|
||||||
}
|
}
|
||||||
|
|
|
@ -430,6 +430,13 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
||||||
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
||||||
yyyyyyyyyyyyyy + zzzzzzzzzzz
|
yyyyyyyyyyyyyy + zzzzzzzzzzz
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# This is not a multiline f-string, but the expression is too long so it should be
|
||||||
|
# wrapped in parentheses.
|
||||||
|
f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
|
aaaaaaaaaaa = f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
@ -906,6 +913,17 @@ if True:
|
||||||
# This f-string should be flattened
|
# This f-string should be flattened
|
||||||
xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
||||||
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (yyyyyyyyyyyyyy + zzzzzzzzzzz)
|
expression } bbbbbbbbbbbbbbbbbbbbbbbb" + (yyyyyyyyyyyyyy + zzzzzzzzzzz)
|
||||||
|
|
||||||
|
# This is not a multiline f-string, but the expression is too long so it should be
|
||||||
|
# wrapped in parentheses.
|
||||||
|
f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb
|
||||||
|
)
|
||||||
|
aaaaaaaaaaa = f"hellooooooooooooooooooooooo \
|
||||||
|
worlddddddddddddddddddddddddddddddddd" + (
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -913,7 +931,7 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
||||||
```diff
|
```diff
|
||||||
--- Stable
|
--- Stable
|
||||||
+++ Preview
|
+++ Preview
|
||||||
@@ -468,5 +468,6 @@
|
@@ -468,16 +468,19 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
# This f-string should be flattened
|
# This f-string should be flattened
|
||||||
|
@ -922,4 +940,23 @@ xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {
|
||||||
+xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {expression} bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
+xxxxxxxxxxxxxxxx = f"aaaaaaaaaaaaaaaaaaaaa {expression} bbbbbbbbbbbbbbbbbbbbbbbb" + (
|
||||||
+ yyyyyyyyyyyyyy + zzzzzzzzzzz
|
+ yyyyyyyyyyyyyy + zzzzzzzzzzz
|
||||||
+)
|
+)
|
||||||
|
|
||||||
|
# This is not a multiline f-string, but the expression is too long so it should be
|
||||||
|
# wrapped in parentheses.
|
||||||
|
-f"hellooooooooooooooooooooooo \
|
||||||
|
- worlddddddddddddddddddddddddddddddddd" + (
|
||||||
|
- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb
|
||||||
|
+(
|
||||||
|
+ f"hellooooooooooooooooooooooo \
|
||||||
|
+ worlddddddddddddddddddddddddddddddddd"
|
||||||
|
+ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
|
)
|
||||||
|
-aaaaaaaaaaa = f"hellooooooooooooooooooooooo \
|
||||||
|
- worlddddddddddddddddddddddddddddddddd" + (
|
||||||
|
- aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb
|
||||||
|
+aaaaaaaaaaa = (
|
||||||
|
+ f"hellooooooooooooooooooooooo \
|
||||||
|
+ worlddddddddddddddddddddddddddddddddd"
|
||||||
|
+ + (aaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbb)
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
## Input
|
## Input
|
||||||
```python
|
```python
|
||||||
|
@ -333,6 +332,27 @@ assert False, +"Implicit concatenated string" "uses {} layout on {} format".form
|
||||||
"a" f'{1=: "abcd \'\'}'
|
"a" f'{1=: "abcd \'\'}'
|
||||||
f'{1=: "abcd \'\'}' "a"
|
f'{1=: "abcd \'\'}' "a"
|
||||||
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
||||||
|
|
||||||
|
# These strings contains escaped newline characters and should be joined, they are
|
||||||
|
# not multiline strings.
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
b"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" b"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 1
|
||||||
|
(f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" "cccccccccccccc \
|
||||||
|
ddddddddddddddddddd") # comment 2
|
||||||
|
(
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" # comment 3
|
||||||
|
"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 4
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
@ -747,4 +767,28 @@ assert False, +"Implicit concatenated stringuses {} layout on {} format".format(
|
||||||
f'a{1=: "abcd \'\'}'
|
f'a{1=: "abcd \'\'}'
|
||||||
f'{1=: "abcd \'\'}a'
|
f'{1=: "abcd \'\'}a'
|
||||||
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
f'{1=: "abcd \'\'}' f"{1=: 'abcd \"\"}"
|
||||||
|
|
||||||
|
# These strings contains escaped newline characters and should be joined, they are
|
||||||
|
# not multiline strings.
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbbcccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
b"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbbcccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbbcccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 1
|
||||||
|
(
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb"
|
||||||
|
"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd"
|
||||||
|
) # comment 2
|
||||||
|
(
|
||||||
|
f"aaaaaaaaaaaaaaaa \
|
||||||
|
bbbbbbbbbbb" # comment 3
|
||||||
|
"cccccccccccccc \
|
||||||
|
ddddddddddddddddddd" # comment 4
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue