Skip to content

Reject multi-line f-string elements before Python 3.12#24355

Merged
charliermarsh merged 2 commits into
mainfrom
charlie/parser-multiline
Apr 5, 2026
Merged

Reject multi-line f-string elements before Python 3.12#24355
charliermarsh merged 2 commits into
mainfrom
charlie/parser-multiline

Conversation

@charliermarsh

Copy link
Copy Markdown
Member

Summary

Before Python 3.12, a replacement field in an f-string can span multiple lines only if the outer f-string is triple-quoted. This was relaxed in Python 3.12, but we weren't rejecting these as syntactically invalid on earlier versions.

Closes #24348.

@charliermarsh charliermarsh added the parser Related to the parser label Apr 2, 2026
@astral-sh-bot

astral-sh-bot Bot commented Apr 2, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch from 4441c92 to 6b9a7e9 Compare April 2, 2026 01:19
@charliermarsh charliermarsh marked this pull request as ready for review April 2, 2026 01:56
@astral-sh-bot astral-sh-bot Bot requested a review from ntBre April 2, 2026 01:56
Comment thread crates/ruff_python_parser/src/parser/expression.rs Outdated
Comment thread crates/ruff/tests/cli/lint.rs Outdated
Comment thread crates/ruff_python_parser/src/parser/expression.rs Outdated
Comment thread crates/ruff_python_parser/src/error.rs
@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch from bf9607e to 0cb5556 Compare April 2, 2026 20:49
@charliermarsh charliermarsh changed the base branch from main to charlie/det April 2, 2026 20:49
@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch from 0cb5556 to 03d8338 Compare April 2, 2026 20:57
charliermarsh added a commit that referenced this pull request Apr 3, 2026
## Summary

Right now these tests are dependent on input order, so changes in the
underlying hash can lead to churn in the fixtures. See, e.g.:
#24355 (comment).
Base automatically changed from charlie/det to main April 3, 2026 13:59
@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch from 03d8338 to da1acf5 Compare April 3, 2026 14:00

@MichaReiser MichaReiser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you

Comment on lines +1915 to +1916
let source = self.source[range].as_bytes();
let has_line_break =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is a non-trivial amount of work, I suggest we gate the checks with an early "Is this <= 3.12?" check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole block is already gated by if !self.options.target_version.supports_pep_701() (which is "Is this <= 3.12?").

let slash_position = TextSize::try_from(slash_position).unwrap();
TextRange::at(range.start() + slash_position, '\\'.text_len())
})
.collect::<Vec<_>>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use peekable here and for comment_ranges instead of collecting or even better, move the backslash and comment emitting code above the has_line_break check and use a single has_backslash_or_comment boolean that you set when there's any comment or backslash.

@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch 2 times, most recently from 130ce44 to 3144fb7 Compare April 5, 2026 15:00
@charliermarsh charliermarsh force-pushed the charlie/parser-multiline branch from 3144fb7 to d2cd4d1 Compare April 5, 2026 15:04
@charliermarsh charliermarsh enabled auto-merge (squash) April 5, 2026 15:05
@charliermarsh charliermarsh merged commit 9a55bc6 into main Apr 5, 2026
48 checks passed
@charliermarsh charliermarsh deleted the charlie/parser-multiline branch April 5, 2026 15:09
charliermarsh added a commit that referenced this pull request Apr 5, 2026
## Summary

See:
#24355 (comment).
Prior to Python 3.12, we need to avoid emitting formatted expressions
that span multiple lines in non-triple quoted f-strings.
KotlinIsland pushed a commit to KotlinIsland/basedpython that referenced this pull request May 1, 2026
## Summary

Right now these tests are dependent on input order, so changes in the
underlying hash can lead to churn in the fixtures. See, e.g.:
astral-sh/ruff#24355 (comment).
KotlinIsland pushed a commit to KotlinIsland/basedpython that referenced this pull request May 1, 2026
## Summary

See:
astral-sh/ruff#24355 (comment).
Prior to Python 3.12, we need to avoid emitting formatted expressions
that span multiple lines in non-triple quoted f-strings.
nicopauss pushed a commit to Intersec/lib-common that referenced this pull request Jun 4, 2026
##### [\`v0.15.10\`](https://github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#01510)

Released on 2026-04-09.

##### Preview features

