confusing_elif.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  2. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  3. # Copyright (c) 2021 Ashley Whetter <ashley@awhetter.co.uk>
  4. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  5. # Copyright (c) 2021 Andreas Finkler <andi.finkler@gmail.com>
  6. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  7. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  8. from astroid import nodes
  9. from pylint.checkers import BaseChecker
  10. from pylint.checkers.utils import check_messages
  11. from pylint.interfaces import IAstroidChecker
  12. from pylint.lint import PyLinter
  13. class ConfusingConsecutiveElifChecker(BaseChecker):
  14. """Checks if "elif" is used right after an indented block that finishes with "if" or "elif" itself."""
  15. __implements__ = IAstroidChecker
  16. name = "confusing_elif"
  17. priority = -1
  18. msgs = {
  19. "R5601": (
  20. "Consecutive elif with differing indentation level, consider creating a function to separate the inner elif",
  21. "confusing-consecutive-elif",
  22. "Used when an elif statement follows right after an indented block which itself ends with if or elif. "
  23. "It may not be ovious if the elif statement was willingly or mistakenly unindented. "
  24. "Extracting the indented if statement into a separate function might avoid confusion and prevent errors.",
  25. )
  26. }
  27. @check_messages("confusing-consecutive-elif")
  28. def visit_if(self, node: nodes.If) -> None:
  29. body_ends_with_if = isinstance(
  30. node.body[-1], nodes.If
  31. ) and self._has_no_else_clause(node.body[-1])
  32. if node.has_elif_block() and body_ends_with_if:
  33. self.add_message("confusing-consecutive-elif", node=node.orelse[0])
  34. @staticmethod
  35. def _has_no_else_clause(node: nodes.If):
  36. orelse = node.orelse
  37. while orelse and isinstance(orelse[0], nodes.If):
  38. orelse = orelse[0].orelse
  39. if not orelse or isinstance(orelse[0], nodes.If):
  40. return True
  41. return False
  42. def register(linter: PyLinter):
  43. """This required method auto registers the checker.
  44. :param linter: The linter to register the checker to.
  45. :type linter: pylint.lint.PyLinter
  46. """
  47. linter.register_checker(ConfusingConsecutiveElifChecker(linter))