-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[pylint]
Implement use-implicit-booleaness-not-len
(PLC1802
)
#14309
base: main
Are you sure you want to change the base?
Conversation
|
code | total | + violation | - violation | + fix | - fix |
---|---|---|---|---|---|
PLC1802 | 16 | 16 | 0 | 0 | 0 |
C1802
)C1802
)
C1802
)[pylint]
Implement use-implicit-booleaness-not-len (C1802
)
[pylint]
Implement use-implicit-booleaness-not-len (C1802
)[pylint]
Implement use-implicit-booleaness-not-len
(C1802
)
[pylint]
Implement use-implicit-booleaness-not-len
(C1802
)[pylint]
Implement use-implicit-booleaness-not-len
(PLC1802
)
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.
Thanks so much for this contribution! Some minor nits/requests for a few more tests. I also didn't get a chance to review the implementation carefully, but I'll hold off for now in case the new tests inspire any changes.
crates/ruff_linter/resources/test/fixtures/pylint/len_as_condition.py
Outdated
Show resolved
Hide resolved
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.
Could you test some more complicated examples involving rebindings/scope since you use those properties of the semantic model? Maybe something like:
def f(cond:bool):
x = [1,2,3]
if cond:
x = [4,5,6]
if len(x):
print(x)
and
def g(cond:bool):
x = [1,2,3]
if cond:
x = [4,5,6]
if len(x):
print(x)
del x
and
def h(cond:bool):
x = [1,2,3]
x = 123
if len(x):
print(x)
and
def outer():
x = [1,2,3]
def inner(x:int):
return x+1
if len(x):
print(x)
or any others you can think of to exercise the implementation.
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.
Good point, this implementation does support only one name assignment in current scope.
It would be fair to check all prior assignments to this.
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.
I'm unsure, there are 3 options:
- check only those without rebindings (as is now)
- check recursively if all of possible rebindings bounds to a sequence-like object
- leave it for type inference
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.
This looks really great, thank you! I think even with the possible misses around control flow, this makes sense to implement. You would have to be pretty pathological to create a custom class C
that implements __len__
and __bool__
where bool(x) != bool(len(x))
for x: C
... so I think it's okay.
| PythonType::List | ||
| PythonType::Set | ||
| PythonType::Tuple | ||
| PythonType::Generator |
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 generators have a length? I think len((x for x in range(10))
gives a TypeError
, whereas (x for x in range(10))
is truthy, so these would not have the same behavior in a boolean test.
|
||
// Attempt to find the binding's value | ||
let Some(binding_value) = find_binding_value(binding, semantic) else { | ||
// check for `vararg` |
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.
Nice! Should we also see if its bound to kwargs
? Those should have a length too.
// Match against specific built-in constructors that return sequences | ||
return semantic | ||
.resolve_builtin_symbol(func) | ||
.is_some_and(|func| matches!(func, "list" | "set" | "dict" | "tuple")); |
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.
How about range
, str
, repr
, or other builtins that return sets, dicts, etc.?
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.
I'm gonna explore that, good point.
Summary
This PR implements
use-implicit-booleaness-not-len
/C1802
Test Plan
Checked against pylint tests:
Notes
len-as-condition
might be solution)References
PEP 8 on empty sequences and implicit booleaness