From 53246b725edfd902ac7cdc7f3f466ce29fa1d14d Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 11 Aug 2023 14:19:21 -0400 Subject: [PATCH] Allow return type annotations to use their own parentheses (#6436) ## Summary This PR modifies our logic for wrapping return type annotations. Previously, we _always_ wrapped the annotation in parentheses if it expanded; however, Black only exhibits this behavior when the function parameters is empty (i.e., it doesn't and can't break). In other cases, it uses the normal parenthesization rules, allowing nodes to bring their own parentheses. For example, given: ```python def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]: ... def xxxxxxxxxxxxxxxxxxxxxxxxxxxx(x) -> Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]: ... ``` Black will format as: ```python def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] ): ... def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( x, ) -> Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]: ... ``` Whereas, prior to this PR, Ruff would format as: ```python def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] ): ... def xxxxxxxxxxxxxxxxxxxxxxxxxxxx( x, ) -> ( Set[ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ] ): ... ``` Closes https://github.com/astral-sh/ruff/issues/6431. ## Test Plan Before: - `zulip`: 0.99702 - `django`: 0.99784 - `warehouse`: 0.99585 - `build`: 0.75623 - `transformers`: 0.99470 - `cpython`: 0.75988 - `typeshed`: 0.74853 After: - `zulip`: 0.99724 - `django`: 0.99791 - `warehouse`: 0.99586 - `build`: 0.75623 - `transformers`: 0.99474 - `cpython`: 0.75956 - `typeshed`: 0.74857 --- .../test/fixtures/ruff/statement/function.py | 64 --- .../ruff/statement/return_annotation.py | 182 +++++++ .../src/expression/mod.rs | 40 +- .../src/expression/parentheses.rs | 9 +- .../src/statement/stmt_function_def.rs | 86 ++- ..._cases__return_annotation_brackets.py.snap | 15 +- .../format@statement__function.py.snap | 199 ------- ...ormat@statement__return_annotation.py.snap | 513 ++++++++++++++++++ 8 files changed, 814 insertions(+), 294 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/return_annotation.py create mode 100644 crates/ruff_python_formatter/tests/snapshots/format@statement__return_annotation.py.snap 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 +``` + + +