fixhunt

ruff RET501

labelled unknown
Runs / 2026-10-17T20-31-28-333Z-3plb-ruff-RET501ruff 0.17.12026-10-17 20:31 UTC8 programs × 2 round(s)16 programs, 9 fired, 9 fixed, 0 findings, 2 rejectedmodel cost $0.0030

Analysis Nemotron 3 Super

Flags explicit 'return None' statements where None is the only possible return value across all code paths in a function.

Fix: Rewrites 'return None' to 'return' when None is the only possible return value across all code paths in the function.

8 hazards
  • The rewrite removes trailing comments attached to the return statement, altering source code readability.
  • If the function contains a docstring after the return statement (unreachable but syntactically valid), the rewrite may affect parsing in edge cases involving AST transformations.
  • In rare cases where 'return None' is used to deliberately suppress a generator yield (though syntactically invalid as mixed yield/return), the rewrite could interfere with tooling expectations.
  • If the return statement is part of a larger expression via line continuation (e.g., using backslash), rewriting may break syntax due to changed token structure.
  • The fix may interfere with custom decorators or metaclasses that inspect bytecode offsets, as removing 'None' alters the bytecode length and jump targets.
  • In debugging contexts, explicit 'return None' may be used to set breakpoints; removing it could affect debug symbol mapping.
  • If the function is overridden in a subclass and the return type is part of a contract (e.g., via structural typing), changing the return may affect type checker inferences.
  • The rewrite may eliminate a deliberate 'return None' used to avoid returning a tuple in a single-expression function where implicit return would otherwise produce None anyway—no behavioral change, but may violate stylistic intent in codebases that enforce explicit returns for clarity.

Programs

