-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Check expr that are implicitly true for lack dunder bool/len #10666
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
8a311a1
to
32f1737
Compare
mypy/types.py
Outdated
@@ -842,6 +842,14 @@ def __init__(self, typ: mypy.nodes.TypeInfo, args: Sequence[Type], | |||
# Literal context. | |||
self.last_known_value = last_known_value | |||
|
|||
if ( | |||
self.type and | |||
not self.type.has_readable_member('__bool__') and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to check for Python 2 __nonzero__
?
This comment has been minimized.
This comment has been minimized.
32f1737
to
b3b301f
Compare
This comment has been minimized.
This comment has been minimized.
Since subclasses can freely add |
I see that. Factoring such "knowledge" into unreachable code detection would be practically incorrect. def f(a: object) -> None:
a.foobar()
_ = len(a) For the cases above it's easy to justify a type error since, while I wonder how it'll be practical to add such check, perhaps through an optional warning, i.e. make it more of a linting rule and not something that affects analysis? I keep seeing this in real world code, e.g. def f(a: Iterable) -> None:
if a: # signature says Iterable but we're really expecting Collection and up
... and while it's not technically incorrect, it's likely an error in typing (s/Iterable/Collection/). Here's another favorite: class MyMessage(google.protobuf.Message):
...
def f(a: MyMessage) -> None:
if a.sub_message: # user assumes missing Protobuf field manifests as None but it's an eagerly created empty message
... Per Protobuf guidance, one shouldn't inherit from messages, so those could indeed be marked |
This sounds like a good match for an optional check that can be activated via enabling some error code. Also |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
c84a736
to
801f274
Compare
@JukkaL Would it be OK to bolt this unto Also, is there anything that can print a node in a way that's short enough for messages, e.g.
Obviously I'd like something friendlier than |
This comment has been minimized.
This comment has been minimized.
I don't know that anything else in mypy prints nodes, and in general it looks pretty noisy to me. Most mypy error messages just print out a type and it seems to be fine. If you want, consider special casing RefExpr, and not printing the node for anything else. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
4ba5401
to
c58bb6a
Compare
Thanks for updating the copy @hauntsaninja. |
f49ceda
to
e101c01
Compare
e101c01
to
13af1eb
Compare
@ikonst Just made some more copy changes (including changing the error code name) and added some test case for My concerns with the phrasing:
|
Oh actually, I think I like the error code "bool-conversion" for this. Thoughts? |
Didn't yet review all changes, but "bool-conversion" rings like that "strict bool" feature we used to have. That is, it insinuates that relying on bool conversion is wrong perhaps even in cases where it's idiomatic Python. |
docs/source/error_code_list2.rst
Outdated
-------------------------------------------------------------------------------- | ||
|
||
Warn when an expression whose type does not implement __bool__ or __len__ is used in boolean context | ||
since it could always evaluate to true (if one also assumes that no subtype implements __bool__ or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"You should always take an umbrella since it could always rain" -- not exactly what we mean :)
Maybe something like "since by default objects evaluate as true"?
The thing with subtypes is correct but I wonder if we can position it differently? Essentially we're warning due to lack of type-based guarantee of meaningful bool. What would we say if the python data model would be defined in such a way that len(object()) == 0)
? What would our warning, if any, be like?
@hauntsaninja ping :) |
Yeah, something like "since by default objects always evaluate to true (this warning assumes that no subtype implements bool ..." seems good to me. |
Thanks for making this happen! |
Optional check which disallows using implicitly true expressions (whose type has no
__bool__
nor__len__
) in boolean context. The check is only enabled when using strict optionals.This does not affect type narrowing / reachability which would continue seeing such types as "can be true / can be false". The rationale is that it's not an error per the data model to evaluate any object as boolean, and an always-true behavior could be overridden in a derived type.
Some builtin fixtures are updated to make sure common types (str, bool, int) are not considered to be always true.