ruff TC005
labelled unknown
Analysis Nemotron 3 Super
Flags empty if TYPE_CHECKING blocks containing only pass statements.
Fix: Removes the entire if TYPE_CHECKING: block when its body consists solely of a pass statement, preserving surrounding indentation and code structure.
8 hazards
- Removing the block may change line numbers, affecting debuggers or stack traces that rely on exact source locations.
- If the block contains a trailing comment after pass, the comment will be deleted along with the block.
- In nested contexts like class or function definitions, removing the block alters the indentation depth of subsequent lines, potentially breaking syntax if mixed tabs/spaces exist.
- When the block follows a try/except/finally clause, its removal can change the visual grouping and lead to misinterpretation of exception handling scope.
- If the block is the only statement in a suite (e.g., a lambda or one-liner if), deleting it may result in an empty suite, causing a syntax error.
- In async functions, removing the block could affect the perceived coroutine structure if tools inspect raw source for await placement context.
- When the block is adjacent to a multi-line statement (e.g., with implicit line continuation), its removal may alter the boundary detection for the continued line.
- If the block is used as a placeholder for future type annotations (common in stubs), removing it deletes intentional design intent documented via comments nearby.
Findings
| program | oracle | label | judge | status | summary |
|---|---|---|---|---|---|
| r1-p002 | fix-changes-behavior | labelled safe | real · high | related | The rule removes an `if TYPE_CHECKING:` block without ensuring `TYPE_CHECKING` is imported from `typing`. In the original code, the block is never executed bec… |
| r1-p003 | fix-changes-behavior | labelled safe | real · high | related | The fix removes the `if TYPE_CHECKING:` block without ensuring `TYPE_CHECKING` is imported from `typing`. The original code crashed with a NameError because `T… |
| r1-p004 | fix-changes-behavior | labelled safe | real · high | duplicate | The fix removes the entire `if TYPE_CHECKING:` block, but the program does not import `TYPE_CHECKING` from `typing`. Before the fix, the block raises a `NameEr… |
| r1-p005 | fix-changes-behavior | labelled safe | real · high | duplicate | The fix removes the `if TYPE_CHECKING:` guard but leaves a `pass` statement, changing the code from a no-op (when TYPE_CHECKING is undefined) to an uncondition… |
| r1-p007 | fix-changes-behavior | labelled safe | real · high | duplicate | The rule removes an `if TYPE_CHECKING:` block without ensuring `TYPE_CHECKING` is imported from `typing`. In the original code, the `NameError` occurs at runti… |
| r1-p008 | fix-changes-behavior | labelled safe | real · high | duplicate | The fix replaces the TYPE_CHECKING conditional block with an unconditional 'pass' statement, which changes the function's behavior from raising a NameError (wh… |
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Context manager without close() - object with write() but no __exit__ | context managers that are not files, objects with write() but no close() |
| r1-p002 | fix-changes-behavior | Nested class/function definition with mixed tabs/spaces indentation | In nested contexts like class or function definitions, removing the block alters the indentation depth of subsequent lines, potentially breaking syntax if mixed tabs/spaces exist. |
| r1-p003 | fix-changes-behavior | Mutable default argument aliased after rewrite | mutable default arguments and aliasing after a rewrite |
| r1-p004 | fix-changes-behavior | Type annotation evaluated at runtime with typing-only import | type annotations evaluated at runtime, typing-only imports |
| r1-p005 | fix-changes-behavior | Generator vs list: iterator consumed once | generators vs lists, iterators consumed once, lazy vs eager evaluation |
| r1-p006 | invalid | Multi-line statement with implicit line continuation affected by block removal | When the block is adjacent to a multi-line statement (e.g., with implicit line continuation), its removal may alter the boundary detection for the continued line. |
| r1-p007 | fix-changes-behavior | Evaluation order and side effects in expression merging | evaluation order and side effects when expressions are merged, duplicated or deleted |
| r1-p008 | fix-changes-behavior | Keyword-only, positional-only, *args/**kwargs forwarding with defaults | keyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once |
| r2-p001 | silent prediction off | Lambda with TYPE_CHECKING block | If the block is the only statement in a suite (e.g., a lambda or one-liner if), deleting it may result in an empty suite, causing a syntax error. |
| r2-p002 | fix ok | StringIO context manager with write but no close | context managers that are not files, objects with write() but no close() |
| r2-p003 | fix ok | Bytes vs str in binary file write after TYPE_CHECKING block | bytes vs str, encoding and newline handling, text vs binary modes |
| r2-p004 | fix ok | Exception in finally clause after TYPE_CHECKING block deletion | exceptions raised inside the rewritten code, finally/else clauses, exception chaining |
| r2-p005 | fix ok | Partial write in binary mode with truncated data | file modes and truncation, partial writes, non-str data passed to write |
| r2-p006 | silent prediction off | Evaluation order: side effect in TYPE_CHECKING condition affects later assignment | evaluation order and side effects when expressions are merged, duplicated or deleted |
| r2-p007 | fix ok | Placeholder for future type annotation with comment | If the block is used as a placeholder for future type annotations (common in stubs), removing it deletes intentional design intent documented via comments nearby. |
| r2-p008 | invalid | Dict ordering and duplicate keys after TYPE_CHECKING block removal | dict ordering, duplicate keys, __hash__/__eq__ overrides, sets |
Sources
r1-p001 · Context manager without close() - object with write() but no __exit__
1import io
2class Writer:
3 def __init__(self):
4 self.buf = ''
5 def write(self, s):
6 self.buf += s
7w = Writer()
8with w:
9 if TYPE_CHECKING:
10 pass
11print(w.buf)
--- orig.py 2026-10-17 16:39:41.450756856 -0400 +++ fixed.py 2026-10-17 16:39:41.845774376 -0400 @@ -6,6 +6,5 @@ self.buf += s w = Writer() with w: - if TYPE_CHECKING: - pass + pass print(w.buf)
r1-p002 · Nested class/function definition with mixed tabs/spaces indentation
1def outer():
2 if True:
3 x = 1
4 else:
5 pass
6 if TYPE_CHECKING:
7 pass
8 print(x) # uses space indent; mixing with tab above causes IndentationError if block removed incorrectly
9outer()
--- orig.py 2026-10-17 16:39:42.056783730 -0400 +++ fixed.py 2026-10-17 16:39:42.439800691 -0400 @@ -3,7 +3,5 @@ x = 1 else: pass - if TYPE_CHECKING: - pass print(x) # uses space indent; mixing with tab above causes IndentationError if block removed incorrectly outer()
r1-p003 · Mutable default argument aliased after rewrite
1def f(a=None):
2 if a is None:
3 a = []
4 if TYPE_CHECKING:
5 pass
6 a.append(1)
7 return a
8print(f())
9print(f())
--- orig.py 2026-10-17 16:39:42.534804899 -0400 +++ fixed.py 2026-10-17 16:39:42.912821638 -0400 @@ -1,8 +1,6 @@ def f(a=None): if a is None: a = [] - if TYPE_CHECKING: - pass a.append(1) return a print(f())
r1-p004 · Type annotation evaluated at runtime with typing-only import
1from typing import List
2if TYPE_CHECKING:
3 pass
4x: List[int] = []
5print(type(x).__name__)
--- orig.py 2026-10-17 16:39:43.012826066 -0400 +++ fixed.py 2026-10-17 16:39:43.396843045 -0400 @@ -1,5 +1,3 @@ from typing import List -if TYPE_CHECKING: - pass x: List[int] = [] print(type(x).__name__)
r1-p005 · Generator vs list: iterator consumed once
1def gen():
2 if TYPE_CHECKING:
3 pass
4 yield 1
5 yield 2
6it = gen()
7print(next(it))
8print(list(it)) # second consumption: should be [2] if gen() is lazy; if broken, may be [] or error
--- orig.py 2026-10-17 16:39:43.505847865 -0400 +++ fixed.py 2026-10-17 16:39:43.895865110 -0400 @@ -1,6 +1,4 @@ def gen(): - if TYPE_CHECKING: - pass yield 1 yield 2 it = gen()
r1-p006 · Multi-line statement with implicit line continuation affected by block removal (generated program does not parse: Traceback (most recent call last):)
1total = (
2 1 +
3 2
4 if TYPE_CHECKING:
5 pass
6 + 3
7)
8print(total)
r1-p007 · Evaluation order and side effects in expression merging
1side = 0
2if TYPE_CHECKING:
3 pass
4side += 1
5print(side)
--- orig.py 2026-10-17 16:39:44.154876551 -0400 +++ fixed.py 2026-10-17 16:39:44.537893460 -0400 @@ -1,5 +1,3 @@ side = 0 -if TYPE_CHECKING: - pass side += 1 print(side)
r1-p008 · Keyword-only, positional-only, *args/**kwargs forwarding with defaults
1def f(pos, /, kw=None, *args, **kwargs):
2 if TYPE_CHECKING:
3 pass
4 return pos, kw, args, kwargs
5print(f(1, kw=2, extra=3))
--- orig.py 2026-10-17 16:39:44.636897831 -0400 +++ fixed.py 2026-10-17 16:39:45.025915003 -0400 @@ -1,5 +1,3 @@ def f(pos, /, kw=None, *args, **kwargs): - if TYPE_CHECKING: - pass return pos, kw, args, kwargs print(f(1, kw=2, extra=3))
r2-p001 · Lambda with TYPE_CHECKING block
1import typing
2f = lambda: [] if typing.TYPE_CHECKING else [print(1)]
3try:
4 f()
5except SyntaxError as e:
6 print(f'SyntaxError: {e}')
1
r2-p002 · StringIO context manager with write but no close
1import io, typing
2buf = io.StringIO()
3with buf:
4 if typing.TYPE_CHECKING:
5 pass
6 buf.write('hello')
7print(buf.getvalue())
--- orig.py 2026-10-17 16:39:49.486111068 -0400 +++ fixed.py 2026-10-17 16:39:49.873128023 -0400 @@ -1,7 +1,5 @@ import io, typing buf = io.StringIO() with buf: - if typing.TYPE_CHECKING: - pass buf.write('hello') print(buf.getvalue())
r2-p003 · Bytes vs str in binary file write after TYPE_CHECKING block
1import typing
2with open('test.bin', 'wb') as f:
3 if typing.TYPE_CHECKING:
4 pass
5 f.write(b'hello' if not typing.TYPE_CHECKING else 'world')
6with open('test.bin', 'rb') as f:
7 print(f.read())
8import os
9os.remove('test.bin')
--- orig.py 2026-10-17 16:39:50.096137786 -0400 +++ fixed.py 2026-10-17 16:39:50.276145660 -0400 @@ -1,7 +1,5 @@ import typing with open('test.bin', 'wb') as f: - if typing.TYPE_CHECKING: - pass f.write(b'hello' if not typing.TYPE_CHECKING else 'world') with open('test.bin', 'rb') as f: print(f.read())
b'hello'
r2-p004 · Exception in finally clause after TYPE_CHECKING block deletion
1import typing
2try:
3 if typing.TYPE_CHECKING:
4 pass
5 raise ValueError('bad')
6except ValueError as e:
7 print(f'Caught: {e}')
8finally:
9 print('finally runs')
10 if False:
11 raise RuntimeError('should not happen')
--- orig.py 2026-10-17 16:39:50.384150384 -0400 +++ fixed.py 2026-10-17 16:39:50.563158215 -0400 @@ -1,7 +1,5 @@ import typing try: - if typing.TYPE_CHECKING: - pass raise ValueError('bad') except ValueError as e: print(f'Caught: {e}')
Caught: bad finally runs
r2-p005 · Partial write in binary mode with truncated data
1import typing
2with open('test.bin', 'wb') as f:
3 if typing.TYPE_CHECKING:
4 pass
5 f.write(b'12345')
6 f.seek(2)
7 f.write(b'99')
8with open('test.bin', 'rb') as f:
9 print(f.read())
10import os
11os.remove('test.bin')
--- orig.py 2026-10-17 16:39:50.671162939 -0400 +++ fixed.py 2026-10-17 16:39:50.850170769 -0400 @@ -1,7 +1,5 @@ import typing with open('test.bin', 'wb') as f: - if typing.TYPE_CHECKING: - pass f.write(b'12345') f.seek(2) f.write(b'99')
b'12995'
r2-p006 · Evaluation order: side effect in TYPE_CHECKING condition affects later assignment
1import typing
2counter = 0
3def inc():
4 global counter
5 counter += 1
6 return counter
7if typing.TYPE_CHECKING and inc():
8 x = 1
9else:
10 x = 2
11print(f'x={x}, counter={counter}')
x=2, counter=0
r2-p007 · Placeholder for future type annotation with comment
1import typing
2# TODO: Add proper type for config when module stabilizes
3if typing.TYPE_CHECKING:
4 pass
5config = {} # runtime dict, will be typed later
6print(f'config={config}')
--- orig.py 2026-10-17 16:39:51.109182092 -0400 +++ fixed.py 2026-10-17 16:39:51.288189910 -0400 @@ -1,6 +1,4 @@ import typing # TODO: Add proper type for config when module stabilizes -if typing.TYPE_CHECKING: - pass config = {} # runtime dict, will be typed later print(f'config={config}')
config={}r2-p008 · Dict ordering and duplicate keys after TYPE_CHECKING block removal (generated program does not parse: Traceback (most recent call last):)
1import typing
2d = {}
3if typing.TYPE_CHECKING:
4 pass
5d['a'] = 1
6d['b'] = 2
7d['a'] = 3 # duplicate key
8for k, v in d.items():
9 print(f'{k}:{v}', end=' ')}
10print()