idresulttitlehazard
r1-p001fix okClass attribute shadowing via descriptorclass attributes vs instance attributes, descriptors, properties with side effects
r1-p002fix okFloat rounding and int coercionint/float/bool coercion, Decimal, division and rounding
r1-p003fix okMutable default aliasing after rewritemutable default arguments and aliasing after a rewrite
r1-p004silent prediction offExplicit return None to avoid tuple in lambda-like contextThe rewrite may eliminate a deliberate 'return None' used to avoid returning a tuple in a single-expression function where implicit return would otherwise produce None anyway—no behavioral change, but
r1-p005invalidDocstring after return (unreachable)If the function contains a docstring after the return statement (unreachable but syntactically valid), the rewrite may affect parsing in edge cases involving AST transformations.
r1-p006fix okFile write with truncation and partial writefile modes and truncation, partial writes, non-str data passed to write
r1-p007silent prediction offTrailing comment on return statementThe rewrite removes trailing comments attached to the return statement, altering source code readability.
r1-p008fix okDict ordering with duplicate keys and custom hashdict ordering, duplicate keys, __hash__/__eq__ overrides, sets
r2-p001silent prediction offSubclass override and structural typingChanging `return None` to `return` may affect type checker inferences in overridden methods under structural typing (e.g., Protocol).
r2-p002fix ok prediction offMutable default argument aliasingRewriting `return None` to `return` does not change behavior, but the interaction with mutable defaults must be shown to be deterministic.
r2-p003fix okKeyword-only, positional-only, *args/**kwargs forwardingThe rewritten `return` statement must be shown to preserve argument forwarding behavior in complex signatures.
r2-p004invalidBytes vs str, encoding and newline handlingThe rewrite must not alter byte/str handling in I/O contexts where `None` return is part of a pipeline.
r2-p005silentDeliberate suppression of generator yield (tooling expectation)Although syntactically invalid to mix yield and return, some tools may expect `return None` to signal intentional absence of yield; removing it may confuse static analysis.
r2-p006fix okWalrus, comprehensions scoping, late binding in closuresThe rewritten `return` must not alter closure capture in nested scopes involving walrus or comprehensions.
r2-p007fix okString formatting edge cases: %, format, f-strings, repr vs strThe rewrite must not interfere with string formatting logic that depends on the function's return being None.
r2-p008silent prediction offAvoiding tuple return in single-expression functionThe rewrite removes explicit `return None` which was used for stylistic clarity to avoid implying a tuple return, though behaviorally identical.

Sources

r1-p001 · Class attribute shadowing via descriptor
1class A:
2 x = 1
3 def __get__(self, obj, owner):
4 print('get', obj.x if obj else owner)
5 return obj.x if obj else 2
6
7def f():
8 a = A()
9 a.x = 99
10 return None # explicit
11
12r = f()
13print('returned:', r)
14print('a.x:', A().x)
--- orig.py	2026-10-17 16:31:36.470652535 -0400
+++ fixed.py	2026-10-17 16:31:36.639652704 -0400
@@ -7,7 +7,7 @@
 def f():
     a = A()
     a.x = 99
-    return None  # explicit
+    return  # explicit
 
 r = f()
 print('returned:', r)
returned: None
a.x: 1
r1-p002 · Float rounding and int coercion
1from decimal import Decimal, getcontext
2getcontext().prec = 3
3
4def f():
5 x = Decimal('1.005')
6 y = float(round(x, 2))
7 z = int(y * 100)
8 return None # explicit
9
10r = f()
11print('returned:', r)
12print('z:', z if 'z' in locals() else 'undefined')
--- orig.py	2026-10-17 16:31:36.738652804 -0400
+++ fixed.py	2026-10-17 16:31:36.917652983 -0400
@@ -5,7 +5,7 @@
     x = Decimal('1.005')
     y = float(round(x, 2))
     z = int(y * 100)
-    return None  # explicit
+    return  # explicit
 
 r = f()
 print('returned:', r)
returned: None
z: undefined
r1-p003 · Mutable default aliasing after rewrite
1def f(l=[]):
2 l.append(1)
3 return None # explicit
4
5a = f()
6b = f()
7print('returned a:', a)
8print('returned b:', b)
9print('default list:', f.__defaults__[0])
--- orig.py	2026-10-17 16:31:37.032653099 -0400
+++ fixed.py	2026-10-17 16:31:37.204653272 -0400
@@ -1,6 +1,6 @@
 def f(l=[]):
     l.append(1)
-    return None  # explicit
+    return  # explicit
 
 a = f()
 b = f()
returned a: None
returned b: None
default list: [1, 1]
r1-p004 · Explicit return None to avoid tuple in lambda-like context
1def f():
2 return None # explicit for clarity; avoids tuple perception
3
4g = lambda: None
5print('f() is g():', f() is g())
6print('type(f()):', type(f()))
7print('f().__class__:', f().__class__)
f() is g(): True
type(f()): <class 'NoneType'>
f().__class__: <class 'NoneType'>
r1-p005 · Docstring after return (unreachable) (generated program is not deterministic)
1def f():
2 return None
3 """This docstring is unreachable but legal."""
4 x = 1
5
6try:
7 r = f()
8 print('returned:', r)
9 print('locals after:', locals())
10except Exception as e:
11 print(type(e).__name__, str(e))
r1-p006 · File write with truncation and partial write
1def f():
2 with open('test.txt', 'w') as fp:
3 fp.write(b'hello') # TypeError expected
4 return None # explicit
5
6try:
7 f()
8except Exception as e:
9 print(type(e).__name__, str(e))
10finally:
11 try:
12 with open('test.txt') as fp:
13 print('file content:', repr(fp.read()))
14 except:
15 print('file content: (unreadable)')
--- orig.py	2026-10-17 16:31:37.529653600 -0400
+++ fixed.py	2026-10-17 16:31:37.706653778 -0400
@@ -1,7 +1,7 @@
 def f():
     with open('test.txt', 'w') as fp:
         fp.write(b'hello')  # TypeError expected
-    return None  # explicit
+    return  # explicit
 
 try:
     f()
TypeError write() argument must be str, not bytes
file content: ''
r1-p007 · Trailing comment on return statement
1def f():
2 return None # important: signals completion
3
4r = f()
5print('returned:', r)
6print('comment lost?')
returned: None
comment lost?
r1-p008 · Dict ordering with duplicate keys and custom hash
1class K:
2 def __init__(self, v):
3 self.v = v
4 def __hash__(self):
5 return hash(self.v)
6 def __eq__(self, other):
7 return isinstance(other, K) and self.v == other.v
8
9def f():
10 d = {K(1): 'a', K(1): 'b'} # duplicate key
11 return None # explicit
12
13r = f()
14print('returned:', r)
15print('dict built?')
--- orig.py	2026-10-17 16:31:37.942654016 -0400
+++ fixed.py	2026-10-17 16:31:38.111654188 -0400
@@ -8,7 +8,7 @@
 
 def f():
     d = {K(1): 'a', K(1): 'b'}  # duplicate key
-    return None  # explicit
+    return  # explicit
 
 r = f()
 print('returned:', r)
returned: None
dict built?
r2-p001 · Subclass override and structural typing
1from typing import Protocol
2
3class P(Protocol):
4 def f(self) -> None: ...
5
6class Base:
7 def f(self):
8 return None
9
10class Derived(Base):
11 def f(self):
12 return None
13
14x: P = Derived()
15print(type(x.f()))
<class 'NoneType'>
r2-p002 · Mutable default argument aliasing
1def f(a=[]):
2 a.append(1)
3 return None
4
5print(f())
6print(f())
7print(f())
--- orig.py	2026-10-17 16:31:42.459658638 -0400
+++ fixed.py	2026-10-17 16:31:42.624658810 -0400
@@ -1,6 +1,6 @@
 def f(a=[]):
     a.append(1)
-    return None
+    return
 
 print(f())
 print(f())
None
None
None
r2-p003 · Keyword-only, positional-only, *args/**kwargs forwarding
1def f(pos, /, *, kw=None, **kwargs):
2 if pos == 0:
3 return
4 return None
5
6print(f(0))
7print(f(1, kw=2, extra=3))
--- orig.py	2026-10-17 16:31:42.726658915 -0400
+++ fixed.py	2026-10-17 16:31:42.895659090 -0400
@@ -1,7 +1,7 @@
 def f(pos, /, *, kw=None, **kwargs):
     if pos == 0:
         return
-    return None
+    return
 
 print(f(0))
 print(f(1, kw=2, extra=3))
None
None
r2-p004 · Bytes vs str, encoding and newline handling (generated program does not parse: Traceback (most recent call last):)
1def f():
2 b = b'hello\nworld'
3 if b'
4' in b:
5 return None
6 return b
7
8result = f()
9print(result is None)
r2-p005 · Deliberate suppression of generator yield (tooling expectation)
1def f():
2 if False:
3 yield 1
4 return None
5
6try:
7 list(f())
8 print('no error')
9except Exception as e:
10 print(type(e).__name__, e)
no error
r2-p006 · Walrus, comprehensions scoping, late binding in closures
1def f():
2 x = 0
3 if (x := x + 1) > 0:
4 return
5 return None
6
7print(f())
--- orig.py	2026-10-17 16:31:43.279659489 -0400
+++ fixed.py	2026-10-17 16:31:43.440659657 -0400
@@ -2,6 +2,6 @@
     x = 0
     if (x := x + 1) > 0:
         return
-    return None
+    return
 
 print(f())
None
r2-p007 · String formatting edge cases: %, format, f-strings, repr vs str
1def f():
2 s = 'hello'
3 if len(s) == 5:
4 return
5 return None
6
7print('Result: {}'.format(f()))
--- orig.py	2026-10-17 16:31:43.540659761 -0400
+++ fixed.py	2026-10-17 16:31:43.699659927 -0400
@@ -2,6 +2,6 @@
     s = 'hello'
     if len(s) == 5:
         return
-    return None
+    return
 
 print('Result: {}'.format(f()))
Result: None
r2-p008 · Avoiding tuple return in single-expression function
1def f():
2 return None
3
4print(type(f()))
<class 'NoneType'>