mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00

## Summary Closes #10337. I've fixed the code to count usage of variable. Usage count inside the block is reset when there is a following statement. - continue - break - return ## Test Plan Add test case.
230 lines
8.7 KiB
Python
230 lines
8.7 KiB
Python
import itertools
|
|
from itertools import groupby
|
|
|
|
shoppers = ["Jane", "Joe", "Sarah"]
|
|
items = [
|
|
("lettuce", "greens"),
|
|
("tomatoes", "greens"),
|
|
("cucumber", "greens"),
|
|
("chicken breast", "meats & fish"),
|
|
("salmon", "meats & fish"),
|
|
("ice cream", "frozen items"),
|
|
]
|
|
|
|
carts = {shopper: [] for shopper in shoppers}
|
|
|
|
|
|
def collect_shop_items(shopper, items):
|
|
# Imagine this an expensive database query or calculation that is
|
|
# advantageous to batch.
|
|
carts[shopper] += items
|
|
|
|
|
|
# Invoking the `groupby` function directly
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
for shopper in shoppers:
|
|
shopper = shopper.title()
|
|
collect_shop_items(shopper, section_items) # B031
|
|
# We're outside the nested loop and used the group again.
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
collect_shop_items("Jane", section_items)
|
|
collect_shop_items("Joe", section_items) # B031
|
|
|
|
|
|
# Make sure to detect in other loop constructs as well - `while` loop
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
countdown = 3
|
|
while countdown > 0:
|
|
collect_shop_items(shopper, section_items) # B031
|
|
countdown -= 1
|
|
|
|
# Make sure to detect in other loop constructs as well - `list` comprehension
|
|
collection = []
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
collection.append([list(section_items) for _ in range(3)]) # B031
|
|
|
|
unique_items = set()
|
|
another_set = set()
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
# For nested loops, it should not flag the usage of the name
|
|
for item in section_items:
|
|
unique_items.add(item)
|
|
|
|
# But it should be detected when used again
|
|
for item in section_items: # B031
|
|
another_set.add(item)
|
|
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
# Variable has been overridden, skip checking
|
|
section_items = list(unique_items)
|
|
collect_shop_items("Jane", section_items)
|
|
collect_shop_items("Jane", section_items)
|
|
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
# Variable has been overridden, skip checking
|
|
# Not a realistic situation, just for testing purpose
|
|
(section_items := list(unique_items))
|
|
collect_shop_items("Jane", section_items)
|
|
collect_shop_items("Jane", section_items)
|
|
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
# This is ok
|
|
collect_shop_items("Jane", section_items)
|
|
|
|
# Invocation via the `itertools` module
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
for shopper in shoppers:
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers] # B031
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
# The variable is overridden, skip checking.
|
|
_ = [_ for section_items in range(3)]
|
|
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers]
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
_ = [item for item in section_items]
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
# The iterator is being used for the second time.
|
|
_ = [(item1, item2) for item1 in section_items for item2 in section_items] # B031
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
else:
|
|
collect_shop_items(shopper, section_items)
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
# Mutually exclusive branches shouldn't trigger the warning
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items) # B031
|
|
elif _section == "frozen items":
|
|
collect_shop_items(shopper, section_items) # B031
|
|
else:
|
|
collect_shop_items(shopper, section_items) # B031
|
|
collect_shop_items(shopper, section_items) # B031
|
|
elif _section == "frozen items":
|
|
# Mix `match` and `if` statements
|
|
match shopper:
|
|
case "Jane":
|
|
collect_shop_items(shopper, section_items)
|
|
if _section == "fourth":
|
|
collect_shop_items(shopper, section_items) # B031
|
|
case _:
|
|
collect_shop_items(shopper, section_items)
|
|
else:
|
|
collect_shop_items(shopper, section_items)
|
|
# Now, it should detect
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
# Mutually exclusive branches shouldn't trigger the warning
|
|
match _section:
|
|
case "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
match shopper:
|
|
case "Jane":
|
|
collect_shop_items(shopper, section_items) # B031
|
|
case _:
|
|
collect_shop_items(shopper, section_items) # B031
|
|
case "frozen items":
|
|
collect_shop_items(shopper, section_items)
|
|
collect_shop_items(shopper, section_items) # B031
|
|
case _:
|
|
collect_shop_items(shopper, section_items)
|
|
# Now, it should detect
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
for group in groupby(items, key=lambda p: p[1]):
|
|
# This is bad, but not detected currently
|
|
collect_shop_items("Jane", group[1])
|
|
collect_shop_items("Joe", group[1])
|
|
|
|
|
|
# https://github.com/astral-sh/ruff/issues/4050
|
|
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
for item in section_items:
|
|
collect_shop_items(shopper, item)
|
|
elif _section == "frozen items":
|
|
_ = [item for item in section_items]
|
|
else:
|
|
collect_shop_items(shopper, section_items)
|
|
|
|
# Make sure we ignore - but don't fail on more complicated invocations
|
|
for _key, (_value1, _value2) in groupby(
|
|
[("a", (1, 2)), ("b", (3, 4)), ("a", (5, 6))], key=lambda p: p[1]
|
|
):
|
|
collect_shop_items("Jane", group[1])
|
|
collect_shop_items("Joe", group[1])
|
|
|
|
# Make sure we ignore - but don't fail on more complicated invocations
|
|
for (_key1, _key2), (_value1, _value2) in groupby(
|
|
[(("a", "a"), (1, 2)), (("b", "b"), (3, 4)), (("a", "a"), (5, 6))],
|
|
key=lambda p: p[1],
|
|
):
|
|
collect_shop_items("Jane", group[1])
|
|
collect_shop_items("Joe", group[1])
|
|
|
|
# Shouldn't trigger the warning when there is a continue, break statement.
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
continue
|
|
elif _section == "frozen items":
|
|
collect_shop_items(shopper, section_items)
|
|
break
|
|
collect_shop_items(shopper, section_items)
|
|
|
|
# Shouldn't trigger the warning when there is a return statement.
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
return
|
|
elif _section == "frozen items":
|
|
return section_items
|
|
collect_shop_items(shopper, section_items)
|
|
|
|
# Should trigger the warning for duplicate access, even if is a return statement after.
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
collect_shop_items(shopper, section_items)
|
|
return
|
|
|
|
# Should trigger the warning for duplicate access, even if is a return in another branch.
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
return
|
|
elif _section == "frozen items":
|
|
collect_shop_items(shopper, section_items)
|
|
collect_shop_items(shopper, section_items)
|
|
|
|
# Should trigger, since only one branch has a return statement.
|
|
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
|
if _section == "greens":
|
|
collect_shop_items(shopper, section_items)
|
|
return
|
|
elif _section == "frozen items":
|
|
collect_shop_items(shopper, section_items)
|
|
collect_shop_items(shopper, section_items) # B031
|
|
|
|
# Let's redefine the `groupby` function to make sure we pick up the correct one.
|
|
# NOTE: This should always be at the end of the file.
|
|
def groupby(data, key=None):
|
|
pass
|
|
|
|
|
|
for name, group in groupby(items):
|
|
collect_shop_items("Jane", items)
|
|
# This shouldn't be flagged as the `groupby` function is different
|
|
collect_shop_items("Joe", items)
|