From 4b4447664baae88b4b320f629a58da84ea76fa92 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 25 Oct 2025 17:25:09 -0400 Subject: [PATCH] Handle backslashes in f-string interpolations for RUF027 Updated the RUF027 rule to only skip f-string suggestions for Python < 3.12 when backslashes appear in interpolations, not in the entire string. Adjusted test cases and snapshots to reflect this refined behavior. --- .../resources/test/fixtures/ruff/RUF027_0.py | 6 +++--- .../src/rules/ruff/rules/missing_fstring_syntax.rs | 14 ++++++++------ ...er__rules__ruff__tests__RUF027_RUF027_0.py.snap | 10 +++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF027_0.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF027_0.py index 12fe577263..04c482bd52 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF027_0.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF027_0.py @@ -85,8 +85,8 @@ def in_type_def(): def fuzz_bug(): c('{\t"i}') -# Test case for backslash handling in f-strings -# Should not trigger RUF027 for Python < 3.12 due to backslashes +# Test case for backslash handling in f-string interpolations +# Should not trigger RUF027 for Python < 3.12 due to backslashes in interpolations def backslash_test(): x = "test" - print("Hello {x}\\n") # Should not trigger RUF027 for Python < 3.12 + print("Hello {'\\n'}{x}") # Should not trigger RUF027 for Python < 3.12 diff --git a/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs b/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs index fd756a61ba..47a396c374 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/missing_fstring_syntax.rs @@ -186,12 +186,6 @@ fn should_be_fstring( return false; } - // Check if the string contains backslashes and target Python version is < 3.12 - // F-strings with backslashes are only valid in Python 3.12+ - if literal.value.contains('\\') && checker.target_version() < PythonVersion::PY312 { - return false; - } - let fstring_expr = format!("f{}", locator.slice(literal)); let Ok(parsed) = parse_expression(&fstring_expr) else { return false; @@ -223,6 +217,14 @@ fn should_be_fstring( for f_string in value.f_strings() { let mut has_name = false; for element in f_string.elements.interpolations() { + // Check if the interpolation expression contains backslashes + // F-strings with backslashes in interpolations are only valid in Python 3.12+ + let interpolation_text = &fstring_expr[element.range()]; + if interpolation_text.contains('\\') && checker.target_version() < PythonVersion::PY312 + { + return false; + } + if let ast::Expr::Name(ast::ExprName { id, .. }) = element.expression.as_ref() { if arg_names.contains(id) { return false; diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF027_RUF027_0.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF027_RUF027_0.py.snap index 70348ca744..977628639d 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF027_RUF027_0.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF027_RUF027_0.py.snap @@ -326,13 +326,13 @@ RUF027 [*] Possible f-string without an `f` prefix | 90 | def backslash_test(): 91 | x = "test" -92 | print("Hello {x}\\n") # Should not trigger RUF027 for Python < 3.12 - | ^^^^^^^^^^^^^^ +92 | print("Hello {'\\n'}{x}") # Should not trigger RUF027 for Python < 3.12 + | ^^^^^^^^^^^^^^^^^^ | help: Add `f` prefix -89 | # Should not trigger RUF027 for Python < 3.12 due to backslashes +89 | # Should not trigger RUF027 for Python < 3.12 due to backslashes in interpolations 90 | def backslash_test(): 91 | x = "test" - - print("Hello {x}\\n") # Should not trigger RUF027 for Python < 3.12 -92 + print(f"Hello {x}\\n") # Should not trigger RUF027 for Python < 3.12 + - print("Hello {'\\n'}{x}") # Should not trigger RUF027 for Python < 3.12 +92 + print(f"Hello {'\\n'}{x}") # Should not trigger RUF027 for Python < 3.12 note: This is an unsafe fix and may change runtime behavior