Skip to content

Commit 0eb4a99

Browse files
author
Charulata Lodha
committed
update check_for_missing_annotations to give note for None return type, give missing arg names in err msg
1 parent 40e3eb5 commit 0eb4a99

File tree

3 files changed

+94
-35
lines changed

3 files changed

+94
-35
lines changed

mypy/checker.py

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,32 +1633,65 @@ def is_reverse_op_method(self, method_name: str) -> bool:
16331633

16341634
def check_for_missing_annotations(self, fdef: FuncItem) -> None:
16351635
# Check for functions with unspecified/not fully specified types.
1636-
def is_unannotated_any(t: Type) -> bool:
1636+
def is_unannotated_any(t: Type | None) -> bool:
16371637
if not isinstance(t, ProperType):
16381638
return False
16391639
return isinstance(t, AnyType) and t.type_of_any == TypeOfAny.unannotated
16401640

1641+
unannotated_args = [
1642+
a.variable.name
1643+
for a in fdef.arguments
1644+
if not (a.variable.is_cls or a.variable.is_self)
1645+
and is_unannotated_any(a.variable.type)
1646+
]
1647+
16411648
has_explicit_annotation = isinstance(fdef.type, CallableType) and any(
1642-
not is_unannotated_any(t) for t in fdef.type.arg_types + [fdef.type.ret_type]
1649+
unannotated_args + [fdef.type.ret_type]
16431650
)
16441651

16451652
show_untyped = not self.is_typeshed_stub or self.options.warn_incomplete_stub
16461653
check_incomplete_defs = self.options.disallow_incomplete_defs and has_explicit_annotation
1647-
if show_untyped and (self.options.disallow_untyped_defs or check_incomplete_defs):
1648-
if fdef.type is None and self.options.disallow_untyped_defs:
1649-
if not fdef.arguments or (
1650-
len(fdef.arguments) == 1
1651-
and (fdef.arg_names[0] == "self" or fdef.arg_names[0] == "cls")
1652-
):
1653-
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
1654-
if not has_return_statement(fdef) and not fdef.is_generator:
1655-
self.note(
1656-
'Use "-> None" if function does not return a value',
1654+
1655+
def handle_function_args_type_annotation() -> None:
1656+
if fdef.type is None:
1657+
if fdef.arguments:
1658+
if len(fdef.arguments) == 1 and (
1659+
fdef.arguments[0].variable.is_cls or fdef.arguments[0].variable.is_self
1660+
):
1661+
# its not an error
1662+
pass
1663+
else:
1664+
self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef)
1665+
1666+
elif isinstance(fdef.type, CallableType):
1667+
if unannotated_args:
1668+
if len(unannotated_args) < 5:
1669+
self.fail(
1670+
message_registry.ARGUMENT_TYPE_EXPECTED.format(
1671+
", ".join(f'"{arg}"' for arg in unannotated_args)
1672+
),
16571673
fdef,
1658-
code=codes.NO_UNTYPED_DEF,
16591674
)
1660-
else:
1661-
self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef)
1675+
else:
1676+
self.fail(
1677+
message_registry.ARGUMENT_TYPE_EXPECTED.format(
1678+
str(", ".join(f'"{arg}"' for arg in unannotated_args[:5]) + "...")
1679+
),
1680+
fdef,
1681+
)
1682+
1683+
def handle_return_type_annotation() -> None:
1684+
if fdef.type is None:
1685+
has_return = has_return_statement(fdef)
1686+
if has_return:
1687+
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
1688+
if not has_return and not fdef.is_generator:
1689+
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
1690+
self.note(
1691+
'Use "-> None" if function does not return a value',
1692+
fdef,
1693+
code=codes.NO_UNTYPED_DEF,
1694+
)
16621695
elif isinstance(fdef.type, CallableType):
16631696
ret_type = get_proper_type(fdef.type.ret_type)
16641697
if is_unannotated_any(ret_type):
@@ -1671,8 +1704,13 @@ def is_unannotated_any(t: Type) -> bool:
16711704
elif fdef.is_coroutine and isinstance(ret_type, Instance):
16721705
if is_unannotated_any(self.get_coroutine_return_type(ret_type)):
16731706
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
1674-
if any(is_unannotated_any(t) for t in fdef.type.arg_types):
1675-
self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef)
1707+
1708+
if show_untyped and (self.options.disallow_untyped_defs or check_incomplete_defs):
1709+
if (isinstance(fdef.type, CallableType) and check_incomplete_defs) or (
1710+
self.options.disallow_untyped_defs
1711+
):
1712+
handle_return_type_annotation()
1713+
handle_function_args_type_annotation()
16761714

