diff options
author | ethan-leba <ethanleba5@gmail.com> | 2020-05-23 14:02:45 -0400 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2020-08-31 21:16:25 +0200 |
commit | 5c84f0dafe79ca1631f70cf8355a5ccc7f9ed232 (patch) | |
tree | fb0608c6896969ffd202bc771831bcbc70203b5c /pylint/checkers/refactoring.py | |
parent | 21d168a2449f97e45f369f159b2c279572eb64d5 (diff) | |
download | pylint-git-5c84f0dafe79ca1631f70cf8355a5ccc7f9ed232.tar.gz |
Add bool() check for len-as-condition
Diffstat (limited to 'pylint/checkers/refactoring.py')
-rw-r--r-- | pylint/checkers/refactoring.py | 47 |
1 files changed, 26 insertions, 21 deletions
diff --git a/pylint/checkers/refactoring.py b/pylint/checkers/refactoring.py index d1f2adce8..4d93299d9 100644 --- a/pylint/checkers/refactoring.py +++ b/pylint/checkers/refactoring.py @@ -39,6 +39,7 @@ import collections import itertools import tokenize from functools import reduce +from typing import Optional import astroid from astroid import decorators @@ -59,24 +60,10 @@ def _if_statement_is_always_returning(if_node, returning_node_class): return False -def _is_len_call(node): - """Checks if node is len(SOMETHING).""" - return ( - isinstance(node, astroid.Call) - and isinstance(node.func, astroid.Name) - and node.func.name == "len" - ) - - def _is_constant_zero(node): return isinstance(node, astroid.Const) and node.value == 0 -def _node_is_test_condition(node): - """ Checks if node is an if, while, assert or if expression statement.""" - return isinstance(node, (astroid.If, astroid.While, astroid.Assert, astroid.IfExp)) - - def _is_trailing_comma(tokens, index): """Check if the given token is a trailing comma @@ -122,6 +109,26 @@ def _is_trailing_comma(tokens, index): return False +def _is_call_of_name(node: astroid.node_classes.NodeNG, name: str) -> bool: + """Checks if node is a function call with the given name""" + return ( + isinstance(node, astroid.Call) + and isinstance(node.func, astroid.Name) + and node.func.name == name + ) + + +def _is_test_condition( + node: astroid.node_classes.NodeNG, + parent: Optional[astroid.node_classes.NodeNG] = None, +) -> bool: + """Returns true if the given node is being tested for truthiness""" + parent = parent or node.parent + if isinstance(parent, (astroid.While, astroid.If, astroid.IfExp, astroid.Assert)): + return node is parent.test or parent.test.parent_of(node) + return _is_call_of_name(parent, "bool") and parent.parent_of(node) + + class RefactoringChecker(checkers.BaseTokenChecker): """Looks for code which can be refactored @@ -1512,6 +1519,7 @@ class LenChecker(checkers.BaseChecker): * while not len(sequence): * assert len(sequence): * assert not len(sequence): + * bool(len(sequence)) """ __implements__ = (interfaces.IAstroidChecker,) @@ -1538,7 +1546,7 @@ class LenChecker(checkers.BaseChecker): # a len(S) call is used inside a test condition # could be if, while, assert or if expression statement # e.g. `if len(S):` - if _is_len_call(node): + if _is_call_of_name(node, "len"): # the len() call could also be nested together with other # boolean operations, e.g. `if z or len(x):` parent = node.parent @@ -1547,11 +1555,8 @@ class LenChecker(checkers.BaseChecker): # we're finally out of any nested boolean operations so check if # this len() call is part of a test condition - if not _node_is_test_condition(parent): - return - if not (node is parent.test or parent.test.parent_of(node)): - return - self.add_message("len-as-condition", node=node) + if _is_test_condition(node, parent): + self.add_message("len-as-condition", node=node) @utils.check_messages("len-as-condition") def visit_unaryop(self, node): @@ -1561,7 +1566,7 @@ class LenChecker(checkers.BaseChecker): if ( isinstance(node, astroid.UnaryOp) and node.op == "not" - and _is_len_call(node.operand) + and _is_call_of_name(node.operand, "len") ): self.add_message("len-as-condition", node=node) |