From 987111f5fbc9bd7b1243a1bed7002248bfaded7d Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 11 Jul 2023 08:08:08 +0200 Subject: [PATCH] Format `ExpressionStarred` nodes (#5654) --- .../test/fixtures/ruff/expression/starred.py | 15 ++ .../ruff_python_formatter/src/comments/mod.rs | 14 +- .../src/comments/placement.rs | 16 ++ .../src/comments/visitor.rs | 7 + .../src/expression/expr_starred.rs | 30 ++- ...tibility@miscellaneous__decorators.py.snap | 36 +-- ...ibility@py_310__starred_for_target.py.snap | 25 +- ...lack_compatibility@py_311__pep_654.py.snap | 240 ------------------ ...ompatibility@py_311__pep_654_style.py.snap | 197 -------------- ...black_compatibility@py_38__pep_572.py.snap | 10 +- ...lack_compatibility@py_38__python38.py.snap | 10 +- ...patibility@simple_cases__comments6.py.snap | 11 +- ...atibility@simple_cases__expression.py.snap | 95 ++----- ...mpatibility@simple_cases__fmtonoff.py.snap | 4 +- .../format@expression__starred.py.snap | 61 +++++ .../snapshots/format@statement__with.py.snap | 2 +- 16 files changed, 192 insertions(+), 581 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/starred.py delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654.py.snap delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654_style.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/format@expression__starred.py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/starred.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/starred.py new file mode 100644 index 0000000000..76483d7fa1 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/starred.py @@ -0,0 +1,15 @@ +call( + # Leading starred comment + * # Trailing star comment + [ + # Leading value commnt + [What, i, this, s, very, long, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + ] # trailing value comment +) + +call( + # Leading starred comment + * ( # Leading value commnt + [What, i, this, s, very, long, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + ) # trailing value comment +) diff --git a/crates/ruff_python_formatter/src/comments/mod.rs b/crates/ruff_python_formatter/src/comments/mod.rs index 63c148fff8..71912c60fd 100644 --- a/crates/ruff_python_formatter/src/comments/mod.rs +++ b/crates/ruff_python_formatter/src/comments/mod.rs @@ -87,11 +87,12 @@ //! //! It is possible to add an additional optional label to [`SourceComment`] If ever the need arises to distinguish two *dangling comments* in the formatting logic, +use ruff_text_size::TextRange; use std::cell::Cell; use std::fmt::Debug; use std::rc::Rc; -use rustpython_parser::ast::Mod; +use rustpython_parser::ast::{Mod, Ranged}; pub(crate) use format::{ dangling_comments, dangling_node_comments, leading_alternate_branch_comments, leading_comments, @@ -114,7 +115,7 @@ mod placement; mod visitor; /// A comment in the source document. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct SourceComment { /// The location of the comment in the source document. slice: SourceCodeSlice, @@ -155,15 +156,20 @@ impl SourceComment { pub(crate) fn is_unformatted(&self) -> bool { !self.is_formatted() } -} -impl SourceComment { /// Returns a nice debug representation that prints the source code for every comment (and not just the range). pub(crate) fn debug<'a>(&'a self, source_code: SourceCode<'a>) -> DebugComment<'a> { DebugComment::new(self, source_code) } } +impl Ranged for SourceComment { + #[inline] + fn range(&self) -> TextRange { + self.slice.range() + } +} + /// The position of a comment in the source text. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub(crate) enum CommentLinePosition { diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index 85b6534f49..bc8df853de 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -38,6 +38,7 @@ pub(super) fn place_comment<'a>( handle_slice_comments, handle_attribute_comment, handle_expr_if_comment, + handle_trailing_expression_starred_star_end_of_line_comment, ]; for handler in HANDLERS { comment = match handler(comment, locator) { @@ -1215,6 +1216,21 @@ fn handle_expr_if_comment<'a>( CommentPlacement::Default(comment) } +fn handle_trailing_expression_starred_star_end_of_line_comment<'a>( + comment: DecoratedComment<'a>, + _locator: &Locator, +) -> CommentPlacement<'a> { + if comment.line_position().is_own_line() || comment.following_node().is_none() { + return CommentPlacement::Default(comment); + } + + let AnyNodeRef::ExprStarred(starred) = comment.enclosing_node() else { + return CommentPlacement::Default(comment); + }; + + CommentPlacement::leading(starred.as_any_node_ref(), comment) +} + /// Looks for a token in the range that contains no other tokens except for parentheses outside /// the expression ranges fn find_only_token_in_range(range: TextRange, locator: &Locator, token_kind: TokenKind) -> Token { diff --git a/crates/ruff_python_formatter/src/comments/visitor.rs b/crates/ruff_python_formatter/src/comments/visitor.rs index 143d381321..947dc4b31b 100644 --- a/crates/ruff_python_formatter/src/comments/visitor.rs +++ b/crates/ruff_python_formatter/src/comments/visitor.rs @@ -454,6 +454,13 @@ impl<'a> DecoratedComment<'a> { } } +impl Ranged for DecoratedComment<'_> { + #[inline] + fn range(&self) -> TextRange { + self.slice.range() + } +} + impl From> for SourceComment { fn from(decorated: DecoratedComment) -> Self { Self::new(decorated.slice, decorated.line_position) diff --git a/crates/ruff_python_formatter/src/expression/expr_starred.rs b/crates/ruff_python_formatter/src/expression/expr_starred.rs index 3711deb92e..bb8492e0da 100644 --- a/crates/ruff_python_formatter/src/expression/expr_starred.rs +++ b/crates/ruff_python_formatter/src/expression/expr_starred.rs @@ -1,22 +1,32 @@ +use rustpython_parser::ast::ExprStarred; + +use ruff_formatter::write; + use crate::comments::Comments; use crate::expression::parentheses::{ default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize, }; -use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter}; -use ruff_formatter::{write, Buffer, FormatResult}; -use rustpython_parser::ast::ExprStarred; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatExprStarred; impl FormatNodeRule for FormatExprStarred { - fn fmt_fields(&self, _item: &ExprStarred, f: &mut PyFormatter) -> FormatResult<()> { - write!( - f, - [not_yet_implemented_custom_text( - "*NOT_YET_IMPLEMENTED_ExprStarred" - )] - ) + fn fmt_fields(&self, item: &ExprStarred, f: &mut PyFormatter) -> FormatResult<()> { + let ExprStarred { + range: _, + value, + ctx: _, + } = item; + + write!(f, [text("*"), value.format()]) + } + + fn fmt_dangling_comments(&self, node: &ExprStarred, f: &mut PyFormatter) -> FormatResult<()> { + debug_assert_eq!(f.context().comments().dangling_comments(node), []); + + Ok(()) } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap index 020bb3c340..7793a98149 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap @@ -200,7 +200,7 @@ def f(): + +## + -+@decorator(*NOT_YET_IMPLEMENTED_ExprStarred) ++@decorator(*args) +def f(): + ... + @@ -216,7 +216,7 @@ def f(): ## -@(decorator) -+@decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) ++@decorator(*args, **kwargs) def f(): ... @@ -225,7 +225,7 @@ def f(): -@sequence["decorator"] +@decorator( -+ *NOT_YET_IMPLEMENTED_ExprStarred, ++ *args, + **kwargs, +) def f(): @@ -257,7 +257,7 @@ def f(): + +## + -+@dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred) ++@dotted.decorator(*args) +def f(): + ... + @@ -271,7 +271,7 @@ def f(): + +## + -+@dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) ++@dotted.decorator(*args, **kwargs) +def f(): + ... + @@ -279,7 +279,7 @@ def f(): +## + +@dotted.decorator( -+ *NOT_YET_IMPLEMENTED_ExprStarred, ++ *args, + **kwargs, +) +def f(): @@ -309,7 +309,7 @@ def f(): + +## + -+@double.dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred) ++@double.dotted.decorator(*args) +def f(): + ... + @@ -323,7 +323,7 @@ def f(): + +## + -+@double.dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) ++@double.dotted.decorator(*args, **kwargs) +def f(): + ... + @@ -331,7 +331,7 @@ def f(): +## + +@double.dotted.decorator( -+ *NOT_YET_IMPLEMENTED_ExprStarred, ++ *args, + **kwargs, +) +def f(): @@ -392,7 +392,7 @@ def f(): ## -@decorator(*NOT_YET_IMPLEMENTED_ExprStarred) +@decorator(*args) def f(): ... @@ -406,7 +406,7 @@ def f(): ## -@decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) +@decorator(*args, **kwargs) def f(): ... @@ -414,7 +414,7 @@ def f(): ## @decorator( - *NOT_YET_IMPLEMENTED_ExprStarred, + *args, **kwargs, ) def f(): @@ -444,7 +444,7 @@ def f(): ## -@dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred) +@dotted.decorator(*args) def f(): ... @@ -458,7 +458,7 @@ def f(): ## -@dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) +@dotted.decorator(*args, **kwargs) def f(): ... @@ -466,7 +466,7 @@ def f(): ## @dotted.decorator( - *NOT_YET_IMPLEMENTED_ExprStarred, + *args, **kwargs, ) def f(): @@ -496,7 +496,7 @@ def f(): ## -@double.dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred) +@double.dotted.decorator(*args) def f(): ... @@ -510,7 +510,7 @@ def f(): ## -@double.dotted.decorator(*NOT_YET_IMPLEMENTED_ExprStarred, **kwargs) +@double.dotted.decorator(*args, **kwargs) def f(): ... @@ -518,7 +518,7 @@ def f(): ## @double.dotted.decorator( - *NOT_YET_IMPLEMENTED_ExprStarred, + *args, **kwargs, ) def f(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__starred_for_target.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__starred_for_target.py.snap index da4a8b4b11..dad8c213dc 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__starred_for_target.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__starred_for_target.py.snap @@ -39,21 +39,8 @@ async for x in ( ```diff --- Black +++ Ruff -@@ -1,27 +1,19 @@ --for x in *a, *b: -+for x in *NOT_YET_IMPLEMENTED_ExprStarred, *NOT_YET_IMPLEMENTED_ExprStarred: - print(x) - --for x in a, b, *c: -+for x in a, b, *NOT_YET_IMPLEMENTED_ExprStarred: - print(x) - --for x in *a, b, c: -+for x in *NOT_YET_IMPLEMENTED_ExprStarred, b, c: - print(x) - --for x in *a, b, *c: -+for x in *NOT_YET_IMPLEMENTED_ExprStarred, b, *NOT_YET_IMPLEMENTED_ExprStarred: +@@ -10,18 +10,10 @@ + for x in *a, b, *c: print(x) -async for x in *a, *b: @@ -80,16 +67,16 @@ async for x in ( ## Ruff Output ```py -for x in *NOT_YET_IMPLEMENTED_ExprStarred, *NOT_YET_IMPLEMENTED_ExprStarred: +for x in *a, *b: print(x) -for x in a, b, *NOT_YET_IMPLEMENTED_ExprStarred: +for x in a, b, *c: print(x) -for x in *NOT_YET_IMPLEMENTED_ExprStarred, b, c: +for x in *a, b, c: print(x) -for x in *NOT_YET_IMPLEMENTED_ExprStarred, b, *NOT_YET_IMPLEMENTED_ExprStarred: +for x in *a, b, *c: print(x) NOT_YET_IMPLEMENTED_StmtAsyncFor diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654.py.snap deleted file mode 100644 index b7995833b5..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654.py.snap +++ /dev/null @@ -1,240 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654.py ---- -## Input - -```py -try: - raise OSError("blah") -except* ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except* ValueError: - pass - -try: - try: - raise ValueError(42) - except: - try: - raise TypeError(int) - except* Exception: - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - raise FalsyEG("eg", [TypeError(1), ValueError(2)]) - except* TypeError as e: - tes = e - raise - except* ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - raise orig - except* (TypeError, ValueError) as e: - raise SyntaxError(3) from e -except BaseException as e: - exc = e - -try: - try: - raise orig - except* OSError as e: - raise TypeError(3) from e -except ExceptionGroup as e: - exc = e -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,5 +1,5 @@ - try: -- raise OSError("blah") -+ NOT_YET_IMPLEMENTED_StmtRaise - except* ExceptionGroup as e: - pass - -@@ -14,10 +14,10 @@ - - try: - try: -- raise ValueError(42) -+ NOT_YET_IMPLEMENTED_StmtRaise - except: - try: -- raise TypeError(int) -+ NOT_YET_IMPLEMENTED_StmtRaise - except* Exception: - pass - 1 / 0 -@@ -26,10 +26,10 @@ - - try: - try: -- raise FalsyEG("eg", [TypeError(1), ValueError(2)]) -+ NOT_YET_IMPLEMENTED_StmtRaise - except* TypeError as e: - tes = e -- raise -+ NOT_YET_IMPLEMENTED_StmtRaise - except* ValueError as e: - ves = e - pass -@@ -38,16 +38,16 @@ - - try: - try: -- raise orig -+ NOT_YET_IMPLEMENTED_StmtRaise - except* (TypeError, ValueError) as e: -- raise SyntaxError(3) from e -+ NOT_YET_IMPLEMENTED_StmtRaise - except BaseException as e: - exc = e - - try: - try: -- raise orig -+ NOT_YET_IMPLEMENTED_StmtRaise - except* OSError as e: -- raise TypeError(3) from e -+ NOT_YET_IMPLEMENTED_StmtRaise - except ExceptionGroup as e: - exc = e -``` - -## Ruff Output - -```py -try: - NOT_YET_IMPLEMENTED_StmtRaise -except* ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except* ValueError: - pass - -try: - try: - NOT_YET_IMPLEMENTED_StmtRaise - except: - try: - NOT_YET_IMPLEMENTED_StmtRaise - except* Exception: - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - NOT_YET_IMPLEMENTED_StmtRaise - except* TypeError as e: - tes = e - NOT_YET_IMPLEMENTED_StmtRaise - except* ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - NOT_YET_IMPLEMENTED_StmtRaise - except* (TypeError, ValueError) as e: - NOT_YET_IMPLEMENTED_StmtRaise -except BaseException as e: - exc = e - -try: - try: - NOT_YET_IMPLEMENTED_StmtRaise - except* OSError as e: - NOT_YET_IMPLEMENTED_StmtRaise -except ExceptionGroup as e: - exc = e -``` - -## Black Output - -```py -try: - raise OSError("blah") -except* ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except* ValueError: - pass - -try: - try: - raise ValueError(42) - except: - try: - raise TypeError(int) - except* Exception: - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - raise FalsyEG("eg", [TypeError(1), ValueError(2)]) - except* TypeError as e: - tes = e - raise - except* ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - raise orig - except* (TypeError, ValueError) as e: - raise SyntaxError(3) from e -except BaseException as e: - exc = e - -try: - try: - raise orig - except* OSError as e: - raise TypeError(3) from e -except ExceptionGroup as e: - exc = e -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654_style.py.snap deleted file mode 100644 index b0bc8e8f17..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_311__pep_654_style.py.snap +++ /dev/null @@ -1,197 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654_style.py ---- -## Input - -```py -try: - raise OSError("blah") -except * ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except *ValueError: - pass - -try: - try: - raise ValueError(42) - except: - try: - raise TypeError(int) - except *(Exception): - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - raise FalsyEG("eg", [TypeError(1), ValueError(2)]) - except \ - *TypeError as e: - tes = e - raise - except * ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - raise orig - except *(TypeError, ValueError, *OTHER_EXCEPTIONS) as e: - raise SyntaxError(3) from e -except BaseException as e: - exc = e - -try: - try: - raise orig - except\ - * OSError as e: - raise TypeError(3) from e -except ExceptionGroup as e: - exc = e -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -39,7 +39,7 @@ - try: - try: - raise orig -- except* (TypeError, ValueError, *OTHER_EXCEPTIONS) as e: -+ except* (TypeError, ValueError, *NOT_YET_IMPLEMENTED_ExprStarred) as e: - raise SyntaxError(3) from e - except BaseException as e: - exc = e -``` - -## Ruff Output - -```py -try: - raise OSError("blah") -except* ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except* ValueError: - pass - -try: - try: - raise ValueError(42) - except: - try: - raise TypeError(int) - except* Exception: - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - raise FalsyEG("eg", [TypeError(1), ValueError(2)]) - except* TypeError as e: - tes = e - raise - except* ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - raise orig - except* (TypeError, ValueError, *NOT_YET_IMPLEMENTED_ExprStarred) as e: - raise SyntaxError(3) from e -except BaseException as e: - exc = e - -try: - try: - raise orig - except* OSError as e: - raise TypeError(3) from e -except ExceptionGroup as e: - exc = e -``` - -## Black Output - -```py -try: - raise OSError("blah") -except* ExceptionGroup as e: - pass - - -try: - async with trio.open_nursery() as nursery: - # Make two concurrent calls to child() - nursery.start_soon(child) - nursery.start_soon(child) -except* ValueError: - pass - -try: - try: - raise ValueError(42) - except: - try: - raise TypeError(int) - except* Exception: - pass - 1 / 0 -except Exception as e: - exc = e - -try: - try: - raise FalsyEG("eg", [TypeError(1), ValueError(2)]) - except* TypeError as e: - tes = e - raise - except* ValueError as e: - ves = e - pass -except Exception as e: - exc = e - -try: - try: - raise orig - except* (TypeError, ValueError, *OTHER_EXCEPTIONS) as e: - raise SyntaxError(3) from e -except BaseException as e: - exc = e - -try: - try: - raise orig - except* OSError as e: - raise TypeError(3) from e -except ExceptionGroup as e: - exc = e -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap index 53e4192d06..f64c729232 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572.py.snap @@ -72,7 +72,7 @@ while x := f(x): (y := f(x)) y0 = (y1 := f(x)) foo(x=(y := f(x))) -@@ -19,29 +19,29 @@ +@@ -19,10 +19,10 @@ pass @@ -86,10 +86,8 @@ while x := f(x): +lambda x: True x = (y := 0) (z := (y := (x := 0))) --(info := (name, phone, *rest)) -+(info := (name, phone, *NOT_YET_IMPLEMENTED_ExprStarred)) - (x := 1, 2) - (total := total + tax) + (info := (name, phone, *rest)) +@@ -31,17 +31,17 @@ len(lines := f.readlines()) foo(x := 3, cat="vector") foo(cat=(category := "vector")) @@ -144,7 +142,7 @@ lambda x: True lambda x: True x = (y := 0) (z := (y := (x := 0))) -(info := (name, phone, *NOT_YET_IMPLEMENTED_ExprStarred)) +(info := (name, phone, *rest)) (x := 1, 2) (total := total + tax) len(lines := f.readlines()) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__python38.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__python38.py.snap index 4f4ceeb54b..d706374274 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__python38.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__python38.py.snap @@ -31,13 +31,7 @@ def t(): ```diff --- Black +++ Ruff -@@ -3,19 +3,19 @@ - - def starred_return(): - my_list = ["value2", "value3"] -- return "value1", *my_list -+ return "value1", *NOT_YET_IMPLEMENTED_ExprStarred - +@@ -8,14 +8,14 @@ def starred_yield(): my_list = ["value2", "value3"] @@ -66,7 +60,7 @@ def t(): def starred_return(): my_list = ["value2", "value3"] - return "value1", *NOT_YET_IMPLEMENTED_ExprStarred + return "value1", *my_list def starred_yield(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap index a27f99c5bf..6be2a77bc1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap @@ -141,7 +141,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite an_element_with_a_long_value = calls() or more_calls() and more() # type: bool tup = ( -@@ -100,19 +98,32 @@ +@@ -100,19 +98,30 @@ ) c = call( @@ -177,10 +177,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite + ], # type: ignore ) --aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] -+aaaaaaaaaaaaa, bbbbbbbbb = map( -+ list, map(itertools.chain.from_iterable, zip(*NOT_YET_IMPLEMENTED_ExprStarred)) -+) # type: ignore[arg-type] + aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] ``` ## Ruff Output @@ -312,9 +309,7 @@ call_to_some_function_asdf( ], # type: ignore ) -aaaaaaaaaaaaa, bbbbbbbbb = map( - list, map(itertools.chain.from_iterable, zip(*NOT_YET_IMPLEMENTED_ExprStarred)) -) # type: ignore[arg-type] +aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type] ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap index b743d9c808..b8aff61aa7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap @@ -316,32 +316,9 @@ last_call() () (1,) (1, 2) -@@ -69,40 +72,37 @@ - 2, - 3, - ] --[*a] --[*range(10)] -+[*NOT_YET_IMPLEMENTED_ExprStarred] -+[*NOT_YET_IMPLEMENTED_ExprStarred] - [ -- *a, -+ *NOT_YET_IMPLEMENTED_ExprStarred, - 4, - 5, - ] - [ - 4, -- *a, -+ *NOT_YET_IMPLEMENTED_ExprStarred, - 5, - ] - [ - this_is_a_very_long_variable_which_will_force_a_delimiter_split, - element, +@@ -87,22 +90,19 @@ another, -- *more, -+ *NOT_YET_IMPLEMENTED_ExprStarred, + *more, ] -{i for i in (1, 2, 3)} -{(i**2) for i in (1, 2, 3)} @@ -375,20 +352,15 @@ last_call() Python3 > Python2 > COBOL Life is Life call() -@@ -115,10 +115,10 @@ +@@ -115,7 +115,7 @@ arg, another, kwarg="hey", - **kwargs + **kwargs, ) # note: no trailing comma pre-3.6 --call(*gidgets[:2]) --call(a, *gidgets[:2]) -+call(*NOT_YET_IMPLEMENTED_ExprStarred) -+call(a, *NOT_YET_IMPLEMENTED_ExprStarred) - call(**self.screen_kwargs) - call(b, **self.screen_kwargs) - lukasz.langa.pl + call(*gidgets[:2]) + call(a, *gidgets[:2]) @@ -131,34 +131,28 @@ tuple[str, ...] tuple[str, int, float, dict[str, int]] @@ -397,6 +369,9 @@ last_call() - int, - float, - dict[str, int], +-] +-very_long_variable_name_filters: t.List[ +- t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], + ( + str, + int, @@ -404,9 +379,6 @@ last_call() + dict[str, int], + ) ] --very_long_variable_name_filters: t.List[ -- t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]], --] -xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore - sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__) -) @@ -446,7 +418,7 @@ last_call() numpy[np.newaxis, :] (str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None) {"2.7": dead, "3.7": long_live or die_hard} -@@ -181,11 +175,11 @@ +@@ -181,10 +175,10 @@ (SomeName) SomeName (Good, Bad, Ugly) @@ -454,26 +426,14 @@ last_call() -((i**2) for i in (1, 2, 3)) -((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) -(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) --(*starred,) +(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) +(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) +(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) +(NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) -+(*NOT_YET_IMPLEMENTED_ExprStarred,) + (*starred,) { "id": "1", - "type": "type", -@@ -200,32 +194,22 @@ - c = 1 - d = (1,) + a + (2,) - e = (1,).count(1) --f = 1, *range(10) --g = 1, *"ten" -+f = 1, *NOT_YET_IMPLEMENTED_ExprStarred -+g = 1, *NOT_YET_IMPLEMENTED_ExprStarred - what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set( - vars_to_remove - ) +@@ -208,24 +202,14 @@ what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( vars_to_remove ) @@ -506,7 +466,7 @@ last_call() Ø = set() authors.łukasz.say_thanks() mapping = { -@@ -237,29 +221,33 @@ +@@ -237,10 +221,10 @@ def gen(): @@ -521,10 +481,10 @@ last_call() async def f(): - await some.complicated[0].call(with_args=(True or (1 is not 1))) +@@ -248,18 +232,22 @@ --print(*[] or [1]) + print(*[] or [1]) -print(**{1: 3} if False else {x: x for x in range(3)}) -print(*lambda x: x) -assert not Test, "Short message" @@ -532,13 +492,12 @@ last_call() - force=False -), "Short message" -assert parens is TooMany -+print(*NOT_YET_IMPLEMENTED_ExprStarred) +print( + **{1: 3} + if False + else {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} +) -+print(*NOT_YET_IMPLEMENTED_ExprStarred) ++print(*lambda x: True) +NOT_YET_IMPLEMENTED_StmtAssert +NOT_YET_IMPLEMENTED_StmtAssert +NOT_YET_IMPLEMENTED_StmtAssert @@ -664,23 +623,23 @@ str or None if (1 if True else 2) else str or bytes or None 2, 3, ] -[*NOT_YET_IMPLEMENTED_ExprStarred] -[*NOT_YET_IMPLEMENTED_ExprStarred] +[*a] +[*range(10)] [ - *NOT_YET_IMPLEMENTED_ExprStarred, + *a, 4, 5, ] [ 4, - *NOT_YET_IMPLEMENTED_ExprStarred, + *a, 5, ] [ this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, - *NOT_YET_IMPLEMENTED_ExprStarred, + *more, ] {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} {NOT_IMPLEMENTED_set_value for value in NOT_IMPLEMENTED_set} @@ -709,8 +668,8 @@ call( kwarg="hey", **kwargs, ) # note: no trailing comma pre-3.6 -call(*NOT_YET_IMPLEMENTED_ExprStarred) -call(a, *NOT_YET_IMPLEMENTED_ExprStarred) +call(*gidgets[:2]) +call(a, *gidgets[:2]) call(**self.screen_kwargs) call(b, **self.screen_kwargs) lukasz.langa.pl @@ -771,7 +730,7 @@ SomeName (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []) -(*NOT_YET_IMPLEMENTED_ExprStarred,) +(*starred,) { "id": "1", "type": "type", @@ -786,8 +745,8 @@ b = (1,) c = 1 d = (1,) + a + (2,) e = (1,).count(1) -f = 1, *NOT_YET_IMPLEMENTED_ExprStarred -g = 1, *NOT_YET_IMPLEMENTED_ExprStarred +f = 1, *range(10) +g = 1, *"ten" what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set( vars_to_remove ) @@ -823,13 +782,13 @@ async def f(): await some.complicated[0].call(with_args=(True or (1 is not 1))) -print(*NOT_YET_IMPLEMENTED_ExprStarred) +print(*[] or [1]) print( **{1: 3} if False else {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value for key, value in NOT_IMPLEMENTED_dict} ) -print(*NOT_YET_IMPLEMENTED_ExprStarred) +print(*lambda x: True) NOT_YET_IMPLEMENTED_StmtAssert NOT_YET_IMPLEMENTED_StmtAssert NOT_YET_IMPLEMENTED_StmtAssert diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap index 883baf2298..51aa7fb138 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap @@ -316,7 +316,7 @@ d={'a':1, # fmt: off - a , b = *hello - 'unformatted' -+ a, b = *NOT_YET_IMPLEMENTED_ExprStarred ++ a, b = *hello + "unformatted" # fmt: on @@ -507,7 +507,7 @@ def import_as_names(): def testlist_star_expr(): # fmt: off - a, b = *NOT_YET_IMPLEMENTED_ExprStarred + a, b = *hello "unformatted" # fmt: on diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__starred.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__starred.py.snap new file mode 100644 index 0000000000..57108edf12 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__starred.py.snap @@ -0,0 +1,61 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/starred.py +--- +## Input +```py +call( + # Leading starred comment + * # Trailing star comment + [ + # Leading value commnt + [What, i, this, s, very, long, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + ] # trailing value comment +) + +call( + # Leading starred comment + * ( # Leading value commnt + [What, i, this, s, very, long, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] + ) # trailing value comment +) +``` + +## Output +```py +call( + # Leading starred comment + # Trailing star comment + *[ + # Leading value commnt + [ + What, + i, + this, + s, + very, + long, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + ] + ] # trailing value comment +) + +call( + # Leading starred comment + # Leading value commnt + *( + [ + What, + i, + this, + s, + very, + long, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + ] + ) # trailing value comment +) +``` + + + diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap index 52be9741ad..9f1f6eded7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap @@ -121,7 +121,7 @@ with ( # currently unparsable by black: https://github.com/psf/black/issues/3678 with (NOT_YET_IMPLEMENTED_generator_key for NOT_YET_IMPLEMENTED_generator_key in []): pass -with (a, *NOT_YET_IMPLEMENTED_ExprStarred): +with (a, *b): pass ```