ruff/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM108.py
Brent Westbrook 23c98849fc
Preserve quotes in generated f-strings (#15794)
## Summary

This is another follow-up to #15726 and #15778, extending the
quote-preserving behavior to f-strings and deleting the now-unused
`Generator::quote` field.

## Details
I also made one unrelated change to `rules/flynt/helpers.rs` to remove a
`to_string` call for making a `Box<str>` and tweaked some arguments to
some of the `Generator::unparse_f_string` methods to make the code
easier to follow, in my opinion. Happy to revert especially the latter
of these if needed.

Unfortunately this still does not fix the issue in #9660, which appears
to be more of an escaping issue than a quote-preservation issue. After
#15726, the result is now `a = f'# {"".join([])}' if 1 else ""` instead
of `a = f"# {''.join([])}" if 1 else ""` (single quotes on the outside
now), but we still don't have the desired behavior of double quotes
everywhere on Python 3.12+. I added a test for this but split it off
into another branch since it ended up being unaddressed here, but my
`dbg!` statements showed the correct preferred quotes going into
[`UnicodeEscape::with_preferred_quote`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_python_literal/src/escape.rs#L54).

## Test Plan

Existing rule and `Generator` tests.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-01-29 13:28:22 -05:00

210 lines
3.1 KiB
Python

# SIM108
if a:
b = c
else:
b = d
# OK
b = c if a else d
# OK
if a:
b = c
elif c:
b = a
else:
b = d
# OK
if True:
pass
elif a:
b = 1
else:
b = 2
# SIM108
if True:
pass
else:
if a:
b = 1
else:
b = 2
import sys
# OK
if sys.version_info >= (3, 9):
randbytes = random.randbytes
else:
randbytes = _get_random_bytes
# OK
if sys.platform == "darwin":
randbytes = random.randbytes
else:
randbytes = _get_random_bytes
# OK
if sys.platform.startswith("linux"):
randbytes = random.randbytes
else:
randbytes = _get_random_bytes
# SIM108 (without fix due to comments)
if x > 0:
# test test
abc = x
else:
# test test test
abc = -x
# OK (too long)
if parser.errno == BAD_FIRST_LINE:
req = wrappers.Request(sock, server=self._server)
else:
req = wrappers.Request(
sock,
parser.get_method(),
parser.get_scheme() or _scheme,
parser.get_path(),
parser.get_version(),
parser.get_query_string(),
server=self._server,
)
# SIM108
if a:
b = "cccccccccccccccccccccccccccccccccß"
else:
b = "ddddddddddddddddddddddddddddddddd💣"
# OK (too long)
if True:
if a:
b = ccccccccccccccccccccccccccccccccccc
else:
b = ddddddddddddddddddddddddddddddddddd
# OK (too long with tabs)
if True:
if a:
b = ccccccccccccccccccccccccccccccccccc
else:
b = ddddddddddddddddddddddddddddddddddd
# SIM108 (without fix due to trailing comment)
if True:
exitcode = 0
else:
exitcode = 1 # Trailing comment
# SIM108
if True: x = 3 # Foo
else: x = 5
# SIM108
if True: # Foo
x = 3
else:
x = 5
# OK
def f():
if True:
x = yield 3
else:
x = yield 5
from typing import TYPE_CHECKING
# OK
if TYPE_CHECKING:
x = 3
else:
x = 5
# SIM108 - should suggest
# z = cond or other_cond
if cond:
z = cond
else:
z = other_cond
# SIM108 - should suggest
# z = cond and other_cond
if not cond:
z = cond
else:
z = other_cond
# SIM108 - should suggest
# z = not cond and other_cond
if cond:
z = not cond
else:
z = other_cond
# SIM108 does not suggest
# a binary option in these cases,
# despite the fact that `bool`
# is a subclass of both `int` and `float`
# so, e.g. `True == 1`.
# (Of course, these specific expressions
# should be simplified for other reasons...)
if True:
z = 1
else:
z = other
if False:
z = 1
else:
z = other
if 1:
z = True
else:
z = other
# SIM108 does not suggest a binary option in this
# case, since we'd be reducing the number of calls
# from Two to one.
if foo():
z = foo()
else:
z = other
# SIM108 does not suggest a binary option in this
# case, since we'd be reducing the number of calls
# from Two to one.
if foo():
z = not foo()
else:
z = other
# These two cases double as tests for f-string quote preservation. The first
# f-string should preserve its double quotes, and the second should preserve
# single quotes
if cond:
var = "str"
else:
var = f"{first}-{second}"
if cond:
var = "str"
else:
var = f'{first}-{second}'