diff --git a/README.rst b/README.rst index edcc363..8c8c725 100644 --- a/README.rst +++ b/README.rst @@ -611,6 +611,8 @@ UNRELEASED * B018: also report ``map()``, ``filter()``, ``reversed()``, ``zip()`` and ``enumerate()`` calls used as statements. They return lazy iterators, so ``map(print, items)`` on its own line never calls ``print`` (#452) +* B909: also check mutations in ``if`` conditions and ``elif``/``else`` + branches inside loops (#606) 26.9.30 ~~~~~~~ diff --git a/bugbear.py b/bugbear.py index 218afe0..5b65b23 100644 --- a/bugbear.py +++ b/bugbear.py @@ -3318,9 +3318,12 @@ def visit_Call(self, node: ast.Call) -> None: self.generic_visit(node) def visit_If(self, node: ast.If) -> None: + self.visit(node.test) self._conditional_block += 1 self.visit(node.body) self._conditional_block += 1 + self.visit(node.orelse) + self._conditional_block += 1 def visit(self, node: ast.AST | list[ast.stmt]) -> ast.AST | list[ast.stmt] | None: """Like super-visit but supports iteration over lists.""" diff --git a/tests/eval_files/b909.py b/tests/eval_files/b909.py index fcea105..1fe2d91 100644 --- a/tests/eval_files/b909.py +++ b/tests/eval_files/b909.py @@ -177,3 +177,33 @@ def __init__(self, ls): for key in list(some_dict.keys()): some_dict.pop(key) # no error + + +# Visit every branch and the condition of an if statement. +some_list = [1, 2, 3] +for elem in some_list: + if elem == 1: + pass + elif elem == 2: + some_list.remove(elem) # B909: 8 + else: + some_list.clear() # B909: 8 + +for _ in some_list: + if some_list.pop(): # B909: 7 + break + +for elem in some_list: + if elem == 1: + some_list.remove(elem) # B909: 8 + else: + some_list.clear() + break + +for elem in some_list: + if elem == 1: + pass + else: + some_list.remove(elem) # B909: 8 + if elem == 2: + break