From f40905ce8b872e3806c206e2f16cfe369010b8bf Mon Sep 17 00:00:00 2001 From: Ryan Downing <26515643+r-downing@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:17:01 -0500 Subject: [PATCH 1/2] add unit test for b018 at all levels --- tests/b018_nested.py | 44 +++++++++++++++++++++++++++++++++++++++++++ tests/test_bugbear.py | 21 +++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 tests/b018_nested.py diff --git a/tests/b018_nested.py b/tests/b018_nested.py new file mode 100644 index 0000000..3888d25 --- /dev/null +++ b/tests/b018_nested.py @@ -0,0 +1,44 @@ + +X = 1 +False # bad + + +def func(y): + a = y + 1 + 5.5 # bad + return a + + +class TestClass: + GOOD = [1, 3] + [5, 6] # bad + + def method(self, xx, yy=5): + t = (xx,) + (yy,) # bad + + while 1: + i = 3 + 4 # bad + for n in range(i): + j = 5 + 1.5 # bad + if j < n: + u = {1, 2} + {4, 5} # bad + elif j == n: + u = {1, 2, 3} + {4, 5, 6} # bad + else: + u = {2, 3} + {4, 6} # bad + try: + 1j # bad + r = 2j + except Exception: + r = 3j + 5 # bad + finally: + 4j # bad + r += 1 + return u + t diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 00ad471..b0043c8 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -331,6 +331,27 @@ def test_b018_modules(self): ] self.assertEqual(errors, self.errors(*expected)) + def test_b018_nested(self): + filename = Path(__file__).absolute().parent / "b018_nested.py" + bbc = BugBearChecker(filename=str(filename)) + errors = list(bbc.run()) + + expected = [ + B018(3, 0, vars=("Constant",)), + B018(8, 4, vars=("Constant",)), + B018(14, 4, vars=("List",)), + B018(18, 8, vars=("Tuple",)), + B018(22, 12, vars=("Constant",)), + B018(25, 16, vars=("Constant",)), + B018(28, 20, vars=("Set",)), + B018(31, 20, vars=("Set",)), + B018(34, 20, vars=("Set",)), + B018(36, 24, vars=("Constant",)), + B018(40, 24, vars=("Constant",)), + B018(42, 24, vars=("Constant",)), + ] + self.assertEqual(errors, self.errors(*expected)) + def test_b019(self): filename = Path(__file__).absolute().parent / "b019.py" bbc = BugBearChecker(filename=str(filename)) From 21b61c33a116cc5f849ff66823d20b63d43716b3 Mon Sep 17 00:00:00 2001 From: Ryan Downing <26515643+r-downing@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:24:05 -0500 Subject: [PATCH 2/2] fix b018 to detect useless statements at all levels --- bugbear.py | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/bugbear.py b/bugbear.py index 2aba936..f78c879 100644 --- a/bugbear.py +++ b/bugbear.py @@ -370,6 +370,8 @@ def visit(self, node): if is_contextful: self.contexts.pop() + self.check_for_b018(node) + def visit_ExceptHandler(self, node): if node.type is None: self.errors.append(B001(node.lineno, node.col_offset)) @@ -447,7 +449,6 @@ def visit_Call(self, node): self.generic_visit(node) def visit_Module(self, node): - self.check_for_b018(node) self.generic_visit(node) def visit_Assign(self, node): @@ -503,7 +504,6 @@ def visit_FunctionDef(self, node): self.check_for_b901(node) self.check_for_b902(node) self.check_for_b006_and_b008(node) - self.check_for_b018(node) self.check_for_b019(node) self.check_for_b021(node) self.check_for_b906(node) @@ -511,7 +511,6 @@ def visit_FunctionDef(self, node): def visit_ClassDef(self, node): self.check_for_b903(node) - self.check_for_b018(node) self.check_for_b021(node) self.check_for_b024_and_b027(node) self.generic_visit(node) @@ -1164,31 +1163,30 @@ def check_for_b903(self, node): self.errors.append(B903(node.lineno, node.col_offset)) def check_for_b018(self, node): - for subnode in node.body: - if not isinstance(subnode, ast.Expr): - continue - if isinstance( - subnode.value, - ( - ast.List, - ast.Set, - ast.Dict, - ast.Tuple, - ), - ) or ( - isinstance(subnode.value, ast.Constant) - and ( - isinstance(subnode.value.value, (int, float, complex, bytes, bool)) - or subnode.value.value is None - ) - ): - self.errors.append( - B018( - subnode.lineno, - subnode.col_offset, - vars=(subnode.value.__class__.__name__,), - ) + if not isinstance(node, ast.Expr): + return + if isinstance( + node.value, + ( + ast.List, + ast.Set, + ast.Dict, + ast.Tuple, + ), + ) or ( + isinstance(node.value, ast.Constant) + and ( + isinstance(node.value.value, (int, float, complex, bytes, bool)) + or node.value.value is None + ) + ): + self.errors.append( + B018( + node.lineno, + node.col_offset, + vars=(node.value.__class__.__name__,), ) + ) def check_for_b021(self, node): if (