fixhunt

ruff FURB187

labelled unknown
Runs / 2026-10-17T21-37-15-830Z-em3h-ruff-FURB187ruff 0.17.12026-10-17 21:37 UTC8 programs × 2 round(s)16 programs, 4 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0031

Analysis Nemotron 3 Super

Flags inefficient list reversal patterns like assignment via slicing, reversed(), or list(reversed()) when .reverse() could be used.

Fix: Replaces the assignment target variable with an in-place .reverse() call on the same variable, e.g., 'l = l[::-1]' becomes 'l.reverse()'.

12 hazards
  • If the list is aliased elsewhere (e.g., another variable references the same list), mutating via .reverse() affects all aliases, whereas slicing creates a new list leaving original aliases unchanged.
  • If the reversed result is used immediately in an expression (e.g., function call), replacing with .reverse() breaks because .reverse() returns None, altering return values or side effects.
  • If the assignment is part of a larger expression (e.g., tuple unpacking or conditional), rewriting to a statement changes syntax and may cause invalid Python.
  • If the variable is deleted or reassigned later, the timing of mutation vs. creation affects memory usage and reference counting, potentially impacting __del__ methods or weakrefs.
  • If the list contains mutable elements and the code relies on the original list being unchanged for later use, .reverse() causes premature mutation.
  • If the slice operation includes step or bounds (e.g., l[::-1] vs l[1:5:-1]), the fix incorrectly applies .reverse() which only does full reversal, changing semantics.
  • If the variable is a property or item assignment (e.g., self.lst = self.lst[::-1]), the fix cannot be applied as .reverse() requires a direct variable name, not an attribute or subscript.
  • If the code depends on the identity of the list object (e.g., checking 'l is original' after reversal), .reverse() preserves identity while slicing creates a new object, breaking identity checks.
  • If the reversed list is iterated over before reassignment (e.g., for i in l[::-1]: ...; l = l[::-1]), moving .reverse() earlier changes iteration order due to premature mutation.
  • If the list reversal is guarded by a condition (e.g., if flag: l = l[::-1]), the fix must stay within the block; extracting .reverse() out of scope breaks conditioning.
  • If the variable is used in a lambda or comprehension after reversal but before reassignment, early mutation via .reverse() alters the iterable seen by the lambda/comprehension.
  • If the assignment occurs in a class or module level and the list is shared across instances or imports, in-place mutation introduces cross-instance state sharing bugs.

Programs

idresulttitlehazard
r1-p001silent prediction offMutable element relianceIf the list contains mutable elements and the code relies on the original list being unchanged for later use, .reverse() causes premature mutation.
r1-p002silent prediction offImmediate use in expressionIf the reversed result is used immediately in an expression (e.g., function call), replacing with .reverse() breaks because .reverse() returns None, altering return values or side effects.
r1-p003silentLarger expression contextIf the assignment is part of a larger expression (e.g., tuple unpacking or conditional), rewriting to a statement changes syntax and may cause invalid Python.
r1-p004silent prediction offFile modes and truncationfile modes and truncation, partial writes, non-str data passed to write
r1-p005silent prediction offProperty or item assignmentIf the variable is a property or item assignment (e.g., self.lst = self.lst[::-1]), the fix cannot be applied as .reverse() requires a direct variable name, not an attribute or subscript.
r1-p006silent prediction offMutable default arguments and aliasingmutable default arguments and aliasing after a rewrite
r1-p007silent prediction offClass vs instance attributesclass attributes vs instance attributes, descriptors, properties with side effects
r1-p008silent prediction offPathlib vs os.path behaviorpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths
r2-p001silent prediction offAsync generator yields different results due to delayed mutationasync def/await ordering, async generators
r2-p002fired, no fixDeletion after reversal affects weakref callback timingIf the variable is deleted or reassigned later, the timing of mutation vs. creation affects memory usage and reference counting, potentially impacting __del__ methods or weakrefs.
r2-p003fired, no fixConditional reversal inside if-block must not be liftedIf the list reversal is guarded by a condition (e.g., if flag: l = l[::-1]), the fix must stay within the block; extracting .reverse() out of scope breaks conditioning.
r2-p004silent prediction offKeyword-only argument captures original list before mutationkeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r2-p005fired, no fixComment attached to reversal line is lost after fixcomments and docstrings attached to the removed or moved code
r2-p006silent prediction offIdentity check fails after fix because new object createdIf the code depends on the identity of the list object (e.g., checking 'l is original' after reversal), .reverse() preserves identity while slicing creates a new object, breaking identity checks.
r2-p007silent prediction offBytes reversal produces mojibake if decoded as text laterbytes vs str, encoding and newline handling, text vs binary modes
r2-p008fired, no fixFloat list reversal affects later sum due to precisionint/float/bool coercion, Decimal, division and rounding

