diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2023-04-26 13:16:21 +0200 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2023-05-02 20:01:39 +0200 |
commit | 26686d544eab7236a92e56cc90c34b6e3a37f473 (patch) | |
tree | ca93e25911bdaa0608653d2a215d0b0fda5fbe1a /pylint/checkers | |
parent | 87fe5e58c2013b4b1949836c142993464b153237 (diff) | |
download | pylint-git-26686d544eab7236a92e56cc90c34b6e3a37f473.tar.gz |
Merge the compare-to-zero extensions to 'implicit_booleaness_checker'
Diffstat (limited to 'pylint/checkers')
-rw-r--r-- | pylint/checkers/refactoring/implicit_booleaness_checker.py | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/pylint/checkers/refactoring/implicit_booleaness_checker.py b/pylint/checkers/refactoring/implicit_booleaness_checker.py index 3215d7ecd..ff1a36205 100644 --- a/pylint/checkers/refactoring/implicit_booleaness_checker.py +++ b/pylint/checkers/refactoring/implicit_booleaness_checker.py @@ -4,6 +4,8 @@ from __future__ import annotations +import itertools + import astroid from astroid import bases, nodes, util @@ -12,6 +14,14 @@ from pylint.checkers import utils from pylint.interfaces import HIGH, INFERENCE +def _is_constant_zero(node: str | nodes.NodeNG) -> bool: + # We have to check that node.value is not False because node.value == 0 is True + # when node.value is False + return ( + isinstance(node, astroid.Const) and node.value == 0 and node.value is not False + ) + + class ImplicitBooleanessChecker(checkers.BaseChecker): """Checks for incorrect usage of comparisons or len() inside conditions. @@ -70,6 +80,12 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): "used to check for emptiness; Use implicit booleaness instead " "of a collection classes; empty collections are considered as false", ), + "C1804": ( + '"%s" can be simplified to "%s" as 0 is falsey', + "use-implicit-booleaness-not-comparison-to-zero", + "Used when Pylint detects comparison to a 0 constant.", + {"default_enabled": False, "old_names": [("C2001", "compare-to-zero")]}, + ), } options = () @@ -149,6 +165,49 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): @utils.only_required_for_messages("use-implicit-booleaness-not-comparison") def visit_compare(self, node: nodes.Compare) -> None: self._check_use_implicit_booleaness_not_comparison(node) + self._check_compare_to_zero(node) + + @utils.only_required_for_messages("compare-to-zero") + def _check_compare_to_zero(self, node: nodes.Compare) -> None: + # pylint: disable=duplicate-code + _operators = ["!=", "==", "is not", "is"] + # note: astroid.Compare has the left most operand in node.left + # while the rest are a list of tuples in node.ops + # the format of the tuple is ('compare operator sign', node) + # here we squash everything into `ops` to make it easier for processing later + ops: list[tuple[str, nodes.NodeNG]] = [("", node.left)] + ops.extend(node.ops) + iter_ops = iter(ops) + all_ops = list(itertools.chain(*iter_ops)) + + for ops_idx in range(len(all_ops) - 2): + op_1 = all_ops[ops_idx] + op_2 = all_ops[ops_idx + 1] + op_3 = all_ops[ops_idx + 2] + error_detected = False + + # 0 ?? X + if _is_constant_zero(op_1) and op_2 in _operators: + error_detected = True + op = op_3 + # X ?? 0 + elif op_2 in _operators and _is_constant_zero(op_3): + error_detected = True + op = op_1 + + if error_detected: + original = f"{op_1.as_string()} {op_2} {op_3.as_string()}" + suggestion = ( + op.as_string() + if op_2 in {"!=", "is not"} + else f"not {op.as_string()}" + ) + self.add_message( + "compare-to-zero", + args=(original, suggestion), + node=node, + confidence=HIGH, + ) def _check_use_implicit_booleaness_not_comparison( self, node: nodes.Compare |