diff --git a/crates/ruff/resources/test/fixtures/ruff/RUF010.py b/crates/ruff/resources/test/fixtures/ruff/RUF010.py index b27082124d276..cc3e9c7831e09 100644 --- a/crates/ruff/resources/test/fixtures/ruff/RUF010.py +++ b/crates/ruff/resources/test/fixtures/ruff/RUF010.py @@ -1,4 +1,5 @@ bla = b"bla" +d = {"a": b"bla", "b": b"bla", "c": b"bla"} def foo(one_arg): @@ -7,6 +8,8 @@ def foo(one_arg): f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + f"{foo(bla)}" # OK f"{str(bla, 'ascii')}, {str(bla, encoding='cp1255')}" # OK diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 63899c91be20e..ef5cac45b04ae 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -3613,7 +3613,7 @@ where .rules .enabled(Rule::ExplicitFStringTypeConversion) { - ruff::rules::explicit_f_string_type_conversion(self, expr, value, *conversion); + ruff::rules::explicit_f_string_type_conversion(self, value, *conversion); } } _ => {} diff --git a/crates/ruff/src/rules/ruff/rules/explicit_f_string_type_conversion.rs b/crates/ruff/src/rules/ruff/rules/explicit_f_string_type_conversion.rs index b3160580d0252..1981e27008da8 100644 --- a/crates/ruff/src/rules/ruff/rules/explicit_f_string_type_conversion.rs +++ b/crates/ruff/src/rules/ruff/rules/explicit_f_string_type_conversion.rs @@ -1,9 +1,7 @@ -use ruff_text_size::TextSize; use rustpython_parser::ast::{self, Expr, ExprKind}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::helpers::unparse_expr; use crate::checkers::ast::Checker; use crate::registry::AsRule; @@ -44,7 +42,6 @@ impl AlwaysAutofixableViolation for ExplicitFStringTypeConversion { /// RUF010 pub(crate) fn explicit_f_string_type_conversion( checker: &mut Checker, - expr: &Expr, formatted_value: &Expr, conversion: ast::Int, ) { @@ -70,43 +67,29 @@ pub(crate) fn explicit_f_string_type_conversion( return; }; - if !matches!(id.as_str(), "str" | "repr" | "ascii") { - return; + let conversion = match id.as_str() { + "ascii" => 'a', + "str" => 's', + "repr" => 'r', + _ => return, }; if !checker.ctx.is_builtin(id) { return; } - let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, formatted_value.range()); + let formatted_value_range = formatted_value.range(); + let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, formatted_value_range); if checker.patch(diagnostic.kind.rule()) { - // Replace the call node with its argument and a conversion flag. - let mut conv_expr = expr.clone(); - let ExprKind::FormattedValue(ast::ExprFormattedValue { - ref mut conversion, - ref mut value, - .. - }) = conv_expr.node else { - return; - }; - - *conversion = match id.as_str() { - "ascii" => ast::Int::new(ast::ConversionFlag::Ascii as u32), - "str" => ast::Int::new(ast::ConversionFlag::Str as u32), - "repr" => ast::Int::new(ast::ConversionFlag::Repr as u32), - &_ => unreachable!(), - }; - - value.node = args[0].node.clone(); - - diagnostic.set_fix(Fix::automatic(Edit::range_replacement( - unparse_expr(&conv_expr, checker.stylist), - formatted_value - .range() - .sub_start(TextSize::from(1)) - .add_end(TextSize::from(1)), - ))); + let arg_range = args[0].range(); + let remove_call = Edit::deletion(formatted_value_range.start(), arg_range.start()); + let add_conversion = Edit::replacement( + format!("!{conversion}"), + arg_range.end(), + formatted_value_range.end(), + ); + diagnostic.set_fix(Fix::automatic_edits(remove_call, [add_conversion])); } checker.diagnostics.push(diagnostic); diff --git a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF010_RUF010.py.snap b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF010_RUF010.py.snap index dadca107f9428..26fc6ede22b47 100644 --- a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF010_RUF010.py.snap +++ b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF010_RUF010.py.snap @@ -1,61 +1,124 @@ --- source: crates/ruff/src/rules/ruff/mod.rs --- -RUF010.py:8:4: RUF010 [*] Use conversion in f-string +RUF010.py:9:4: RUF010 [*] Use conversion in f-string | - 8 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 + 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 | ^^^^^^^^ RUF010 - 9 | -10 | f"{foo(bla)}" # OK +10 | +11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 | = help: Replace f-string function call with conversion ℹ Fix -5 5 | pass -6 6 | -7 7 | -8 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 - 8 |+f"{bla!s}, {repr(bla)}, {ascii(bla)}" # RUF010 -9 9 | -10 10 | f"{foo(bla)}" # OK -11 11 | - -RUF010.py:8:16: RUF010 [*] Use conversion in f-string - | - 8 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +6 6 | pass +7 7 | +8 8 | +9 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 + 9 |+f"{bla!s}, {repr(bla)}, {ascii(bla)}" # RUF010 +10 10 | +11 11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 +12 12 | + +RUF010.py:9:16: RUF010 [*] Use conversion in f-string + | + 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 | ^^^^^^^^^ RUF010 - 9 | -10 | f"{foo(bla)}" # OK +10 | +11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 | = help: Replace f-string function call with conversion ℹ Fix -5 5 | pass -6 6 | -7 7 | -8 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 - 8 |+f"{str(bla)}, {bla!r}, {ascii(bla)}" # RUF010 -9 9 | -10 10 | f"{foo(bla)}" # OK -11 11 | - -RUF010.py:8:29: RUF010 [*] Use conversion in f-string - | - 8 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +6 6 | pass +7 7 | +8 8 | +9 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 + 9 |+f"{str(bla)}, {bla!r}, {ascii(bla)}" # RUF010 +10 10 | +11 11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 +12 12 | + +RUF010.py:9:29: RUF010 [*] Use conversion in f-string + | + 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 | ^^^^^^^^^^ RUF010 - 9 | -10 | f"{foo(bla)}" # OK +10 | +11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + | + = help: Replace f-string function call with conversion + +ℹ Fix +6 6 | pass +7 7 | +8 8 | +9 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 + 9 |+f"{str(bla)}, {repr(bla)}, {bla!a}" # RUF010 +10 10 | +11 11 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 +12 12 | + +RUF010.py:11:4: RUF010 [*] Use conversion in f-string + | +11 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +12 | +13 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + | ^^^^^^^^^^^ RUF010 +14 | +15 | f"{foo(bla)}" # OK + | + = help: Replace f-string function call with conversion + +ℹ Fix +8 8 | +9 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +10 10 | +11 |-f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + 11 |+f"{d['a']!s}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 +12 12 | +13 13 | f"{foo(bla)}" # OK +14 14 | + +RUF010.py:11:19: RUF010 [*] Use conversion in f-string + | +11 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +12 | +13 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + | ^^^^^^^^^^^^ RUF010 +14 | +15 | f"{foo(bla)}" # OK + | + = help: Replace f-string function call with conversion + +ℹ Fix +8 8 | +9 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +10 10 | +11 |-f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + 11 |+f"{str(d['a'])}, {d['b']!r}, {ascii(d['c'])}" # RUF010 +12 12 | +13 13 | f"{foo(bla)}" # OK +14 14 | + +RUF010.py:11:35: RUF010 [*] Use conversion in f-string + | +11 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +12 | +13 | f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + | ^^^^^^^^^^^^^ RUF010 +14 | +15 | f"{foo(bla)}" # OK | = help: Replace f-string function call with conversion ℹ Fix -5 5 | pass -6 6 | -7 7 | -8 |-f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 - 8 |+f"{str(bla)}, {repr(bla)}, {bla!a}" # RUF010 -9 9 | -10 10 | f"{foo(bla)}" # OK -11 11 | +8 8 | +9 9 | f"{str(bla)}, {repr(bla)}, {ascii(bla)}" # RUF010 +10 10 | +11 |-f"{str(d['a'])}, {repr(d['b'])}, {ascii(d['c'])}" # RUF010 + 11 |+f"{str(d['a'])}, {repr(d['b'])}, {d['c']!a}" # RUF010 +12 12 | +13 13 | f"{foo(bla)}" # OK +14 14 |