diff options
| author | Ben Green <benhgreen@icloud.com> | 2018-05-15 10:55:12 -0400 |
|---|---|---|
| committer | Ashley Whetter <AWhetter@users.noreply.github.com> | 2018-05-15 07:55:12 -0700 |
| commit | 5bc4529031350397665629cdafe7172c422ab32f (patch) | |
| tree | 45e314f0e4fe45368ac23eff28f4945b098b1a25 | |
| parent | c011c53ba207b40904b43d60f3f1ede328cd6372 (diff) | |
| download | pylint-git-5bc4529031350397665629cdafe7172c422ab32f.tar.gz | |
Add check for unhashable dict keys (fixes #586) (#2089)
| -rw-r--r-- | CONTRIBUTORS.txt | 2 | ||||
| -rw-r--r-- | ChangeLog | 4 | ||||
| -rw-r--r-- | doc/whatsnew/2.0.rst | 4 | ||||
| -rw-r--r-- | pylint/checkers/typecheck.py | 23 | ||||
| -rw-r--r-- | pylint/test/functional/unhashable_dict_key.py | 11 | ||||
| -rw-r--r-- | pylint/test/functional/unhashable_dict_key.txt | 3 |
6 files changed, 45 insertions, 2 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 84531db7d..024eb7e06 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -173,6 +173,8 @@ Order doesn't matter (not that much, at least ;) * Marianna Polatoglou: minor contribution for wildcard import check +* Ben Green: contributor + * Benjamin Freeman: contributor * Fureigh: contributor @@ -4,6 +4,10 @@ Pylint's ChangeLog What's New in Pylint 2.0? ========================= + * Add `unhashable-dict-key` check. + + Closes #586 + * Don't warn that a global variable is unused if it is defined by an import Close #1453 diff --git a/doc/whatsnew/2.0.rst b/doc/whatsnew/2.0.rst index a0313a100..cfa74e55d 100644 --- a/doc/whatsnew/2.0.rst +++ b/doc/whatsnew/2.0.rst @@ -115,6 +115,10 @@ New checkers some_value = some_call() return locals() + * New `unhashable-dict-key` check added to detect dict lookups using + unhashable keys such as lists or dicts. + + Other Changes ============= diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index b24f7498c..c7acdb67d 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -288,6 +288,10 @@ MSGS = { 'Emitted whenever we can detect that a class is using, ' 'as a metaclass, something which might be invalid for using as ' 'a metaclass.'), + 'E1140': ("Dict key is unhashable", + 'unhashable-dict-key', + "Emitted when a dict key is not hashable" + "(i.e. doesn't define __hash__ method)"), 'W1113': ('Keyword argument before variable positional arguments list ' 'in the definition of %s function', 'keyword-arg-before-vararg', @@ -1241,13 +1245,28 @@ accessed. Python regular expressions are accepted.'} if op in ['in', 'not in']: self._check_membership_test(right) - @check_messages('unsubscriptable-object', 'unsupported-assignment-operation', - 'unsupported-delete-operation') + @check_messages('unsubscriptable-object', + 'unsupported-assignment-operation', + 'unsupported-delete-operation', + 'unhashable-dict-key') def visit_subscript(self, node): supported_protocol = None if isinstance(node.value, (astroid.ListComp, astroid.DictComp)): return + if isinstance(node.value, astroid.Dict): + # Assert dict key is hashable + inferred = safe_infer(node.slice.value) + if inferred is not None: + try: + hash_fn = next(inferred.igetattr('__hash__')) + except (astroid.InferenceError, TypeError): + pass + else: + if getattr(hash_fn, 'value', True) is None: + self.add_message('unhashable-dict-key', + node=node.value) + if node.ctx == astroid.Load: supported_protocol = supports_getitem msg = 'unsubscriptable-object' diff --git a/pylint/test/functional/unhashable_dict_key.py b/pylint/test/functional/unhashable_dict_key.py new file mode 100644 index 000000000..54b3065a5 --- /dev/null +++ b/pylint/test/functional/unhashable_dict_key.py @@ -0,0 +1,11 @@ +# pylint: disable=missing-docstring,expression-not-assigned,too-few-public-methods,pointless-statement + + +class Unhashable(object): + __hash__ = list.__hash__ + +{}[[1, 2, 3]] # [unhashable-dict-key] +{}[{}] # [unhashable-dict-key] +{}[Unhashable()] # [unhashable-dict-key] +{'foo': 'bar'}['foo'] +{'foo': 'bar'}[42] diff --git a/pylint/test/functional/unhashable_dict_key.txt b/pylint/test/functional/unhashable_dict_key.txt new file mode 100644 index 000000000..77c3b9287 --- /dev/null +++ b/pylint/test/functional/unhashable_dict_key.txt @@ -0,0 +1,3 @@ +unhashable-dict-key:7::Dict key is unhashable +unhashable-dict-key:8::Dict key is unhashable +unhashable-dict-key:9::Dict key is unhashable |
