diff options
| author | Claudiu Popa <pcmanticore@gmail.com> | 2014-09-03 16:21:07 +0300 |
|---|---|---|
| committer | Claudiu Popa <pcmanticore@gmail.com> | 2014-09-03 16:21:07 +0300 |
| commit | 6037ef699f802301d0c36ac04a06f1fc5f59849f (patch) | |
| tree | 3916ea30078d81ffcc997b27b6d8208e579f0adf /checkers/variables.py | |
| parent | 20312a8446defe63b4e6a8dc55af115cbc2c6220 (diff) | |
| download | pylint-git-6037ef699f802301d0c36ac04a06f1fc5f59849f.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 6a26f5a7b..12a26adbd 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. |
