summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaura M?dioni <laura.medioni@logilab.fr>2015-10-26 09:35:59 +0100
committerLaura M?dioni <laura.medioni@logilab.fr>2015-10-26 09:35:59 +0100
commit3820ddea785c532428642ae2f96d9e8bff244fbf (patch)
treed391102c5ff6157bae0b50392c5acdbf51d0a6c2
parent6f59c12b55765a3cc721864e8882f1e8e49bcb58 (diff)
downloadpylint-3820ddea785c532428642ae2f96d9e8bff244fbf.tar.gz
Check for nots too many in comparison expressions
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--pylint/checkers/base.py31
-rw-r--r--pylint/test/functional/superfluous_parens.py2
-rw-r--r--pylint/test/functional/unneeded_not.py41
-rw-r--r--pylint/test/functional/unneeded_not.txt12
5 files changed, 86 insertions, 2 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 8317678..f05e053 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -75,4 +75,4 @@ Order doesn't matter (not that much, at least ;)
* Laura Medioni (Logilab, on behalf of the CNES): misplaced-comparison-constant,
no-classmethod-decorator, no-staticmethod-decorator, too-many-nested-blocks,
- too-many-boolean-expressions \ No newline at end of file
+ too-many-boolean-expressions, unneeded-not \ No newline at end of file
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index 74a1f64..e8a4396 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -1843,6 +1843,36 @@ class ElifChecker(BaseTokenChecker):
(len(self._nested_blocks),
self.config.max_nested_blocks))
+class NotChecker(_BasicChecker):
+ """checks for nots too many in comparison expressions
+
+ - "not not" should trigger a warning
+ - "not" followed by a comparison should trigger a warning
+ """
+ msgs = {'W0126': ('One not too many in "%s", should be "%s"',
+ 'unneeded-not',
+ 'Used when a boolean expression contains an unneeded '
+ 'negation.'),
+ }
+
+ @check_messages('unneeded-not')
+ def visit_unaryop(self, node):
+ if node.op != 'not':
+ return
+ operand = node.operand
+ if isinstance(operand, astroid.UnaryOp) and operand.op == 'not':
+ self.add_message('unneeded-not', node=node,
+ args=(node.as_string(),
+ operand.operand.as_string()))
+ elif isinstance(operand, astroid.Compare):
+ operator, _ = operand.ops[0]
+ reverse_op = {'<': '>=', '<=': '>', '>': '<=', '>=': '<',
+ '==': '!=', '!=': '=='}
+ suggestion = node.as_string().replace('not ', '').replace(
+ operator, reverse_op[operator])
+ self.add_message('unneeded-not', node=node,
+ args=(node.as_string(), suggestion))
+
def register(linter):
"""required method to auto register this checker"""
@@ -1853,5 +1883,6 @@ def register(linter):
linter.register_checker(PassChecker(linter))
linter.register_checker(LambdaForComprehensionChecker(linter))
linter.register_checker(ComparisonChecker(linter))
+ linter.register_checker(NotChecker(linter))
linter.register_checker(RecommandationChecker(linter))
linter.register_checker(ElifChecker(linter))
diff --git a/pylint/test/functional/superfluous_parens.py b/pylint/test/functional/superfluous_parens.py
index 417163e..13418cd 100644
--- a/pylint/test/functional/superfluous_parens.py
+++ b/pylint/test/functional/superfluous_parens.py
@@ -1,6 +1,6 @@
"""Test the superfluous-parens warning."""
from __future__ import print_function
-
+# pylint: disable=unneeded-not
i = 3
if (i == 5): # [superfluous-parens]
pass
diff --git a/pylint/test/functional/unneeded_not.py b/pylint/test/functional/unneeded_not.py
new file mode 100644
index 0000000..882a238
--- /dev/null
+++ b/pylint/test/functional/unneeded_not.py
@@ -0,0 +1,41 @@
+"""Check exceeding negations in boolean expressions trigger warnings"""
+
+# pylint: disable=singleton-comparison
+
+def unneeded_not():
+ """This is not ok
+ """
+ bool_var = True
+ someint = 2
+ if not not bool_var: # [unneeded-not]
+ pass
+ if not someint == 1: # [unneeded-not]
+ pass
+ if not someint != 1: # [unneeded-not]
+ pass
+ if not someint < 1: # [unneeded-not]
+ pass
+ if not someint > 1: # [unneeded-not]
+ pass
+ if not someint <= 1: # [unneeded-not]
+ pass
+ if not someint >= 1: # [unneeded-not]
+ pass
+ if not not someint: # [unneeded-not]
+ pass
+ if not bool_var == True: # [unneeded-not]
+ pass
+ if not bool_var == False: # [unneeded-not]
+ pass
+ if not bool_var != True: # [unneeded-not]
+ pass
+ if not True == True: # [unneeded-not]
+ pass
+
+
+def not_checked():
+ """This is ok"""
+ bool_var = True
+ someint = 2
+ if not(bool_var == False and someint == 1):
+ pass
diff --git a/pylint/test/functional/unneeded_not.txt b/pylint/test/functional/unneeded_not.txt
new file mode 100644
index 0000000..83100a4
--- /dev/null
+++ b/pylint/test/functional/unneeded_not.txt
@@ -0,0 +1,12 @@
+unneeded-not:10:unneeded_not:One not too many in "not not bool_var", should be "bool_var"
+unneeded-not:12:unneeded_not:One not too many in "not someint == 1", should be "someint != 1"
+unneeded-not:14:unneeded_not:One not too many in "not someint != 1", should be "someint == 1"
+unneeded-not:16:unneeded_not:One not too many in "not someint < 1", should be "someint >= 1"
+unneeded-not:18:unneeded_not:One not too many in "not someint > 1", should be "someint <= 1"
+unneeded-not:20:unneeded_not:One not too many in "not someint <= 1", should be "someint > 1"
+unneeded-not:22:unneeded_not:One not too many in "not someint >= 1", should be "someint < 1"
+unneeded-not:24:unneeded_not:One not too many in "not not someint", should be "someint"
+unneeded-not:26:unneeded_not:One not too many in "not bool_var == True", should be "bool_var != True"
+unneeded-not:28:unneeded_not:One not too many in "not bool_var == False", should be "bool_var != False"
+unneeded-not:30:unneeded_not:One not too many in "not bool_var != True", should be "bool_var == True"
+unneeded-not:32:unneeded_not:One not too many in "not True == True", should be "True != True"