mirror of https://github.com/astral-sh/ruff
Improve raw string handling in static_join_to_fstring
Refined logic to better detect when non-raw representation is needed, especially for cases involving backslashes and quote delimiters. Updated test fixtures and snapshots to clarify behavior and expected fixes for edge cases mixing raw and non-raw strings.
This commit is contained in:
parent
d371dde2bf
commit
b6caf8c92b
|
|
@ -36,7 +36,7 @@ nok14 = "".join((r"", '"')) # First is raw, second is not - would break syntax
|
||||||
nok15 = "".join((r"", "\\")) # First is raw, second has backslash - would break syntax
|
nok15 = "".join((r"", "\\")) # First is raw, second has backslash - would break syntax
|
||||||
nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
|
|
||||||
# Test that all-raw strings still work (should be OK)
|
# Test that all-raw strings still work (should be OK)
|
||||||
ok7 = "".join((r"", r"something")) # Both are raw - OK
|
ok7 = "".join((r"", r"something")) # Both are raw - OK
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,14 @@ fn build_fstring(joiner: &str, joinees: &[Expr], flags: FStringFlags) -> Option<
|
||||||
// use non-raw representation. This handles cases where raw strings would create invalid
|
// use non-raw representation. This handles cases where raw strings would create invalid
|
||||||
// syntax or behavior changes.
|
// syntax or behavior changes.
|
||||||
if any_raw && !content.is_empty() {
|
if any_raw && !content.is_empty() {
|
||||||
let needs_non_raw = content.contains(['\n', '\r', '\0']) || {
|
let needs_non_raw = content.contains(['\r', '\0']) || {
|
||||||
// Check if content contains characters that would break raw string syntax
|
// Check if content contains characters that would break raw string syntax
|
||||||
let quote_char = flags.quote_str();
|
let quote_char = flags.quote_str();
|
||||||
content.contains(quote_char) || content.contains('\\')
|
// A raw string cannot end with a single backslash if it's immediately
|
||||||
|
// followed by the quote delimiter, as that would be invalid syntax.
|
||||||
|
let ends_with_backslash = content.ends_with('\\')
|
||||||
|
&& (content.len() == 1 || content.chars().nth_back(1) != Some('\\'));
|
||||||
|
content.contains(quote_char) || ends_with_backslash
|
||||||
};
|
};
|
||||||
|
|
||||||
if needs_non_raw {
|
if needs_non_raw {
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ help: Replace with `f"{secrets.token_urlsafe()}a{secrets.token_hex()}"`
|
||||||
13 | nok2 = a.join(["1", "2", "3"]) # Not OK (not a static joiner)
|
13 | nok2 = a.join(["1", "2", "3"]) # Not OK (not a static joiner)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `'line1\nline2'` instead of string join
|
FLY002 [*] Consider f-string instead of string join
|
||||||
--> FLY002.py:20:8
|
--> FLY002.py:20:8
|
||||||
|
|
|
|
||||||
18 | nok7 = "a".join([f"foo{8}", "bar"]) # Not OK (contains an f-string)
|
18 | nok7 = "a".join([f"foo{8}", "bar"]) # Not OK (contains an f-string)
|
||||||
|
|
@ -134,15 +134,16 @@ FLY002 [*] Consider `'line1\nline2'` instead of string join
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
21 | nok9 = '\n'.join([r"raw string", '<""">', "<'''>"]) # Not OK (both triple-quote delimiters appear; should bail)
|
21 | nok9 = '\n'.join([r"raw string", '<""">', "<'''>"]) # Not OK (both triple-quote delimiters appear; should bail)
|
||||||
|
|
|
|
||||||
help: Replace with `'line1\nline2'`
|
help: Replace with f-string
|
||||||
17 | nok6 = "a".join(x for x in "feefoofum") # Not OK (generator)
|
17 | nok6 = "a".join(x for x in "feefoofum") # Not OK (generator)
|
||||||
18 | nok7 = "a".join([f"foo{8}", "bar"]) # Not OK (contains an f-string)
|
18 | nok7 = "a".join([f"foo{8}", "bar"]) # Not OK (contains an f-string)
|
||||||
19 | # https://github.com/astral-sh/ruff/issues/19887
|
19 | # https://github.com/astral-sh/ruff/issues/19887
|
||||||
- nok8 = '\n'.join([r'line1','line2'])
|
- nok8 = '\n'.join([r'line1','line2'])
|
||||||
20 + nok8 = 'line1\nline2'
|
20 + nok8 = r'''line1
|
||||||
21 | nok9 = '\n'.join([r"raw string", '<""">', "<'''>"]) # Not OK (both triple-quote delimiters appear; should bail)
|
21 + line2'''
|
||||||
22 |
|
22 | nok9 = '\n'.join([r"raw string", '<""">', "<'''>"]) # Not OK (both triple-quote delimiters appear; should bail)
|
||||||
23 | # Regression test for: https://github.com/astral-sh/ruff/issues/7197
|
23 |
|
||||||
|
24 | # Regression test for: https://github.com/astral-sh/ruff/issues/7197
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `"raw string\n<\"\"\">\n<'''>"` instead of string join
|
FLY002 [*] Consider `"raw string\n<\"\"\">\n<'''>"` instead of string join
|
||||||
|
|
@ -267,7 +268,7 @@ help: Replace with `"\\"`
|
||||||
36 + nok15 = "\\" # First is raw, second has backslash - would break syntax
|
36 + nok15 = "\\" # First is raw, second has backslash - would break syntax
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `"\x00"` instead of string join
|
FLY002 [*] Consider `"\x00"` instead of string join
|
||||||
|
|
@ -278,7 +279,7 @@ FLY002 [*] Consider `"\x00"` instead of string join
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
|
|
|
|
||||||
help: Replace with `"\x00"`
|
help: Replace with `"\x00"`
|
||||||
34 | # Mixing raw and non-raw strings can cause syntax errors or behavior changes
|
34 | # Mixing raw and non-raw strings can cause syntax errors or behavior changes
|
||||||
|
|
@ -287,7 +288,7 @@ help: Replace with `"\x00"`
|
||||||
- nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
- nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
37 + nok16 = "\x00" # First is raw, second has null byte - would introduce null bytes
|
37 + nok16 = "\x00" # First is raw, second has null byte - would introduce null bytes
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
40 |
|
40 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
@ -298,7 +299,7 @@ FLY002 [*] Consider `"\r"` instead of string join
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
|
|
|
|
||||||
help: Replace with `"\r"`
|
help: Replace with `"\r"`
|
||||||
35 | nok14 = "".join((r"", '"')) # First is raw, second is not - would break syntax
|
35 | nok14 = "".join((r"", '"')) # First is raw, second is not - would break syntax
|
||||||
|
|
@ -306,27 +307,27 @@ help: Replace with `"\r"`
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
- nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
- nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
38 + nok17 = "\r" # First is raw, second has carriage return - would change behavior
|
38 + nok17 = "\r" # First is raw, second has carriage return - would change behavior
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
40 |
|
40 |
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `"\\r"` instead of string join
|
FLY002 [*] Consider `r"\r"` instead of string join
|
||||||
--> FLY002.py:39:9
|
--> FLY002.py:39:9
|
||||||
|
|
|
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
40 |
|
40 |
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
|
|
|
|
||||||
help: Replace with `"\\r"`
|
help: Replace with `r"\r"`
|
||||||
36 | nok15 = "".join((r"", "\\")) # First is raw, second has backslash - would break syntax
|
36 | nok15 = "".join((r"", "\\")) # First is raw, second has backslash - would break syntax
|
||||||
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
37 | nok16 = "".join((r"", "\0")) # First is raw, second has null byte - would introduce null bytes
|
||||||
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
38 | nok17 = "".join((r"", "\r")) # First is raw, second has carriage return - would change behavior
|
||||||
- nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
- nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
39 + nok18 = "\\r" # First is raw, second has backslash escape followed by r
|
39 + nok18 = r"\r" # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
40 |
|
40 |
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
42 | ok7 = "".join((r"", r"something")) # Both are raw - OK
|
42 | ok7 = "".join((r"", r"something")) # Both are raw - OK
|
||||||
|
|
@ -341,7 +342,7 @@ FLY002 [*] Consider `r"something"` instead of string join
|
||||||
43 | ok8 = "\n".join((r"line1", r'line2')) # Both are raw - OK
|
43 | ok8 = "\n".join((r"line1", r'line2')) # Both are raw - OK
|
||||||
|
|
|
|
||||||
help: Replace with `r"something"`
|
help: Replace with `r"something"`
|
||||||
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash escape followed by r
|
39 | nok18 = "".join((r"", "\\r")) # First is raw, second has backslash followed by literal r - OK (no special handling needed)
|
||||||
40 |
|
40 |
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
- ok7 = "".join((r"", r"something")) # Both are raw - OK
|
- ok7 = "".join((r"", r"something")) # Both are raw - OK
|
||||||
|
|
@ -351,7 +352,7 @@ help: Replace with `r"something"`
|
||||||
45 | # Test that all-non-raw strings still work (should be OK)
|
45 | # Test that all-non-raw strings still work (should be OK)
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `"line1\nline2"` instead of string join
|
FLY002 [*] Consider f-string instead of string join
|
||||||
--> FLY002.py:43:7
|
--> FLY002.py:43:7
|
||||||
|
|
|
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
|
|
@ -361,15 +362,16 @@ FLY002 [*] Consider `"line1\nline2"` instead of string join
|
||||||
44 |
|
44 |
|
||||||
45 | # Test that all-non-raw strings still work (should be OK)
|
45 | # Test that all-non-raw strings still work (should be OK)
|
||||||
|
|
|
|
||||||
help: Replace with `"line1\nline2"`
|
help: Replace with f-string
|
||||||
40 |
|
40 |
|
||||||
41 | # Test that all-raw strings still work (should be OK)
|
41 | # Test that all-raw strings still work (should be OK)
|
||||||
42 | ok7 = "".join((r"", r"something")) # Both are raw - OK
|
42 | ok7 = "".join((r"", r"something")) # Both are raw - OK
|
||||||
- ok8 = "\n".join((r"line1", r'line2')) # Both are raw - OK
|
- ok8 = "\n".join((r"line1", r'line2')) # Both are raw - OK
|
||||||
43 + ok8 = "line1\nline2" # Both are raw - OK
|
43 + ok8 = r"""line1
|
||||||
44 |
|
44 + line2""" # Both are raw - OK
|
||||||
45 | # Test that all-non-raw strings still work (should be OK)
|
45 |
|
||||||
46 | ok9 = "".join(("", '"')) # Both are non-raw - OK
|
46 | # Test that all-non-raw strings still work (should be OK)
|
||||||
|
47 | ok9 = "".join(("", '"')) # Both are non-raw - OK
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
FLY002 [*] Consider `'"'` instead of string join
|
FLY002 [*] Consider `'"'` instead of string join
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue