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:
|
Requirements:
|
||||||
The line contains a string which is surrounded by parentheses and:
|
The line contains a string which is surrounded by parentheses and:
|
||||||
- The target string is NOT the only argument to a function call).
|
- 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:
|
Transformations:
|
||||||
The parentheses mentioned in the 'Requirements' section are stripped.
|
The parentheses mentioned in the 'Requirements' section are stripped.
|
||||||
@ -3292,14 +3294,51 @@ def do_match(self, line: Line) -> TMatchResult:
|
|||||||
string_parser = StringParser()
|
string_parser = StringParser()
|
||||||
next_idx = string_parser.parse(LL, string_idx)
|
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...
|
# Should be followed by a non-empty RPAR...
|
||||||
if (
|
if (
|
||||||
is_valid_index(next_idx)
|
is_valid_index(next_idx)
|
||||||
and LL[next_idx].type == token.RPAR
|
and LL[next_idx].type == token.RPAR
|
||||||
and not is_empty_rpar(LL[next_idx])
|
and not is_empty_rpar(LL[next_idx])
|
||||||
):
|
):
|
||||||
# That RPAR should NOT be followed by a '.' symbol.
|
# That RPAR should NOT be followed by anything with higher
|
||||||
if is_valid_index(next_idx + 1) and LL[next_idx + 1].type == token.DOT:
|
# 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
|
continue
|
||||||
|
|
||||||
return Ok(string_idx)
|
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_equivalent(source, actual)
|
||||||
black.assert_stable(source, actual, black.FileMode())
|
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)
|
@patch("black.dump_to_file", dump_to_stderr)
|
||||||
def test_comments(self) -> None:
|
def test_comments(self) -> None:
|
||||||
source, expected = read_data("comments")
|
source, expected = read_data("comments")
|
||||||
|
Loading…
Reference in New Issue
Block a user