diff options
author | cpopa <devnull@localhost> | 2014-09-03 16:21:07 +0300 |
---|---|---|
committer | cpopa <devnull@localhost> | 2014-09-03 16:21:07 +0300 |
commit | 2da14d2ceee5d3d1727dc8abdf47af96e68bc63c (patch) | |
tree | c226617ff17d333a6dbd1e45fe3dbdc4aa5fa532 /checkers/variables.py | |
parent | b4e426cb7443d465bfb0b641b1e9e69f66b330e6 (diff) | |
download | pylint-2da14d2ceee5d3d1727dc8abdf47af96e68bc63c.tar.gz |
Extend the cases where 'undefined-variable' and 'used-before-assignment' can be detected. Closes issue #291.
Diffstat (limited to 'checkers/variables.py')
-rw-r--r-- | checkers/variables.py | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/checkers/variables.py b/checkers/variables.py index 6a26f5a..12a26ad 100644 --- a/checkers/variables.py +++ b/checkers/variables.py @@ -37,6 +37,7 @@ import six SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") +PY3K = sys.version_info >= (3, 0) def in_for_else_branch(parent, stmt): """Returns True if stmt in inside the else branch for a parent For stmt.""" @@ -731,9 +732,8 @@ builtins. Remember that you should avoid to define new builtins when possible.' # class A: # b = 1 # c = lambda b=b: b * b - class_assignment = (isinstance(frame, astroid.Class) and - name in frame.locals) - if not class_assignment: + if not (isinstance(frame, astroid.Class) and + name in frame.locals): continue # the name has already been consumed, only check it's not a loop # variable used outside the loop @@ -773,10 +773,35 @@ builtins. Remember that you should avoid to define new builtins when possible.' maybee0601 = not any(isinstance(child, astroid.Nonlocal) and name in child.names for child in defframe.get_children()) + + # Handle a couple of class scoping issues. + annotation_return = False + # The class reuses itself in the class scope. + recursive_klass = (frame is defframe and + defframe.parent_of(node) and + isinstance(defframe, astroid.Class) and + node.name == defframe.name) if (self._to_consume[-1][-1] == 'lambda' and isinstance(frame, astroid.Class) and name in frame.locals): maybee0601 = True + elif (isinstance(defframe, astroid.Class) and + isinstance(frame, astroid.Function)): + # Special rule for function return annotations, + # which uses the same name as the class where + # the function lives. + if (PY3K and node is frame.returns and + defframe.parent_of(frame.returns)): + maybee0601 = annotation_return = True + + if (maybee0601 and defframe.name in defframe.locals and + defframe.locals[name][0].lineno < frame.lineno): + # Detect class assignments with the same + # name as the class. In this case, no warning + # should be raised. + maybee0601 = False + elif recursive_klass: + maybee0601 = True else: maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno @@ -785,8 +810,11 @@ builtins. Remember that you should avoid to define new builtins when possible.' and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))): - if defstmt is stmt and isinstance(node, (astroid.DelName, - astroid.AssName)): + if recursive_klass or (defstmt is stmt and + isinstance(node, (astroid.DelName, + astroid.AssName))): + self.add_message('undefined-variable', args=name, node=node) + elif annotation_return: self.add_message('undefined-variable', args=name, node=node) elif self._to_consume[-1][-1] != 'lambda': # E0601 may *not* occurs in lambda scope. |