fixhunt

ruff E711

labelled unknown
Runs / 2026-10-17T20-47-50-035Z-s55l-ruff-E711ruff 0.17.12026-10-17 20:47 UTC8 programs × 2 round(s)16 programs, 12 fired, 0 fixed, 0 findings, 3 rejectedmodel cost $0.0040

Analysis Nemotron 3 Super

Flags non-is comparison operations against None.

Fix: Replaces '==' with 'is' and '!=' with 'is not' when comparing against None literal.

12 hazards
  • If __eq__ is overridden, 'x == None' may differ from 'x is None', changing truth value.
  • If __ne__ is overridden, 'x != None' may differ from 'x is not None', altering logic flow.
  • Rewriting 'None == x' to 'None is x' fails if x defines __eq__ but not symmetric behavior via __eq__ delegation.
  • Comments attached to the operator (e.g., 'x == # comment\nNone') may be lost or misplaced during token replacement.
  • In expressions like 'f(x == None)', rewriting changes operand type from bool to NoneType, breaking function expectations.
  • If None is shadowed locally (e.g., 'None = 5'), the fix incorrectly rewrites to 'is 5' or 'is not 5', causing NameError or wrong semantics.
  • In default arguments like 'def f(x=None): if x == None:', the fix alters comparison but not the default, creating inconsistency if None is rebound.
  • When used with Optional types in generics, 'x == None' might rely on overloaded equality in proxies, which 'is' bypasses.
  • In NumPy arrays, 'arr == None' returns an array of bools, while 'arr is None' is always False — rewrite breaks vectorized intent.
  • If the comparison is part of a larger expression like '(x == None) and y', rewriting may short-circuit differently due to truth value of None vs bool.
  • In eval() or exec() contexts, the rewritten string may fail if the local scope doesn't contain the built-in None after rebinding.
  • If the code relies on the == operator returning a non-boolean (e.g., custom proxy), replacing it with 'is' forces a bool, breaking downstream usage.

Programs

idresulttitlehazard
r1-p001fired, no fixMutable Default Argument Aliasing After RewriteRewriting `x == None` to `x is None` in a mutable default argument context breaks aliasing checks due to shared mutable state across calls.
r1-p002fired, no fixFunction Expecting Boolean from Equality ComparisonRewriting `x == None` to `x is None` inside a function call changes the operand from bool to NoneType, breaking functions that expect boolean input.
r1-p003fired, no fixBytes vs Str Encoding and Newline HandlingRewriting `data == None` in binary/text I/O context can mask encoding-aware checks, leading to silent data corruption when comparing byte/str literals.
r1-p004fired, no fixOptional Types and Overloaded Equality in ProxiesRewriting `x == None` to `x is None` bypasses `__eq__` overloads in proxy objects (e.g., Optional wrappers), breaking lazy validation or logging.
r1-p005fired, no fixComments and Docstrings Attached to Removed CodeRewriting removes the `== None` expression, taking its inline comment with it and losing documentation about why the check exists.
r1-p006fired, no fixStar-Imports, Module-Level Side Effects, __all__Rewriting `x == None` in module init changes timing of side effects if the variable is defined via star-import and __all__ affects visibility.
r1-p007invalidGenerators vs Lists: Iterators Consumed OnceRewriting `x == None` in a generator expression changes when the check occurs, causing premature consumption or mismatch in lazy evaluation.
r1-p008fired, no fixClass Attributes vs Instance Attributes, Descriptors, PropertiesRewriting `self.x == None` to `self.x is None` bypasses `__get__` descriptors, breaking computed or validated attributes.
r2-p001silent prediction offRuntime Type Annotation Evaluation with Forward Reftype annotations evaluated at runtime, typing-only imports
r2-p002fired, no fixOptional Generic Proxy with Overloaded EqualityWhen used with Optional types in generics, 'x == None' might rely on overloaded equality in proxies, which 'is' bypasses.
r2-p003invalidLocal Shadowing of None Breaks RewriteIf None is shadowed locally (e.g., 'None = 5'), the fix incorrectly rewrites to 'is 5' or 'is not 5', causing NameError or wrong semantics.
r2-p004fired, no fixEvaluation Order with Side Effects in Argumentsevaluation order and side effects when expressions are merged, duplicated or deleted
r2-p005fired, no fixDict with Custom __eq__ and Hash Collisiondict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r2-p006fired, no fixOverridden __eq__ Changes SemanticsIf __eq__ is overridden, 'x == None' may differ from 'x is None', changing truth value.
r2-p007fired, no fixClass Attribute vs Descriptor with Side Effectsclass attributes vs instance attributes, descriptors, properties with side effects
r2-p008invalidEval Context After Local None RebindingIn eval() or exec() contexts, the rewritten string may fail if the local scope doesn't contain the built-in None after rebinding.

