From 3820ddea785c532428642ae2f96d9e8bff244fbf Mon Sep 17 00:00:00 2001 From: Laura M?dioni Date: Mon, 26 Oct 2015 09:35:59 +0100 Subject: Check for nots too many in comparison expressions --- CONTRIBUTORS.txt | 2 +- pylint/checkers/base.py | 31 +++++++++++++++++++++ pylint/test/functional/superfluous_parens.py | 2 +- pylint/test/functional/unneeded_not.py | 41 ++++++++++++++++++++++++++++ pylint/test/functional/unneeded_not.txt | 12 ++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 pylint/test/functional/unneeded_not.py create mode 100644 pylint/test/functional/unneeded_not.txt 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" -- cgit v1.2.1