Skip to content

Commit b3d94dc

Browse files
cdce8phauntsaninja
andauthored
Add error-code for truthy-function (#13686)
A separate error code for `truthy-function` so it can be enabled by default. Closes #12621 Co-authored-by: Shantanu <[email protected]>
1 parent ea606b4 commit b3d94dc

8 files changed

+47
-17
lines changed

docs/source/error_code_list2.rst

+13
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,19 @@ except that attempting to invoke an undefined method (e.g. ``__len__``) results
258258
while attempting to evaluate an object in boolean context without a concrete implementation results in a truthy value.
259259

260260

261+
Check that function isn't used in boolean context [truthy-function]
262+
-------------------------------------------------------------------
263+
264+
Functions will always evaluate to true in boolean contexts.
265+
266+
.. code-block:: python
267+
268+
def f():
269+
...
270+
271+
if f: # Error: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
272+
pass
273+
261274
.. _ignore-without-code:
262275

263276
Check that ``# type: ignore`` include an error code [ignore-without-code]

mypy/errorcodes.py

+5
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ def __str__(self) -> str:
155155
"General",
156156
default_enabled=False,
157157
)
158+
TRUTHY_FUNCTION: Final[ErrorCode] = ErrorCode(
159+
"truthy-function",
160+
"Warn about function that always evaluate to true in boolean contexts",
161+
"General",
162+
)
158163
NAME_MATCH: Final = ErrorCode(
159164
"name-match", "Check that type definition has consistent naming", "General"
160165
)

mypy/message_registry.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
148148
code=codes.TRUTHY_BOOL,
149149
)
150150
FUNCTION_ALWAYS_TRUE: Final = ErrorMessage(
151-
"Function {} could always be true in boolean context", code=codes.TRUTHY_BOOL
151+
"Function {} could always be true in boolean context", code=codes.TRUTHY_FUNCTION
152152
)
153153
NOT_CALLABLE: Final = "{} not callable"
154154
TYPE_MUST_BE_USED: Final = "Value of type {} must be used"

test-data/unit/check-errorcodes.test

+10-8
Original file line numberDiff line numberDiff line change
@@ -842,19 +842,21 @@ if bad_union: # E: "__main__.bad_union" has type "Union[Foo, object]" of which
842842
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]
843843
pass
844844

845-
def f():
846-
pass
847-
if f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]
848-
pass
849-
if not f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]
850-
pass
851-
conditional_result = 'foo' if f else 'bar' # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-bool]
852-
853845
lst: List[int] = []
854846
if lst:
855847
pass
856848
[builtins fixtures/list.pyi]
857849

850+
[case testTruthyFunctions]
851+
# flags: --strict-optional
852+
def f():
853+
pass
854+
if f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
855+
pass
856+
if not f: # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
857+
pass
858+
conditional_result = 'foo' if f else 'bar' # E: Function "Callable[[], Any]" could always be true in boolean context [truthy-function]
859+
858860
[case testNoOverloadImplementation]
859861
from typing import overload
860862

test-data/unit/check-incremental.test

+5-2
Original file line numberDiff line numberDiff line change
@@ -6017,12 +6017,15 @@ tmp/m.py:2: error: Argument "age" to "foo" has incompatible type "str"; expected
60176017
[case testDisableEnableErrorCodesIncremental]
60186018
# flags: --disable-error-code truthy-bool
60196019
# flags2: --enable-error-code truthy-bool
6020-
def foo() -> int: ...
6020+
class Foo:
6021+
pass
6022+
6023+
foo = Foo()
60216024
if foo:
60226025
...
60236026
[out]
60246027
[out2]
6025-
main:4: error: Function "Callable[[], int]" could always be true in boolean context
6028+
main:7: error: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context
60266029

60276030
[case testModuleAsProtocolImplementationSerialize]
60286031
import m

test-data/unit/check-inline-config.test

+10-4
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,11 @@ main:1: error: Setting "strict" not supported in inline configuration: specify i
166166
[case testInlineErrorCodes]
167167
# flags: --strict-optional
168168
# mypy: enable-error-code="ignore-without-code,truthy-bool"
169+
class Foo:
170+
pass
169171

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

174176
[case testInlineErrorCodesOverrideConfig]
@@ -178,8 +180,10 @@ import tests.bar
178180
import tests.baz
179181
[file foo.py]
180182
# mypy: disable-error-code="truthy-bool"
183+
class Foo:
184+
pass
181185

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

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

194198
[file tests/baz.py]
195199
# mypy: disable-error-code="truthy-bool"
200+
class Foo:
201+
pass
196202

197-
def foo() -> int: ...
203+
foo = Foo()
198204
if foo: ...
199205
42 + "no" # type: ignore
200206

test-data/unit/check-python38.test

+1-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def f(x: int = (c := 4)) -> int:
310310
z2: NT # E: Variable "NT" is not valid as a type \
311311
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
312312

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

test-data/unit/check-unreachable-code.test

+2-1
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,8 @@ class Case1:
936936
return False and self.missing() # E: Right operand of "and" is never evaluated
937937

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

941942
def property_decorator_missing(self) -> bool:
942943
return True

0 commit comments

Comments
 (0)