Sources

r1-p001 · Mutable Default Argument Aliasing After Rewrite
1def f(a=[]):
2 if a == None:
3 a = [1]
4 a.append(2)
5 print(a)
6f()
7f()
8f()
[2]
[2, 2]
[2, 2, 2]
r1-p002 · Function Expecting Boolean from Equality Comparison
1def expects_bool(b):
2 return 'yes' if b else 'no'
3x = None
4result = expects_bool(x == None)
5print(result)
yes
r1-p003 · Bytes vs Str Encoding and Newline Handling
1data = b'hello\n'
2if data == None:
3 data = b''
4print(len(data))
5print(repr(data))
6
b'hello\n'
r1-p004 · Optional Types and Overloaded Equality in Proxies
1class OptionalInt:
2 def __init__(self, value):
3 self.value = value
4 def __eq__(self, other):
5 print('eq called')
6 return self.value is None and other is None
7x = OptionalInt(None)
8print(x == None)
eq called
True
r1-p005 · Comments and Docstrings Attached to Removed Code
1x = None
2# Check if uninitialized before use
3if x == None: # safety guard for hot path
4 x = 42
5print(x)
42
r1-p006 · Star-Imports, Module-Level Side Effects, __all__
1__all__ = ['config']
2config = None
3if config == None:
4 config = {'mode': 'prod'}
5print(config)
{'mode': 'prod'}
r1-p007 · Generators vs Lists: Iterators Consumed Once (generated program does not parse: Traceback (most recent call last):)
1def gen():
2 for i in [None, 1]:
3 yield i
4 g = gen()
5print(list(x == None for x in g))
r1-p008 · Class Attributes vs Instance Attributes, Descriptors, Properties
1class Validator:
2 def __get__(self, obj, objtype):
3 print('get called')
4 return getattr(obj, '_x', None)
5class MyClass:
6 x = Validator()
7 def __init__(self):
8 self._x = None
9obj = MyClass()
10print(obj.x == None)
get called
True
r2-p001 · Runtime Type Annotation Evaluation with Forward Ref
1import typing
2from typing import get_type_hints
3T = typing.TypeVar('T')
4def func(x: 'Optional[T]') -> None:
5 pass
6try:
7 hints = get_type_hints(func)
8 x = hints['x']
9 # This annotation is evaluated at runtime via get_type_hints
10 # It contains a string forward-ref that resolves to Optional[T]
11 # Now test equality with None inside a context that depends on the resolved type
12 val = None
13 if x == type(val): # Rule triggers: compares type(None) == type(None) via ==
14 print('equal')
15 else:
16 print('not equal')
17except Exception as e:
18 print(f'{type(e).__name__}: {e}')
NameError: name 'Optional' is not defined
r2-p002 · Optional Generic Proxy with Overloaded Equality
1class Proxy:
2 def __init__(self, value):
3 self.value = value
4 def __eq__(self, other):
5 if other is None:
6 return self.value is None
7 return self.value == other
8 def __is__(self, other): # Not real, but we simulate intent
9 pass
10# Simulate a generic Optional-like wrapper
11Maybe = Proxy
12x = Maybe(None)
13if x == None: # Rule triggers: uses __eq__, returns True because value is None
14 print('eq_true')
15else:
16 print('eq_false')
17# After fix: x is None -> False, because x is a Proxy instance, not None
18if x is None:
19 print('is_true')
20else:
21 print('is_false')
eq_true
is_false
r2-p003 · Local Shadowing of None Breaks Rewrite (generated program does not parse: Traceback (most recent call last):)
1def test():
2 None = 5 # Shadow built-in None
3 x = 5
4 try:
5 if x == None: # Rule triggers: compares x == 5 -> True
6 print('eq_true')
7 else:
8 print('eq_false')
9 except Exception as e:
10 print(f'{type(e).__name__}: {e}')
11 # After broken fix: becomes 'x is 5' -> True (same value, but still wrong intent)
12 # But we show the original behavior is preserved in this version
13 # The hazard is that the fix would break meaning — we show what the fix would do
14 # by simulating the incorrect rewrite
15 try:
16 if x is None: # This is what the buggy fix would produce
17 print('is_true')
18 else:
19 print('is_false')
20 except Exception as e:
21 print(f'{type(e).__name__}: {e}')
22test()
r2-p004 · Evaluation Order with Side Effects in Arguments
1counter = 0
2
3def side_effect():
4 global counter
5 counter += 1
6 return None
7
8x = side_effect() # counter=1, x=None
9if x == None: # Rule triggers: x is None from side_effect, but we use ==
10 print(f'eq_true, counter={counter}')
11else:
12 print(f'eq_false, counter={counter}')
13
14# Reset and test again to show determinism
15counter = 0
16x = side_effect()
17if x is None: # What the fix would produce
18 print(f'is_true, counter={counter}')
19else:
20 print(f'is_false, counter={counter}')
eq_true, counter=1
is_true, counter=1
r2-p005 · Dict with Custom __eq__ and Hash Collision
1class EqNone:
2 def __eq__(self, other):
3 return other is None or (isinstance(other, EqNone) and self.value == other.value)
4 def __hash__(self):
5 return 42 # Fixed hash to cause collision
6 def __init__(self, value):
7 self.value = value
8
9# Create two distinct EqNone instances that hash to same bucket
10a = EqNone(1)
11b = EqNone(2)
12d = {a: 'first', b: 'second'} # Due to hash collision, second overwrites first? Actually, dict uses eq too
13# But we care about == None behavior
14val = EqNone(99)
15if val == None: # Rule triggers: returns False because val.value=99 != None
16 print('eq_true')
17else:
18 print('eq_false')
19# After fix: val is None -> False (same result here, but we show the path)
20# To make it differ, change __eq__ to return True for None
21class EqNoneTrue:
22 def __eq__(self, other):
23 return other is None
24 def __hash__(self):
25 return 0
26v = EqNoneTrue()
27if v == None: # True due to __eq__
28 print('eq_true_overridden')
29else:
30 print('eq_false_overridden')
31if v is None: # Fix would make this: False
32 print('is_true_overridden')
33else:
34 print('is_false_overridden')
eq_true
eq_true_overridden
is_false_overridden
r2-p006 · Overridden __eq__ Changes Semantics
1class FakeNone:
2 def __eq__(self, other):
3 return True # Pretends to equal everything, including None
4 def __ne__(self, other):
5 return False
6
7x = FakeNone()
8print(f'x == None: {x == None}') # Rule triggers: True due to __eq__
9print(f'x is None: {x is None}') # False: x is instance, not None
10# After fix: 'x is None' would be False — but originally 'x == None' was True
11# So behavior flips: True -> False
12print(f'After fix simulation: {x is None}')
x == None: True
x is None: False
After fix simulation: False
r2-p007 · Class Attribute vs Descriptor with Side Effects
1class Descriptor:
2 def __get__(self, obj, objtype):
3 print('Descriptor accessed')
4 return None
5 def __set__(self, obj, value):
6 pass
7
8class MyClass:
9 attr = Descriptor()
10 def __init__(self):
11 self.instance_attr = None
12
13obj = MyClass()
14# Trigger rule on class attribute access via instance
15if obj.attr == None: # Rule triggers: calls __get__ -> prints, returns None -> None == None -> True
16 print('class_attr_eq_true')
17else:
18 print('class_attr_eq_false')
19# After fix: obj.attr is None -> still True, but __get__ called once (same)
20# Now make it differ: change descriptor to return something equal to None but not None
21class FakeNoneDescriptor:
22 def __get__(self, obj, objtype):
23 print('Descriptor accessed')
24 return FakeNone() # From previous class
25
26class MyClass2:
27 attr = FakeNoneDescriptor()
28
29obj2 = MyClass2()
30if obj2.attr == None: # __eq__ of FakeNone returns True -> True
31 print('fake_desc_eq_true')
32else:
33 print('fake_desc_eq_false')
34if obj2.attr is None: # Fix: returns False
35 print('fake_desc_is_true')
36else:
37 print('fake_desc_is_false')
Descriptor accessed
class_attr_eq_true
Descriptor accessed
r2-p008 · Eval Context After Local None Rebinding (generated program does not parse: Traceback (most recent call last):)
1def test_eval():
2 None = 42 # Shadow built-in None
3 x = None # x is 42
4 # Original code: x == None -> 42 == 42 -> True
5 try:
6 result = eval('x == None') # Uses local None=42 -> True
7 print(f'eval_result: {result}')
8 except Exception as e:
9 print(f'{type(e).__name__}: {e}')
10 # After buggy fix: becomes 'x is None' -> but None is now 42 -> 'x is 42' -> True (same)
11 # To make it fail, we need the fix to refer to built-in None, but it's shadowed
12 # So simulate: what if the fix incorrectly used the built-in None name without checking scope?
13 # But Ruff's fix is local — it rewrites the AST, so 'is None' still refers to local None
14 # So we show: if the fix were to somehow use the built-in None (which it doesn't), it would break
15 # Instead, we show the hazard: if None is rebound, the fix still works locally — but the *intent* is broken
16 # We instead show: in eval, if the string is rewritten and executed in a scope where None is not defined...
17 del None # Remove local None
18 try:
19 # This would be the broken fix: if Ruff rewrote to 'x is None' but None is gone
20 result = eval('x is None') # NameError: name 'None' is not defined
21 print(f'eval_is_result: {result}')
22 except Exception as e:
23 print(f'{type(e).__name__}: {e}')
24
25test_eval()