fixhunt

ruff SIM211

labelled unknown
Runs / 2026-10-17T20-35-55-070Z-oj84-ruff-SIM211ruff 0.17.12026-10-17 20:35 UTC8 programs × 2 round(s)16 programs, 16 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0040

Analysis Nemotron 3 Super

Flags conditional expressions where the true branch is False and the false branch is True.

Fix: Rewrites the expression as 'not <condition>' where <condition> is the original if-expression's test.

10 hazards
  • The condition may have side effects; while evaluation order is preserved, the negation changes the truth value semantics but not execution timing.
  • If the condition is a variable named 'not', rewriting could create a syntax error due to keyword conflict (e.g., 'False if not else True' → 'not not' is valid but 'False if not else True' parses as condition='not').
  • If the condition uses a walrus operator (e.g., 'False if (x := y) else True'), the rewrite 'not (x := y)' remains valid but alters where the assignment expression is evaluated in larger contexts.
  • In a lambda default argument like 'lambda cond=False if x else True: ...', the rewrite to 'lambda cond=not x: ...' preserves behavior but may affect introspection or source display.
  • If the expression appears in a statement where the value is ignored (e.g., 'False if x else True; pass'), removing the conditional expression via rewrite does not drop the statement but replaces its value, which is safe since the value was unused.
  • If the condition is a comma-separated tuple (e.g., 'False if (a, b) else True'), the rewrite 'not (a, b)' is valid and equivalent since tuple truthiness is preserved under negation.
  • If the condition is a slice object (e.g., 'False if s[x:y] else True'), the rewrite 'not s[x:y]' is valid and equivalent because slice objects are always truthy, so both sides evaluate to False and True respectively — wait, no: slice objects are truthy, so 'False if slice else True' is False, and 'not slice' is False — actually equivalent. No hazard here.
  • If the condition is a NumPy array, 'not arr' raises ValueError due to ambiguity, whereas the conditional expression would have worked if the array was used in a boolean context only after explicit reduction — but in the original, 'False if arr else True' would already raise ValueError on the condition evaluation, so the rewrite fails at the same point.
  • If the condition is a custom object with __bool__ returning an integer subclass where negation behaves differently due to __inv__, but Python's 'not' always coerces to bool first, so it is equivalent.
  • If the expression is part of a larger expression and parentheses are omitted relying on operator precedence, the rewrite 'not cond' may bind differently than the original conditional expression (which has low precedence), e.g., in 'False if x else True or y' vs 'not x or y' — but the original parses as (False if x else (True or y))? No: conditional expressions have lower precedence than 'or', so 'False if x else True or y' is (False if x else True) or y, and 'not x or y' is (not x) or y — and since (False if x else True) equals (not x), they are equivalent. So no hazard.

Programs

