Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/error_code_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ returns ``None``:
# OK: we don't do anything with the return value
f()

# Error: "f" does not return a value [func-returns-value]
# Error: "f" does not return a value (or returns None) [func-returns-value]
if f():
print("not false")

Expand Down
12 changes: 4 additions & 8 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,14 +1025,10 @@ def does_not_return_value(self, callee_type: Type | None, context: Context) -> N
callee_type = get_proper_type(callee_type)
if isinstance(callee_type, FunctionLike):
name = callable_name(callee_type)
if name is not None:
self.fail(
f"{capitalize(name)} does not return a value",
context,
code=codes.FUNC_RETURNS_VALUE,
)
else:
self.fail("Function does not return a value", context, code=codes.FUNC_RETURNS_VALUE)
message = "{} does not return a value (or returns None)".format(
"Function" if name is None else capitalize(name)
)
Copy link
Copy Markdown
Contributor

@ikonst ikonst Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
message = "{} does not return a value (or returns None)".format(
"Function" if name is None else capitalize(name)
)
name = capitalize(name or "Function")
message = f"{name} does not return a value (or returns None)"

Copy link
Copy Markdown
Contributor Author

@atugushev atugushev Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
message = "{} does not return a value (or returns None)".format(
"Function" if name is None else capitalize(name)
)
name = "Function" if name is None else capitalize(name)
message = f"{name} does not return a value (or returns None)"

Thanks for the suggestion! By using an f-string along with the name variable the code definitely looks better. One note, perhaps we could keep an explicit check for None as in the original code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I "simplified" it because an empty string wouldn't make for a good interpolation either, but it also suggests it's a possibility where currently it's not.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the implementation of callable_name() it seems to me that we could omit the capitalization:

mypy/mypy/messages.py

Lines 2963 to 2967 in b49be10

def callable_name(type: FunctionLike) -> str | None:
name = type.get_name()
if name is not None and name[0] != "<":
return f'"{name}"'.replace(" of ", '" of "')
return name

It always returns either None, "Foo", or <Foo>, where capitalization does not seem to be applicable. What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in dceaac1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better!

self.fail(message, context, code=codes.FUNC_RETURNS_VALUE)

def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None:
"""Report an error about using an deleted type as an rvalue."""
Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -553,15 +553,15 @@ from typing import Callable

def f() -> None: pass

x = f() # E: "f" does not return a value [func-returns-value]
x = f() # E: "f" does not return a value (or returns None) [func-returns-value]

class A:
def g(self) -> None: pass

y = A().g() # E: "g" of "A" does not return a value [func-returns-value]
y = A().g() # E: "g" of "A" does not return a value (or returns None) [func-returns-value]

c: Callable[[], None]
z = c() # E: Function does not return a value [func-returns-value]
z = c() # E: Function does not return a value (or returns None) [func-returns-value]

[case testErrorCodeInstantiateAbstract]
from abc import abstractmethod
Expand Down
66 changes: 33 additions & 33 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1062,15 +1062,15 @@ class A:
a: A
o: object
if int():
a = f() # E: "f" does not return a value
a = f() # E: "f" does not return a value (or returns None)
if int():
o = a() # E: Function does not return a value
o = a() # E: Function does not return a value (or returns None)
if int():
o = A().g(a) # E: "g" of "A" does not return a value
o = A().g(a) # E: "g" of "A" does not return a value (or returns None)
if int():
o = A.g(a, a) # E: "g" of "A" does not return a value
A().g(f()) # E: "f" does not return a value
x: A = f() # E: "f" does not return a value
o = A.g(a, a) # E: "g" of "A" does not return a value (or returns None)
A().g(f()) # E: "f" does not return a value (or returns None)
x: A = f() # E: "f" does not return a value (or returns None)
f()
A().g(a)
[builtins fixtures/tuple.pyi]
Expand All @@ -1079,15 +1079,15 @@ A().g(a)
import typing
def f() -> None: pass

if f(): # E: "f" does not return a value
if f(): # E: "f" does not return a value (or returns None)
pass
elif f(): # E: "f" does not return a value
elif f(): # E: "f" does not return a value (or returns None)
pass
while f(): # E: "f" does not return a value
while f(): # E: "f" does not return a value (or returns None)
pass
def g() -> object:
return f() # E: "f" does not return a value
raise f() # E: "f" does not return a value
return f() # E: "f" does not return a value (or returns None)
raise f() # E: "f" does not return a value (or returns None)
[builtins fixtures/exception.pyi]

[case testNoneReturnTypeWithExpressions]
Expand All @@ -1098,13 +1098,13 @@ class A:
def __add__(self, x: 'A') -> 'A': pass

a: A
[f()] # E: "f" does not return a value
f() + a # E: "f" does not return a value
a + f() # E: "f" does not return a value
f() == a # E: "f" does not return a value
a != f() # E: "f" does not return a value
[f()] # E: "f" does not return a value (or returns None)
f() + a # E: "f" does not return a value (or returns None)
a + f() # E: "f" does not return a value (or returns None)
f() == a # E: "f" does not return a value (or returns None)
a != f() # E: "f" does not return a value (or returns None)
cast(A, f())
f().foo # E: "f" does not return a value
f().foo # E: "f" does not return a value (or returns None)
[builtins fixtures/list.pyi]

[case testNoneReturnTypeWithExpressions2]
Expand All @@ -1117,14 +1117,14 @@ class A:

