]> git.madduck.net Git - etc/vim.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

don't strip brackets before lsqb (#1575) (#1590)
authorDavid Szotten <davidszotten@gmail.com>
Fri, 14 Aug 2020 02:20:46 +0000 (03:20 +0100)
committerGitHub <noreply@github.com>
Fri, 14 Aug 2020 02:20:46 +0000 (19:20 -0700)
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

src/black/__init__.py
tests/data/percent_precedence.py [new file with mode: 0644]
tests/test_black.py

index 36c03946b550178de64207752bb0c0e47ecc9ad0..b660e4788b496fd36e7868dca308a35dcf60c63d 100644 (file)
@@ -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).
     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.
 
     Transformations:
         The parentheses mentioned in the 'Requirements' section are stripped.
@@ -3292,14 +3294,51 @@ class StringParenStripper(StringTransformer):
             string_parser = StringParser()
             next_idx = string_parser.parse(LL, string_idx)
 
             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])
             ):
             # 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)
                     continue
 
                 return Ok(string_idx)
diff --git a/tests/data/percent_precedence.py b/tests/data/percent_precedence.py
new file mode 100644 (file)
index 0000000..44d30f1
--- /dev/null
@@ -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))
index f4055581f86adf1301d041fbc15e087a557d5c8f..3c766330a08c65608fb893246be674512e54352e 100644 (file)
@@ -501,6 +501,14 @@ class BlackTestCase(unittest.TestCase):
         black.assert_equivalent(source, actual)
         black.assert_stable(source, actual, black.FileMode())
 
         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")
     @patch("black.dump_to_file", dump_to_stderr)
     def test_comments(self) -> None:
         source, expected = read_data("comments")