Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

redefined_variable_type.py 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
  2. # Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
  3. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  4. # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
  5. import six
  6. import astroid
  7. from pylint.checkers import BaseChecker
  8. from pylint.checkers.utils import check_messages, is_none, node_type
  9. from pylint.interfaces import IAstroidChecker
  10. BUILTINS = six.moves.builtins.__name__
  11. class MultipleTypesChecker(BaseChecker):
  12. """Checks for variable type redefinitions (NoneType excepted)
  13. At a function, method, class or module scope
  14. This rule could be improved:
  15. - Currently, if an attribute is set to different types in 2 methods of a
  16. same class, it won't be detected (see functional test)
  17. - One could improve the support for inference on assignment with tuples,
  18. ifexpr, etc. Also it would be great to have support for inference on
  19. str.split()
  20. """
  21. __implements__ = IAstroidChecker
  22. name = 'multiple_types'
  23. msgs = {'R0204': ('Redefinition of %s type from %s to %s',
  24. 'redefined-variable-type',
  25. 'Used when the type of a variable changes inside a '
  26. 'method or a function.'
  27. ),
  28. }
  29. def visit_classdef(self, _):
  30. self._assigns.append({})
  31. @check_messages('redefined-variable-type')
  32. def leave_classdef(self, _):
  33. self._check_and_add_messages()
  34. visit_functiondef = visit_classdef
  35. leave_functiondef = leave_module = leave_classdef
  36. def visit_module(self, _):
  37. self._assigns = [{}]
  38. def _check_and_add_messages(self):
  39. assigns = self._assigns.pop()
  40. for name, args in assigns.items():
  41. if len(args) <= 1:
  42. continue
  43. orig_node, orig_type = args[0]
  44. # Check if there is a type in the following nodes that would be
  45. # different from orig_type.
  46. for redef_node, redef_type in args[1:]:
  47. if redef_type == orig_type:
  48. continue
  49. # if a variable is defined to several types in a if node,
  50. # this is not actually redefining.
  51. orig_parent = orig_node.parent
  52. redef_parent = redef_node.parent
  53. if isinstance(orig_parent, astroid.If):
  54. if orig_parent == redef_parent:
  55. if (redef_node in orig_parent.orelse and
  56. orig_node not in orig_parent.orelse):
  57. orig_node, orig_type = redef_node, redef_type
  58. continue
  59. elif (isinstance(redef_parent, astroid.If) and
  60. redef_parent in orig_parent.nodes_of_class(astroid.If)):
  61. orig_node, orig_type = redef_node, redef_type
  62. continue
  63. orig_type = orig_type.replace(BUILTINS + ".", '')
  64. redef_type = redef_type.replace(BUILTINS + ".", '')
  65. self.add_message('redefined-variable-type', node=redef_node,
  66. args=(name, orig_type, redef_type))
  67. break
  68. def visit_assign(self, node):
  69. # we don't handle multiple assignment nor slice assignment
  70. target = node.targets[0]
  71. if isinstance(target, (astroid.Tuple, astroid.Subscript)):
  72. return
  73. # ignore NoneType
  74. if is_none(node):
  75. return
  76. _type = node_type(node.value)
  77. if _type:
  78. self._assigns[-1].setdefault(target.as_string(), []).append(
  79. (node, _type.pytype()))
  80. def register(linter):
  81. """Required method to auto register this checker.
  82. :param linter: Main interface object for Pylint plugins
  83. :type linter: Pylint object
  84. """
  85. linter.register_checker(MultipleTypesChecker(linter))