Fix \r and \r\n handling in t- and f-string debug texts (#18673)

This commit is contained in:
Micha Reiser 2025-06-15 07:53:06 +02:00 committed by GitHub
parent 5e02d839d5
commit 8237d4670c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 79 additions and 4 deletions

3
.gitattributes vendored
View file

@ -5,6 +5,9 @@ crates/ruff_linter/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_2.py text eol=crlf
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_3.py text eol=crlf
crates/ruff_python_formatter/resources/test/fixtures/ruff/f-string-carriage-return-newline.py text eol=crlf
crates/ruff_python_formatter/tests/snapshots/format@f-string-carriage-return-newline.py.snap text eol=crlf
crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring_code_examples_crlf.py text eol=crlf
crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap text eol=crlf

View file

@ -8,4 +8,8 @@ ij_formatter_enabled = false
[docstring_tab_indentation.py]
generated_code = true
ij_formatter_enabled = false
ij_formatter_enabled = false
[f-string-carriage-return-newline.py]
generated_code = true
ij_formatter_enabled = false

View file

@ -0,0 +1,8 @@
# Regression test for https://github.com/astral-sh/ruff/issues/18667
f"{
1=
}"
t"{
1=
}"

View file

@ -1,6 +1,6 @@
use std::borrow::Cow;
use ruff_formatter::{Buffer, RemoveSoftLinesBuffer, format_args, write};
use ruff_formatter::{Buffer, FormatOptions as _, RemoveSoftLinesBuffer, format_args, write};
use ruff_python_ast::{
AnyStringFlags, ConversionFlag, Expr, InterpolatedElement, InterpolatedStringElement,
InterpolatedStringLiteralElement, StringFlags,
@ -178,9 +178,9 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
write!(
f,
[
text(&debug_text.leading),
NormalizedDebugText(&debug_text.leading),
verbatim_text(&**expression),
text(&debug_text.trailing),
NormalizedDebugText(&debug_text.trailing),
]
)?;
@ -316,3 +316,18 @@ fn needs_bracket_spacing(expr: &Expr, context: &PyFormatContext) -> bool {
Expr::Dict(_) | Expr::DictComp(_) | Expr::Set(_) | Expr::SetComp(_)
)
}
struct NormalizedDebugText<'a>(&'a str);
impl Format<PyFormatContext<'_>> for NormalizedDebugText<'_> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
let normalized = normalize_newlines(self.0, ['\r']);
f.write_element(FormatElement::Text {
text_width: TextWidth::from_text(&normalized, f.options().indent_width()),
text: normalized.into_owned().into_boxed_str(),
});
Ok(())
}
}

View file

@ -196,6 +196,24 @@ impl Transformer for Normalizer {
transformer::walk_expr(self, expr);
}
fn visit_interpolated_string_element(
&self,
interpolated_string_element: &mut InterpolatedStringElement,
) {
let InterpolatedStringElement::Interpolation(interpolation) = interpolated_string_element
else {
return;
};
let Some(debug) = &mut interpolation.debug_text else {
return;
};
// Changing the newlines to the configured newline is okay because Python normalizes all newlines to `\n`
debug.leading = debug.leading.replace("\r\n", "\n").replace('\r', "\n");
debug.trailing = debug.trailing.replace("\r\n", "\n").replace('\r', "\n");
}
fn visit_string_literal(&self, string_literal: &mut ast::StringLiteral) {
static STRIP_DOC_TESTS: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(

View file

@ -0,0 +1,27 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/f-string-carriage-return-newline.py
---
## Input
```python
# Regression test for https://github.com/astral-sh/ruff/issues/18667
f"{
1=
}"
t"{
1=
}"
```
## Output
```python
# Regression test for https://github.com/astral-sh/ruff/issues/18667
f"{
1=
}"
t"{
1=
}"
```