Skip to content

Commit 4f6e90f

Browse files
committed
fix void check on decorated functions
1 parent 8f76e10 commit 4f6e90f

12 files changed

+76
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
### Fixes
1515
- Render star args in error messages properly (#551)
1616
- The first argument to `cast` is analyzed as evaluated (#564)
17+
- Decorated functions that return `None` correctly warn when used (#572)
1718

1819
## [2.2.1]
1920
### Fixes

mypy/checkexpr.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,10 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
688688
if (
689689
not allow_none_return
690690
and isinstance(ret_type, NoneType)
691-
and self.always_returns_none(e.callee)
691+
# field lies about its return type and must be special-cased
692+
and fullname != "dataclasses.field"
692693
):
693694
self.chk.msg.does_not_return_value(callee_type, e)
694-
return AnyType(TypeOfAny.from_error)
695695
return ret_type
696696

697697
def check_str_format_call(self, e: CallExpr) -> None:
@@ -759,6 +759,8 @@ def always_returns_none(self, node: Expression) -> bool:
759759

760760
def defn_returns_none(self, defn: SymbolNode | None) -> bool:
761761
"""Check if `defn` can _only_ return None."""
762+
if isinstance(defn, Decorator):
763+
defn = defn.func
762764
if isinstance(defn, FuncDef):
763765
return isinstance(defn.type, CallableType) and isinstance(
764766
get_proper_type(defn.type.ret_type), NoneType

test-data/unit/check-based-misc.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,26 @@ def f():
154154
def guard(x: object) -> x is int: # E: You need to put quotes around the entire type-guard, or enable `__future__.annotations` [misc]
155155
...
156156
[builtins fixtures/tuple.pyi]
157+
158+
159+
[case testUsingDecoratedNone]
160+
from typing import TypeVar, Callable
161+
T = TypeVar("T")
162+
def deco(t: T) -> T: ...
163+
@deco
164+
def f1(): ...
165+
166+
a = f1() # E: "f1" does not return a value [func-returns-value]
167+
168+
def becomes_none(f: object) -> Callable[[], None]: ...
169+
170+
@becomes_none
171+
def f2() -> object: ...
172+
173+
a = f2() # E: "f2" does not return a value [func-returns-value]
174+
def becomes_object(f: object) -> Callable[[], object]: ...
175+
176+
@becomes_object
177+
def f3(): ...
178+
179+
a = f3()

test-data/unit/check-bound.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class C(Generic[T]):
5555
return self.t
5656
c1: C[None]
5757
c1.get()
58-
d = c1.get()
58+
d = c1.get() # E: "get" of "C" does not return a value
5959
reveal_type(d) # N: Revealed type is "None"
6060

6161

@@ -83,7 +83,7 @@ def f(g: Callable[[], T]) -> T:
8383
return g()
8484
def h() -> None: pass
8585
f(h)
86-
a = f(h)
86+
a = f(h) # E: "f" does not return a value
8787
reveal_type(a) # N: Revealed type is "None"
8888

8989

test-data/unit/check-expressions.test

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,15 +1079,17 @@ class A:
10791079
a: A
10801080
o: object
10811081
if int():
1082-
a = f() # E: "f" does not return a value
1082+
a = f() # E: "f" does not return a value \
1083+
# E: Incompatible types in assignment (expression has type "None", variable has type "A")
10831084
if int():
10841085
o = a() # E: Function does not return a value
10851086
if int():
10861087
o = A().g(a) # E: "g" of "A" does not return a value
10871088
if int():
10881089
o = A.g(a, a) # E: "g" of "A" does not return a value
10891090
A().g(f()) # E: "f" does not return a value
1090-
x: A = f() # E: "f" does not return a value
1091+
x: A = f() # E: "f" does not return a value \
1092+
# E: Incompatible types in assignment (expression has type "None", variable has type "A")
10911093
f()
10921094
A().g(a)
10931095
[builtins fixtures/tuple.pyi]
@@ -1104,7 +1106,8 @@ while f(): # E: "f" does not return a value
11041106
pass
11051107
def g() -> object:
11061108
return f() # E: "f" does not return a value
1107-
raise f() # E: "f" does not return a value
1109+
raise f() # E: Exception must be derived from BaseException \
1110+
# E: "f" does not return a value
11081111
[builtins fixtures/exception.pyi]
11091112

11101113
[case testNoneReturnTypeWithExpressions]
@@ -1116,12 +1119,17 @@ class A:
11161119

11171120
a: A
11181121
[f()] # E: "f" does not return a value
1119-
f() + a # E: "f" does not return a value
1120-
a + f() # E: "f" does not return a value
1121-
f() == a # E: "f" does not return a value
1122-
a != f() # E: "f" does not return a value
1122+
f() + a # E: "f" does not return a value \
1123+
# E: Unsupported left operand type for + ("None")
1124+
a + f() # E: "f" does not return a value \
1125+
# E: Unsupported operand types for + ("A" and "None")
1126+
f() == a # E: "f" does not return a value \
1127+
# E: Unsupported left operand type for == ("None")
1128+
a != f() # E: Unsupported left operand type for != ("A") \
1129+
# E: "f" does not return a value
11231130
cast(A, f())
1124-
f().foo # E: "f" does not return a value
1131+
f().foo # E: "f" does not return a value \
1132+
# E: "None" has no attribute "foo"
11251133
[builtins fixtures/list.pyi]
11261134

11271135
[case testNoneReturnTypeWithExpressions2]
@@ -1134,11 +1142,16 @@ class A:
11341142

11351143
a: A
11361144
b: bool
1137-
f() in a # E: "f" does not return a value # E: Unsupported right operand type for in ("A")
1138-
a < f() # E: "f" does not return a value
1139-
f() <= a # E: "f" does not return a value
1140-
a in f() # E: "f" does not return a value
1141-
-f() # E: "f" does not return a value
1145+
f() in a # E: "f" does not return a value \
1146+
# E: Unsupported right operand type for in ("A")
1147+
a < f() # E: Unsupported left operand type for < ("A") \
1148+
# E: "f" does not return a value
1149+
f() <= a # E: "f" does not return a value \
1150+
# E: Unsupported left operand type for <= ("None")
1151+
a in f() # E: Unsupported right operand type for in ("None") \
1152+
# E: "f" does not return a value
1153+
-f() # E: Unsupported operand type for unary - ("None") \
1154+
# E: "f" does not return a value
11421155
not f() # E: "f" does not return a value
11431156
f() and b # E: "f" does not return a value
11441157
b or f() # E: "f" does not return a value

test-data/unit/check-functions.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,19 +2576,19 @@ from typing import TypeVar, Optional
25762576
T = TypeVar('T')
25772577
def X(val: T) -> T: ...
25782578
x_in = None
2579-
def Y(x: Optional[str] = X(x_in)): ...
2579+
def Y(x: Optional[str] = X(x_in)): ... # E: "X" does not return a value
25802580

2581-
xx: Optional[int] = X(x_in)
2581+
xx: Optional[int] = X(x_in) # E: "X" does not return a value
25822582
[out]
25832583

25842584
[case testNoComplainInferredNoneStrict]
25852585
from typing import TypeVar, Optional
25862586
T = TypeVar('T')
25872587
def X(val: T) -> T: ...
25882588
x_in = None
2589-
def Y(x: Optional[str] = X(x_in)): ...
2589+
def Y(x: Optional[str] = X(x_in)): ... # E: "X" does not return a value
25902590

2591-
xx: Optional[int] = X(x_in)
2591+
xx: Optional[int] = X(x_in) # E: "X" does not return a value
25922592
[out]
25932593

25942594
[case testNoComplainNoneReturnFromUntyped]

test-data/unit/check-inference-context.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,8 @@ class M(Generic[_KT, _VT]):
963963
def get(self, k: _KT, default: _T) -> _T: ...
964964

965965
def f(d: M[_KT, _VT], k: _KT) -> _VT:
966-
return d.get(k, None) # E: Incompatible return value type (got "None", expected "_VT")
966+
return d.get(k, None) # E: "get" of "M" does not return a value \
967+
# E: Incompatible return value type (got "None", expected "_VT")
967968

968969
[case testGenericMethodCalledInGenericContext2]
969970
from typing import TypeVar, Generic, Union

test-data/unit/check-inference.test

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,8 @@ for e, f in [[]]: # E: Need type annotation for "e" \
11801180
[case testForStatementInferenceWithVoid]
11811181
def f() -> None: pass
11821182

1183-
for x in f(): # E: "f" does not return a value
1183+
for x in f(): # E: "f" does not return a value \
1184+
# E: "None" has no attribute "__iter__" (not iterable)
11841185
pass
11851186
[builtins fixtures/for.pyi]
11861187

@@ -2119,6 +2120,8 @@ arr.append(arr.append(1))
21192120
[builtins fixtures/list.pyi]
21202121
[out]
21212122
main:3: error: "append" of "list" does not return a value
2123+
main:3: error: Argument 1 to "append" of "list" has incompatible type "None"; expected "int"
2124+
21222125

21232126
-- Multipass
21242127
-- ---------

test-data/unit/check-optional.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]"
119119

120120
[case testLambdaReturningNone]
121121
f = lambda: None
122-
x = f()
122+
x = f() # E: Function does not return a value
123123
reveal_type(x) # N: Revealed type is "None"
124124

125125
[case testNoneArgumentType]
@@ -362,7 +362,8 @@ def g(x: Optional[int]) -> int:
362362
pass
363363

364364
x = f() # E: "f" does not return a value
365-
f() + 1 # E: "f" does not return a value
365+
f() + 1 # E: "f" does not return a value \
366+
# E: Unsupported left operand type for + ("None")
366367
g(f()) # E: "f" does not return a value
367368

368369
[case testEmptyReturn]

test-data/unit/check-overloading.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1298,7 +1298,7 @@ def g(x: U, y: V) -> None:
12981298
# N: Possible overload variants: \
12991299
# N: def [T <: str] f(x: T) -> T \
13001300
# N: def [T <: str] f(x: List[T]) -> None
1301-
a = f([x])
1301+
a = f([x]) # E: "f" does not return a value
13021302
reveal_type(a) # N: Revealed type is "None"
13031303
f([y]) # E: Value of type variable "T" of "f" cannot be "V"
13041304
f([x, y]) # E: Value of type variable "T" of "f" cannot be "object"

test-data/unit/check-protocols.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,9 @@ class P2(Protocol):
14541454
T = TypeVar('T')
14551455
def f(x: Callable[[T, T], None]) -> T: pass
14561456
def g(x: P, y: P2) -> None: pass
1457-
x = f(g)
1457+
x = f(g) # E: "f" does not return a value
14581458
reveal_type(x) # N: Revealed type is "None"
1459+
14591460
[case testMeetProtocolWithNormal]
14601461
from typing import Protocol, Callable, TypeVar
14611462

test-data/unit/check-varargs.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ c: C
5252

5353
f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A"
5454
f(a, b, c) # E: Argument 3 to "f" has incompatible type "C"; expected "A"
55-
f(g()) # E: "g" does not return a value
56-
f(a, g()) # E: "g" does not return a value
55+
f(g()) # E: "g" does not return a value \
56+
# E: Argument 1 to "f" has incompatible type "None"; expected "A"
57+
f(a, g()) # E: "g" does not return a value \
58+
# E: Argument 2 to "f" has incompatible type "None"; expected "A"
5759
f()
5860
f(a)
5961
f(b)

0 commit comments

Comments
 (0)