comparetozero.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. # Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
  2. # Copyright (c) 2017-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
  3. # Copyright (c) 2019, 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  4. # Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
  5. # Copyright (c) 2020 Anthony Sottile <asottile@umich.edu>
  6. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  7. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  8. # Copyright (c) 2021 bernie gray <bfgray3@users.noreply.github.com>
  9. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  10. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  11. """Looks for comparisons to zero."""
  12. import itertools
  13. from typing import Any, Iterable
  14. import astroid
  15. from astroid import nodes
  16. from pylint import checkers, interfaces
  17. from pylint.checkers import utils
  18. def _is_constant_zero(node):
  19. return isinstance(node, astroid.Const) and node.value == 0
  20. class CompareToZeroChecker(checkers.BaseChecker):
  21. """Checks for comparisons to zero.
  22. Most of the times you should use the fact that integers with a value of 0 are false.
  23. An exception to this rule is when 0 is allowed in the program and has a
  24. different meaning than None!
  25. """
  26. __implements__ = (interfaces.IAstroidChecker,)
  27. # configuration section name
  28. name = "compare-to-zero"
  29. msgs = {
  30. "C2001": (
  31. "Avoid comparisons to zero",
  32. "compare-to-zero",
  33. "Used when Pylint detects comparison to a 0 constant.",
  34. )
  35. }
  36. priority = -2
  37. options = ()
  38. @utils.check_messages("compare-to-zero")
  39. def visit_compare(self, node: nodes.Compare) -> None:
  40. _operators = ["!=", "==", "is not", "is"]
  41. # note: astroid.Compare has the left most operand in node.left
  42. # while the rest are a list of tuples in node.ops
  43. # the format of the tuple is ('compare operator sign', node)
  44. # here we squash everything into `ops` to make it easier for processing later
  45. ops = [("", node.left)]
  46. ops.extend(node.ops)
  47. iter_ops: Iterable[Any] = iter(ops)
  48. ops = list(itertools.chain(*iter_ops))
  49. for ops_idx in range(len(ops) - 2):
  50. op_1 = ops[ops_idx]
  51. op_2 = ops[ops_idx + 1]
  52. op_3 = ops[ops_idx + 2]
  53. error_detected = False
  54. # 0 ?? X
  55. if _is_constant_zero(op_1) and op_2 in _operators:
  56. error_detected = True
  57. # X ?? 0
  58. elif op_2 in _operators and _is_constant_zero(op_3):
  59. error_detected = True
  60. if error_detected:
  61. self.add_message("compare-to-zero", node=node)
  62. def register(linter):
  63. """Required method to auto register this checker."""
  64. linter.register_checker(CompareToZeroChecker(linter))