- \[`flake8-logging`] Allow closures in except handlers (`LOG004`) ([#24464](astral-sh/ruff#24464))
- \[`flake8-self`] Make `SLF` diagnostics robust to non-self-named variables ([#24281](astral-sh/ruff#24281))
- \[`flake8-simplify`] Make the fix for `collapsible-if` safe in `preview` (`SIM102`) ([#24371](astral-sh/ruff#24371))

##### Bug fixes

- Avoid emitting multi-line f-string elements before Python 3.12 ([#24377](astral-sh/ruff#24377))
- Avoid syntax error from `E502` fixes in f-strings and t-strings ([#24410](astral-sh/ruff#24410))
- Strip form feeds from indent passed to `dedent_to` ([#24381](astral-sh/ruff#24381))
- \[`pyupgrade`] Fix panic caused by handling of octals (`UP012`) ([#24390](astral-sh/ruff#24390))
- Reject multi-line f-string elements before Python 3.12 ([#24355](astral-sh/ruff#24355))

##### Rule changes

- \[`ruff`] Treat f-string interpolation as potential side effect (`RUF019`) ([#24426](astral-sh/ruff#24426))

##### Server

- Add support for custom file extensions ([#24463](astral-sh/ruff#24463))

##### Documentation

- Document adding fixes in CONTRIBUTING.md ([#24393](astral-sh/ruff#24393))
- Fix JSON typo in settings example ([#24517](astral-sh/ruff#24517))

##### Contributors

- [@charliermarsh](https://github.com/charliermarsh)
- [@dylwil3](https://github.com/dylwil3)
- [@silverstein](https://github.com/silverstein)
- [@anishgirianish](https://github.com/anishgirianish)
- [@shizukushq](https://github.com/shizukushq)
- [@zanieb](https://github.com/zanieb)
- [@AlexWaygood](https://github.com/AlexWaygood)
##### [\`v0.15.9\`](https://github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#0159)

Released on 2026-04-02.

##### Preview features

- \[`pyflakes`] Flag annotated variable redeclarations as `F811` in preview mode ([#24244](astral-sh/ruff#24244))
- \[`ruff`] Allow dunder-named assignments in non-strict mode for `RUF067` ([#24089](astral-sh/ruff#24089))

##### Bug fixes

- \[`flake8-errmsg`] Avoid shadowing existing `msg` in fix for `EM101` ([#24363](astral-sh/ruff#24363))
- \[`flake8-simplify`] Ignore pre-initialization references in `SIM113` ([#24235](astral-sh/ruff#24235))
- \[`pycodestyle`] Fix `W391` fixes for consecutive empty notebook cells ([#24236](astral-sh/ruff#24236))
- \[`pyupgrade`] Fix `UP008` nested class matching ([#24273](astral-sh/ruff#24273))
- \[`pyupgrade`] Ignore strings with string-only escapes (`UP012`) ([#16058](astral-sh/ruff#16058))
- \[`ruff`] `RUF072`: skip formfeeds on dedent ([#24308](astral-sh/ruff#24308))
- \[`ruff`] Avoid re-using symbol in `RUF024` fix ([#24316](astral-sh/ruff#24316))
- \[`ruff`] Parenthesize expression in `RUF050` fix ([#24234](astral-sh/ruff#24234))
- Disallow starred expressions as values of starred expressions ([#24280](astral-sh/ruff#24280))

##### Rule changes

- \[`flake8-simplify`] Suppress `SIM105` for `except*` before Python 3.12 ([#23869](astral-sh/ruff#23869))
- \[`pyflakes`] Extend `F507` to flag `%`-format strings with zero placeholders ([#24215](astral-sh/ruff#24215))
- \[`pyupgrade`] `UP018` should detect more unnecessarily wrapped literals (UP018) ([#24093](astral-sh/ruff#24093))
- \[`pyupgrade`] Fix `UP008` callable scope handling to support lambdas ([#24274](astral-sh/ruff#24274))
- \[`ruff`] `RUF010`: Mark fix as unsafe when it deletes a comment ([#24270](astral-sh/ruff#24270))

##### Formatter

- Add `nested-string-quote-style` formatting option ([#24312](astral-sh/ruff#24312))

##### Documentation

- \[`flake8-bugbear`] Clarify RUF071 fix safety for non-path string comparisons ([#24149](astral-sh/ruff#24149))
- \[`flake8-type-checking`] Clarify import cycle wording for `TC001`/`TC002`/`TC003` ([#24322](astral-sh/ruff#24322))

##### Other changes

- Avoid rendering fix lines with trailing whitespace after `|` ([#24343](astral-sh/ruff#24343))

##### Contributors

- [@charliermarsh](https://github.com/charliermarsh)
- [@MichaReiser](https://github.com/MichaReiser)
- [@tranhoangtu-it](https://github.com/tranhoangtu-it)
- [@dylwil3](https://github.com/dylwil3)
- [@zsol](https://github.com/zsol)
- [@renovate](https://github.com/renovate)
- [@bitloi](https://github.com/bitloi)
- [@danparizher](https://github.com/danparizher)
- [@chinar-amrutkar](https://github.com/chinar-amrutkar)
- [@second-ed](https://github.com/second-ed)
- [@getehen](https://github.com/getehen)
- [@Redovo1](https://github.com/Redovo1)
- [@matthewlloyd](https://github.com/matthewlloyd)
- [@zanieb](https://github.com/zanieb)
- [@InSyncWithFoo](https://github.com/InSyncWithFoo)
- [@RenzoMXD](https://github.com/RenzoMXD)

Renovate-Branch: renovate/2024.6-ruff-0.15.x
Change-Id: Id4bd542d4f128b509284d9dcda312f2b39c29964
Priv-Id: 28ebcacdeffa50cec7a52eee59091a75ca5e9539
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parser Related to the parser

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ruff fails to detect python 3.10 unterminated string literal syntax error

3 participants