diff --git a/README.rst b/README.rst index 792d3eb..1f3a530 100644 --- a/README.rst +++ b/README.rst @@ -117,6 +117,9 @@ instead of ``except (SomeError,):``. **B014**: Redundant exception types in ``except (Exception, TypeError):``. Write ``except Exception:``, which catches exactly the same exceptions. +**B015**: Pointless comparison. This comparison does nothing but +wastes CPU instructions. Remove it. + Python 3 compatibility warnings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index eb96788..63f7919 100644 --- a/bugbear.py +++ b/bugbear.py @@ -1,17 +1,16 @@ import ast import builtins +import itertools +import logging +import re from collections import namedtuple from contextlib import suppress from functools import lru_cache, partial -import itertools from keyword import iskeyword -import logging -import re import attr import pycodestyle - __version__ = "20.1.4" LOG = logging.getLogger("flake8.bugbear") @@ -300,6 +299,9 @@ def visit_Try(self, node): self.check_for_b012(node) self.generic_visit(node) + def visit_Compare(self, node): + self.check_for_b015(node) + def compose_call_path(self, node): if isinstance(node, ast.Attribute): yield from self.compose_call_path(node.value) @@ -371,6 +373,10 @@ def _loop(node, bad_node_types): for child in node.finalbody: _loop(child, (ast.Return, ast.Continue, ast.Break)) + def check_for_b015(self, node): + if isinstance(self.node_stack[-2], ast.Expr): + self.errors.append(B015(node.lineno, node.col_offset)) + def walk_function_body(self, node): def _loop(parent, node): if isinstance(node, (ast.AsyncFunctionDef, ast.FunctionDef)): @@ -658,6 +664,12 @@ def visit(self, node): "select.error", } } +B015 = Error( + message=( + "B015 Pointless comparison. This comparison does nothing but wastes " + "CPU instructions. Remove it." + ) +) # Those could be false positives but it's more dangerous to let them slip # through if they're not. diff --git a/tests/b015.py b/tests/b015.py new file mode 100644 index 0000000..0351c5c --- /dev/null +++ b/tests/b015.py @@ -0,0 +1,29 @@ +""" +Should emit: +B015 - on lines 8, 12, 22, 29 +""" + +assert 1 == 1 + +1 == 1 + +assert 1 in (1, 2) + +1 in (1, 2) + + +if 1 == 2: + pass + + +def test(): + assert 1 in (1, 2) + + 1 in (1, 2) + + +data = [x for x in [1, 2, 3] if x in (1, 2)] + + +class TestClass: + 1 == 1 diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index d6161af..db10a3c 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -24,6 +24,7 @@ B012, B013, B014, + B015, B301, B302, B303, @@ -182,6 +183,13 @@ def test_b014(self): ) self.assertEqual(errors, expected) + def test_b015(self): + filename = Path(__file__).absolute().parent / "b015.py" + bbc = BugBearChecker(filename=str(filename)) + errors = list(bbc.run()) + expected = self.errors(B015(8, 0), B015(12, 0), B015(22, 4), B015(29, 4)) + self.assertEqual(errors, expected) + def test_b301_b302_b305(self): filename = Path(__file__).absolute().parent / "b301_b302_b305.py" bbc = BugBearChecker(filename=str(filename))