summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryory8 <39745367+yory8@users.noreply.github.com>2019-03-20 08:41:16 +0000
committerClaudiu Popa <pcmanticore@gmail.com>2019-03-20 09:41:16 +0100
commita138a1a8f4bd1ff4ff5718c8b23a9b82c27aef3c (patch)
treef73920bd7dc6827bd0f84e93af32a411ac37e20f
parent30b2ae713eb4453b305407772b29ea68d7c3d074 (diff)
downloadpylint-git-a138a1a8f4bd1ff4ff5718c8b23a9b82c27aef3c.tar.gz
Add new linter: dict-iter-missing-items
Add a new linter to check against forgotten calls to `.items()` when iterating through a dictionary in a `for` loop. Close #2761
-rw-r--r--CONTRIBUTORS.txt3
-rw-r--r--ChangeLog5
-rw-r--r--doc/whatsnew/2.4.rst6
-rw-r--r--pylint/checkers/typecheck.py28
-rw-r--r--pylint/test/functional/dict_iter_missing_items.py23
-rw-r--r--pylint/test/functional/dict_iter_missing_items.txt1
6 files changed, 63 insertions, 3 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 18ae9a83c..cb37e6255 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -272,6 +272,8 @@ contributors:
* Fantix King (UChicago): contributor
+* Yory (yory8): contributor
+
* Thomas Hisch: contributor
* Clément Pit-Claudel : contributor
@@ -283,4 +285,3 @@ contributors:
* Bluesheeptoken: contributor
* Michael Scott Cuthbert: contributor
-
diff --git a/ChangeLog b/ChangeLog
index 095f040f4..160dcaf1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -42,6 +42,10 @@ Release date: TBA
This check is emitted when ``pylint`` finds a class variable that conflicts with a slot
name, which would raise a ``ValueError`` at runtime.
+* Added new check: dict-iter-missing-items (E1141)
+
+ Close #2761
+
* Fix issue with pylint name in output of python -m pylint --version
Close #2764
@@ -52,7 +56,6 @@ Release date: TBA
Close #2816
-
What's New in Pylint 2.3.0?
===========================
diff --git a/doc/whatsnew/2.4.rst b/doc/whatsnew/2.4.rst
index 45a6b832a..b96e0992f 100644
--- a/doc/whatsnew/2.4.rst
+++ b/doc/whatsnew/2.4.rst
@@ -13,6 +13,11 @@ Summary -- Release highlights
New checkers
============
+* We added a new check message ``dict-iter-missing-items``.
+ This is emitted when trying to iterate through a dict in a for loop without calling its .items() method.
+
+ Closes #2761
+
* We added a new check message ``missing-parentheses-for-call-in-test``.
This is emitted in case a call to a function is made inside a test but
it misses parentheses.
@@ -28,7 +33,6 @@ New checkers
__slots__ = ('first', 'second')
first = 1
-
Other Changes
=============
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index fa95d7cc4..61aff8f93 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -353,6 +353,12 @@ MSGS = {
"Emitted when a dict key is not hashable "
"(i.e. doesn't define __hash__ method).",
),
+ "E1141": (
+ "Unpacking a dictionary in iteration without calling .items()",
+ "dict-iter-missing-items",
+ "Emitted when trying to iterate through"
+ "a dict without calling .items()",
+ ),
"W1113": (
"Keyword argument before variable positional arguments list "
"in the definition of %s function",
@@ -1522,6 +1528,28 @@ accessed. Python regular expressions are accepted.",
if not supported_protocol(inferred):
self.add_message(msg, args=node.value.as_string(), node=node.value)
+ @check_messages("dict-items-missing-iter")
+ def visit_for(self, node):
+ if not isinstance(node.target, astroid.node_classes.Tuple):
+ # target is not a tuple
+ return
+ if not len(node.target.elts) == 2:
+ # target is not a tuple of two elements
+ return
+
+ iterable = node.iter
+ if not isinstance(iterable, astroid.node_classes.Name):
+ # it's not a bare variable
+ return
+
+ inferred = safe_infer(iterable)
+ if not inferred:
+ return
+ if not isinstance(inferred, astroid.node_classes.Dict):
+ # the iterable is not a dict
+ return
+
+ self.add_message("dict-iter-missing-items", node=node)
class IterableChecker(BaseChecker):
"""
diff --git a/pylint/test/functional/dict_iter_missing_items.py b/pylint/test/functional/dict_iter_missing_items.py
new file mode 100644
index 000000000..333f58990
--- /dev/null
+++ b/pylint/test/functional/dict_iter_missing_items.py
@@ -0,0 +1,23 @@
+# pylint: disable=invalid-name, consider-iterating-dictionary, missing-docstring, import-error
+from unknown import Uninferable
+
+d = {1: 1, 2: 2}
+l = [1, 2]
+s1 = {1, 2}
+s2 = {1, 2, 3}
+
+# Positive
+for k, v in d: # [dict-iter-missing-items]
+ pass
+
+# Negative
+for k, v in d.items():
+ pass
+for k in d.keys():
+ pass
+for i, v in enumerate(l):
+ pass
+for i, v in s1.intersection(s2):
+ pass
+for k, v in Uninferable:
+ pass
diff --git a/pylint/test/functional/dict_iter_missing_items.txt b/pylint/test/functional/dict_iter_missing_items.txt
new file mode 100644
index 000000000..0e939ee33
--- /dev/null
+++ b/pylint/test/functional/dict_iter_missing_items.txt
@@ -0,0 +1 @@
+dict-iter-missing-items:10::Unpacking a dictionary in iteration without calling .items()