summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcpopa <devnull@localhost>2013-12-27 18:41:47 +0200
committercpopa <devnull@localhost>2013-12-27 18:41:47 +0200
commit54bba4b019a2da504351469ee3cef7c1c8665817 (patch)
treeb15d4e15cd0d29fe8e4881747386e46790320eea
parent3927b761d923c9d8d32290c5a694acda9083480b (diff)
downloadpylint-54bba4b019a2da504351469ee3cef7c1c8665817.tar.gz
Enhance used-before-assignment check to look for nonlocal uses.
-rw-r--r--ChangeLog4
-rw-r--r--checkers/variables.py25
-rw-r--r--test/input/func_used_before_assignment_py30.py27
-rw-r--r--test/messages/func_used_before_assignment_py30.txt2
4 files changed, 58 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index f5b7192..c3c05e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,10 @@
ChangeLog for Pylint
====================
+--
+ * Enhance the check for 'used-before-assignment' to look
+ for 'nonlocal' uses.
+
2013-12-22 -- 1.1.0
* Add new check for use of deprecated pragma directives "pylint:disable-msg"
or "pylint:enable-msg" (I0022, deprecated-pragma) which was previously
diff --git a/checkers/variables.py b/checkers/variables.py
index 90b7fe7..4ad82c4 100644
--- a/checkers/variables.py
+++ b/checkers/variables.py
@@ -507,6 +507,31 @@ builtins. Remember that you should avoid to define new builtins when possible.'
# defined in global or builtin scope
if defframe.root().lookup(name)[1]:
maybee0601 = False
+ else:
+ # check if we have a nonlocal
+ frame = node.frame()
+ frame_parent = frame.parent
+ if (isinstance(frame, astroid.Function) and
+ isinstance(frame_parent, astroid.Function)):
+ # detect that the name exists
+ # in the upper level
+ defined_higher = False
+ for assign in frame_parent.get_children():
+ if not isinstance(assign, astroid.Assign):
+ continue
+ for target in assign.targets:
+ if (isinstance(target, astroid.AssName) and
+ target.name == name):
+ defined_higher = True
+ break
+ if defined_higher:
+ break
+ for child in frame.get_children():
+ if not isinstance(child, astroid.Nonlocal):
+ continue
+ if name in child.names and defined_higher:
+ maybee0601 = False
+ break
if (maybee0601
and stmt.fromlineno <= defstmt.fromlineno
and not is_defined_before(node)
diff --git a/test/input/func_used_before_assignment_py30.py b/test/input/func_used_before_assignment_py30.py
new file mode 100644
index 0000000..04987f3
--- /dev/null
+++ b/test/input/func_used_before_assignment_py30.py
@@ -0,0 +1,27 @@
+"""Check for nonlocal and used-before-assignment"""
+# pylint: disable=missing-docstring, unused-variable
+
+__revision__ = 0
+
+def test_ok():
+ """ uses nonlocal """
+ cnt = 1
+ def wrap():
+ nonlocal cnt
+ cnt = cnt + 1
+ wrap()
+
+def test_fail():
+ """ doesn't use nonlocal """
+ cnt = 1
+ def wrap():
+ cnt = cnt + 1
+ wrap()
+
+def test2_fail():
+ """ uses nonlocal, but without an
+ outer label defined. """
+ def wrap():
+ nonlocal cnt
+ cnt = cnt + 1
+ wrap()
diff --git a/test/messages/func_used_before_assignment_py30.txt b/test/messages/func_used_before_assignment_py30.txt
new file mode 100644
index 0000000..f731b3c
--- /dev/null
+++ b/test/messages/func_used_before_assignment_py30.txt
@@ -0,0 +1,2 @@
+E: 18:test_fail.wrap: Using variable 'cnt' before assignment
+E: 26:test2_fail.wrap: Using variable 'cnt' before assignment \ No newline at end of file