Skip to content

Commit f6fb60e

Browse files
authored
Improve Suggestion for empty TupleType syntax error (#9670)
* Improve Suggestion for empty TupleType syntax error The mypy syntax for a function or method that takes zero parameters is `() -> …`. Some new mypy users will reason by symmetry that the syntax for a method that returns nothing is likely `(…) -> ()`. A user who incorrectly annotates a function with `… -> ()` will be given the suggestion `Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn)`. This suggestion is unlikely to help correct the user's misconception about how to annotate the return type of a method that does not explicitly return a value. This PR adds a case to TupleType syntax error handling that returns a helpful suggestion in the case where the tuple contains zero items. Note: The error message casing in TupleType has grown large enough that it likely warrants relocation to `MessageBuilder`, but as there is not a `MessageBuilder` in accessible from `TypeAnalyzer` I have decided to add this single error case the easy way. There is a preexisting comment about the inaccessibility of `MessageBuilder` in `RypeAnalyzer`'s `cannot_resolve_type method`. I have added a test to `check-functions.test` that verifies the new suggestion is printed when `-> ()` is used as a return type annotation. I have also tested that a valid return type of `-> Tuple[()]` remains without error. * Clarify suggestion message
1 parent 985a20d commit f6fb60e

File tree

2 files changed

+15
-2
lines changed

2 files changed

+15
-2
lines changed

mypy/typeanal.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,10 @@ def visit_tuple_type(self, t: TupleType) -> Type:
537537
# generate errors elsewhere, and Tuple[t1, t2, ...] must be used instead.
538538
if t.implicit and not self.allow_tuple_literal:
539539
self.fail('Syntax error in type annotation', t, code=codes.SYNTAX)
540-
if len(t.items) == 1:
540+
if len(t.items) == 0:
541+
self.note('Suggestion: Use Tuple[()] instead of () for an empty tuple, or '
542+
'None for a function without a return value', t, code=codes.SYNTAX)
543+
elif len(t.items) == 1:
541544
self.note('Suggestion: Is there a spurious trailing comma?', t, code=codes.SYNTAX)
542545
else:
543546
self.note('Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn)', t,

test-data/unit/check-functions.test

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class B(A):
4949
def f(self, b: str, a: int) -> None: pass # E: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "int" \
5050
# N: This violates the Liskov substitution principle \
5151
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides \
52-
# E: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"
52+
# E: Argument 2 of "f" is incompatible with supertype "A"; supertype defines the argument type as "str"
5353

5454
class C(A):
5555
def f(self, foo: int, bar: str) -> None: pass
@@ -249,6 +249,16 @@ if int():
249249
class A: pass
250250
[builtins fixtures/tuple.pyi]
251251

252+
[case testReturnEmptyTuple]
253+
from typing import Tuple
254+
def f(x): # type: (int) -> () # E: Syntax error in type annotation \
255+
# N: Suggestion: Use Tuple[()] instead of () for an empty tuple, or None for a function without a return value
256+
pass
257+
258+
def g(x: int) -> Tuple[()]:
259+
pass
260+
[builtins fixtures/tuple.pyi]
261+
252262
[case testFunctionSubtypingWithVoid]
253263
from typing import Callable
254264
f = None # type: Callable[[], None]

0 commit comments

Comments
 (0)