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 index 7b6db24c03..d3590f2f8c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py @@ -501,3 +501,24 @@ match pattern_match_or: # own line ): ... + + +# Single-element tuples. +match pattern: + case (a,): + pass + + case (a, b): + pass + + case (a, b,): + pass + + case a,: + pass + + case a, b: + pass + + case a, b,: + pass diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_sequence.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_sequence.rs index 7d8cea85f1..f6bb3152cd 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_sequence.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_sequence.rs @@ -1,4 +1,4 @@ -use ruff_formatter::{Format, FormatResult}; +use ruff_formatter::{format_args, Format, FormatResult}; use ruff_python_ast::node::AnyNodeRef; use ruff_python_ast::PatternMatchSequence; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; @@ -20,14 +20,25 @@ impl FormatNodeRule for FormatPatternMatchSequence { let dangling = comments.dangling(item); let sequence_type = SequenceType::from_pattern(item, f.context().source()); - if patterns.is_empty() { - return match sequence_type { - SequenceType::List => empty_parenthesized("[", dangling, "]").fmt(f), - SequenceType::Tuple | SequenceType::TupleNoParens => { - empty_parenthesized("(", dangling, ")").fmt(f) - } - }; + + match (patterns.as_slice(), sequence_type) { + // If the sequence is empty, the parentheses with any dangling comments. + ([], SequenceType::Tuple | SequenceType::TupleNoParens) => { + return empty_parenthesized("(", dangling, ")").fmt(f) + } + ([], SequenceType::List) => return empty_parenthesized("[", dangling, "]").fmt(f), + + // A single-element tuple should always be parenthesized, and the trailing comma + // should never cause it to expand. + ([elt], SequenceType::Tuple | SequenceType::TupleNoParens) => { + return parenthesized("(", &format_args![elt.format(), token(",")], ")") + .with_dangling_comments(dangling) + .fmt(f) + } + + _ => {} } + let items = format_with(|f| { f.join_comma_separated(range.end()) .nodes(patterns.iter()) 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 index 2ca2c18fa8..bc0a0bce3b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap @@ -507,6 +507,27 @@ match pattern_match_or: # own line ): ... + + +# Single-element tuples. +match pattern: + case (a,): + pass + + case (a, b): + pass + + case (a, b,): + pass + + case a,: + pass + + case a, b: + pass + + case a, b,: + pass ``` ## Output @@ -1038,6 +1059,33 @@ match pattern_match_or: # own line ): ... + + +# Single-element tuples. +match pattern: + case (a,): + pass + + case (a, b): + pass + + case ( + a, + b, + ): + pass + + case (a,): + pass + + case a, b: + pass + + case ( + a, + b, + ): + pass ```