a: A
b: bool
f() in a # E: "f" does not return a value # E: Unsupported right operand type for in ("A")
a < f() # E: "f" does not return a value
f() <= a # E: "f" does not return a value
a in f() # E: "f" does not return a value
-f() # E: "f" does not return a value
not f() # E: "f" does not return a value
f() and b # E: "f" does not return a value
b or f() # E: "f" does not return a value
f() in a # E: "f" does not return a value (or returns None) # E: Unsupported right operand type for in ("A")
a < f() # E: "f" does not return a value (or returns None)
f() <= a # E: "f" does not return a value (or returns None)
a in f() # E: "f" does not return a value (or returns None)
-f() # E: "f" does not return a value (or returns None)
not f() # E: "f" does not return a value (or returns None)
f() and b # E: "f" does not return a value (or returns None)
b or f() # E: "f" does not return a value (or returns None)
[builtins fixtures/bool.pyi]


Expand Down Expand Up @@ -1424,7 +1424,7 @@ if int():
[case testConditionalExpressionWithEmptyCondition]
import typing
def f() -> None: pass
x = 1 if f() else 2 # E: "f" does not return a value
x = 1 if f() else 2 # E: "f" does not return a value (or returns None)

[case testConditionalExpressionWithSubtyping]
import typing
Expand Down Expand Up @@ -1487,7 +1487,7 @@ from typing import List, Union
x = []
y = ""
x.append(y) if bool() else x.append(y)
z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value
z = x.append(y) if bool() else x.append(y) # E: "append" of "list" does not return a value (or returns None)
[builtins fixtures/list.pyi]

-- Special cases
Expand Down Expand Up @@ -1587,7 +1587,7 @@ def f(x: int) -> None:
[builtins fixtures/for.pyi]
[out]
main:1: error: The return type of a generator function should be "Generator" or one of its supertypes
main:2: error: "f" does not return a value
main:2: error: "f" does not return a value (or returns None)
main:2: error: Argument 1 to "f" has incompatible type "str"; expected "int"

[case testYieldExpressionWithNone]
Expand All @@ -1607,7 +1607,7 @@ from typing import Iterator
def f() -> Iterator[int]:
yield 5
def g() -> Iterator[int]:
a = yield from f() # E: Function does not return a value
a = yield from f() # E: Function does not return a value (or returns None)

[case testYieldFromGeneratorHasValue]
from typing import Iterator, Generator
Expand All @@ -1622,12 +1622,12 @@ def g() -> Iterator[int]:
[case testYieldFromTupleExpression]
from typing import Generator
def g() -> Generator[int, None, None]:
x = yield from () # E: Function does not return a value
x = yield from (0, 1, 2) # E: Function does not return a value
x = yield from () # E: Function does not return a value (or returns None)
x = yield from (0, 1, 2) # E: Function does not return a value (or returns None)
x = yield from (0, "ERROR") # E: Incompatible types in "yield from" (actual type "object", expected type "int") \
# E: Function does not return a value
# E: Function does not return a value (or returns None)
x = yield from ("ERROR",) # E: Incompatible types in "yield from" (actual type "str", expected type "int") \
# E: Function does not return a value
# E: Function does not return a value (or returns None)
[builtins fixtures/tuple.pyi]

-- dict(...)
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ if int():
if int():
f = o # E: Incompatible types in assignment (expression has type "object", variable has type "Callable[[], None]")
if int():
f = f() # E: Function does not return a value
f = f() # E: Function does not return a value (or returns None)

if int():
f = f
Expand Down
8 changes: 4 additions & 4 deletions test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class B: pass
[case testLvarInitializedToVoid]
import typing
def f() -> None:
a = g() # E: "g" does not return a value
#b, c = g() # "g" does not return a value TODO
a = g() # E: "g" does not return a value (or returns None)
#b, c = g() # "g" does not return a value (or returns None) TODO

def g() -> None: pass
[out]
Expand Down Expand Up @@ -1180,7 +1180,7 @@ for e, f in [[]]: # E: Need type annotation for "e" \
[case testForStatementInferenceWithVoid]
def f() -> None: pass

for x in f(): # E: "f" does not return a value
for x in f(): # E: "f" does not return a value (or returns None)
pass
[builtins fixtures/for.pyi]

Expand Down Expand Up @@ -2118,7 +2118,7 @@ arr = []
arr.append(arr.append(1))
[builtins fixtures/list.pyi]
[out]
main:3: error: "append" of "list" does not return a value
main:3: error: "append" of "list" does not return a value (or returns None)

-- Multipass
-- ---------
Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-optional.test
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,9 @@ def f() -> None:
def g(x: Optional[int]) -> int:
pass

x = f() # E: "f" does not return a value
f() + 1 # E: "f" does not return a value
g(f()) # E: "f" does not return a value
x = f() # E: "f" does not return a value (or returns None)
f() + 1 # E: "f" does not return a value (or returns None)
g(f()) # E: "f" does not return a value (or returns None)

[case testEmptyReturn]
def f() -> None:
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ class C(B): pass
import typing
def f() -> None: pass

(None, f()) # E: "f" does not return a value
(f(), None) # E: "f" does not return a value
(None, f()) # E: "f" does not return a value (or returns None)
(f(), None) # E: "f" does not return a value (or returns None)
[builtins fixtures/tuple.pyi]


Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ c: C

f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "A"
f(a, b, c) # E: Argument 3 to "f" has incompatible type "C"; expected "A"
f(g()) # E: "g" does not return a value
f(a, g()) # E: "g" does not return a value
f(g()) # E: "g" does not return a value (or returns None)
f(a, g()) # E: "g" does not return a value (or returns None)
f()
f(a)
f(b)
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/pythoneval-asyncio.test
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ try:
finally:
loop.close()
[out]
_program.py:11: error: Function does not return a value
_program.py:11: error: Function does not return a value (or returns None)

[case testErrorReturnIsNotTheSameType]
from typing import Any
Expand Down