diff options
author | yory8 <39745367+yory8@users.noreply.github.com> | 2019-03-20 08:41:16 +0000 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-03-20 09:41:16 +0100 |
commit | a138a1a8f4bd1ff4ff5718c8b23a9b82c27aef3c (patch) | |
tree | f73920bd7dc6827bd0f84e93af32a411ac37e20f | |
parent | 30b2ae713eb4453b305407772b29ea68d7c3d074 (diff) | |
download | pylint-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.txt | 3 | ||||
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/whatsnew/2.4.rst | 6 | ||||
-rw-r--r-- | pylint/checkers/typecheck.py | 28 | ||||
-rw-r--r-- | pylint/test/functional/dict_iter_missing_items.py | 23 | ||||
-rw-r--r-- | pylint/test/functional/dict_iter_missing_items.txt | 1 |
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 - @@ -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() |