Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ def visit_For(self, node: ast.For) -> None:
self.check_for_b020(node)
self.check_for_b023(node)
self.check_for_b031(node)
self.check_for_b038(node)
self.check_for_b909(node)
Comment on lines 592 to 596
Comment on lines 592 to 596
self.generic_visit(node)

Expand Down Expand Up @@ -1207,6 +1208,48 @@ def check_for_b031(self, loop_node: ast.For) -> None: # noqa: C901
num_usages += 1
if num_usages > 1:
self.add_error("B031", node, node.id)
def check_for_b038(self, node: ast.For) -> None:
"""
Comment on lines 1209 to +1212
Comment on lines 1209 to +1212
Check that the number of variables unpacked from zip() matches the number of
arguments passed to zip(). Warn if there are unused variables (especially _).
"""
# Only check for loops with zip() calls
if not (
isinstance(node.iter, ast.Call)
and isinstance(node.iter.func, ast.Name)
and node.iter.func.id == "zip"
):
return

# Only check when unpacking (target is a tuple)
if not isinstance(node.target, ast.Tuple):
return

# Get the unpacked variables from the target tuple
unpacked_vars = [elt for elt in node.target.elts if isinstance(elt, ast.Name)]
if not unpacked_vars:
return

# Get the number of arguments to zip()
num_zip_args = len(node.iter.args)

# If counts don't match, it is always an error
if num_zip_args != len(unpacked_vars):
self.add_error("B038", node)
return
Comment on lines +1224 to +1239
Comment on lines +1224 to +1239

# If counts match but we have unused "_" variables
# Check if any unpacked variable is "_" and is not used in the body
body_finder = NameFinder()
for stmt in node.body:
body_finder.visit(stmt)

for var_node in unpacked_vars:
# If variable name is "_" and it is not used in the body
if var_node.id == "_" and "_" not in body_finder.names:
self.add_error("B038", node)
return
Comment on lines +1241 to +1251
Comment on lines +1241 to +1251


def _get_names_from_tuple(self, node: ast.Tuple) -> Iterator[str]:
for dim in node.elts:
Expand Down Expand Up @@ -2497,6 +2540,12 @@ def __call__(self, lineno: int, col: int, vars: tuple[object, ...] = ()) -> erro
"B037": Error(
message="B037 Class `__init__` methods must not return or yield any values."
),
"B038": Error(
message=(
"B038 Either the number of arguments to `zip()` does not match the number "
"of unpacked variables, or there is an unused `_` in the unpacking."
)
),
Comment on lines +2543 to +2548
Comment on lines +2543 to +2548
"B039": Error(
message=(
"B039 ContextVar with mutable literal or function call as default. "
Expand Down
Loading