idresulttitlehazard
r1-p001fired, no fixCustom boolean-like object with overridden __neg__/__pos__If the condition is a custom object with __bool__ returning an integer subclass where negation behaves differently due to __inv__, but Python's 'not' always coerces to bool first, so it is equivalent.
r1-p002fired, no fix prediction offConditional in larger expression relying on precedenceIf the expression is part of a larger expression and parentheses are omitted relying on operator precedence, the rewrite 'not cond' may bind differently than the original conditional expression (which
r1-p003fired, no fixCondition with side effects (function call)The condition may have side effects; while evaluation order is preserved, the negation changes the truth value semantics but not execution timing.
r1-p004fired, no fixMutable default argument captured in closuremutable default arguments and aliasing after a rewrite
r1-p005fired, no fixWalrus operator in conditionIf the condition uses a walrus operator (e.g., 'False if (x := y) else True'), the rewrite 'not (x := y)' remains valid but alters where the assignment expression is evaluated in larger contexts.
r1-p006fired, no fixException in condition with finally clauseexceptions raised inside the rewritten code, finally/else clauses, exception chaining
r1-p007fired, no fix prediction offSlice object as condition (near-miss, no trigger expected)If the condition is a slice object (e.g., 'False if s[x:y] else True'), the rewrite 'not s[x:y]' is valid and equivalent because slice objects are always truthy...
r1-p008fired, no fixNumeric coercion edge case: numpy-like scalar (but using int/float/Decimal)int/float/bool coercion, Decimal, division and rounding
r2-p001fired, no fixDict Ordering & Duplicate Keysdict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r2-p002fired, no fixComments & Docstrings Attachedcomments and docstrings attached to the removed or moved code
r2-p003fired, no fixLambda Default Argument IntrospectionIn a lambda default argument like 'lambda cond=False if x else True: ...', the rewrite to 'lambda cond=not x: ...' preserves behavior but may affect introspection or source display.
r2-p004fired, no fixType Annotations Evaluated at Runtimetype annotations evaluated at runtime, typing-only imports
r2-p005fired, no fixBytes vs Str Encodingbytes vs str, encoding and newline handling, text vs binary modes
r2-p006fired, no fixShadowed Builtinsshadowed builtins and names redefined later in the module
r2-p007fired, no fixStar-Imports & Module Side Effectsstar-imports, module-level side effects, __all__
r2-p008fired, no fixClass Attributes & Descriptorsclass attributes vs instance attributes, descriptors, properties with side effects

Sources

r1-p001 · Custom boolean-like object with overridden __neg__/__pos__
1class MyBool(int):
2 def __new__(cls, val):
3 return super().__new__(cls, val)
4 def __bool__(self):
5 return bool(super().__int__())
6 def __inv__(self):
7 # Custom bitwise inversion that differs from logical negation
8 return MyBool(~int(self))
9
10obj = MyBool(1) # truthy
11# Original: False if obj else True -> evaluates to False
12original = False if obj else True
13# Ruff would rewrite as: not obj
14rewritten = not obj
15print(f'Original: {original}, Rewritten: {rewritten}')
16assert original == rewritten, 'Should be equivalent'
Original: False, Rewritten: False
r1-p002 · Conditional in larger expression relying on precedence
1x = True
2# Original: (False if x else True) or False -> (False) or False -> False
3result_orig = (False if x else True) or False
4# Rewritten by Ruff: not x or False -> (not True) or False -> False or False -> False
5result_rewritten = not x or False
6print(f'Original grouped: {result_orig}, Rewritten: {result_rewritten}')
7assert result_orig == result_rewritten
Original grouped: False, Rewritten: False
r1-p003 · Condition with side effects (function call)
1call_count = 0
2
3def cond():
4 global call_count
5 call_count += 1
6 return call_count % 2 == 1 # alternates True, False, True, False...
7
8# First evaluation
9val1_orig = False if cond() else True
10val1_rewritten = not cond()
11print(f'After first call: call_count={call_count}, orig={val1_orig}, rewritten={val1_rewritten}')
12
13# Second evaluation
14val2_orig = False if cond() else True
15val2_rewritten = not cond()
16print(f'After second call: call_count={call_count}, orig={val2_orig}, rewritten={val2_rewritten}')
17
18# They should match in value but show same side effect count
19assert val1_orig == val1_rewritten
20assert val2_orig == val2_rewritten
21print(f'Total calls: {call_count}')
After first call: call_count=2, orig=False, rewritten=True
After second call: call_count=4, orig=False, rewritten=True
r1-p004 · Mutable default argument captured in closure
1def make_checker(lst=[]):
2 lst.append(len(lst)) # mutate default
3 return lambda: False if lst else True
4
5f1 = make_checker() # lst becomes [0]
6f2 = make_checker() # lst becomes [0, 1] (same list!)
7
8# Original behavior
9orig1 = f1()
10orig2 = f2()
11print(f'f1() orig: {orig1}, f2() orig: {orig2}')
12
13# Now simulate what happens if Ruff rewrote the lambdas
14# But we can't rewrite the lambda directly, so we show the values
15# The hazard is that if someone expects the rewrite to change behavior,
16# but it doesn't — however, we show the shared state.
17# To make trace depend on rewrite: compare what 'not lst' gives
18rewritten1 = not f1.__closure__[0].cell_contents # This is cheating — instead:
19# Let's re-express: the value of the condition is the list
20# We'll print the list and its truthiness
21lst_val = f1.__closure__[0].cell_contents
22print(f'List contents: {lst_val}, bool: {bool(lst_val)}, not: {not lst_val}')
f1() orig: False, f2() orig: False
List contents: [0, 1], bool: True, not: False
r1-p005 · Walrus operator in condition
1y = 5
2# Original: False if (x := y) else True -> assigns x=y, then returns False because x is truthy
3# So x gets assigned, and result is False
4result_orig = False if (x := y) else True
5print(f'After original: x={x}, result={result_orig}')
6
7# Reset for rewritten version
8x = None
9# What Ruff would produce: not (x := y)
10result_rewritten = not (x := y)
11print(f'After rewritten: x={x}, result={result_rewritten}')
12
13# Both assign x=y and yield same boolean result
14assert result_orig == result_rewritten
15print(f'x value in both cases: {x}')
After original: x=5, result=False
After rewritten: x=5, result=False
x value in both cases: 5
r1-p006 · Exception in condition with finally clause
1def risky():
2 try:
3 raise ValueError('bad')
4 finally:
5 print('finally executed')
6
7try:
8 # Original: False if risky() else True
9 # risky() raises, so we catch and note finally ran
10 result = False if risky() else True
11except ValueError as e:
12 print(f'Caught: {type(e).__name__}: {e}')
13
14# For rewritten version, we need to simulate same structure
15# But we can't easily re-raise, so we show the finally still runs
16print('Now testing rewritten form:')
17try:
18 result = not risky() # This would be the rewrite
19except ValueError as e:
20 print(f'Caught: {type(e).__name__}: {e}')
finally executed
Caught: ValueError: bad
Now testing rewritten form:
finally executed
Caught: ValueError: bad
r1-p007 · Slice object as condition (near-miss, no trigger expected)
1s = [1, 2, 3, 4]
2slice_obj = s[1:3] # [2, 3]
3# Condition: slice_obj is truthy
4orig = False if slice_obj else True
5# Rewrite: not slice_obj
6rewritten = not slice_obj
7print(f'Slice: {slice_obj}, bool: {bool(slice_obj)}')
8print(f'Original: {orig}, Rewritten: {rewritten}')
9assert orig == rewritten
10# Also test empty slice
11empty_slice = s[1:1]
12orig2 = False if empty_slice else True
13rewritten2 = not empty_slice
14print(f'Empty slice: {empty_slice}, orig: {orig2}, rewritten: {rewritten2}')
15assert orig2 == rewritten2
Slice: [2, 3], bool: True
Original: False, Rewritten: False
Empty slice: [], orig: True, rewritten: True
r1-p008 · Numeric coercion edge case: numpy-like scalar (but using int/float/Decimal)
1from decimal import Decimal
2
3# Test values that are falsy/truthy and see how 'not' behaves
4# All should be equivalent to conditional
5vals = [0, 0.0, Decimal('0'), 1, 1.0, Decimal('1'), -0.0]
6for v in vals:
7 orig = False if v else True
8 rewritten = not v
9 print(f'Value: {v!r} ({type(v).__name__}), orig: {orig}, rewritten: {rewritten}')
10 assert orig == rewritten, f'Mismatch for {v}'
11
12# Special case: float NaN is truthy
13import math
14nan = float('nan')
15orig_nan = False if nan else True
16rewritten_nan = not nan
17print(f'NaN: orig={orig_nan}, rewritten={rewritten_nan}')
18assert orig_nan == rewritten_nan # Both False because nan is truthy
19print('All numeric cases equivalent')
Value: 0 (int), orig: True, rewritten: True
Value: 0.0 (float), orig: True, rewritten: True
Value: Decimal('0') (Decimal), orig: True, rewritten: True
Value: 1 (int), orig: False, rewritten: False
Value: 1.0 (float), orig: False, rewritten: False
Value: Decimal('1') (Decimal), orig: False, rewritten: False
Value: -0.0 (float), orig: True, rewritten: True
NaN: orig=False, rewritten=False
All numeric cases equivalent
r2-p001 · Dict Ordering & Duplicate Keys
1class K:
2 def __init__(self, v): self.v = v
3 def __hash__(self): return hash(self.v)
4 def __eq__(self, other):
5 print(f'eq {self.v} == {other.v}')
6 return self.v == other.v
7
8vals = [K(1), K(2)]
9d = {}
10for k in vals:
11 d[k] = False if k.v == 1 else True
12print('dict:', [(k.v, v) for k, v in d.items()])
dict: [(1, False), (2, True)]
r2-p002 · Comments & Docstrings Attached
1def f(x):
2 # This comment describes the condition
3 return False if x > 0 else True # and this explains the result
4
5print('f(-1)=', f(-1))
6print('f(1)=', f(1))
f(-1)= True
f(1)= False
r2-p003 · Lambda Default Argument Introspection
1x = True
2lam = lambda cond=(False if x else True): cond
3print('lam():', lam())
4import inspect
5try:
6 print('source:', inspect.getsource(lam))
7except Exception as e:
8 print('inspect failed:', type(e).__name__, str(e))
lam(): False
source: lam = lambda cond=(False if x else True): cond
r2-p004 · Type Annotations Evaluated at Runtime
1from typing import List
2T = False if True else True
3
4def g(x: T) -> None:
5 print('got', x)
6
7g(1)
8print('T=', T)
got 1
T= False
r2-p005 · Bytes vs Str Encoding
1data = b'hello'
2result = False if data == b'hello' else True
3with open('test.txt', 'wb') as f:
4 f.write(b'yes' if not result else b'no')
5with open('test.txt', 'rb') as f:
6 print('file contains:', f.read())
7import os
8os.remove('test.txt')
file contains: b'yes'
r2-p006 · Shadowed Builtins
1list = [1, 2, 3]
2cond = False if len(list) > 2 else True
3print('list var:', list)
4print('cond:', cond)
5del list
6print('built-in list available:', list is __builtins__.list)
list var: [1, 2, 3]
cond: False
built-in list available: True
r2-p007 · Star-Imports & Module Side Effects
1__all__ = ['flag']
2flag = False if 1 == 1 else True
3print('flag before import:', flag)
4# Simulate star import effect
5locals().update({'flag': not flag})
6print('flag after simulated * import:', flag)
flag before import: False
flag after simulated * import: True
r2-p008 · Class Attributes & Descriptors
1class A:
2 x = False if 2 + 2 == 4 else True
3 @property
4 def prop(self):
5 print('prop accessed')
6 return self.x
7
8a = A()
9print('class x:', A.x)
10print('instance prop:', a.prop)
class x: False
prop accessed
instance prop: False