fixhunt

ruff TC005: fix changes program behavior

fix-changes-behaviorlabelled safereal · highrelated
Findings / 2026-10-17T20-39-34-299Z-g1ez-ruff-TC005 / r1-p002ruff 0.17.19 → 6 lines2026-10-17 20:39 UTC

Hypothesis Nemotron 3 Super predicted to fire fired 1×

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.

Program

before
1def outer():
2 if True:
3 pass
4 if TYPE_CHECKING:
5 pass
6outer()
after
1def outer():
2 if True:
3 pass
4outer()

Output

stdout beforeexit 1
(no stdout)
NameError: name 'TYPE_CHECKING' is not defined
stdout afterexit 0
(no stdout)
fired
1 before · 0 after
runs
each version twice, same file name, no network

Judge Nemotron 3 Ultra advisory real · high

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 because `TYPE_CHECKING` is false at runtime, but the name must still be defined. The fix deletes the block, eliminating the `NameError` and changing the exit status from 1 to 0. The documentation presents the rewrite as a safe removal of dead code, but it is only safe when `TYPE_CHECKING` is properly imported.

Explanation Nemotron 3 Super unverified

The original program defines `outer()` with two conditional blocks; when called, it evaluates both conditions—`True` always executes its body, while `if TYPE_CHECKING:` raises a `NameError` because `TYPE_CHECKING` is undefined in runtime scope. After removing that block via the fix, calling `outer()` only executes the first condition (`if True:`), which does nothing, allowing the function to complete without error. Thus, the behavior changes from raising an exception to returning normally, proving the rewrite is not semantics‑preserving despite being labeled "safe."

Reproduce

file
prog.py (the before program)
minimized program
r1-p002.py
evidence sheet
r1-p002.md
regression fixture
r1-p002.py the minimized program with a header stating the observed change, for the tool's test suite
python3 prog.py
ruff check --preview --isolated --select TC005 --fix prog.py
python3 prog.py

Upstream search GitHub issues Tavily Nemotron 3 Super + Ultra

TC005 fix · TC005 remove unused conditional block · TC005 delete dead if TYPE_CHECKING guard · TC005 eliminate unreachable type checking import

  • related astral-sh/ruff/issues/28868 same rule TC005 but the existing report is about follow-up errors after a TC004 fix, not the removal of an undefined TYPE_CHECKING block
  • related astral-sh/ruff/issues/15681 same TC003/TC002/TC001 rules and TYPE_CHECKING construct but false positives on imports with manual TYPE_CHECKING and __future__.annotations
  • related astral-sh/ruff/pull/16669 same TYPE_CHECKING construct and SIM108 rule but about considering all TYPE_CHECKING symbols, not removing undefined blocks
  • related astral-sh/ruff/issues/15526 same TYPE_CHECKING construct but about self-assignment detection inside blocks, not removal of undefined blocks
  • related astral-sh/ruff/issues/6236 same TYPE_CHECKING construct but about moving imports under TYPE_CHECKING when added to __all__
  • related astral-sh/ruff/issues/11245 same TYPE_CHECKING construct but false positive F821 on circular reference, not removal of undefined blocks
  • related astral-sh/ruff/issues/24713 same TYPE_CHECKING construct but PLR6301 override detection shadowed by else branch
  • related astral-sh/ruff/issues/9197 same TYPE_CHECKING construct but moving typing imports into block causes linter panic
  • related astral-sh/ruff/issues/9298 same TYPE_CHECKING construct but F401 false positive on quoted generic parameter moved out of block

Details

program
Nested class/function definition with mixed tabs/spaces indentation
hazard
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.
oracle
exit 1 → 0 stderr before: NameError: name 'TYPE_CHECKING' is not defined stderr after: (none)
run
2026-10-17T20-39-34-299Z-g1ez-ruff-TC005
generated program (9 lines) and the fix's diff
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()
rule documentation given to the models
# empty-type-checking-block (TC005)

Derived from the **flake8-type-checking** linter.

Fix is always available.

## What it does
Checks for an empty type-checking block.

## Why is this bad?
The type-checking block does not do anything and should be removed to avoid
confusion.

## Example
```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    pass

print("Hello, world!")
```

Use instead:
```python
print("Hello, world!")
```

## References
- [PEP 563: Runtime annotation resolution and `TYPE_CHECKING`](https://peps.python.org/pep-0563/#runtime-annotation-resolution-and-type-checking)