Repository: psf/black

Issue:
Internal Error when formatting bracketed return annotations
**Describe the bug**

When formatting a `*.py` file with bracketed returns annotations the error below is returned:
```
error: cannot format test.py: INTERNAL ERROR: Black produced code that is not equivalent to the source on pass 1.  Please report a bug on https://github.com/psf/black/issues.  This diff might be helpful: /var/folders/9m/cps802s92k77tpy3z1w56hxr0000gn/T/blk_fu7q8w3k.log
```

**To Reproduce**

For example:
`test.py`
```
returning_a_deeply_nested_import_of_a_type = str

def some_method_with_a_really_long_name() -> (
    returning_a_deeply_nested_import_of_a_type
):
    pass

```

run `black -l 79` on the above file

**Expected behavior**

The file is formatted and the bracketed form is maintained.

**Environment (please complete the following information):**

- Version: main
- OS and Python version: macOS v11.4/Python 3.9.4

**Does this bug also happen on main?**

<!-- To answer this, you have two options:

Yes with the error:
```
INTERNAL ERROR: Black produced code that is not equivalent to the source on pass 1.  Please report a bug on https://github.com/psf/black/issues.  This diff might be helpful: /tmp/blk_h9jlhx47.log

--- src
+++ dst
@@ -38,17 +38,23 @@
         )  # /Pass
       decorator_list=
       name=
         'some_method_with_a_really_long_name',  # str
       returns=
-        Name(
+        Tuple(
           ctx=
             Load(
             )  # /Load
-          id=
-            'returning_a_deeply_nested_import_of_a_type',  # str
-        )  # /Name
+          elts=
+            Name(
+              ctx=
+                Load(
+                )  # /Load
+              id=
+                'returning_a_deeply_nested_import_of_a_type',  # str
+            )  # /Name
+        )  # /Tuple
       type_comment=
         None,  # NoneType
     )  # /FunctionDef
   type_ignores=
 )  # /Module
\ No newline at end of file
```



Discussion:
Hi, thanks for the report! Just for easy inspection, I've reproduced this on the [playground](https://black.vercel.app/?version=main&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ADvAJtdAD2IimZxl1N_WmXWfJWiTYrsR8PVUMVCmlX9AGcSxXVVaAzFaCqZeq0vDEZAOQmo-sPNbDIyiGH_bz_rPH7OZj-aOxy4vQCbGUW3zhDhBAUdotlZuHL93sXg0CBuunj4okoLP3qKEYRv_gizmGINogFCIRRywh14k-1KAXPuPekG2hU6AHw16VWjc9Gbhp9VkeQr-GA9iErygfQAAAC682Xh1u_4egABtwHwAQAAMKJefrHEZ_sCAAAAAARZWg==).

Resolving patch:
diff --git a/src/black/linegen.py b/src/black/linegen.py
--- a/src/black/linegen.py
+++ b/src/black/linegen.py
@@ -7,7 +7,7 @@
 
 from dataclasses import dataclass, field
 
-from black.nodes import WHITESPACE, STATEMENT, STANDALONE_COMMENT
+from black.nodes import WHITESPACE, RARROW, STATEMENT, STANDALONE_COMMENT
 from black.nodes import ASSIGNMENTS, OPENING_BRACKETS, CLOSING_BRACKETS
 from black.nodes import Visitor, syms, first_child_is_arith, ensure_visible
 from black.nodes import is_docstring, is_empty_tuple, is_one_tuple, is_one_tuple_between
@@ -574,6 +574,20 @@ def bracket_split_build_line(
                 original.is_def
                 and opening_bracket.value == "("
                 and not any(leaf.type == token.COMMA for leaf in leaves)
+                # In particular, don't add one within a parenthesized return annotation.
+                # Unfortunately the indicator we're in a return annotation (RARROW) may
+                # be defined directly in the parent node, the parent of the parent ...
+                # and so on depending on how complex the return annotation is.
+                # This isn't perfect and there's some false negatives but they are in
+                # contexts were a comma is actually fine.
+                and not any(
+                    node.prev_sibling.type == RARROW
+                    for node in (
+                        leaves[0].parent,
+                        getattr(leaves[0].parent, "parent", None),
+                    )
+                    if isinstance(node, Node) and isinstance(node.prev_sibling, Leaf)
+                )
             )
 
             if original.is_import or no_commas:
diff --git a/src/black/nodes.py b/src/black/nodes.py
--- a/src/black/nodes.py
+++ b/src/black/nodes.py
@@ -135,6 +135,8 @@
 BRACKETS = OPENING_BRACKETS | CLOSING_BRACKETS
 ALWAYS_NO_SPACE = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
 
+RARROW = 55
+
 
 class Visitor(Generic[T]):
     """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""


Test patch:
diff --git a/tests/data/function_trailing_comma.py b/tests/data/function_trailing_comma.py
--- a/tests/data/function_trailing_comma.py
+++ b/tests/data/function_trailing_comma.py
@@ -21,6 +21,34 @@ def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
 ]:
     json = {"k": {"k2": {"k3": [1,]}}}
 
+
+
+# The type annotation shouldn't get a trailing comma since that would change its type.
+# Relevant bug report: https://github.com/psf/black/issues/2381.
+def some_function_with_a_really_long_name() -> (
+    returning_a_deeply_nested_import_of_a_type_i_suppose
+):
+    pass
+
+
+def some_method_with_a_really_long_name(very_long_parameter_so_yeah: str, another_long_parameter: int) -> (
+    another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not
+):
+    pass
+
+
+def func() -> (
+    also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too)
+):
+    pass
+
+
+def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(
+        this_shouldn_t_get_a_trailing_comma_too
+    ))
+):
+    pass
+
 # output
 
 def f(
@@ -85,4 +113,38 @@ def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
                 ]
             }
         }
-    }
\ No newline at end of file
+    }
+
+
+# The type annotation shouldn't get a trailing comma since that would change its type.
+# Relevant bug report: https://github.com/psf/black/issues/2381.
+def some_function_with_a_really_long_name() -> (
+    returning_a_deeply_nested_import_of_a_type_i_suppose
+):
+    pass
+
+
+def some_method_with_a_really_long_name(
+    very_long_parameter_so_yeah: str, another_long_parameter: int
+) -> (
+    another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not
+):
+    pass
+
+
+def func() -> (
+    also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(
+        this_shouldn_t_get_a_trailing_comma_too
+    )
+):
+    pass
+
+
+def func() -> (
+    (
+        also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(
+            this_shouldn_t_get_a_trailing_comma_too
+        )
+    )
+):
+    pass