fixhunt

ruff SIM221

labelled unknown
Runs / 2026-10-17T20-37-01-750Z-5ai0-ruff-SIM221ruff 0.17.12026-10-17 20:37 UTC8 programs × 2 round(s)16 programs, 3 fired, 0 fixed, 0 findings, 2 rejectedmodel cost $0.0028

Analysis Nemotron 3 Super

Flags 'or' expressions where one operand is the logical negation of the other, such as 'x or not x', which always evaluates to True.

Fix: Rewrites the expression to the literal True, preserving any surrounding expression structure but replacing the entire 'or' expression with True.

8 hazards
  • The rewrite removes any side effects from evaluating the left operand first, such as in a function call with mutable state changes.
  • If the left operand is a property accessor (__getattr__ or __getattribute__), its side effects are lost when replaced with True.
  • When the operand is an indexing operation like lst[i], any __getitem__ side effects or exceptions are eliminated by the rewrite.
  • In chained comparisons like (x < y) or not (x < y), the rewrite removes the evaluation of x and y entirely, altering behavior if they have side effects.
  • If the expression appears in a context relying on its specific truthy value for type narrowing (e.g., in an if condition), replacing with True may affect static analysis but not runtime behavior—yet this is still a semantic shift in expression value.
  • Comments attached to the operands or operator (e.g., x # comment or not # comment x) may be lost during the AST rewrite.
  • In rare cases where the expression is used via eval() or exec() with dynamically scoped names, the rewrite could alter name resolution if operands contain undefined variables that would have raised NameError.
  • If the operand is a short-circuiting expression itself (e.g., f() or g()), the rewrite prevents evaluation of the right-hand operand in the negation, removing potential side effects from g().

Programs

idresulttitlehazard
r1-p001silent prediction offProperty Accessor Side Effects LostIf the left operand is a property accessor (__getattr__ or __getattribute__), its side effects are lost when replaced with True.
r1-p002invalidShadowed Builtins and Redefined Namesshadowed builtins and names redefined later in the module
r1-p003silent prediction offChained Comparison Side Effects RemovedIn chained comparisons like (x < y) or not (x < y), the rewrite removes the evaluation of x and y entirely, altering behavior if they have side effects.
r1-p004silent prediction offExceptions Raised Inside Rewritten Codeexceptions raised inside the rewritten code, finally/else clauses, exception chaining
r1-p005fired, no fixComments Lost During AST RewriteComments attached to the operands or operator (e.g., x # comment or not # comment x) may be lost during the AST rewrite.
r1-p006silent prediction offSide Effects from Left Operand Evaluation RemovedThe rewrite removes any side effects from evaluating the left operand first, such as in a function call with mutable state changes.
r1-p007fired, no fix prediction offStar-Imports and Module-Level Side Effectsstar-imports, module-level side effects, __all__
r1-p008invalidTruthiness of Custom __bool__/__len__truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '=='
r2-p001silent prediction offLeft Operand Side Effects RemovedThe rewrite removes any side effects from evaluating the left operand first, such as in a function call with mutable state changes.
r2-p002silent prediction offGenerator Consumption Hazardgenerators vs lists, iterators consumed once, lazy vs eager evaluation
r2-p003fired, no fixType Narrowing ContextIf the expression appears in a context relying on its specific truthy value for type narrowing (e.g., in an if condition), replacing with True may affect static analysis but not runtime behavior—yet t
r2-p004silent prediction offShort-Circuiting Operand HazardIf the operand is a short-circuiting expression itself (e.g., f() or g()), the rewrite prevents evaluation of the right-hand operand in the negation, removing potential side effects from g().
r2-p005silentBytes vs Str Encoding Hazardbytes vs str, encoding and newline handling, text vs binary modes
r2-p006silent prediction offProperty Accessor Side Effects LostIf the left operand is a property accessor (__getattr__ or __getattribute__), its side effects are lost when replaced with True.
r2-p007silent prediction offMutable Default Arguments and Aliasingmutable default arguments and aliasing after a rewrite
r2-p008silent prediction offEvaluation Order and Side Effects Mergedevaluation order and side effects when expressions are merged, duplicated or deleted

Sources

r1-p001 · Property Accessor Side Effects Lost
1class A:
2 def __init__(self):
3 self.count = 0
4 def __getattr__(self, name):
5 self.count += 1
6 return 42 if name == 'x' else None
7
8a = A()
9print(a.x or not a.x)
10print('count:', a.count)
42
count: 1
r1-p002 · Shadowed Builtins and Redefined Names (generated program does not parse: Traceback (most recent call last):)
1def or():
2 return False
3
4def not_():
5 return True
6
7print(or() or not not_())
8
9# Later redefinition
10def or():
11 return True
12
13def not_():
14 return False
15
16print(or() or not not_())
r1-p003 · Chained Comparison Side Effects Removed
1counter = 0
2
3def get_x():
4 global counter
5 counter += 1
6 return counter
7
8def get_y():
9 global counter
10 counter += 10
11 return counter
12
13print((get_x() < get_y()) or not (get_x() < get_y()))
14print('counter:', counter)
True
counter: 11
r1-p004 · Exceptions Raised Inside Rewritten Code
1def f():
2 try:
3 raise ValueError('bad')
4 except ValueError as e:
5 print('caught:', e)
6 raise RuntimeError('worse') from e
7
8try:
9 print(f() or not f())
10except Exception as e:
11 print(type(e).__name__ + ':', e)
caught: bad
RuntimeError: worse
r1-p005 · Comments Lost During AST Rewrite
1x = 0 # init
2print(
3 x # left operand
4 or
5 not # operator comment
6 x # right operand
7)
8print('x after:', x)
True
x after: 0
r1-p006 · Side Effects from Left Operand Evaluation Removed
1state = []
2
3def log_and_return(val):
4 state.append(val)
5 return val
6
7print(log_and_return(1) or not log_and_return(1))
8print('state:', state)
1
state: [1]
r1-p007 · Star-Imports and Module-Level Side Effects
1__all__ = ['flag']
2flag = False
3
4# Simulate star-import by direct use
5print(flag or not flag)
6flag = True
7print(flag or not flag)
True
True
r1-p008 · Truthiness of Custom __bool__/__len__ (generated program is not deterministic)
1class B:
2 def __init__(self, val):
3 self.val = val
4 def __bool__(self):
5 print('__bool__ called')
6 return self.val > 0
7
8b = B(-1)
9print(b or not b)
10b = B(1)
11print(b or not b)
r2-p001 · Left Operand Side Effects Removed
1counter = [0]
2def inc():
3 counter[0] += 1
4 return counter[0]
5x = inc()
6y = inc() or not inc()
7print('left:', x, 'middle:', y)
left: 1 middle: 2
r2-p002 · Generator Consumption Hazard
1def gen():
2 yield 1
3 yield 2
4g = gen()
5a = next(g)
6b = list(g) or not list(g)
7print('first:', a, 'rest:', b)
first: 1 rest: [2]
r2-p003 · Type Narrowing Context
1x = 0
2if x or not x:
3 print('branch taken')
4else:
5 print('branch not taken')
6print('x:', x)
branch taken
x: 0
r2-p004 · Short-Circuiting Operand Hazard
1called = []
2def f():
3 called.append('f')
4 return 0
5def g():
6 called.append('g')
7 return 1
8result = (f() or g()) or not (f() or g())
9print('result:', result, 'called:', called)
result: 1 called: ['f', 'g']
r2-p005 · Bytes vs Str Encoding Hazard
1data = b'hello\nworld'
2text = data.decode()
3line_count = text.count('\n') or not text.count('\n')
4print('lines:', line_count)
lines: 1
r2-p006 · Property Accessor Side Effects Lost
1class Obj:
2 def __getattr__(self, name):
3 print(f'accessing {name}')
4 return 42
5obj = Obj()
6obj.counter = 0
7def get_val():
8 obj.counter += 1
9 return obj.value
10x = get_val() or not get_val()
11print('counter:', obj.counter, 'x:', x)
accessing value
counter: 1 x: 42
r2-p007 · Mutable Default Arguments and Aliasing
1def f(x=[]):
2 x.append(1)
3 return len(x)
4a = f()
5b = f() or not f()
6print('a:', a, 'b:', b)
a: 1 b: 2
r2-p008 · Evaluation Order and Side Effects Merged
1log = []
2def side(x):
3 log.append(x)
4 return x
5result = side(1) or not side(2)
6print('log:', log, 'result:', result)
log: [1] result: 1