diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/function.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/function.py index d242751143..42e643032c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/function.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/function.py @@ -371,67 +371,3 @@ def f( # first # third ): ... - -# Handle comments on empty tuple return types. -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - # comment -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - 1 -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - 1, 2 -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - (1, 2) -): ... - -def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 - self, m: Match[str], data: str -) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: - ... - -def double(a: int # Hello -) -> (int): - return 2 * a - -def double(a: int) -> ( # Hello - int -): - return 2*a - -def double(a: int) -> ( # Hello -): - return 2*a - -# Breaking over parameters and return types. (Black adds a trailing comma when the -# function arguments break here with a single argument; we do not.) -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: - ... - -def f(a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> a: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: - ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/return_annotation.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/return_annotation.py new file mode 100644 index 0000000000..f7b043d477 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/return_annotation.py @@ -0,0 +1,182 @@ +# Handle comments on empty tuple return types. +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + # comment +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + 1 +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + 1, 2 +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + (1, 2) +): ... + +def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 + self, m: Match[str], data: str +) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: + ... + +def double(a: int # Hello +) -> (int): + return 2 * a + +def double(a: int) -> ( # Hello + int +): + return 2*a + +def double(a: int) -> ( # Hello +): + return 2*a + +# Breaking over parameters and return types. (Black adds a trailing comma when the +# function arguments break here with a single argument; we do not.) +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: + ... + +def f(a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> a: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: + ... + +# Breaking return type annotations. Black adds parentheses if the parameters are +# empty; otherwise, it leverages the expressions own parentheses if possible. +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(*args) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( # foo +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + # bar +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X + Y + foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X + Y + foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X and Y and foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X and Y and foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X | Y | foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X | Y | foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + + +def double() -> first_item and foo.bar.baz().bop(1,): + return 2 * a + + +# Dangling comments on return annotations. +def double(a: int) -> ( + int # Hello +): + return 2*a + +def double(a: int) -> ( + foo.bar # Hello +): + return 2*a + +def double(a: int) -> ( + [int] # Hello +): + return 2*a + +def double(a: int) -> ( + int | list[int] # Hello +): + pass + +def double(a: int) -> ( + int | list[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int] # Hello +): + pass diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index b32f720475..d5f867f8fc 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -183,25 +183,45 @@ impl Format> for MaybeParenthesizeExpression<'_> { needs_parentheses } } - Parenthesize::Optional | Parenthesize::IfBreaks => needs_parentheses, + Parenthesize::Optional + | Parenthesize::IfBreaks + | Parenthesize::IfBreaksOrIfRequired => needs_parentheses, }; match needs_parentheses { - OptionalParentheses::Multiline if *parenthesize != Parenthesize::IfRequired => { - if can_omit_optional_parentheses(expression, f.context()) { - optional_parentheses(&expression.format().with_options(Parentheses::Never)) - .fmt(f) - } else { + OptionalParentheses::Multiline => match parenthesize { + Parenthesize::IfBreaksOrIfRequired => { parenthesize_if_expands(&expression.format().with_options(Parentheses::Never)) .fmt(f) } - } + Parenthesize::IfRequired => { + expression.format().with_options(Parentheses::Never).fmt(f) + } + Parenthesize::Optional | Parenthesize::IfBreaks => { + if can_omit_optional_parentheses(expression, f.context()) { + optional_parentheses(&expression.format().with_options(Parentheses::Never)) + .fmt(f) + } else { + parenthesize_if_expands( + &expression.format().with_options(Parentheses::Never), + ) + .fmt(f) + } + } + }, + OptionalParentheses::Never => match parenthesize { + Parenthesize::IfBreaksOrIfRequired => { + parenthesize_if_expands(&expression.format().with_options(Parentheses::Never)) + .fmt(f) + } + + Parenthesize::Optional | Parenthesize::IfBreaks | Parenthesize::IfRequired => { + expression.format().with_options(Parentheses::Never).fmt(f) + } + }, OptionalParentheses::Always => { expression.format().with_options(Parentheses::Always).fmt(f) } - OptionalParentheses::Never | OptionalParentheses::Multiline => { - expression.format().with_options(Parentheses::Never).fmt(f) - } } } } diff --git a/crates/ruff_python_formatter/src/expression/parentheses.rs b/crates/ruff_python_formatter/src/expression/parentheses.rs index 6864c80458..c9ab19225a 100644 --- a/crates/ruff_python_formatter/src/expression/parentheses.rs +++ b/crates/ruff_python_formatter/src/expression/parentheses.rs @@ -51,6 +51,11 @@ pub(crate) enum Parenthesize { /// * The expression is not enclosed by another parenthesized expression and it expands over multiple lines /// * The expression has leading or trailing comments. Adding parentheses is desired to prevent the comments from wandering. IfRequired, + + /// Parenthesizes the expression if the group doesn't fit on a line (e.g., even name expressions are parenthesized), or if + /// the expression doesn't break, but _does_ reports that it always requires parentheses in this position (e.g., walrus + /// operators in function return annotations). + IfBreaksOrIfRequired, } impl Parenthesize { @@ -193,8 +198,8 @@ pub(crate) struct FormatOptionalParentheses<'content, 'ast> { impl<'ast> Format> for FormatOptionalParentheses<'_, 'ast> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { - // The group id is used as a condition in [`in_parentheses_only`] to create a conditional group - // that is only active if the optional parentheses group expands. + // The group id is used as a condition in [`in_parentheses_only_group`] to create a + // conditional group that is only active if the optional parentheses group expands. let parens_id = f.group_id("optional_parentheses"); let mut f = WithNodeLevel::new(NodeLevel::Expression(Some(parens_id)), f); diff --git a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs index 746b69de42..fcf4a0f603 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs @@ -1,9 +1,10 @@ use ruff_formatter::write; -use ruff_python_ast::{Ranged, StmtFunctionDef}; -use ruff_python_trivia::lines_after_ignoring_trivia; +use ruff_python_ast::{Parameters, Ranged, StmtFunctionDef}; +use ruff_python_trivia::{lines_after_ignoring_trivia, SimpleTokenKind, SimpleTokenizer}; use crate::comments::{leading_comments, trailing_comments}; -use crate::expression::parentheses::{optional_parentheses, Parentheses}; +use crate::expression::maybe_parenthesize_expression; +use crate::expression::parentheses::{Parentheses, Parenthesize}; use crate::prelude::*; use crate::statement::suite::SuiteKind; use crate::FormatNodeRule; @@ -60,18 +61,71 @@ impl FormatNodeRule for FormatStmtFunctionDef { let format_inner = format_with(|f: &mut PyFormatter| { write!(f, [item.parameters.format()])?; + if let Some(return_annotation) = item.returns.as_ref() { write!(f, [space(), text("->"), space()])?; + if return_annotation.is_tuple_expr() { write!( f, [return_annotation.format().with_options(Parentheses::Never)] )?; + } else if comments.has_trailing_comments(return_annotation.as_ref()) { + // Intentionally parenthesize any return annotations with trailing comments. + // This avoids an instability in cases like: + // ```python + // def double( + // a: int + // ) -> ( + // int # Hello + // ): + // pass + // ``` + // If we allow this to break, it will be formatted as follows: + // ```python + // def double( + // a: int + // ) -> int: # Hello + // pass + // ``` + // On subsequent formats, the `# Hello` will be interpreted as a dangling + // comment on a function, yielding: + // ```python + // def double(a: int) -> int: # Hello + // pass + // ``` + // Ideally, we'd reach that final formatting in a single pass, but doing so + // requires that the parent be aware of how the child is formatted, which + // is challenging. As a compromise, we break those expressions to avoid an + // instability. + write!( + f, + [return_annotation.format().with_options(Parentheses::Always)] + )?; } else { write!( f, - [optional_parentheses( - &return_annotation.format().with_options(Parentheses::Never), + [maybe_parenthesize_expression( + return_annotation, + item, + if empty_parameters(&item.parameters, f.context().source()) { + // If the parameters are empty, add parentheses if the return annotation + // breaks at all. + Parenthesize::IfBreaksOrIfRequired + } else { + // Otherwise, use our normal rules for parentheses, which allows us to break + // like: + // ```python + // def f( + // x, + // ) -> Tuple[ + // int, + // int, + // ]: + // ... + // ``` + Parenthesize::IfBreaks + }, )] )?; } @@ -100,3 +154,25 @@ impl FormatNodeRule for FormatStmtFunctionDef { Ok(()) } } + +/// Returns `true` if [`Parameters`] is empty (no parameters, no comments, etc.). +fn empty_parameters(parameters: &Parameters, source: &str) -> bool { + let mut tokenizer = SimpleTokenizer::new(source, parameters.range()) + .filter(|token| !matches!(token.kind, SimpleTokenKind::Whitespace)); + + let Some(lpar) = tokenizer.next() else { + return false; + }; + if !matches!(lpar.kind, SimpleTokenKind::LParen) { + return false; + } + + let Some(rpar) = tokenizer.next() else { + return false; + }; + if !matches!(rpar.kind, SimpleTokenKind::RParen) { + return false; + } + + true +} diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap index 36e4656eb0..c943a9e048 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap @@ -113,17 +113,6 @@ def foo() -> tuple[int, int, int,]: return 2 * a -@@ -54,7 +58,9 @@ - a: int, - b: int, - c: int, --) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: -+) -> ( -+ intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -+): - return 2 - - ``` ## Ruff Output @@ -189,9 +178,7 @@ def foo( a: int, b: int, c: int, -) -> ( - intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds -): +) -> intsdfsafafafdfdsasdfsfsdfasdfafdsafdfdsfasdskdsdsfdsafdsafsdfdasfffsfdsfdsafafhdskfhdsfjdslkfdlfsdkjhsdfjkdshfkljds: return 2 diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__function.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__function.py.snap index d9f8a55ccf..8fba525759 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__function.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__function.py.snap @@ -377,70 +377,6 @@ def f( # first # third ): ... - -# Handle comments on empty tuple return types. -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - # comment -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - 1 -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - 1, 2 -): ... - -def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] - (1, 2) -): ... - -def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 - self, m: Match[str], data: str -) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: - ... - -def double(a: int # Hello -) -> (int): - return 2 * a - -def double(a: int) -> ( # Hello - int -): - return 2*a - -def double(a: int) -> ( # Hello -): - return 2*a - -# Breaking over parameters and return types. (Black adds a trailing comma when the -# function arguments break here with a single argument; we do not.) -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: - ... - -def f(a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> a: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: - ... ``` ## Output @@ -969,141 +905,6 @@ def f( # first /, # second ): ... - - -# Handle comments on empty tuple return types. -def zrevrangebylex( - self, - name: _Key, - max: _Value, - min: _Value, - start: int | None = None, - num: int | None = None, -) -> ( # type: ignore[override] -): - ... - - -def zrevrangebylex( - self, - name: _Key, - max: _Value, - min: _Value, - start: int | None = None, - num: int | None = None, -) -> ( # type: ignore[override] - # comment -): - ... - - -def zrevrangebylex( - self, - name: _Key, - max: _Value, - min: _Value, - start: int | None = None, - num: int | None = None, -) -> 1: # type: ignore[override] - ... - - -def zrevrangebylex( - self, - name: _Key, - max: _Value, - min: _Value, - start: int | None = None, - num: int | None = None, -) -> ( # type: ignore[override] - 1, - 2, -): - ... - - -def zrevrangebylex( - self, - name: _Key, - max: _Value, - min: _Value, - start: int | None = None, - num: int | None = None, -) -> (1, 2): # type: ignore[override] - ... - - -def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 - self, m: Match[str], data: str -) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: - ... - - -def double( - a: int, # Hello -) -> int: - return 2 * a - - -def double(a: int) -> int: # Hello - return 2 * a - - -def double( - a: int -) -> ( # Hello -): - return 2 * a - - -# Breaking over parameters and return types. (Black adds a trailing comma when the -# function arguments break here with a single argument; we do not.) -def f( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - - -def f( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a -) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - - -def f( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -) -> a: - ... - - -def f( - a -) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> ( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -): - ... - - -def f[ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -]() -> a: - ... - - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: - ... - - -def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]( - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -) -> a: - ... ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap new file mode 100644 index 0000000000..44840e33cd --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap @@ -0,0 +1,513 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/return_annotation.py +--- +## Input +```py +# Handle comments on empty tuple return types. +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + # comment +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + 1 +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + 1, 2 +): ... + +def zrevrangebylex(self, name: _Key, max: _Value, min: _Value, start: int | None = None, num: int | None = None) -> ( # type: ignore[override] + (1, 2) +): ... + +def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 + self, m: Match[str], data: str +) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: + ... + +def double(a: int # Hello +) -> (int): + return 2 * a + +def double(a: int) -> ( # Hello + int +): + return 2*a + +def double(a: int) -> ( # Hello +): + return 2*a + +# Breaking over parameters and return types. (Black adds a trailing comma when the +# function arguments break here with a single argument; we do not.) +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: + ... + +def f(a) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> a: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa](aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) -> a: + ... + +# Breaking return type annotations. Black adds parentheses if the parameters are +# empty; otherwise, it leverages the expressions own parentheses if possible. +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(*args) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( # foo +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + # bar +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X + Y + foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X + Y + foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X and Y and foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X and Y and foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> (X | Y | foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> (X | Y | foooooooooooooooooooooooooooooooooooo()): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + + +def double() -> first_item and foo.bar.baz().bop(1,): + return 2 * a + + +# Dangling comments on return annotations. +def double(a: int) -> ( + int # Hello +): + return 2*a + +def double(a: int) -> ( + foo.bar # Hello +): + return 2*a + +def double(a: int) -> ( + [int] # Hello +): + return 2*a + +def double(a: int) -> ( + int | list[int] # Hello +): + pass + +def double(a: int) -> ( + int | list[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int] # Hello +): + pass +``` + +## Output +```py +# Handle comments on empty tuple return types. +def zrevrangebylex( + self, + name: _Key, + max: _Value, + min: _Value, + start: int | None = None, + num: int | None = None, +) -> ( # type: ignore[override] +): + ... + + +def zrevrangebylex( + self, + name: _Key, + max: _Value, + min: _Value, + start: int | None = None, + num: int | None = None, +) -> ( # type: ignore[override] + # comment +): + ... + + +def zrevrangebylex( + self, + name: _Key, + max: _Value, + min: _Value, + start: int | None = None, + num: int | None = None, +) -> 1: # type: ignore[override] + ... + + +def zrevrangebylex( + self, + name: _Key, + max: _Value, + min: _Value, + start: int | None = None, + num: int | None = None, +) -> ( # type: ignore[override] + 1, + 2, +): + ... + + +def zrevrangebylex( + self, + name: _Key, + max: _Value, + min: _Value, + start: int | None = None, + num: int | None = None, +) -> (1, 2): # type: ignore[override] + ... + + +def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issues/10197 + self, m: Match[str], data: str +) -> Union[Tuple[None, None, None], Tuple[Element, int, int]]: + ... + + +def double( + a: int, # Hello +) -> int: + return 2 * a + + +def double(a: int) -> int: # Hello + return 2 * a + + +def double( + a: int +) -> ( # Hello +): + return 2 * a + + +# Breaking over parameters and return types. (Black adds a trailing comma when the +# function arguments break here with a single argument; we do not.) +def f( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + + +def f( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, a +) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + + +def f( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +) -> a: + ... + + +def f( + a +) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]() -> ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +): + ... + + +def f[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]() -> a: + ... + + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +) -> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: + ... + + +def f[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +) -> a: + ... + + +# Breaking return type annotations. Black adds parentheses if the parameters are +# empty; otherwise, it leverages the expressions own parentheses if possible. +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ] +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ] +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + ] +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + *args +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( # foo +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + # bar +) -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> X + Y + foooooooooooooooooooooooooooooooooooo(): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> X + Y + foooooooooooooooooooooooooooooooooooo(): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + X and Y and foooooooooooooooooooooooooooooooooooo() +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> X and Y and foooooooooooooooooooooooooooooooooooo(): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> X | Y | foooooooooooooooooooooooooooooooooooo(): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> X | Y | foooooooooooooooooooooooooooooooooooo(): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( + x +) -> ( + X | Y | foooooooooooooooooooooooooooooooooooo() # comment +): + ... + + +def double() -> ( + first_item + and foo.bar.baz().bop( + 1, + ) +): + return 2 * a + + +# Dangling comments on return annotations. +def double( + a: int +) -> ( + int # Hello +): + return 2 * a + + +def double( + a: int +) -> ( + foo.bar # Hello +): + return 2 * a + + +def double( + a: int +) -> ( + [int] # Hello +): + return 2 * a + + +def double( + a: int +) -> ( + int | list[int] # Hello +): + pass + + +def double( + a: int +) -> ( + int + | list[ + int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int + ] # Hello +): + pass +``` + + +