From 001aa486df8566aaf74f7f601684321f9ef2d53b Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Tue, 8 Aug 2023 18:48:49 +0530 Subject: [PATCH] Add formatting for `StmtMatch` (#6286) ## Summary This PR adds support for `StmtMatch` with subs for `MatchCase`. ## Test Plan Add a few additional test cases around `match` statement, comments, line breaks. resolves: #6298 --- .../test/fixtures/ruff/statement/match.py | 57 +++ .../src/comments/placement.rs | 104 +---- .../src/comments/visitor.rs | 8 - .../src/other/match_case.rs | 37 +- .../src/statement/stmt_match.rs | 41 +- ...y@py_310__pattern_matching_complex.py.snap | 358 +++++++++++------- ...ty@py_310__pattern_matching_extras.py.snap | 247 ++++++++---- ...y@py_310__pattern_matching_generic.py.snap | 57 +-- ...ty@py_310__pattern_matching_simple.py.snap | 227 +++++++---- ...ity@py_310__pattern_matching_style.py.snap | 37 +- ...py_310__remove_newline_after_match.py.snap | 27 +- .../snapshots/format@statement__match.py.snap | 126 ++++++ 12 files changed, 882 insertions(+), 444 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py create mode 100644 crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py new file mode 100644 index 0000000000..e1c8d5baf4 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py @@ -0,0 +1,57 @@ +# leading match comment +match foo: # dangling match comment + case "bar": + pass + + +# leading match comment +match ( # leading expr comment + # another leading expr comment + foo # trailing expr comment + # another trailing expr comment +): # dangling match comment + case "bar": + pass + + +# leading match comment +match ( # hello + foo # trailing expr comment + , # another +): # dangling match comment + case "bar": + pass + + +match [ # comment + first, + second, + third +]: # another comment + case ["a", "b", "c"]: + pass + +match ( # comment + "a b c" +).split(): # another comment + case ["a", "b", "c"]: + pass + + +match ( # comment + # let's go + yield foo +): # another comment + case ["a", "b", "c"]: + pass + + +match aaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: # comment + case "sshhhhhhhh": + pass + + +def foo(): + match inside_func: # comment + case "bar": + pass diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index f7d90a98bb..1385b8ffa5 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -63,7 +63,6 @@ pub(super) fn place_comment<'a>( CommentPlacement::Default(comment) } } - AnyNodeRef::MatchCase(match_case) => handle_match_comment(comment, match_case, locator), AnyNodeRef::ModModule(_) => { handle_module_level_own_line_comment_before_class_or_function_comment(comment, locator) } @@ -210,6 +209,10 @@ fn is_first_statement_in_body(statement: AnyNodeRef, has_body: AnyNodeRef) -> bo are_same_optional(statement, body.first()) } + AnyNodeRef::StmtMatch(ast::StmtMatch { cases, .. }) => { + are_same_optional(statement, cases.first()) + } + _ => false, } } @@ -407,105 +410,6 @@ fn handle_own_line_comment_in_clause<'a>( CommentPlacement::Default(comment) } -/// Handles leading comments in front of a match case or a trailing comment of the `match` statement. -/// ```python -/// match pt: -/// # Leading `case(x, y)` comment -/// case (x, y): -/// return Point3d(x, y, 0) -/// # Leading `case (x, y, z)` comment -/// case _: -/// ``` -fn handle_match_comment<'a>( - comment: DecoratedComment<'a>, - match_case: &'a MatchCase, - locator: &Locator, -) -> CommentPlacement<'a> { - // Must be an own line comment after the last statement in a match case - if comment.line_position().is_end_of_line() || comment.following_node().is_some() { - return CommentPlacement::Default(comment); - } - - // And its parent match statement. - let Some(match_stmt) = comment.enclosing_parent().and_then(AnyNodeRef::stmt_match) else { - return CommentPlacement::Default(comment); - }; - - // Get the next sibling (sibling traversal would be really nice) - let current_case_index = match_stmt - .cases - .iter() - .position(|case| case == match_case) - .expect("Expected case to belong to parent match statement."); - - let next_case = match_stmt.cases.get(current_case_index + 1); - - let comment_indentation = indentation_at_offset(comment.slice().range().start(), locator) - .unwrap_or_default() - .len(); - let match_case_indentation = indentation(locator, match_case).unwrap().len(); - - if let Some(next_case) = next_case { - // The comment's indentation is less or equal to the `case` indention and there's a following - // `case` arm. - // ```python - // match pt: - // case (x, y): - // return Point3d(x, y, 0) - // # Leading `case (x, y, z)` comment - // case _: - // pass - // ``` - // Attach the `comment` as leading comment to the next case. - if comment_indentation <= match_case_indentation { - CommentPlacement::leading(next_case, comment) - } else { - // Otherwise, delegate to `handle_trailing_body_comment` - // ```python - // match pt: - // case (x, y): - // return Point3d(x, y, 0) - // # Trailing case body comment - // case _: - // pass - // ``` - CommentPlacement::Default(comment) - } - } else { - // Comment after the last statement in a match case... - let match_stmt_indentation = indentation(locator, match_stmt).unwrap_or_default().len(); - - if comment_indentation <= match_case_indentation - && comment_indentation > match_stmt_indentation - { - // The comment's indent matches the `case` indent (or is larger than the `match`'s indent). - // ```python - // match pt: - // case (x, y): - // return Point3d(x, y, 0) - // case _: - // pass - // # Trailing match comment - // ``` - // This is a trailing comment of the last case. - CommentPlacement::trailing(match_case, comment) - } else { - // Delegate to `handle_trailing_body_comment` because it's either a trailing indent - // for the last statement in the `case` body or a comment for the parent of the `match` - // - // ```python - // match pt: - // case (x, y): - // return Point3d(x, y, 0) - // case _: - // pass - // # trailing case comment - // ``` - CommentPlacement::Default(comment) - } - } -} - /// Determine where to attach an own line comment after a branch depending on its indentation fn handle_own_line_comment_after_branch<'a>( comment: DecoratedComment<'a>, diff --git a/crates/ruff_python_formatter/src/comments/visitor.rs b/crates/ruff_python_formatter/src/comments/visitor.rs index 9e60c5833d..0b18ea76d4 100644 --- a/crates/ruff_python_formatter/src/comments/visitor.rs +++ b/crates/ruff_python_formatter/src/comments/visitor.rs @@ -73,7 +73,6 @@ impl<'a> CommentsVisitor<'a> { enclosing: enclosing_node, preceding: self.preceding_node, following: Some(node), - parent: self.parents.iter().rev().nth(1).copied(), line_position: text_position(*comment_range, self.source_code), slice: self.source_code.slice(*comment_range), }; @@ -131,7 +130,6 @@ impl<'a> CommentsVisitor<'a> { let comment = DecoratedComment { enclosing: node, preceding: self.preceding_node, - parent: self.parents.last().copied(), following: None, line_position: text_position(*comment_range, self.source_code), slice: self.source_code.slice(*comment_range), @@ -340,7 +338,6 @@ pub(super) struct DecoratedComment<'a> { enclosing: AnyNodeRef<'a>, preceding: Option>, following: Option>, - parent: Option>, line_position: CommentLinePosition, slice: SourceCodeSlice, } @@ -366,11 +363,6 @@ impl<'a> DecoratedComment<'a> { self.enclosing } - /// Returns the parent of the enclosing node, if any - pub(super) fn enclosing_parent(&self) -> Option> { - self.parent - } - /// Returns the slice into the source code. pub(super) fn slice(&self) -> &SourceCodeSlice { &self.slice diff --git a/crates/ruff_python_formatter/src/other/match_case.rs b/crates/ruff_python_formatter/src/other/match_case.rs index 91a584657e..0bbe0572bc 100644 --- a/crates/ruff_python_formatter/src/other/match_case.rs +++ b/crates/ruff_python_formatter/src/other/match_case.rs @@ -1,12 +1,45 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; use ruff_python_ast::MatchCase; +use crate::expression::maybe_parenthesize_expression; +use crate::expression::parentheses::Parenthesize; +use crate::not_yet_implemented_custom_text; +use crate::prelude::*; +use crate::{FormatNodeRule, PyFormatter}; + #[derive(Default)] pub struct FormatMatchCase; impl FormatNodeRule for FormatMatchCase { fn fmt_fields(&self, item: &MatchCase, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let MatchCase { + range: _, + pattern: _, + guard, + body, + } = item; + + write!( + f, + [ + text("case"), + space(), + not_yet_implemented_custom_text("NOT_YET_IMPLEMENTED_Pattern"), + ] + )?; + + if let Some(guard) = guard { + write!( + f, + [ + space(), + text("if"), + space(), + maybe_parenthesize_expression(guard, item, Parenthesize::IfBreaks) + ] + )?; + } + + write!(f, [text(":"), block_indent(&body.format())]) } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_match.rs b/crates/ruff_python_formatter/src/statement/stmt_match.rs index 073acc1722..6971950c90 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_match.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_match.rs @@ -1,12 +1,49 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; use ruff_python_ast::StmtMatch; +use crate::comments::trailing_comments; +use crate::expression::maybe_parenthesize_expression; +use crate::expression::parentheses::Parenthesize; +use crate::prelude::*; +use crate::{FormatNodeRule, PyFormatter}; + #[derive(Default)] pub struct FormatStmtMatch; impl FormatNodeRule for FormatStmtMatch { fn fmt_fields(&self, item: &StmtMatch, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let StmtMatch { + range: _, + subject, + cases, + } = item; + + let comments = f.context().comments().clone(); + let dangling_item_comments = comments.dangling_comments(item); + + // There can be at most one dangling comment after the colon in a match statement. + debug_assert!(dangling_item_comments.len() <= 1); + + write!( + f, + [ + text("match"), + space(), + maybe_parenthesize_expression(subject, item, Parenthesize::IfBreaks), + text(":"), + trailing_comments(dangling_item_comments) + ] + )?; + + for case in cases { + write!(f, [block_indent(&case.format())])?; + } + + Ok(()) + } + + fn fmt_dangling_comments(&self, _node: &StmtMatch, _f: &mut PyFormatter) -> FormatResult<()> { + // Handled as part of `fmt_fields` + Ok(()) } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap index ce5a833fd2..e498853715 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_complex.py.snap @@ -156,180 +156,192 @@ match x: ```diff --- Black +++ Ruff -@@ -1,144 +1,60 @@ - # Cases sampled from Lib/test/test_patma.py +@@ -2,143 +2,143 @@ # case black_test_patma_098 --match x: + match x: - case -0j: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_142 --match x: + match x: - case bytes(z): -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_073 --match x: + match x: - case 0 if 0: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern if 0: + y = 0 - case 0 if 1: -- y = 1 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern if 1: + y = 1 # case black_test_patma_006 --match 3: + match 3: - case 0 | 1 | 2 | 3: -- x = True -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + x = True # case black_test_patma_049 --match x: + match x: - case [0, 1] | [1, 0]: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_check_sequence_then_mapping --match x: + match x: - case [*_]: -- return "seq" ++ case NOT_YET_IMPLEMENTED_Pattern: + return "seq" - case {}: -- return "map" -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + return "map" # case black_test_patma_035 --match x: + match x: - case {0: [1, 2, {}]}: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case {0: [1, 2, {}] | True} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: -- y = 1 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 1 - case []: -- y = 2 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_107 --match x: + match x: - case 0.25 + 1.75j: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_097 --match x: + match x: - case -0j: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_007 --match 4: + match 4: - case 0 | 1 | 2 | 3: -- x = True -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + x = True # case black_test_patma_154 --match x: + match x: - case 0 if x: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern if x: + y = 0 # case black_test_patma_134 --match x: + match x: - case {1: 0}: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case {0: 0}: -- y = 1 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 1 - case {**z}: -- y = 2 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_185 --match Seq(): + match Seq(): - case [*_]: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_063 --match x: + match x: - case 1: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case 1: -- y = 1 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 1 # case black_test_patma_248 --match x: + match x: - case {"foo": bar}: -- y = bar -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = bar # case black_test_patma_019 --match (0, 1, 2): + match (0, 1, 2): - case [0, 1, *x, 2]: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_052 --match x: + match x: - case [0]: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case [1, 0] if (x := x[:0]): -- y = 1 ++ case NOT_YET_IMPLEMENTED_Pattern if x := x[:0]: + y = 1 - case [1, 0]: -- y = 2 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_191 --match w: + match w: - case [x, y, *_]: -- z = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_110 --match x: + match x: - case -0.25 - 1.75j: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_151 --match (x,): + match (x,): - case [y]: -- z = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_114 --match x: + match x: - case A.B.C.D: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_232 --match x: + match x: - case None: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_058 --match x: + match x: - case 0: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_233 --match x: + match x: - case False: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_078 --match x: + match x: - case []: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case [""]: -- y = 1 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 1 - case "": -- y = 2 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_156 --match x: + match x: - case z: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_189 --match w: + match w: - case [x, y, *rest]: -- z = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_042 --match x: + match x: - case (0 as z) | (1 as z) | (2 as z) if z == x % 2: -- y = 0 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern if z == x % 2: + y = 0 # case black_test_patma_034 --match x: + match x: - case {0: [1, 2, {}]}: -- y = 0 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 - case {0: [1, 2, {}] | False} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}: -- y = 1 ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 1 - case []: -- y = 2 -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 2 ``` ## Ruff Output @@ -338,63 +350,147 @@ match x: # Cases sampled from Lib/test/test_patma.py # case black_test_patma_098 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_142 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_073 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern if 0: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern if 1: + y = 1 # case black_test_patma_006 -NOT_YET_IMPLEMENTED_StmtMatch +match 3: + case NOT_YET_IMPLEMENTED_Pattern: + x = True # case black_test_patma_049 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_check_sequence_then_mapping -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + return "seq" + case NOT_YET_IMPLEMENTED_Pattern: + return "map" # case black_test_patma_035 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern: + y = 1 + case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_107 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_097 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_007 -NOT_YET_IMPLEMENTED_StmtMatch +match 4: + case NOT_YET_IMPLEMENTED_Pattern: + x = True # case black_test_patma_154 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern if x: + y = 0 # case black_test_patma_134 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern: + y = 1 + case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_185 -NOT_YET_IMPLEMENTED_StmtMatch +match Seq(): + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_063 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern: + y = 1 # case black_test_patma_248 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = bar # case black_test_patma_019 -NOT_YET_IMPLEMENTED_StmtMatch +match (0, 1, 2): + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_052 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern if x := x[:0]: + y = 1 + case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_191 -NOT_YET_IMPLEMENTED_StmtMatch +match w: + case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_110 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_151 -NOT_YET_IMPLEMENTED_StmtMatch +match (x,): + case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_114 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_232 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_058 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_233 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_078 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern: + y = 1 + case NOT_YET_IMPLEMENTED_Pattern: + y = 2 # case black_test_patma_156 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 # case black_test_patma_189 -NOT_YET_IMPLEMENTED_StmtMatch +match w: + case NOT_YET_IMPLEMENTED_Pattern: + z = 0 # case black_test_patma_042 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern if z == x % 2: + y = 0 # case black_test_patma_034 -NOT_YET_IMPLEMENTED_StmtMatch +match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 + case NOT_YET_IMPLEMENTED_Pattern: + y = 1 + case NOT_YET_IMPLEMENTED_Pattern: + y = 2 ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap index 23dd576815..f107f563a0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap @@ -131,139 +131,155 @@ match bar1: ```diff --- Black +++ Ruff -@@ -1,119 +1,43 @@ +@@ -1,13 +1,13 @@ import match --match something: + match something: - case [a as b]: -- print(b) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(b) - case [a as b, c, d, e as f]: -- print(f) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(f) - case Point(a as b): -- print(b) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(b) - case Point(int() as x, int() as y): -- print(x, y) -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + print(x, y) - match = 1 +@@ -15,40 +15,43 @@ case: int = re.match(something) --match re.match(case): + match re.match(case): - case type("match", match): -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case match: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass def func(match: case, case: match) -> case: -- match Something(): + match Something(): - case func(match, case): -- ... ++ case NOT_YET_IMPLEMENTED_Pattern: + ... - case another: -- ... -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + ... --match maybe, multiple: + match maybe, multiple: - case perhaps, 5: -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case perhaps, 6,: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass -match more := (than, one), indeed,: - case _, (5, 6): -- pass ++match ( ++ more := (than, one), ++ indeed, ++): ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case [[5], (6)], [7],: -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case _: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass --match a, *b, c: + match a, *b, c: - case [*_]: -- assert "seq" == _ ++ case NOT_YET_IMPLEMENTED_Pattern: + assert "seq" == _ - case {}: -- assert "map" == b -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + assert "map" == b --match match( -- case, -- match( -- match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match -- ), -- case, --): +@@ -59,61 +62,47 @@ + ), + case, + ): - case case( - match=case, - case=re.match( - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong - ), - ): -- pass -+NOT_YET_IMPLEMENTED_StmtMatch - ++ case NOT_YET_IMPLEMENTED_Pattern: + pass +- - case [a as match]: -- pass - ++ case NOT_YET_IMPLEMENTED_Pattern: + pass +- - case case: -- pass -- -- --match match: -- case case: -- pass -- -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass --match a, *b(), c: + + match match: +- case case: ++ case NOT_YET_IMPLEMENTED_Pattern: + pass + + + match a, *b(), c: - case d, *f, g: -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass -- --match something: + + match something: - case { - "key": key as key_1, - "password": PASS.ONE | PASS.TWO | PASS.THREE as password, - }: -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case {"maybe": something(complicated as this) as that}: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass --match something: + match something: - case 1 as a: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch - ++ case NOT_YET_IMPLEMENTED_Pattern: + pass +- - case 2 as b, 3 as c: -- pass - ++ case NOT_YET_IMPLEMENTED_Pattern: + pass +- - case 4 as d, (5 as e), (6 | 7 as g), *h: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass --match bar1: + match bar1: - case Foo(aa=Callable() as aa, bb=int()): -- print(bar1.aa, bar1.bb) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(bar1.aa, bar1.bb) - case _: -- print("no match", "\n") -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + print("no match", "\n") --match bar1: + match bar1: - case Foo( - normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u - ): -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass ``` ## Ruff Output @@ -271,47 +287,112 @@ match bar1: ```py import match -NOT_YET_IMPLEMENTED_StmtMatch +match something: + case NOT_YET_IMPLEMENTED_Pattern: + print(b) + case NOT_YET_IMPLEMENTED_Pattern: + print(f) + case NOT_YET_IMPLEMENTED_Pattern: + print(b) + case NOT_YET_IMPLEMENTED_Pattern: + print(x, y) match = 1 case: int = re.match(something) -NOT_YET_IMPLEMENTED_StmtMatch +match re.match(case): + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass def func(match: case, case: match) -> case: - NOT_YET_IMPLEMENTED_StmtMatch + match Something(): + case NOT_YET_IMPLEMENTED_Pattern: + ... + case NOT_YET_IMPLEMENTED_Pattern: + ... -NOT_YET_IMPLEMENTED_StmtMatch +match maybe, multiple: + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match ( + more := (than, one), + indeed, +): + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match a, *b, c: + case NOT_YET_IMPLEMENTED_Pattern: + assert "seq" == _ + case NOT_YET_IMPLEMENTED_Pattern: + assert "map" == b -NOT_YET_IMPLEMENTED_StmtMatch +match match( + case, + match( + match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match + ), + case, +): + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match match: + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match a, *b(), c: + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match something: + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match something: + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + pass -NOT_YET_IMPLEMENTED_StmtMatch +match bar1: + case NOT_YET_IMPLEMENTED_Pattern: + print(bar1.aa, bar1.bb) + case NOT_YET_IMPLEMENTED_Pattern: + print("no match", "\n") -NOT_YET_IMPLEMENTED_StmtMatch +match bar1: + case NOT_YET_IMPLEMENTED_Pattern: + pass ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap index 6049e7897b..64445949cb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_generic.py.snap @@ -119,49 +119,44 @@ with match() as match: ```diff --- Black +++ Ruff -@@ -23,11 +23,7 @@ - pygram.python_grammar, +@@ -24,9 +24,9 @@ ] -- match match: + match match: - case case: -- match match: ++ case NOT_YET_IMPLEMENTED_Pattern: + match match: - case case: -- pass -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass if all(version.is_python2() for version in target_versions): - # Python 2-only code, so try Python 2 grammars. -@@ -45,9 +41,7 @@ - +@@ -46,7 +46,7 @@ def test_patma_139(self): x = False -- match x: + match x: - case bool(z): -- y = 0 -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 self.assertIs(x, False) self.assertEqual(y, 0) - self.assertIs(z, x) -@@ -72,16 +66,12 @@ - def test_patma_155(self): +@@ -73,14 +73,14 @@ x = 0 y = None -- match x: + match x: - case 1e1000: -- y = 0 -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + y = 0 self.assertEqual(x, 0) self.assertIs(y, None) x = range(3) -- match x: + match x: - case [y, case as x, z]: -- w = 0 -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + w = 0 # At least one of the above branches must have been taken, because every Python - # version has exactly one of the two 'ASYNC_*' flags ``` ## Ruff Output @@ -192,7 +187,11 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: pygram.python_grammar, ] - NOT_YET_IMPLEMENTED_StmtMatch + match match: + case NOT_YET_IMPLEMENTED_Pattern: + match match: + case NOT_YET_IMPLEMENTED_Pattern: + pass if all(version.is_python2() for version in target_versions): # Python 2-only code, so try Python 2 grammars. @@ -210,7 +209,9 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: def test_patma_139(self): x = False - NOT_YET_IMPLEMENTED_StmtMatch + match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 self.assertIs(x, False) self.assertEqual(y, 0) self.assertIs(z, x) @@ -235,12 +236,16 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]: def test_patma_155(self): x = 0 y = None - NOT_YET_IMPLEMENTED_StmtMatch + match x: + case NOT_YET_IMPLEMENTED_Pattern: + y = 0 self.assertEqual(x, 0) self.assertIs(y, None) x = range(3) - NOT_YET_IMPLEMENTED_StmtMatch + match x: + case NOT_YET_IMPLEMENTED_Pattern: + w = 0 # At least one of the above branches must have been taken, because every Python # version has exactly one of the two 'ASYNC_*' flags diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap index a165da5f84..c5fea0c542 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_simple.py.snap @@ -104,111 +104,129 @@ def where_is(point): ```diff --- Black +++ Ruff -@@ -1,92 +1,27 @@ +@@ -1,92 +1,92 @@ # Cases sampled from PEP 636 examples --match command.split(): + match command.split(): - case [action, obj]: -- ... # interpret action, obj -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret action, obj --match command.split(): + match command.split(): - case [action]: -- ... # interpret single-verb action ++ case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret single-verb action - case [action, obj]: -- ... # interpret action, obj -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret action, obj --match command.split(): + match command.split(): - case ["quit"]: -- print("Goodbye!") -- quit_game() ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Goodbye!") + quit_game() - case ["look"]: -- current_room.describe() ++ case NOT_YET_IMPLEMENTED_Pattern: + current_room.describe() - case ["get", obj]: -- character.get(obj, current_room) ++ case NOT_YET_IMPLEMENTED_Pattern: + character.get(obj, current_room) - case ["go", direction]: -- current_room = current_room.neighbor(direction) -- # The rest of your commands go here -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(direction) + # The rest of your commands go here --match command.split(): + match command.split(): - case ["drop", *objects]: -- for obj in objects: -- character.drop(obj, current_room) -- # The rest of your commands go here -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + for obj in objects: + character.drop(obj, current_room) + # The rest of your commands go here --match command.split(): + match command.split(): - case ["quit"]: -- pass ++ case NOT_YET_IMPLEMENTED_Pattern: + pass - case ["go", direction]: -- print("Going:", direction) ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Going:", direction) - case ["drop", *objects]: -- print("Dropping: ", *objects) ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Dropping: ", *objects) - case _: -- print(f"Sorry, I couldn't understand {command!r}") -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + print(f"Sorry, I couldn't understand {command!r}") --match command.split(): + match command.split(): - case ["north"] | ["go", "north"]: -- current_room = current_room.neighbor("north") ++ case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor("north") - case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: -- ... # Code for picking up the given object -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + ... # Code for picking up the given object --match command.split(): + match command.split(): - case ["go", ("north" | "south" | "east" | "west")]: -- current_room = current_room.neighbor(...) -- # how do I know which direction to go? -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(...) + # how do I know which direction to go? --match command.split(): + match command.split(): - case ["go", ("north" | "south" | "east" | "west") as direction]: -- current_room = current_room.neighbor(direction) -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(direction) --match command.split(): + match command.split(): - case ["go", direction] if direction in current_room.exits: -- current_room = current_room.neighbor(direction) ++ case NOT_YET_IMPLEMENTED_Pattern if direction in current_room.exits: + current_room = current_room.neighbor(direction) - case ["go", _]: -- print("Sorry, you can't go that way") -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Sorry, you can't go that way") --match event.get(): + match event.get(): - case Click(position=(x, y)): -- handle_click_at(x, y) ++ case NOT_YET_IMPLEMENTED_Pattern: + handle_click_at(x, y) - case KeyPress(key_name="Q") | Quit(): -- game.quit() ++ case NOT_YET_IMPLEMENTED_Pattern: + game.quit() - case KeyPress(key_name="up arrow"): -- game.go_north() ++ case NOT_YET_IMPLEMENTED_Pattern: + game.go_north() - case KeyPress(): -- pass # Ignore other keystrokes ++ case NOT_YET_IMPLEMENTED_Pattern: + pass # Ignore other keystrokes - case other_event: -- raise ValueError(f"Unrecognized event: {other_event}") -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + raise ValueError(f"Unrecognized event: {other_event}") --match event.get(): + match event.get(): - case Click((x, y), button=Button.LEFT): # This is a left click -- handle_click_at(x, y) ++ case NOT_YET_IMPLEMENTED_Pattern: + handle_click_at(x, y) - case Click(): -- pass # ignore other clicks -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass # ignore other clicks def where_is(point): -- match point: + match point: - case Point(x=0, y=0): -- print("Origin") ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Origin") - case Point(x=0, y=y): -- print(f"Y={y}") ++ case NOT_YET_IMPLEMENTED_Pattern: + print(f"Y={y}") - case Point(x=x, y=0): -- print(f"X={x}") ++ case NOT_YET_IMPLEMENTED_Pattern: + print(f"X={x}") - case Point(): -- print("Somewhere else") ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Somewhere else") - case _: -- print("Not a point") -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + print("Not a point") ``` ## Ruff Output @@ -216,31 +234,96 @@ def where_is(point): ```py # Cases sampled from PEP 636 examples -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret action, obj -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret single-verb action + case NOT_YET_IMPLEMENTED_Pattern: + ... # interpret action, obj -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + print("Goodbye!") + quit_game() + case NOT_YET_IMPLEMENTED_Pattern: + current_room.describe() + case NOT_YET_IMPLEMENTED_Pattern: + character.get(obj, current_room) + case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(direction) + # The rest of your commands go here -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + for obj in objects: + character.drop(obj, current_room) + # The rest of your commands go here -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + pass + case NOT_YET_IMPLEMENTED_Pattern: + print("Going:", direction) + case NOT_YET_IMPLEMENTED_Pattern: + print("Dropping: ", *objects) + case NOT_YET_IMPLEMENTED_Pattern: + print(f"Sorry, I couldn't understand {command!r}") -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor("north") + case NOT_YET_IMPLEMENTED_Pattern: + ... # Code for picking up the given object -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(...) + # how do I know which direction to go? -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern: + current_room = current_room.neighbor(direction) -NOT_YET_IMPLEMENTED_StmtMatch +match command.split(): + case NOT_YET_IMPLEMENTED_Pattern if direction in current_room.exits: + current_room = current_room.neighbor(direction) + case NOT_YET_IMPLEMENTED_Pattern: + print("Sorry, you can't go that way") -NOT_YET_IMPLEMENTED_StmtMatch +match event.get(): + case NOT_YET_IMPLEMENTED_Pattern: + handle_click_at(x, y) + case NOT_YET_IMPLEMENTED_Pattern: + game.quit() + case NOT_YET_IMPLEMENTED_Pattern: + game.go_north() + case NOT_YET_IMPLEMENTED_Pattern: + pass # Ignore other keystrokes + case NOT_YET_IMPLEMENTED_Pattern: + raise ValueError(f"Unrecognized event: {other_event}") -NOT_YET_IMPLEMENTED_StmtMatch +match event.get(): + case NOT_YET_IMPLEMENTED_Pattern: + handle_click_at(x, y) + case NOT_YET_IMPLEMENTED_Pattern: + pass # ignore other clicks def where_is(point): - NOT_YET_IMPLEMENTED_StmtMatch + match point: + case NOT_YET_IMPLEMENTED_Pattern: + print("Origin") + case NOT_YET_IMPLEMENTED_Pattern: + print(f"Y={y}") + case NOT_YET_IMPLEMENTED_Pattern: + print(f"X={x}") + case NOT_YET_IMPLEMENTED_Pattern: + print("Somewhere else") + case NOT_YET_IMPLEMENTED_Pattern: + print("Not a point") ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap index 56fe93fb72..6c268b7a02 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap @@ -65,22 +65,25 @@ match match( ```diff --- Black +++ Ruff -@@ -1,35 +1,24 @@ --match something: +@@ -1,35 +1,34 @@ + match something: - case b(): -- print(1 + 1) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(1 + 1) - case c( - very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1 - ): -- print(1) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(1) - case c( - very_complex=True, - perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1, - ): -- print(2) ++ case NOT_YET_IMPLEMENTED_Pattern: + print(2) - case a: -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass -match(arg) # comment +match( @@ -106,18 +109,26 @@ match match( + something # fast +) re.match() --match match(): + match match(): - case case( - arg, # comment - ): -- pass -+NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + pass ``` ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtMatch +match something: + case NOT_YET_IMPLEMENTED_Pattern: + print(1 + 1) + case NOT_YET_IMPLEMENTED_Pattern: + print(1) + case NOT_YET_IMPLEMENTED_Pattern: + print(2) + case NOT_YET_IMPLEMENTED_Pattern: + pass match( arg # comment @@ -140,7 +151,9 @@ re.match( something # fast ) re.match() -NOT_YET_IMPLEMENTED_StmtMatch +match match(): + case NOT_YET_IMPLEMENTED_Pattern: + pass ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__remove_newline_after_match.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__remove_newline_after_match.py.snap index 6099825e88..2f1f0ea0b9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__remove_newline_after_match.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__remove_newline_after_match.py.snap @@ -31,28 +31,39 @@ def http_status(status): ```diff --- Black +++ Ruff -@@ -1,13 +1,2 @@ +@@ -1,13 +1,10 @@ def http_status(status): -- match status: + match status: - case 400: -- return "Bad request" ++ case NOT_YET_IMPLEMENTED_Pattern: + return "Bad request" - - case 401: -- return "Unauthorized" ++ case NOT_YET_IMPLEMENTED_Pattern: + return "Unauthorized" - - case 403: -- return "Forbidden" ++ case NOT_YET_IMPLEMENTED_Pattern: + return "Forbidden" - - case 404: -- return "Not found" -+ NOT_YET_IMPLEMENTED_StmtMatch ++ case NOT_YET_IMPLEMENTED_Pattern: + return "Not found" ``` ## Ruff Output ```py def http_status(status): - NOT_YET_IMPLEMENTED_StmtMatch + match status: + case NOT_YET_IMPLEMENTED_Pattern: + return "Bad request" + case NOT_YET_IMPLEMENTED_Pattern: + return "Unauthorized" + case NOT_YET_IMPLEMENTED_Pattern: + return "Forbidden" + case NOT_YET_IMPLEMENTED_Pattern: + return "Not found" ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap new file mode 100644 index 0000000000..42bfbfbedd --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap @@ -0,0 +1,126 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py +--- +## Input +```py +# leading match comment +match foo: # dangling match comment + case "bar": + pass + + +# leading match comment +match ( # leading expr comment + # another leading expr comment + foo # trailing expr comment + # another trailing expr comment +): # dangling match comment + case "bar": + pass + + +# leading match comment +match ( # hello + foo # trailing expr comment + , # another +): # dangling match comment + case "bar": + pass + + +match [ # comment + first, + second, + third +]: # another comment + case ["a", "b", "c"]: + pass + +match ( # comment + "a b c" +).split(): # another comment + case ["a", "b", "c"]: + pass + + +match ( # comment + # let's go + yield foo +): # another comment + case ["a", "b", "c"]: + pass + + +match aaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: # comment + case "sshhhhhhhh": + pass + + +def foo(): + match inside_func: # comment + case "bar": + pass +``` + +## Output +```py +# leading match comment +match foo: # dangling match comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +# leading match comment +match ( + # leading expr comment + # another leading expr comment + foo # trailing expr comment + # another trailing expr comment +): # dangling match comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +# leading match comment +match ( # hello + foo, # trailing expr comment # another +): # dangling match comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +match [first, second, third]: # comment # another comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + +match ( + # comment + "a b c" +).split(): # another comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +match ( + # comment + # let's go + yield foo +): # another comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +match aaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh: # comment + case NOT_YET_IMPLEMENTED_Pattern: + pass + + +def foo(): + match inside_func: # comment + case NOT_YET_IMPLEMENTED_Pattern: + pass +``` + + +