summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaura Médioni <laura.medioni@logilab.fr>2015-12-11 16:56:29 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2016-01-15 21:57:18 +0200
commit104a3269a04132880a9c62358a420d7fccf97fe2 (patch)
tree61da472e8294a2eb360e97acc2ff36d71827c124
parentd913fb6f33f373ecbacce67200917913396304fa (diff)
downloadpylint-git-104a3269a04132880a9c62358a420d7fccf97fe2.tar.gz
Don't apply unneeded-not rule on sets
Closes issue #717
-rw-r--r--ChangeLog2
-rw-r--r--pylint/checkers/base.py57
-rw-r--r--pylint/test/functional/unneeded_not.py11
3 files changed, 51 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index e78c5b062..0ec758874 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,8 @@ ChangeLog for Pylint
messages from the StringFormatChecker would have resulted in no
messages at all.
+ * Don't apply unneeded-not over sets.
+
2016-01-11 -- 1.5.3
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index b5adddf83..edc9c1b75 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -270,6 +270,28 @@ def redefined_by_decorator(node):
return True
return False
+
+def _node_type(node):
+ """Return the inferred type for `node`
+
+ If there is more than one possible type, or if inferred type is YES or None,
+ return None
+ """
+ # check there is only one possible type for the assign node. Else we
+ # don't handle it for now
+ types = set()
+ try:
+ for var_type in node.infer():
+ if var_type == astroid.YES or _is_none(var_type):
+ continue
+ types.add(var_type)
+ if len(types) > 1:
+ return
+ except InferenceError:
+ return
+ return types.pop() if types else None
+
+
class _BasicChecker(BaseChecker):
__implements__ = IAstroidChecker
name = 'basic'
@@ -1864,6 +1886,12 @@ class NotChecker(_BasicChecker):
reverse_op = {'<': '>=', '<=': '>', '>': '<=', '>=': '<', '==': '!=',
'!=': '==', 'in': 'not in', 'is': 'is not'}
+ # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is
+ # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)"
+ skipped_nodes = (astroid.Set, )
+ # 'builtins' py3, '__builtin__' py2
+ skipped_classnames = ['%s.%s' % (six.moves.builtins.__name__, qname)
+ for qname in ('set', 'frozenset')]
@check_messages('unneeded-not')
def visit_unaryop(self, node):
@@ -1883,12 +1911,18 @@ class NotChecker(_BasicChecker):
operator, right = operand.ops[0]
if operator not in self.reverse_op:
return
-
# Ignore __ne__ as function of __eq__
frame = node.frame()
if frame.name == '__ne__' and operator == '==':
return
-
+ for _type in (_node_type(left), _node_type(right)):
+ if not _type:
+ return
+ if isinstance(_type, self.skipped_nodes):
+ return
+ if (isinstance(_type, astroid.Instance) and
+ _type.qname() in self.skipped_classnames):
+ return
suggestion = '%s %s %s' % (left.as_string(),
self.reverse_op[operator],
right.as_string())
@@ -1955,21 +1989,10 @@ class MultipleTypesChecker(BaseChecker):
# ignore NoneType
if _is_none(node):
return
- # check there is only one possible type for the assign node. Else we
- # don't handle it for now
- types = set()
- try:
- for var_type in node.value.infer():
- if var_type == astroid.YES or _is_none(var_type):
- continue
- var_type = var_type.pytype()
- types.add(var_type)
- if len(types) > 1:
- return
- except InferenceError:
- return
- if types:
- self._assigns[-1].setdefault(target.as_string(), []).append((node, types.pop()))
+ _type = _node_type(node.value)
+ if _type:
+ self._assigns[-1].setdefault(target.as_string(), []).append(
+ (node, _type.pytype()))
def register(linter):
diff --git a/pylint/test/functional/unneeded_not.py b/pylint/test/functional/unneeded_not.py
index e98c2e3aa..b95adc930 100644
--- a/pylint/test/functional/unneeded_not.py
+++ b/pylint/test/functional/unneeded_not.py
@@ -1,6 +1,6 @@
"""Check exceeding negations in boolean expressions trigger warnings"""
-# pylint: disable=singleton-comparison, too-many-branches, too-few-public-methods
+# pylint: disable=singleton-comparison,too-many-branches,too-few-public-methods,undefined-variable
def unneeded_not():
"""This is not ok
@@ -37,7 +37,7 @@ def unneeded_not():
pass
-def not_checked():
+def tolerated_statements():
"""This is ok"""
bool_var = True
someint = 2
@@ -49,6 +49,13 @@ def not_checked():
pass
if not 2 <= someint < 3 < 4:
pass
+ if not set('bar') <= set('foobaz'):
+ pass
+ if not set(something) <= 3:
+ pass
+ if not frozenset(something) <= 3:
+ pass
+
class Klass(object):
"""This is also ok"""