summaryrefslogtreecommitdiff
path: root/pylint/checkers/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'pylint/checkers/base.py')
-rw-r--r--pylint/checkers/base.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index 73e0315..12e4e17 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -1882,6 +1882,93 @@ class NotChecker(_BasicChecker):
args=(node.as_string(), suggestion))
+class MultipleTypesChecker(BaseChecker):
+ """Checks for variable type redefinitions (NoneType excepted)
+
+ At a function, method, class or module scope
+ """
+ __implements__ = IAstroidChecker
+
+ name = 'multiple_types'
+ msgs = {'R0204': ('Redefinition of %s type from %s to %s',
+ 'redefined-variable-type',
+ 'Used when the type of a variable changes inside a '
+ 'method or a function.'
+ ),
+ }
+
+ def visit_classdef(self, _):
+ self._class_assigns = {}
+
+ @check_messages('redefined-variable-type')
+ def leave_classdef(self, _):
+ self._check_and_add_messages(self._class_assigns)
+
+ def visit_functiondef(self, _):
+ self._func_assigns = {}
+
+ @check_messages('redefined-variable-type')
+ def leave_functiondef(self, _):
+ self._check_and_add_messages(self._func_assigns)
+
+ def visit_module(self, _):
+ self._module_assigns = {}
+
+ @check_messages('redefined-variable-type')
+ def leave_module(self, _):
+ self._check_and_add_messages(self._module_assigns)
+
+ def _check_and_add_messages(self, assigns):
+ for name, args in assigns.iteritems():
+ if len(args) <= 1:
+ continue
+ orig_node, orig_type = args[0]
+ # check if there is a type in the following nodes that would be
+ # different from orig_type
+ for redef_node, redef_type in args[1:]:
+ if redef_type != orig_type:
+ orig_type = orig_type.replace('__builtin__.', '')
+ redef_type = redef_type.replace('__builtin__.', '')
+ self.add_message('redefined-variable-type', node=redef_node,
+ args=(name, orig_type, redef_type))
+ break
+
+ def visit_assign(self, node):
+ scope = node.scope()
+ if isinstance(scope, astroid.FunctionDef):
+ msgs = self._func_assigns
+ elif isinstance(scope, astroid.Module):
+ msgs = self._module_assigns
+ elif isinstance(scope, astroid.ClassDef):
+ msgs = self._class_assigns
+ else:
+ return
+ # we don't handle multiple assignment nor slice assignment
+ target = node.targets[0]
+ if isinstance(target, (astroid.Tuple, astroid.Subscript)):
+ return
+ # ignore NoneType
+ if node.value.as_string() == 'None':
+ return
+ # check there is only one possible type for the assign node. Else we
+ # don't handle it for now
+ types = set()
+ try:
+ for var_type in node.value.infer():
+ if var_type == astroid.YES or var_type.as_string() == 'None':
+ continue
+ var_type = var_type.pytype()
+ types.add(var_type)
+ if len(types) > 1:
+ print ('more than one possible type for node %s (%s, %s)'
+ % (node.as_string(), types.pop(), types.pop()))
+ return
+ except InferenceError:
+ return
+ if types:
+ msgs.setdefault(target.as_string(), []).append((node, types.pop()))
+
+
def register(linter):
"""required method to auto register this checker"""
linter.register_checker(BasicErrorChecker(linter))
@@ -1894,3 +1981,4 @@ def register(linter):
linter.register_checker(NotChecker(linter))
linter.register_checker(RecommandationChecker(linter))
linter.register_checker(ElifChecker(linter))
+ linter.register_checker(MultipleTypesChecker(linter))