Sources

r1-p001 · Mutable element reliance
1outer = [[1], [2]]
2inner = outer
3lst = inner[::-1]
4print('Before:', [x[0] for x in outer])
5lst[0].append(99)
6print('After modification through lst:', [x[0] for x in outer])
Before: [1, 2]
After modification through lst: [1, 2]
r1-p002 · Immediate use in expression
1def get_first_rev(x):
2 return list(reversed(x))[0]
3try:
4 print(get_first_rev([1, 2, 3]))
5except Exception as e:
6 print(type(e).__name__, e)
3
r1-p003 · Larger expression context
1data = [1, 2, 3]
2if (tmp := data[::-1]) and tmp[0] == 3:
3 print('matched', tmp)
4else:
5 print('no match')
matched [3, 2, 1]
r1-p004 · File modes and truncation
1with open('test.bin', 'wb') as f:
2 data = [b'\x00', b'\x01']
3 f.write(bytes().join(data[::-1]))
4with open('test.bin', 'rb') as f:
5 print(f.read())
6import os
7os.remove('test.bin')
b'\x01\x00'
r1-p005 · Property or item assignment
1class Container:
2 def __init__(self):
3 self._lst = [1, 2, 3]
4 @property
5 def lst(self):
6 return self._lst
7 @lst.setter
8 def lst(self, v):
9 self._lst = v
10c = Container()
11c.lst = c.lst[::-1]
12print(c._lst)
[3, 2, 1]
r1-p006 · Mutable default arguments and aliasing
1def f(x=[]):
2 x = x[::-1]
3 return x
4print(f([1, 2]))
5print(f())
6print(f())
[2, 1]
[]
[]
r1-p007 · Class vs instance attributes
1class A:
2 lst = [1, 2, 3]
3 def meth(self):
4 self.lst = self.lst[::-1]
5a = A()
6a.meth()
7print('class:', A.lst)
8print('instance:', a.lst)
class: [1, 2, 3]
instance: [3, 2, 1]
r1-p008 · Pathlib vs os.path behavior
1from pathlib import Path
2p = Path('a')
3parts = list(p.parts)[::-1]
4print('reversed parts:', parts)
5try:
6 Path(*parts).resolve()
7except Exception as e:
8 print(type(e).__name__, str(e)[:50])
reversed parts: ['a']
r2-p001 · Async generator yields different results due to delayed mutation
1async def gen(l):
2 yield l
3 l = l[::-1]
4 yield l
5
6async def main():
7 l = [1, 2, 3]
8 g = gen(l)
9 print('first:', await g.__anext__())
10 print('second:', await g.__anext__())
11
12import asyncio
13asyncio.run(main())
first: [1, 2, 3]
second: [3, 2, 1]
r2-p002 · Deletion after reversal affects weakref callback timing
1import weakseq
2callbacks = []
3def callback(ref):
4 callbacks.append('called')
5l = [1, 2, 3]
6r = weakref.ref(l, callback)
7l = l[::-1]
8del l
9print('callbacks:', callbacks)
10print('r() is None:', r() is None)
r2-p003 · Conditional reversal inside if-block must not be lifted
1flag = True
2l = [1, 2, 3]
3if flag:
4 l = l[::-1]
5 print('inside if:', l)
6print('after if:', l)
inside if: [3, 2, 1]
after if: [3, 2, 1]
r2-p004 · Keyword-only argument captures original list before mutation
1def f(*, lst=[1, 2, 3]):
2 lst = lst[::-1]
3 return lst
4print('first call:', f())
5print('second call:', f())
first call: [3, 2, 1]
second call: [3, 2, 1]
r2-p005 · Comment attached to reversal line is lost after fix
1# This reversal is critical for state
2l = [1, 2, 3]
3l = l[::-1] # Must happen before logging
4print('reversed:', l)
reversed: [3, 2, 1]
r2-p006 · Identity check fails after fix because new object created
1orig = [1, 2, 3]
2l = orig
3l = l[::-1]
4print('l is orig:', l is orig)
5print('l:', l)
6print('orig:', orig)
l is orig: False
l: [3, 2, 1]
orig: [1, 2, 3]
r2-p007 · Bytes reversal produces mojibake if decoded as text later
1b = b'hello\nworld'
2b = b[::-1]
3s = b.decode('utf-8')
4print('reversed bytes:', b)
5print('decoded string:', repr(s))
reversed bytes: b'dlrow\nolleh'
decoded string: 'dlrow\nolleh'
r2-p008 · Float list reversal affects later sum due to precision
1l = [0.1, 0.2, 0.3]
2s1 = sum(l)
3l = l[::-1]
4s2 = sum(l)
5print('sum before:', s1)
6print('sum after:', s2)
7print('equal?', s1 == s2)
sum before: 0.6
sum after: 0.6
equal? True