summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2018-10-11 08:15:20 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2018-10-11 08:15:20 +0200
commit0a39200932a9143082255e600ff5a7e764520ce8 (patch)
treef8624379e8e815c8c5bc7f7e53347301628ecc6c
parentfcc01516ae176ad3fdedc4497328105f3314e376 (diff)
downloadpylint-git-0a39200932a9143082255e600ff5a7e764520ce8.tar.gz
Consider ``range()`` objects for ``undefined-loop-variable`` leaking from iteration.
Close #2533
-rw-r--r--ChangeLog4
-rw-r--r--pylint/checkers/format.py6
-rw-r--r--pylint/checkers/variables.py11
-rw-r--r--pylint/test/functional/cellvar_escaping_loop.py14
-rw-r--r--pylint/test/functional/cellvar_escaping_loop.txt13
-rw-r--r--pylint/test/functional/undefined_loop_variable.py16
-rw-r--r--pylint/test/functional/undefined_loop_variable.txt3
7 files changed, 45 insertions, 22 deletions
diff --git a/ChangeLog b/ChangeLog
index 962141f2f..51f34f002 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,10 @@ What's New in Pylint 2.2?
Release date: TBA
+ * Consider ``range()`` objects for ``undefined-loop-variable`` leaking from iteration.
+
+ Close #2533
+
* ``deprecated-method`` can use the attribute name for identifying a deprecated method
Previously we were using the fully qualified name, which we still do, but the fully
diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py
index b5f35cd00..38b6649c5 100644
--- a/pylint/checkers/format.py
+++ b/pylint/checkers/format.py
@@ -1333,3 +1333,9 @@ class FormatChecker(BaseTokenChecker):
def register(linter):
"""required method to auto register this checker """
linter.register_checker(FormatChecker(linter))
+
+
+def funkier():
+ for val in range(3):
+ pass
+ print(val)
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 78d78a913..784568ddd 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -64,6 +64,7 @@ PY3K = sys.version_info >= (3, 0)
# for `abc`
METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"}
TYPING_TYPE_CHECKS_GUARDS = frozenset({"typing.TYPE_CHECKING", "TYPE_CHECKING"})
+BUILTIN_RANGE = "builtins.range"
def _is_from_future_import(stmt, name):
@@ -1145,7 +1146,7 @@ class VariablesChecker(BaseChecker):
):
return
- # For functions we can do more by inferring the length of the iterred object
+ # For functions we can do more by inferring the length of the itered object
if not isinstance(assign, astroid.For):
self.add_message("undefined-loop-variable", args=name, node=node)
return
@@ -1155,6 +1156,14 @@ class VariablesChecker(BaseChecker):
except astroid.InferenceError:
self.add_message("undefined-loop-variable", args=name, node=node)
else:
+ if (
+ isinstance(inferred, astroid.Instance)
+ and inferred.qname() == BUILTIN_RANGE
+ ):
+ # Consider range() objects safe, even if they might not yield any results.
+ return
+
+ # Consider sequences.
sequences = (
astroid.List,
astroid.Tuple,
diff --git a/pylint/test/functional/cellvar_escaping_loop.py b/pylint/test/functional/cellvar_escaping_loop.py
index 29107b575..23c6b4b2e 100644
--- a/pylint/test/functional/cellvar_escaping_loop.py
+++ b/pylint/test/functional/cellvar_escaping_loop.py
@@ -35,16 +35,6 @@ def good_case5():
def good_case6():
- """Accept use of the variable after the loop.
-
- There's already a warning about possibly undefined loop variables, and
- the value will not change any more."""
- for i in range(10):
- print(i)
- return lambda: i # [undefined-loop-variable]
-
-
-def good_case7():
"""Accept use of the variable inside return."""
for i in range(10):
if i == 8:
@@ -52,13 +42,13 @@ def good_case7():
return lambda: -1
-def good_case8():
+def good_case7():
"""Lambda defined and called in loop."""
for i in range(10):
print((lambda x: i + x)(1))
-def good_case9():
+def good_case8():
"""Another eager binding of the cell variable."""
funs = []
for i in range(10):
diff --git a/pylint/test/functional/cellvar_escaping_loop.txt b/pylint/test/functional/cellvar_escaping_loop.txt
index bdd7fe9c6..2ac0b58ef 100644
--- a/pylint/test/functional/cellvar_escaping_loop.txt
+++ b/pylint/test/functional/cellvar_escaping_loop.txt
@@ -1,7 +1,6 @@
-undefined-loop-variable:44:good_case6.<lambda>:Using possibly undefined loop variable 'i'
-cell-var-from-loop:77:bad_case.<lambda>:Cell variable i defined in loop
-cell-var-from-loop:82:bad_case2.<lambda>:Cell variable i defined in loop
-cell-var-from-loop:90:bad_case3.<lambda>:Cell variable j defined in loop
-cell-var-from-loop:100:bad_case4.nested:Cell variable i defined in loop
-cell-var-from-loop:121:bad_case5.<lambda>:Cell variable i defined in loop
-cell-var-from-loop:129:bad_case6.<lambda>:Cell variable i defined in loop
+cell-var-from-loop:67:bad_case.<lambda>:Cell variable i defined in loop
+cell-var-from-loop:72:bad_case2.<lambda>:Cell variable i defined in loop
+cell-var-from-loop:80:bad_case3.<lambda>:Cell variable j defined in loop
+cell-var-from-loop:90:bad_case4.nested:Cell variable i defined in loop
+cell-var-from-loop:111:bad_case5.<lambda>:Cell variable i defined in loop
+cell-var-from-loop:119:bad_case6.<lambda>:Cell variable i defined in loop
diff --git a/pylint/test/functional/undefined_loop_variable.py b/pylint/test/functional/undefined_loop_variable.py
index 3840003b5..a984898f8 100644
--- a/pylint/test/functional/undefined_loop_variable.py
+++ b/pylint/test/functional/undefined_loop_variable.py
@@ -1,4 +1,4 @@
-# pylint: disable=missing-docstring
+# pylint: disable=missing-docstring,redefined-builtin
def do_stuff(some_random_list):
for var in some_random_list:
@@ -59,3 +59,17 @@ def do_stuff_with_a_tuple():
for var in (1, 2, 3):
pass
return var
+
+
+def do_stuff_with_a_range():
+ for var in range(1, 2):
+ pass
+ return var
+
+
+def do_stuff_with_redefined_range():
+ def range(key):
+ yield from [1, key]
+ for var in range(3):
+ pass
+ return var # [undefined-loop-variable]
diff --git a/pylint/test/functional/undefined_loop_variable.txt b/pylint/test/functional/undefined_loop_variable.txt
index 33022d454..64cf6388b 100644
--- a/pylint/test/functional/undefined_loop_variable.txt
+++ b/pylint/test/functional/undefined_loop_variable.txt
@@ -1,2 +1,3 @@
undefined-loop-variable:6:do_stuff:Using possibly undefined loop variable 'var'
-undefined-loop-variable:25::Using possibly undefined loop variable 'var1' \ No newline at end of file
+undefined-loop-variable:25::Using possibly undefined loop variable 'var1'
+undefined-loop-variable:75:do_stuff_with_redefined_range:Using possibly undefined loop variable 'var'