From c54e48dce5b9c084e5f154d9fb0bc762c9e9cb07 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 8 May 2023 15:44:57 -0400 Subject: [PATCH] Avoid panics for f-string rewrites at start-of-file (#4291) --- .../pyupgrade/{UP032.py => UP032_0.py} | 0 .../test/fixtures/pyupgrade/UP032_1.py | 1 + crates/ruff/src/rules/pyupgrade/mod.rs | 3 +- .../src/rules/pyupgrade/rules/f_strings.rs | 6 +- ..._rules__pyupgrade__tests__UP032_0.py.snap} | 58 +++++++++---------- ...__rules__pyupgrade__tests__UP032_1.py.snap | 15 +++++ 6 files changed, 52 insertions(+), 31 deletions(-) rename crates/ruff/resources/test/fixtures/pyupgrade/{UP032.py => UP032_0.py} (100%) create mode 100644 crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py rename crates/ruff/src/rules/pyupgrade/snapshots/{ruff__rules__pyupgrade__tests__UP032.py.snap => ruff__rules__pyupgrade__tests__UP032_0.py.snap} (83%) create mode 100644 crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_1.py.snap diff --git a/crates/ruff/resources/test/fixtures/pyupgrade/UP032.py b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_0.py similarity index 100% rename from crates/ruff/resources/test/fixtures/pyupgrade/UP032.py rename to crates/ruff/resources/test/fixtures/pyupgrade/UP032_0.py diff --git a/crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py new file mode 100644 index 0000000000..884f2d9648 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/pyupgrade/UP032_1.py @@ -0,0 +1 @@ +"{} {}".format(a, b) # Intentionally at start-of-file, to ensure graceful handling. diff --git a/crates/ruff/src/rules/pyupgrade/mod.rs b/crates/ruff/src/rules/pyupgrade/mod.rs index 56c2236526..d4cfcec53e 100644 --- a/crates/ruff/src/rules/pyupgrade/mod.rs +++ b/crates/ruff/src/rules/pyupgrade/mod.rs @@ -58,7 +58,8 @@ mod tests { #[test_case(Rule::FormatLiterals, Path::new("UP030_2.py"); "UP030_2")] #[test_case(Rule::PrintfStringFormatting, Path::new("UP031_0.py"); "UP031_0")] #[test_case(Rule::PrintfStringFormatting, Path::new("UP031_1.py"); "UP031_1")] - #[test_case(Rule::FString, Path::new("UP032.py"); "UP032")] + #[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::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 d2c1116264..ad8b6d0d38 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/f_strings.rs @@ -267,7 +267,11 @@ pub(crate) fn f_strings(checker: &mut Checker, summary: &FormatSummary, expr: &E // If necessary, add a space between any leading keyword (`return`, `yield`, `assert`, etc.) // and the string. For example, `return"foo"` is valid, but `returnf"foo"` is not. let existing = checker.locator.slice(TextRange::up_to(expr.start())); - if existing.chars().last().unwrap().is_ascii_alphabetic() { + if existing + .chars() + .last() + .map_or(false, |char| char.is_ascii_alphabetic()) + { contents.insert(0, ' '); } diff --git a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032.py.snap b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_0.py.snap similarity index 83% rename from crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032.py.snap rename to crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_0.py.snap index 2560a9f4ce..fe27ef4530 100644 --- a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032.py.snap +++ b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_0.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff/src/rules/pyupgrade/mod.rs --- -UP032.py:5:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:5:1: UP032 [*] Use f-string instead of `format` call | 5 | ### 6 | @@ -22,7 +22,7 @@ UP032.py:5:1: UP032 [*] Use f-string instead of `format` call 7 7 | "{1} {0}".format(a, b) 8 8 | -UP032.py:7:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:7:1: UP032 [*] Use f-string instead of `format` call | 7 | "{} {}".format(a, b) 8 | @@ -43,7 +43,7 @@ UP032.py:7:1: UP032 [*] Use f-string instead of `format` call 9 9 | "{x.y}".format(x=z) 10 10 | -UP032.py:9:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:9:1: UP032 [*] Use f-string instead of `format` call | 9 | "{1} {0}".format(a, b) 10 | @@ -64,7 +64,7 @@ UP032.py:9:1: UP032 [*] Use f-string instead of `format` call 11 11 | "{.x} {.y}".format(a, b) 12 12 | -UP032.py:11:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:11:1: UP032 [*] Use f-string instead of `format` call | 11 | "{x.y}".format(x=z) 12 | @@ -85,7 +85,7 @@ UP032.py:11:1: UP032 [*] Use f-string instead of `format` call 13 13 | "{} {}".format(a.b, c.d) 14 14 | -UP032.py:13:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:13:1: UP032 [*] Use f-string instead of `format` call | 13 | "{.x} {.y}".format(a, b) 14 | @@ -106,7 +106,7 @@ UP032.py:13:1: UP032 [*] Use f-string instead of `format` call 15 15 | "{}".format(a()) 16 16 | -UP032.py:15:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:15:1: UP032 [*] Use f-string instead of `format` call | 15 | "{} {}".format(a.b, c.d) 16 | @@ -127,7 +127,7 @@ UP032.py:15:1: UP032 [*] Use f-string instead of `format` call 17 17 | "{}".format(a.b()) 18 18 | -UP032.py:17:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:17:1: UP032 [*] Use f-string instead of `format` call | 17 | "{}".format(a()) 18 | @@ -148,7 +148,7 @@ UP032.py:17:1: UP032 [*] Use f-string instead of `format` call 19 19 | "{}".format(a.b().c()) 20 20 | -UP032.py:19:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:19:1: UP032 [*] Use f-string instead of `format` call | 19 | "{}".format(a.b()) 20 | @@ -169,7 +169,7 @@ UP032.py:19:1: UP032 [*] Use f-string instead of `format` call 21 21 | "hello {}!".format(name) 22 22 | -UP032.py:21:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:21:1: UP032 [*] Use f-string instead of `format` call | 21 | "{}".format(a.b().c()) 22 | @@ -190,7 +190,7 @@ UP032.py:21:1: UP032 [*] Use f-string instead of `format` call 23 23 | "{}{b}{}".format(a, c, b=b) 24 24 | -UP032.py:23:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:23:1: UP032 [*] Use f-string instead of `format` call | 23 | "hello {}!".format(name) 24 | @@ -211,7 +211,7 @@ UP032.py:23:1: UP032 [*] Use f-string instead of `format` call 25 25 | "{}".format(0x0) 26 26 | -UP032.py:25:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:25:1: UP032 [*] Use f-string instead of `format` call | 25 | "{}{b}{}".format(a, c, b=b) 26 | @@ -232,7 +232,7 @@ UP032.py:25:1: UP032 [*] Use f-string instead of `format` call 27 27 | "{} {}".format(a, b) 28 28 | -UP032.py:27:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:27:1: UP032 [*] Use f-string instead of `format` call | 27 | "{}".format(0x0) 28 | @@ -253,7 +253,7 @@ UP032.py:27:1: UP032 [*] Use f-string instead of `format` call 29 29 | """{} {}""".format(a, b) 30 30 | -UP032.py:29:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:29:1: UP032 [*] Use f-string instead of `format` call | 29 | "{} {}".format(a, b) 30 | @@ -274,7 +274,7 @@ UP032.py:29:1: UP032 [*] Use f-string instead of `format` call 31 31 | "foo{}".format(1) 32 32 | -UP032.py:31:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:31:1: UP032 [*] Use f-string instead of `format` call | 31 | """{} {}""".format(a, b) 32 | @@ -295,7 +295,7 @@ UP032.py:31:1: UP032 [*] Use f-string instead of `format` call 33 33 | r"foo{}".format(1) 34 34 | -UP032.py:33:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:33:1: UP032 [*] Use f-string instead of `format` call | 33 | "foo{}".format(1) 34 | @@ -316,7 +316,7 @@ UP032.py:33:1: UP032 [*] Use f-string instead of `format` call 35 35 | x = "{a}".format(a=1) 36 36 | -UP032.py:35:5: UP032 [*] Use f-string instead of `format` call +UP032_0.py:35:5: UP032 [*] Use f-string instead of `format` call | 35 | r"foo{}".format(1) 36 | @@ -337,7 +337,7 @@ UP032.py:35:5: UP032 [*] Use f-string instead of `format` call 37 37 | print("foo {} ".format(x)) 38 38 | -UP032.py:37:7: UP032 [*] Use f-string instead of `format` call +UP032_0.py:37:7: UP032 [*] Use f-string instead of `format` call | 37 | x = "{a}".format(a=1) 38 | @@ -358,7 +358,7 @@ UP032.py:37:7: UP032 [*] Use f-string instead of `format` call 39 39 | "{a[b]}".format(a=a) 40 40 | -UP032.py:39:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:39:1: UP032 [*] Use f-string instead of `format` call | 39 | print("foo {} ".format(x)) 40 | @@ -379,7 +379,7 @@ UP032.py:39:1: UP032 [*] Use f-string instead of `format` call 41 41 | "{a.a[b]}".format(a=a) 42 42 | -UP032.py:41:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:41:1: UP032 [*] Use f-string instead of `format` call | 41 | "{a[b]}".format(a=a) 42 | @@ -400,7 +400,7 @@ UP032.py:41:1: UP032 [*] Use f-string instead of `format` call 43 43 | "{}{{}}{}".format(escaped, y) 44 44 | -UP032.py:43:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:43:1: UP032 [*] Use f-string instead of `format` call | 43 | "{a.a[b]}".format(a=a) 44 | @@ -421,7 +421,7 @@ UP032.py:43:1: UP032 [*] Use f-string instead of `format` call 45 45 | "{}".format(a) 46 46 | -UP032.py:45:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:45:1: UP032 [*] Use f-string instead of `format` call | 45 | "{}{{}}{}".format(escaped, y) 46 | @@ -442,7 +442,7 @@ UP032.py:45:1: UP032 [*] Use f-string instead of `format` call 47 47 | '({}={{0!e}})'.format(a) 48 48 | -UP032.py:47:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:47:1: UP032 [*] Use f-string instead of `format` call | 47 | "{}".format(a) 48 | @@ -463,7 +463,7 @@ UP032.py:47:1: UP032 [*] Use f-string instead of `format` call 49 49 | "{[b]}".format(a) 50 50 | -UP032.py:49:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:49:1: UP032 [*] Use f-string instead of `format` call | 49 | '({}={{0!e}})'.format(a) 50 | @@ -484,7 +484,7 @@ UP032.py:49:1: UP032 [*] Use f-string instead of `format` call 51 51 | '{[b]}'.format(a) 52 52 | -UP032.py:51:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:51:1: UP032 [*] Use f-string instead of `format` call | 51 | "{[b]}".format(a) 52 | @@ -505,7 +505,7 @@ UP032.py:51:1: UP032 [*] Use f-string instead of `format` call 53 53 | """{[b]}""".format(a) 54 54 | -UP032.py:53:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:53:1: UP032 [*] Use f-string instead of `format` call | 53 | '{[b]}'.format(a) 54 | @@ -526,7 +526,7 @@ UP032.py:53:1: UP032 [*] Use f-string instead of `format` call 55 55 | '''{[b]}'''.format(a) 56 56 | -UP032.py:55:1: UP032 [*] Use f-string instead of `format` call +UP032_0.py:55:1: UP032 [*] Use f-string instead of `format` call | 55 | """{[b]}""".format(a) 56 | @@ -547,7 +547,7 @@ UP032.py:55:1: UP032 [*] Use f-string instead of `format` call 57 57 | ### 58 58 | # Non-errors -UP032.py:100:11: UP032 [*] Use f-string instead of `format` call +UP032_0.py:100:11: UP032 [*] Use f-string instead of `format` call | 100 | def d(osname, version, release): 101 | return"{}-{}.{}".format(osname, version, release) @@ -565,7 +565,7 @@ UP032.py:100:11: UP032 [*] Use f-string instead of `format` call 102 102 | 103 103 | def e(): -UP032.py:104:10: UP032 [*] Use f-string instead of `format` call +UP032_0.py:104:10: UP032 [*] Use f-string instead of `format` call | 104 | def e(): 105 | yield"{}".format(1) @@ -583,7 +583,7 @@ UP032.py:104:10: UP032 [*] Use f-string instead of `format` call 106 106 | 107 107 | assert"{}".format(1) -UP032.py:107:7: UP032 [*] Use f-string instead of `format` call +UP032_0.py:107:7: UP032 [*] Use f-string instead of `format` call | 107 | assert"{}".format(1) | ^^^^^^^^^^^^^^ UP032 diff --git a/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_1.py.snap b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_1.py.snap new file mode 100644 index 0000000000..ead382b08c --- /dev/null +++ b/crates/ruff/src/rules/pyupgrade/snapshots/ruff__rules__pyupgrade__tests__UP032_1.py.snap @@ -0,0 +1,15 @@ +--- +source: crates/ruff/src/rules/pyupgrade/mod.rs +--- +UP032_1.py:1:1: UP032 [*] Use f-string instead of `format` call + | +1 | "{} {}".format(a, b) # Intentionally at start-of-file, to ensure graceful handling. + | ^^^^^^^^^^^^^^^^^^^^ UP032 + | + = help: Convert to f-string + +ℹ Suggested fix +1 |-"{} {}".format(a, b) # Intentionally at start-of-file, to ensure graceful handling. + 1 |+f"{a} {b}" # Intentionally at start-of-file, to ensure graceful handling. + +