ruff PLR1711
labelled unknown
Analysis Nemotron 3 Super
Flags functions ending with an explicit 'return' or 'return None' when they contain no other return statements.
Fix: Removes the final 'return' or 'return None' statement from the function body, preserving any trailing comment on the same line if present.
10 hazards
- If the function contains a nested function that uses 'return' to escape to the outer function, removing the outer return may alter control flow.
- If the 'return' statement is inside a try block and the finally block raises an exception, removing the return may change which exception propagates.
- If the function is overridden in a subclass and the super() call relies on the implicit None return, changing the return may affect cooperative multiple inheritance behavior.
- If the return statement is followed by a type ignore comment on the same line, removing the line may drop the ignore and cause type-checking errors.
- If the function is part of a protocol or interface that expects an explicit return None for documentation or tooling purposes, removing it may break tooling assumptions.
- If the return statement is the only statement in a function body and the function is used in a context where its __code__.co_flags is inspected, removing the return may change the flag set (e.g., CO_NOFREE).
- If the function contains a late-bound closure that captures a variable defined after the return (unreachable), removing the return does not affect behavior but may mislead readers about reachability.
- If the return statement is written as 'return (expr)' where expr is a parenthesized expression with side effects, removing it may suppress those side effects (though the rule does not fire if there are other returns, this case is prevented by the 'no other returns' condition).
- If the function is decorated with a decorator that inspects the bytecode for the presence of a RETURN_VALUE instruction, removing the return may change the decorator's behavior.
- If the function is invoked via a frame hack (e.g., sys._getframe) that checks f_lasti for the offset of a RETURN_VALUE, removing the return changes the last instruction offset.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Walrus operator affecting comprehension scoping | walrus, comprehensions scoping, late binding in closures and lambdas |
| r1-p002 | fix ok | String formatting with % and .format interacting via returned value | string formatting edge cases: %, format, f-strings, repr vs str, nested quotes |
| r1-p003 | fix ok | Binary mode file write using returned buffer length | bytes vs str, encoding and newline handling, text vs binary modes |
| r1-p004 | fix ok | Property with side effect accessed after implicit None return | class attributes vs instance attributes, descriptors, properties with side effects |
| r1-p005 | fix ok | Nested function escaping via return alters outer flow | If the function contains a nested function that uses 'return' to escape to the outer function, removing the outer return may alter control flow. |
| r1-p006 | silent | Try-finally where finally raises overriding return path | If the 'return' statement is inside a try block and the finally block raises an exception, removing the return may change which exception propagates. |
| r1-p007 | fix ok | Mutable default argument list alias across calls | mutable default arguments and aliasing after a rewrite |
| r1-p008 | silent prediction off | Custom bool and len return None used in truth test | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r2-p001 | invalid | dict_ordering_duplicate_keys_hash_eq_set | dict ordering, duplicate keys, __hash__/__eq__ overrides, sets |
| r2-p002 | fix ok | frame_hack_f_lasti_return_value_offset | If the function is invoked via a frame hack (e.g., sys._getframe) that checks f_lasti for the offset of a RETURN_VALUE, removing the return changes the last instruction offset. |
| r2-p003 | silent prediction off | type_ignore_comment_on_same_line_dropped | If the return statement is followed by a type ignore comment on the same line, removing the line may drop the ignore and cause type-checking errors. |
| r2-p004 | silent prediction off | evaluation_order_side_effects_expressions_merged_deleted | evaluation order and side effects when expressions are merged, duplicated or deleted |
| r2-p005 | silent prediction off | class_attributes_vs_instance_attributes_descriptors_properties_side_effects | class attributes vs instance attributes, descriptors, properties with side effects |
| r2-p006 | fix ok | truthiness_custom_bool_len_none_falsy_is_eq | truthiness of custom __bool__/__len__, None vs falsy, 'is' vs '==' |
| r2-p007 | silent prediction off | function_used_in_context_where___code___co_flags_inspected | If the return statement is the only statement in a function body and the function is used in a context where its __code__.co_flags is inspected, removing the return may change the flag set (e.g., CO_N |
| r2-p008 | silent prediction off | comments_and_docstrings_attached_to_removed_or_moved_code | comments and docstrings attached to the removed or moved code |
Sources
r1-p001 · Walrus operator affecting comprehension scoping
1def f():
2 lst = [x := i*2 for i in range(2)]
3 return None
4print(f())
--- orig.py 2026-10-17 17:01:15.905400004 -0400 +++ fixed.py 2026-10-17 17:01:16.080402139 -0400 @@ -1,4 +1,3 @@ def f(): lst = [x := i*2 for i in range(2)] - return None print(f())
None
r1-p002 · String formatting with % and .format interacting via returned value
1def fmt(val):
2 s = '%s' % val
3 t = '{}{}'.format(s, '!')
4 return None
5r = fmt('A')
6print(r)
7s = fmt('B')
8if s is None:
9 print('got none')
--- orig.py 2026-10-17 17:01:16.182403384 -0400 +++ fixed.py 2026-10-17 17:01:16.352405458 -0400 @@ -1,7 +1,6 @@ def fmt(val): s = '%s' % val t = '{}{}'.format(s, '!') - return None r = fmt('A') print(r) s = fmt('B')
None got none
r1-p003 · Binary mode file write using returned buffer length
1def w():
2 data = b'hi'
3 with open('tmp.bin', 'wb') as fp:
4 n = fp.write(data)
5 return None
6n = w()
7with open('tmp.bin', 'rb') as fp:
8 print(fp.read())
9import os; os.remove('tmp.bin')
--- orig.py 2026-10-17 17:01:16.454406702 -0400 +++ fixed.py 2026-10-17 17:01:16.616408679 -0400 @@ -2,7 +2,6 @@ data = b'hi' with open('tmp.bin', 'wb') as fp: n = fp.write(data) - return None n = w() with open('tmp.bin', 'rb') as fp: print(fp.read())
b'hi'
r1-p004 · Property with side effect accessed after implicit None return
1class C:
2 @property
3 def val(self):
4 self._seen = True
5 return 42
6def f():
7 c = C()
8 _ = c.val
9 return None
10f()
11c = C()
12print(hasattr(c, '_seen'))
--- orig.py 2026-10-17 17:01:16.720409948 -0400 +++ fixed.py 2026-10-17 17:01:16.889412010 -0400 @@ -6,7 +6,6 @@ def f(): c = C() _ = c.val - return None f() c = C() print(hasattr(c, '_seen'))
False
r1-p005 · Nested function escaping via return alters outer flow
1def f():
2 def g():
3 return 'escaped'
4 x = g()
5 y = x + ' more'
6 return None
7res = f()
8print(res)
--- orig.py 2026-10-17 17:01:17.002413388 -0400 +++ fixed.py 2026-10-17 17:01:17.173415474 -0400 @@ -3,6 +3,5 @@ return 'escaped' x = g() y = x + ' more' - return None res = f() print(res)
None
r1-p006 · Try-finally where finally raises overriding return path
1def f():
2 try:
3 return 42
4 finally:
5 raise RuntimeError('finally wins')
6try:
7 f()
8except Exception as e:
9 print(type(e).__name__, str(e))
RuntimeError finally wins
r1-p007 · Mutable default argument list alias across calls
1def appender(item, lst=[]):
2 lst.append(item)
3 return None
4appender(1)
5appender(2)
6f = appender
7f.__defaults__[0].append(3)
8print(appender(0)[2])
--- orig.py 2026-10-17 17:01:17.417418449 -0400 +++ fixed.py 2026-10-17 17:01:17.812423267 -0400 @@ -1,6 +1,5 @@ def appender(item, lst=[]): lst.append(item) - return None appender(1) appender(2) f = appender
r1-p008 · Custom bool and len return None used in truth test
1class T:
2 def __bool__(self):
3 return True
4 def __len__(self):
5 return 0
6def maker():
7 return T()
8x = maker()
9if x:
10 print('true')
11else:
12 print('false')
true
r2-p001 · dict_ordering_duplicate_keys_hash_eq_set (generated program is not deterministic)
1def f():
2 d = {}
3 class K:
4 def __init__(self, v): self.v = v
5 def __hash__(self): return hash(self.v)
6 def __eq__(self, other): return isinstance(other, K) and self.v == other.v
7 d[K(1)] = 'a'
8 d[K(1)] = 'b'
9 return d
10print(sorted(f().items()))
r2-p002 · frame_hack_f_lasti_return_value_offset
1import sys
2
3def f():
4 x = 1
5 y = 2
6 z = x + y
7 return
8
9frame = sys._getframe()
10f()
11offset = frame.f_lasti
12print('offset:', offset)
13if offset < 0:
14 print('negative')
15else:
16 print('non-negative')
--- orig.py 2026-10-17 17:01:24.499504684 -0400 +++ fixed.py 2026-10-17 17:01:24.665506702 -0400 @@ -4,7 +4,6 @@ x = 1 y = 2 z = x + y - return frame = sys._getframe() f()
offset: 66 non-negative
r2-p003 · type_ignore_comment_on_same_line_dropped
1from typing import List
2
3def f() -> List[int]:
4 x = 1
5 return [x] # type: ignore[list-item]
6
7result = f()
8print(len(result))
9for item in result:
10 print(item)
1 1
r2-p004 · evaluation_order_side_effects_expressions_merged_deleted
1counter = 0
2
3def inc():
4 global counter
5 counter += 1
6 return counter
7
8def f():
9 a = inc()
10 b = inc()
11 return a + b
12
13print(f())
3
r2-p005 · class_attributes_vs_instance_attributes_descriptors_properties_side_effects
1class A:
2 x = 1
3 @property
4 def prop(self):
5 return self.x + 1
6
7def f():
8 a = A()
9 a.x = 2
10 return a.prop
11
12print(f())
3
r2-p006 · truthiness_custom_bool_len_none_falsy_is_eq
1class C:
2 def __bool__(self): return False
3 def __len__(self): return 1
4
5def f():
6 c = C()
7 return
8
9obj = f()
10if obj is None:
11 print('is None')
12else:
13 print('not None')
14if obj == None:
15 print('== None')
16else:
17 print('!= None')
18if obj:
19 print('truthy')
20else:
21 print('falsy')
--- orig.py 2026-10-17 17:01:25.186513036 -0400 +++ fixed.py 2026-10-17 17:01:25.359515138 -0400 @@ -4,7 +4,6 @@ def f(): c = C() - return obj = f() if obj is None:
is None == None falsy
r2-p007 · function_used_in_context_where___code___co_flags_inspected
1def f():
2 return
3
4code = f.__code__
5flags = code.co_flags
6print('CO_NOFREE set:', bool(flags & 0x20))
7print('CO_NEWLOCALS set:', bool(flags & 0x02))
CO_NOFREE set: False CO_NEWLOCALS set: True
r2-p008 · comments_and_docstrings_attached_to_removed_or_moved_code
1def f():
2 """This is a docstring."""
3 x = 1 # inline comment
4 return x # trailing comment
5
6print(f.__doc__)
7print(f())
This is a docstring. 1