if the string contains a PERCENT, it's not safe to remove brackets that follow and operator with the same or higher precedence than PERCENT
This commit is contained in:
parent
5e1f620af7
commit
d1ad8730e3
@ -3249,7 +3249,9 @@ class StringParenStripper(StringTransformer):
|
||||
Requirements:
|
||||
The line contains a string which is surrounded by parentheses and:
|
||||
- The target string is NOT the only argument to a function call).
|
||||
- The RPAR is NOT followed by an attribute access (i.e. a dot).
|
||||
- If the target string contains a PERCENT, the brackets are not
|
||||
preceeded or followed by an operator with higher precedence than
|
||||
PERCENT.
|
||||
|
||||
Transformations:
|
||||
The parentheses mentioned in the 'Requirements' section are stripped.
|
||||
@ -3292,14 +3294,51 @@ def do_match(self, line: Line) -> TMatchResult:
|
||||
string_parser = StringParser()
|
||||
next_idx = string_parser.parse(LL, string_idx)
|
||||
|
||||
# if the leaves in the parsed string include a PERCENT, we need to
|
||||
# make sure the initial LPAR is NOT preceded by an operator with
|
||||
# higher or equal precedence to PERCENT
|
||||
if (
|
||||
is_valid_index(idx - 2)
|
||||
and token.PERCENT in {leaf.type for leaf in LL[idx - 1 : next_idx]}
|
||||
and (
|
||||
(
|
||||
LL[idx - 2].type
|
||||
in {
|
||||
token.STAR,
|
||||
token.AT,
|
||||
token.SLASH,
|
||||
token.DOUBLESLASH,
|
||||
token.PERCENT,
|
||||
token.TILDE,
|
||||
token.DOUBLESTAR,
|
||||
token.AWAIT,
|
||||
token.LSQB,
|
||||
token.LPAR,
|
||||
}
|
||||
)
|
||||
or (
|
||||
# only unary PLUS/MINUS
|
||||
not is_valid_index(idx - 3)
|
||||
and (LL[idx - 2].type in {token.PLUS, token.MINUS})
|
||||
)
|
||||
)
|
||||
):
|
||||
continue
|
||||
|
||||
# Should be followed by a non-empty RPAR...
|
||||
if (
|
||||
is_valid_index(next_idx)
|
||||
and LL[next_idx].type == token.RPAR
|
||||
and not is_empty_rpar(LL[next_idx])
|
||||
):
|
||||
# That RPAR should NOT be followed by a '.' symbol.
|
||||
if is_valid_index(next_idx + 1) and LL[next_idx + 1].type == token.DOT:
|
||||
# That RPAR should NOT be followed by anything with higher
|
||||
# precedence than PERCENT
|
||||
if is_valid_index(next_idx + 1) and LL[next_idx + 1].type in {
|
||||
token.DOUBLESTAR,
|
||||
token.LSQB,
|
||||
token.LPAR,
|
||||
token.DOT,
|
||||
}:
|
||||
continue
|
||||
|
||||
return Ok(string_idx)
|
||||
|
39
tests/data/percent_precedence.py
Normal file
39
tests/data/percent_precedence.py
Normal file
@ -0,0 +1,39 @@
|
||||
("" % a) ** 2
|
||||
("" % a)[0]
|
||||
("" % a)()
|
||||
("" % a).b
|
||||
|
||||
2 * ("" % a)
|
||||
2 @ ("" % a)
|
||||
2 / ("" % a)
|
||||
2 // ("" % a)
|
||||
2 % ("" % a)
|
||||
+("" % a)
|
||||
b + ("" % a)
|
||||
-("" % a)
|
||||
b - ("" % a)
|
||||
~("" % a)
|
||||
2 ** ("" % a)
|
||||
await ("" % a)
|
||||
b[("" % a)]
|
||||
b(("" % a))
|
||||
# output
|
||||
("" % a) ** 2
|
||||
("" % a)[0]
|
||||
("" % a)()
|
||||
("" % a).b
|
||||
|
||||
2 * ("" % a)
|
||||
2 @ ("" % a)
|
||||
2 / ("" % a)
|
||||
2 // ("" % a)
|
||||
2 % ("" % a)
|
||||
+("" % a)
|
||||
b + "" % a
|
||||
-("" % a)
|
||||
b - "" % a
|
||||
~("" % a)
|
||||
2 ** ("" % a)
|
||||
await ("" % a)
|
||||
b[("" % a)]
|
||||
b(("" % a))
|
@ -501,6 +501,14 @@ def test_slices(self) -> None:
|
||||
black.assert_equivalent(source, actual)
|
||||
black.assert_stable(source, actual, black.FileMode())
|
||||
|
||||
@patch("black.dump_to_file", dump_to_stderr)
|
||||
def test_percent_precedence(self) -> None:
|
||||
source, expected = read_data("percent_precedence")
|
||||
actual = fs(source)
|
||||
self.assertFormatEqual(expected, actual)
|
||||
black.assert_equivalent(source, actual)
|
||||
black.assert_stable(source, actual, black.FileMode())
|
||||
|
||||
@patch("black.dump_to_file", dump_to_stderr)
|
||||
def test_comments(self) -> None:
|
||||
source, expected = read_data("comments")
|
||||
|
Loading…
Reference in New Issue
Block a user