diff --git a/CHANGES.md b/CHANGES.md index 90a99abe6d5..9dcb9b684c3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -36,6 +36,9 @@ +- Fix `# fmt: skip` crashing with `Cannot parse` on multiline `case` statements that use + backslash line continuation (e.g. `case A \ | B: # fmt: skip`) (#5123) + ### Performance diff --git a/src/black/comments.py b/src/black/comments.py index b3dda1d2a33..38994f03e69 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -630,6 +630,19 @@ def _get_compound_statement_header( return header_leaves +def _prefix_has_real_newline(prefix: str) -> bool: + """Return True if prefix contains a newline that is NOT a line continuation. + + A backslash immediately before a newline (``\\\n``) is a line continuation + and should not be treated as a logical line boundary when collecting nodes + to skip via ``# fmt: skip``. + """ + for i, ch in enumerate(prefix): + if ch == "\n" and (i == 0 or prefix[i - 1] != "\\"): + return True + return False + + def _generate_ignored_nodes_from_fmt_skip( leaf: Leaf, comment: ProtoComment, mode: Mode ) -> Iterator[LN]: @@ -676,7 +689,10 @@ def _generate_ignored_nodes_from_fmt_skip( # Track seen nodes to detect cycles that can occur after tree modifications seen_nodes = {id(current_node)} - while "\n" not in current_node.prefix and current_node.prev_sibling is not None: + while ( + not _prefix_has_real_newline(current_node.prefix) + and current_node.prev_sibling is not None + ): leaf_nodes = list(current_node.prev_sibling.leaves()) next_node = leaf_nodes[-1] if leaf_nodes else current_node diff --git a/tests/data/cases/fmtskip14.py b/tests/data/cases/fmtskip14.py new file mode 100644 index 00000000000..b1478adc4a3 --- /dev/null +++ b/tests/data/cases/fmtskip14.py @@ -0,0 +1,19 @@ +# flags: --minimum-version=3.10 +# Test fmt: skip on multiline case statements with backslash line continuation +# See https://github.com/psf/black/issues/5122 + +match (method, *path.split("/")): + + case ("GET", "parent", _, "resource", resource_id) \ + | ("GET", "resource", resource_id): # fmt: skip + pass + + case _: + pass + + +match x: + case a \ + | b \ + | c: # fmt: skip + pass