16771715
def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None:
16781716
self_type = fill_typevars_with_any(fdef.info)
@@ -2079,8 +2117,7 @@ def check_method_override(
20792117
and (self.options.check_untyped_defs or not defn.is_dynamic())
20802118
and (
20812119
# don't check override for synthesized __replace__ methods from dataclasses
2082-
defn.name != "__replace__"
2083-
or defn.info.metadata.get("dataclass_tag") is None
2120+
defn.name != "__replace__" or defn.info.metadata.get("dataclass_tag") is None
20842121
)
20852122
)
20862123
found_method_base_classes: list[TypeInfo] = []
@@ -4254,7 +4291,8 @@ def check_lvalue(
42544291
self.store_type(lvalue, lvalue_type)
42554292
elif isinstance(lvalue, (TupleExpr, ListExpr)):
42564293
types = [
4257-
self.check_lvalue(sub_expr)[0] or
4294+
self.check_lvalue(sub_expr)[0]
4295+
or
42584296
# This type will be used as a context for further inference of rvalue,
42594297
# we put Uninhabited if there is no information available from lvalue.
42604298
UninhabitedType()
@@ -6787,7 +6825,6 @@ def refine_away_none_in_comparison(
67876825
if_map, else_map = {}, {}
67886826

67896827
if not non_optional_types or (len(non_optional_types) != len(chain_indices)):
6790-
67916828
# Narrow e.g. `Optional[A] == "x"` or `Optional[A] is "x"` to `A` (which may be
67926829
# convenient but is strictly not type-safe):
67936830
for i in narrowable_operand_indices:

mypy/message_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
126126
"Function is missing a return type annotation", codes.NO_UNTYPED_DEF
127127
)
128128
ARGUMENT_TYPE_EXPECTED: Final = ErrorMessage(
129-
"Function is missing a type annotation for one or more arguments", codes.NO_UNTYPED_DEF
129+
"Function is missing a type annotation for one or more arguments: {}", code=codes.ASSIGNMENT
130130
)
131131
KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE: Final = ErrorMessage(
132132
'Keyword argument only valid with "str" key type in call to "dict"'

test-data/unit/check-flags.test

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,39 @@
22
# flags: --disallow-untyped-defs
33
def f(x): pass
44
[out]
5+
main:2: error: Function is missing a return type annotation
6+
main:2: note: Use "-> None" if function does not return a value
57
main:2: error: Function is missing a type annotation
68

7-
[case testUnannotatedArgument]
9+
[case testUnannotatedArgumentSingle]
10+
# flags: --disallow-untyped-defs
11+
def f(x) -> int: return 1
12+
[out]
13+
main:2: error: Function is missing a type annotation for one or more arguments: "x"
14+
15+
[case testUnannotatedArgumentLessThan5]
16+
# flags: --disallow-untyped-defs
17+
def f(x, y, z, a:int, b) -> int: return 1
18+
[out]
19+
main:2: error: Function is missing a type annotation for one or more arguments: "x", "y", "z", "b"
20+
21+
[case testUnannotatedArgument5orMore]
822
# flags: --disallow-untyped-defs
9-
def f(x) -> int: pass
23+
def f(x, y, z, a:int, b, c, d, e, f:int) -> int: return 1
1024
[out]
11-
main:2: error: Function is missing a type annotation for one or more arguments
25+
main:2: error: Function is missing a type annotation for one or more arguments: "x", "y", "z", "b", "c"...
1226

13-
[case testNoArgumentFunction]
27+
[case testNoArgumentValidReturnType]
1428
# flags: --disallow-untyped-defs
15-
def f() -> int: pass
29+
def f() -> int: return 1
1630
[out]
1731

32+
[case testNoArgumentMissingReturnTypeFunction]
33+
# flags: --disallow-untyped-defs
34+
def f(): return 5
35+
[out]
36+
main:2: error: Function is missing a return type annotation
37+
1838
[case testUnannotatedReturn]
1939
# flags: --disallow-untyped-defs
2040
def f(x: int): pass
@@ -46,6 +66,7 @@ def f(self): pass
4666
[out]
4767
main:2: error: Function is missing a return type annotation
4868
main:2: note: Use "-> None" if function does not return a value
69+
main:2: error: Function is missing a type annotation
4970

5071
[case testUnannotatedReturnWithNontrivialReturn]
5172
# flags: --disallow-untyped-defs
@@ -63,7 +84,7 @@ async def f(): # E: Function is missing a return type annotation \
6384

6485
[case testAsyncUnannotatedArgument]
6586
# flags: --disallow-untyped-defs
66-
async def f(x) -> None: # E: Function is missing a type annotation for one or more arguments
87+
async def f(x) -> None: # E: Function is missing a type annotation for one or more arguments: "x"
6788
pass
6889
[builtins fixtures/async_await.pyi]
6990
[typing fixtures/typing-async.pyi]
@@ -85,6 +106,7 @@ def get_tasks(self):
85106
return 'whatever'
86107
[out]
87108
main:2: error: Function is missing a return type annotation
109+
main:2: error: Function is missing a type annotation
88110

89111
[case testDisallowUntypedDefsUntypedDecorator]
90112
# flags: --disallow-untyped-decorators
@@ -659,7 +681,7 @@ import standard, incomplete
659681
def incomplete(x) -> int:
660682
return 0
661683
[file incomplete.py]
662-
def incomplete(x) -> int: # E: Function is missing a type annotation for one or more arguments
684+
def incomplete(x) -> int: # E: Function is missing a type annotation for one or more arguments: "x"
663685
return 0
664686
[file mypy.ini]
665687
\[mypy]
@@ -676,7 +698,7 @@ import standard, incomplete
676698
def incomplete(x) -> int:
677699
return 0
678700
[file incomplete.py]
679-
def incomplete(x) -> int: # E: Function is missing a type annotation for one or more arguments
701+
def incomplete(x) -> int: # E: Function is missing a type annotation for one or more arguments: "x"
680702
return 0
681703
[file pyproject.toml]
682704
\[tool.mypy]
@@ -1351,7 +1373,7 @@ def g(m: Movie) -> Movie:
13511373

13521374
def f(i: int): # E: Function is missing a return type annotation
13531375
pass
1354-
def g(i) -> None: # E: Function is missing a type annotation for one or more arguments
1376+
def g(i) -> None: # E: Function is missing a type annotation for one or more arguments: "i"
13551377
pass
13561378
def h(i: int) -> int: # no error
13571379
return i
@@ -1378,7 +1400,7 @@ def f(i: int, s):
13781400

13791401
[out]
13801402
main:3: error: Function is missing a return type annotation
1381-
main:3: error: Function is missing a type annotation for one or more arguments
1403+
main:3: error: Function is missing a type annotation for one or more arguments: "s"
13821404

13831405
[case testDisallowIncompleteDefsAttrsNoAnnotations]
13841406
# flags: --disallow-incomplete-defs
@@ -1405,7 +1427,7 @@ class Annotated:
14051427
import attrs
14061428

14071429
@attrs.define
1408-
class PartiallyAnnotated: # E: Function is missing a type annotation for one or more arguments
1430+
class PartiallyAnnotated: # E: Function is missing a type annotation for one or more arguments: "baz"
14091431
bar: int = attrs.field()
14101432
baz = attrs.field()
14111433

@@ -2301,13 +2323,13 @@ ignore_errors = True
23012323
# flags: --config-file tmp/mypy.ini
23022324
import x, y, z
23032325
[file x.py]
2304-
def f(a): ... # E: Function is missing a type annotation
2326+
def f(a): ... # E: Function is missing a return type annotation # N: Use "-> None" if function does not return a value # E: Function is missing a type annotation
23052327
def g(a: int) -> int: return f(a)
23062328
[file y.py]
23072329
def f(a): pass
23082330
def g(a: int) -> int: return f(a)
23092331
[file z.py]
2310-
def f(a): pass # E: Function is missing a type annotation
2332+
def f(a): pass # E: Function is missing a return type annotation # N: Use "-> None" if function does not return a value # E: Function is missing a type annotation
23112333
def g(a: int) -> int: return f(a) # E: Call to untyped function "f" in typed context
23122334
[file mypy.ini]
23132335
\[mypy]

0 commit comments

Comments
 (0)