Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error-code for truthy-function #13686

Merged
merged 3 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 13 additions & 0 deletions docs/source/error_code_list2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,19 @@ except that attempting to invoke an undefined method (e.g. ``__len__``) results
while attempting to evaluate an object in boolean context without a concrete implementation results in a truthy value.


Check that function isn't used in boolean context [truthy-function]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be under “enabled by default” rather than “optional checks”, right?

-------------------------------------------------------------------

Functions will always evaluate to true in boolean contexts.

.. code-block:: python

def f():
...

if f: # Error: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
pass

.. _ignore-without-code:

Check that ``# type: ignore`` include an error code [ignore-without-code]
Expand Down
5 changes: 5 additions & 0 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ def __str__(self) -> str:
"General",
default_enabled=False,
)
TRUTHY_FUNCTION: Final[ErrorCode] = ErrorCode(
"truthy-function",
"Warn about function that always evaluate to true in boolean contexts",
"General",
)
NAME_MATCH: Final = ErrorCode(
"name-match", "Check that type definition has consistent naming", "General"
)
Expand Down
2 changes: 1 addition & 1 deletion mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
code=codes.TRUTHY_BOOL,
)
FUNCTION_ALWAYS_TRUE: Final = ErrorMessage(
"Function {} could always be true in boolean context", code=codes.TRUTHY_BOOL
"Function {} could always be true in boolean context", code=codes.TRUTHY_FUNCTION
)
NOT_CALLABLE: Final = "{} not callable"
TYPE_MUST_BE_USED: Final = "Value of type {} must be used"
Expand Down
18 changes: 10 additions & 8 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -841,19 +841,21 @@ if bad_union: # E: "__main__.bad_union" has type "Union[Foo, object]" of which
if not bad_union: # E: "__main__.bad_union" has type "object" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
pass

def f():
pass
if f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]
pass
if not f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]
pass
conditional_result = 'foo' if f else 'bar' # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]

lst: List[int] = []
if lst:
pass
[builtins fixtures/list.pyi]

[case testTruthyFunctions]
# flags: --strict-optional
def f():
pass
if f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
pass
if not f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
pass
conditional_result = 'foo' if f else 'bar' # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]

[case testNoOverloadImplementation]
from typing import overload

Expand Down
7 changes: 5 additions & 2 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -6016,12 +6016,15 @@ tmp/m.py:2: error: Argument "age" to "foo" has incompatible type "str"; expected
[case testDisableEnableErrorCodesIncremental]
# flags: --disable-error-code truthy-bool
# flags2: --enable-error-code truthy-bool
def foo() -> int: ...
class Foo:
pass

foo = Foo()
if foo:
...
[out]
[out2]
main:4: error: Function "Callable[[], int]" could always be true in boolean context
main:7: error: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context

[case testModuleAsProtocolImplementationSerialize]
import m
Expand Down
14 changes: 10 additions & 4 deletions test-data/unit/check-inline-config.test
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,11 @@ main:1: error: Setting "strict" not supported in inline configuration: specify i
[case testInlineErrorCodes]
# flags: --strict-optional
# mypy: enable-error-code="ignore-without-code,truthy-bool"
class Foo:
pass

def foo() -> int: ...
if foo: ... # E: Function "Callable[[], int]" could always be true in boolean context
foo = Foo()
if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context
42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead)

[case testInlineErrorCodesOverrideConfig]
Expand All @@ -178,8 +180,10 @@ import tests.bar
import tests.baz
[file foo.py]
# mypy: disable-error-code="truthy-bool"
class Foo:
pass

def foo() -> int: ...
foo = Foo()
if foo: ...
42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead)

Expand All @@ -193,8 +197,10 @@ if foo: ... # E: Function "Callable[[], int]" could always be true in boolean c

[file tests/baz.py]
# mypy: disable-error-code="truthy-bool"
class Foo:
pass

def foo() -> int: ...
foo = Foo()
if foo: ...
42 + "no" # type: ignore

Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-python38.test
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ def f(x: int = (c := 4)) -> int:
z2: NT # E: Variable "NT" is not valid as a type \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases

if Alias := int:
if Alias := int: # E: Function "Type[int]" could always be true in boolean context
z3: Alias # E: Variable "Alias" is not valid as a type \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases

Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,8 @@ class Case1:
return False and self.missing() # E: Right operand of "and" is never evaluated

def test2(self) -> bool:
return not self.property_decorator_missing and self.missing() # E: Right operand of "and" is never evaluated
return not self.property_decorator_missing and self.missing() # E: Function "Callable[[], bool]" could always be true in boolean context \
# E: Right operand of "and" is never evaluated

def property_decorator_missing(self) -> bool:
return True
Expand Down