From 889a8d5dd27a73aa780e989a850bbdaaa9946a13 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 26 Jan 2022 16:47:36 -0800 Subject: [PATCH] Fix crash on some power hugging cases (#2806) Found by the fuzzer. Repro case: python -m black -c 'importA;()<<0**0#' --- src/black/linegen.py | 2 ++ src/black/lines.py | 4 +++- src/blib2to3/pytree.py | 6 +++++- tests/data/power_op_newline.py | 10 ++++++++++ tests/test_format.py | 6 ++++++ 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/data/power_op_newline.py diff --git a/src/black/linegen.py b/src/black/linegen.py index 9fbdfad..ac60ed1 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -942,6 +942,7 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf if ( prev and prev.type == token.COMMA + and leaf.opening_bracket is not None and not is_one_tuple_between( leaf.opening_bracket, leaf, line.leaves ) @@ -969,6 +970,7 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf if ( prev and prev.type == token.COMMA + and leaf.opening_bracket is not None and not is_one_tuple_between(leaf.opening_bracket, leaf, line.leaves) ): # Never omit bracket pairs with trailing commas. diff --git a/src/black/lines.py b/src/black/lines.py index c602aa6..7d50f02 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -277,7 +277,9 @@ class Line: if self.is_import: return True - if not is_one_tuple_between(closing.opening_bracket, closing, self.leaves): + if closing.opening_bracket is not None and not is_one_tuple_between( + closing.opening_bracket, closing, self.leaves + ): return True return False diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py index bd86270..b203ce5 100644 --- a/src/blib2to3/pytree.py +++ b/src/blib2to3/pytree.py @@ -386,7 +386,8 @@ class Leaf(Base): value: Text fixers_applied: List[Any] bracket_depth: int - opening_bracket: "Leaf" + # Changed later in brackets.py + opening_bracket: Optional["Leaf"] = None used_names: Optional[Set[Text]] _prefix = "" # Whitespace and comments preceding this token in the input lineno: int = 0 # Line where this token starts in the input @@ -399,6 +400,7 @@ class Leaf(Base): context: Optional[Context] = None, prefix: Optional[Text] = None, fixers_applied: List[Any] = [], + opening_bracket: Optional["Leaf"] = None, ) -> None: """ Initializer. @@ -416,6 +418,7 @@ class Leaf(Base): self._prefix = prefix self.fixers_applied: Optional[List[Any]] = fixers_applied[:] self.children = [] + self.opening_bracket = opening_bracket def __repr__(self) -> str: """Return a canonical string representation.""" @@ -448,6 +451,7 @@ class Leaf(Base): self.value, (self.prefix, (self.lineno, self.column)), fixers_applied=self.fixers_applied, + opening_bracket=self.opening_bracket, ) def leaves(self) -> Iterator["Leaf"]: diff --git a/tests/data/power_op_newline.py b/tests/data/power_op_newline.py new file mode 100644 index 0000000..85d434d --- /dev/null +++ b/tests/data/power_op_newline.py @@ -0,0 +1,10 @@ +importA;()<<0**0# + +# output + +importA +( + () + << 0 + ** 0 +) # diff --git a/tests/test_format.py b/tests/test_format.py index a4619b4..88f084e 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -256,3 +256,9 @@ def test_python38() -> None: def test_python39() -> None: source, expected = read_data("python39") assert_format(source, expected, minimum_version=(3, 9)) + + +def test_power_op_newline() -> None: + # requires line_length=0 + source, expected = read_data("power_op_newline") + assert_format(source, expected, mode=black.Mode(line_length=0)) -- 2.39.5