From 25e0538ea74a64899a69663a2de74e2de9923bbb Mon Sep 17 00:00:00 2001 From: Jonathan Plasse <13716151+JonathanPlasse@users.noreply.github.com> Date: Fri, 19 May 2023 12:41:28 +0200 Subject: [PATCH 1/2] Fix UP032 auto-fix with integers --- .../test/fixtures/pyupgrade/UP032_2.py | 5 ++ crates/ruff/src/rules/pyupgrade/mod.rs | 1 + .../src/rules/pyupgrade/rules/f_strings.rs | 19 ++++++- ...__rules__pyupgrade__tests__UP032_2.py.snap | 55 +++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py create mode 100644 crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap diff --git a/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py new file mode 100644 index 0000000000000..c3cab327ca291 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py @@ -0,0 +1,5 @@ +"{.real}".format(1) + +"{0.real}".format(1) + +"{a.real}".format(a=1) diff --git a/crates/ruff/src/rules/pyupgrade/mod.rs b/crates/ruff/src/rules/pyupgrade/mod.rs index 22c30c7e7bf6c..9a5c07dc59770 100644 --- a/crates/ruff/src/rules/pyupgrade/mod.rs +++ b/crates/ruff/src/rules/pyupgrade/mod.rs @@ -59,6 +59,7 @@ mod tests { #[test_case(Rule::PrintfStringFormatting, Path::new("UP031_1.py"); "UP031_1")] #[test_case(Rule::FString, Path::new("UP032_0.py"); "UP032_0")] #[test_case(Rule::FString, Path::new("UP032_1.py"); "UP032_1")] + #[test_case(Rule::FString, Path::new("UP032_2.py"); "UP032_2")] #[test_case(Rule::LRUCacheWithMaxsizeNone, Path::new("UP033_0.py"); "UP033_0")] #[test_case(Rule::LRUCacheWithMaxsizeNone, Path::new("UP033_1.py"); "UP033_1")] #[test_case(Rule::ExtraneousParentheses, Path::new("UP034.py"); "UP034")] diff --git a/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs b/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs index e21bbf0869988..3ed11820e821a 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs @@ -171,24 +171,37 @@ fn try_convert_to_f_string(checker: &Checker, expr: &Expr) -> Option { converted.push('{'); let field = FieldName::parse(&field_name).ok()?; + let has_field_parts = !field.parts.is_empty(); match field.field_type { FieldType::Auto => { let Some(arg) = summary.consume_next() else { return None; }; - converted.push_str(&arg); + if has_field_parts && arg.chars().all(|c| c.is_ascii_digit()) { + converted.push_str(&format!("({arg})")); + } else { + converted.push_str(&arg); + } } FieldType::Index(index) => { let Some(arg) = summary.consume_arg(index) else { return None; }; - converted.push_str(&arg); + if has_field_parts && arg.chars().all(|c| c.is_ascii_digit()) { + converted.push_str(&format!("({arg})")); + } else { + converted.push_str(&arg); + } } FieldType::Keyword(name) => { let Some(arg) = summary.consume_kwarg(&name) else { return None; }; - converted.push_str(&arg); + if has_field_parts && arg.chars().all(|c| c.is_ascii_digit()) { + converted.push_str(&format!("({arg})")); + } else { + converted.push_str(&arg); + } } } diff --git a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap new file mode 100644 index 0000000000000..6b652765951a3 --- /dev/null +++ b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap @@ -0,0 +1,55 @@ +--- +source: crates/ruff/src/rules/pyupgrade/mod.rs +--- +UP032_2.py:1:1: UP032 [*] Use f-string instead of `format` call + | +1 | "{.real}".format(1) + | ^^^^^^^^^^^^^^^^^^^ UP032 +2 | +3 | "{0.real}".format(1) + | + = help: Convert to f-string + +ℹ Suggested fix +1 |-"{.real}".format(1) + 1 |+f"{(1).real}" +2 2 | +3 3 | "{0.real}".format(1) +4 4 | + +UP032_2.py:3:1: UP032 [*] Use f-string instead of `format` call + | +3 | "{.real}".format(1) +4 | +5 | "{0.real}".format(1) + | ^^^^^^^^^^^^^^^^^^^^ UP032 +6 | +7 | "{a.real}".format(a=1) + | + = help: Convert to f-string + +ℹ Suggested fix +1 1 | "{.real}".format(1) +2 2 | +3 |-"{0.real}".format(1) + 3 |+f"{(1).real}" +4 4 | +5 5 | "{a.real}".format(a=1) + +UP032_2.py:5:1: UP032 [*] Use f-string instead of `format` call + | +5 | "{0.real}".format(1) +6 | +7 | "{a.real}".format(a=1) + | ^^^^^^^^^^^^^^^^^^^^^^ UP032 + | + = help: Convert to f-string + +ℹ Suggested fix +2 2 | +3 3 | "{0.real}".format(1) +4 4 | +5 |-"{a.real}".format(a=1) + 5 |+f"{(1).real}" + + From 4fea6a4ba20f3f72d0cfb3508a47a14109ad8d8a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 19 May 2023 14:44:44 -0400 Subject: [PATCH 2/2] Extend test --- .../test/fixtures/pyupgrade/UP032_2.py | 15 +- ...__rules__pyupgrade__tests__UP032_2.py.snap | 228 ++++++++++++++++-- 2 files changed, 218 insertions(+), 25 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py index c3cab327ca291..e6d567c7cd66a 100644 --- a/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py +++ b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_2.py @@ -1,5 +1,16 @@ +# Errors "{.real}".format(1) - "{0.real}".format(1) - "{a.real}".format(a=1) + +"{.real}".format(1.0) +"{0.real}".format(1.0) +"{a.real}".format(a=1.0) + +"{.real}".format(1j) +"{0.real}".format(1j) +"{a.real}".format(a=1j) + +"{.real}".format(0b01) +"{0.real}".format(0b01) +"{a.real}".format(a=0b01) diff --git a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap index 6b652765951a3..1905bcd70be5a 100644 --- a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap +++ b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_2.py.snap @@ -1,55 +1,237 @@ --- source: crates/ruff/src/rules/pyupgrade/mod.rs --- -UP032_2.py:1:1: UP032 [*] Use f-string instead of `format` call +UP032_2.py:2:1: UP032 [*] Use f-string instead of `format` call | -1 | "{.real}".format(1) +2 | # Errors +3 | "{.real}".format(1) | ^^^^^^^^^^^^^^^^^^^ UP032 -2 | -3 | "{0.real}".format(1) +4 | "{0.real}".format(1) +5 | "{a.real}".format(a=1) | = help: Convert to f-string ℹ Suggested fix -1 |-"{.real}".format(1) - 1 |+f"{(1).real}" -2 2 | +1 1 | # Errors +2 |-"{.real}".format(1) + 2 |+f"{(1).real}" 3 3 | "{0.real}".format(1) -4 4 | +4 4 | "{a.real}".format(a=1) +5 5 | UP032_2.py:3:1: UP032 [*] Use f-string instead of `format` call | -3 | "{.real}".format(1) -4 | +3 | # Errors +4 | "{.real}".format(1) 5 | "{0.real}".format(1) | ^^^^^^^^^^^^^^^^^^^^ UP032 -6 | -7 | "{a.real}".format(a=1) +6 | "{a.real}".format(a=1) | = help: Convert to f-string ℹ Suggested fix -1 1 | "{.real}".format(1) -2 2 | +1 1 | # Errors +2 2 | "{.real}".format(1) 3 |-"{0.real}".format(1) 3 |+f"{(1).real}" -4 4 | -5 5 | "{a.real}".format(a=1) +4 4 | "{a.real}".format(a=1) +5 5 | +6 6 | "{.real}".format(1.0) -UP032_2.py:5:1: UP032 [*] Use f-string instead of `format` call +UP032_2.py:4:1: UP032 [*] Use f-string instead of `format` call | +4 | "{.real}".format(1) 5 | "{0.real}".format(1) -6 | -7 | "{a.real}".format(a=1) +6 | "{a.real}".format(a=1) | ^^^^^^^^^^^^^^^^^^^^^^ UP032 +7 | +8 | "{.real}".format(1.0) | = help: Convert to f-string ℹ Suggested fix -2 2 | +1 1 | # Errors +2 2 | "{.real}".format(1) 3 3 | "{0.real}".format(1) -4 4 | -5 |-"{a.real}".format(a=1) - 5 |+f"{(1).real}" +4 |-"{a.real}".format(a=1) + 4 |+f"{(1).real}" +5 5 | +6 6 | "{.real}".format(1.0) +7 7 | "{0.real}".format(1.0) + +UP032_2.py:6:1: UP032 [*] Use f-string instead of `format` call + | + 6 | "{a.real}".format(a=1) + 7 | + 8 | "{.real}".format(1.0) + | ^^^^^^^^^^^^^^^^^^^^^ UP032 + 9 | "{0.real}".format(1.0) +10 | "{a.real}".format(a=1.0) + | + = help: Convert to f-string + +ℹ Suggested fix +3 3 | "{0.real}".format(1) +4 4 | "{a.real}".format(a=1) +5 5 | +6 |-"{.real}".format(1.0) + 6 |+f"{1.0.real}" +7 7 | "{0.real}".format(1.0) +8 8 | "{a.real}".format(a=1.0) +9 9 | + +UP032_2.py:7:1: UP032 [*] Use f-string instead of `format` call + | +7 | "{.real}".format(1.0) +8 | "{0.real}".format(1.0) + | ^^^^^^^^^^^^^^^^^^^^^^ UP032 +9 | "{a.real}".format(a=1.0) + | + = help: Convert to f-string + +ℹ Suggested fix +4 4 | "{a.real}".format(a=1) +5 5 | +6 6 | "{.real}".format(1.0) +7 |-"{0.real}".format(1.0) + 7 |+f"{1.0.real}" +8 8 | "{a.real}".format(a=1.0) +9 9 | +10 10 | "{.real}".format(1j) + +UP032_2.py:8:1: UP032 [*] Use f-string instead of `format` call + | + 8 | "{.real}".format(1.0) + 9 | "{0.real}".format(1.0) +10 | "{a.real}".format(a=1.0) + | ^^^^^^^^^^^^^^^^^^^^^^^^ UP032 +11 | +12 | "{.real}".format(1j) + | + = help: Convert to f-string + +ℹ Suggested fix +5 5 | +6 6 | "{.real}".format(1.0) +7 7 | "{0.real}".format(1.0) +8 |-"{a.real}".format(a=1.0) + 8 |+f"{1.0.real}" +9 9 | +10 10 | "{.real}".format(1j) +11 11 | "{0.real}".format(1j) + +UP032_2.py:10:1: UP032 [*] Use f-string instead of `format` call + | +10 | "{a.real}".format(a=1.0) +11 | +12 | "{.real}".format(1j) + | ^^^^^^^^^^^^^^^^^^^^ UP032 +13 | "{0.real}".format(1j) +14 | "{a.real}".format(a=1j) + | + = help: Convert to f-string + +ℹ Suggested fix +7 7 | "{0.real}".format(1.0) +8 8 | "{a.real}".format(a=1.0) +9 9 | +10 |-"{.real}".format(1j) + 10 |+f"{1j.real}" +11 11 | "{0.real}".format(1j) +12 12 | "{a.real}".format(a=1j) +13 13 | + +UP032_2.py:11:1: UP032 [*] Use f-string instead of `format` call + | +11 | "{.real}".format(1j) +12 | "{0.real}".format(1j) + | ^^^^^^^^^^^^^^^^^^^^^ UP032 +13 | "{a.real}".format(a=1j) + | + = help: Convert to f-string + +ℹ Suggested fix +8 8 | "{a.real}".format(a=1.0) +9 9 | +10 10 | "{.real}".format(1j) +11 |-"{0.real}".format(1j) + 11 |+f"{1j.real}" +12 12 | "{a.real}".format(a=1j) +13 13 | +14 14 | "{.real}".format(0b01) + +UP032_2.py:12:1: UP032 [*] Use f-string instead of `format` call + | +12 | "{.real}".format(1j) +13 | "{0.real}".format(1j) +14 | "{a.real}".format(a=1j) + | ^^^^^^^^^^^^^^^^^^^^^^^ UP032 +15 | +16 | "{.real}".format(0b01) + | + = help: Convert to f-string + +ℹ Suggested fix +9 9 | +10 10 | "{.real}".format(1j) +11 11 | "{0.real}".format(1j) +12 |-"{a.real}".format(a=1j) + 12 |+f"{1j.real}" +13 13 | +14 14 | "{.real}".format(0b01) +15 15 | "{0.real}".format(0b01) + +UP032_2.py:14:1: UP032 [*] Use f-string instead of `format` call + | +14 | "{a.real}".format(a=1j) +15 | +16 | "{.real}".format(0b01) + | ^^^^^^^^^^^^^^^^^^^^^^ UP032 +17 | "{0.real}".format(0b01) +18 | "{a.real}".format(a=0b01) + | + = help: Convert to f-string + +ℹ Suggested fix +11 11 | "{0.real}".format(1j) +12 12 | "{a.real}".format(a=1j) +13 13 | +14 |-"{.real}".format(0b01) + 14 |+f"{0b01.real}" +15 15 | "{0.real}".format(0b01) +16 16 | "{a.real}".format(a=0b01) + +UP032_2.py:15:1: UP032 [*] Use f-string instead of `format` call + | +15 | "{.real}".format(0b01) +16 | "{0.real}".format(0b01) + | ^^^^^^^^^^^^^^^^^^^^^^^ UP032 +17 | "{a.real}".format(a=0b01) + | + = help: Convert to f-string + +ℹ Suggested fix +12 12 | "{a.real}".format(a=1j) +13 13 | +14 14 | "{.real}".format(0b01) +15 |-"{0.real}".format(0b01) + 15 |+f"{0b01.real}" +16 16 | "{a.real}".format(a=0b01) + +UP032_2.py:16:1: UP032 [*] Use f-string instead of `format` call + | +16 | "{.real}".format(0b01) +17 | "{0.real}".format(0b01) +18 | "{a.real}".format(a=0b01) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP032 + | + = help: Convert to f-string + +ℹ Suggested fix +13 13 | +14 14 | "{.real}".format(0b01) +15 15 | "{0.real}".format(0b01) +16 |-"{a.real}".format(a=0b01) + 16 |+f"{0b01.real}"