# ruff UP011: fix changes program behavior

Internal evidence sheet generated by fixhunt. Not an issue text. A human re-verifies, checks the duplicates
below, and writes the upstream report in their own words following the project's AI policy.

- Tool: ruff ruff 0.17.1
- Rule: UP011 (tool labels the fix: unknown, this fix reported as safe)
- Oracle: fix-changes-behavior
- Judge (Nemotron 3 Ultra, advisory): introspection-only, severity low. The program parses its own source code and inspects the AST node for the decorator, checking whether the decorator call has zero arguments. After the fix removes the parentheses, the AST node changes from a Call to an Attribute, so the introspection logic fails with an AttributeError. This is a change only in self-inspection of the rewritten source text, not in the runtime semantics of the decorated function.
- Program: minimized from 13 to 9 lines

## Reproducer

```python
import functools, ast
@functools.lru_cache()
def cached_func(x):
    return x * 2
tree = ast.parse(open(__file__).read())
for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef) and node.name == 'cached_func':
        for dec in node.decorator_list:
                print('Decorator is call:', len(dec.args) == 0 and len(dec.keywords) == 0)
```

## Commands

```sh
python3 prog.py
ruff check --preview --isolated --select UP011 --fix prog.py
python3 prog.py
```

## Observed

```
stdout before:
Decorator is call: True
stdout after:

exit 0 → 1
stderr before: (none)
stderr after: AttributeError: 'Attribute' object has no attribute 'args'
```

## Diff applied by the fix

```diff
--- orig.py	2026-10-22 13:38:58.810615717 -0400
+++ fixed.py	2026-10-22 13:39:01.858667311 -0400
@@ -1,5 +1,5 @@
 import functools, ast
-@functools.lru_cache()
+@functools.lru_cache
 def cached_func(x):
     return x * 2
 tree = ast.parse(open(__file__).read())
```

## Why the rewrite is not equivalent (model draft, verify before use)

The original decorator `@functools.lru_cache()` calls `lru_cache` with no arguments, returning a decorator function that wraps `cached_func`. After rewriting to `@functools.lru_cache`, the decorator becomes the raw function object itself, not its result; thus it does not wrap the function, leaving `cached_func` undecorated. During AST traversal, `dec` now refers to an `Attribute` node (`functools.lru_cache`) instead of a `Call` node, so accessing `.args` raises an AttributeError because attributes lack argument lists. This changes both behavior and output.

## Upstream search

Queries: "UP011 fix", "UP011 decorator parentheses removal", "lru_cache empty call transformation", "AST FunctionDef decorator args access"

- related: https://github.com/astral-sh/ruff/issues/1913
  Same UP011 rule but different construct: that report is about `lru_cache(max_size=None)` → `lru_cache`, this one is about `lru_cache()` → `lru_cache`.
- related: https://github.com/astral-sh/ruff/issues/827
  Meta-issue tracking pyupgrade implementation including UP011, not the specific `lru_cache()` → `lru_cache` defect.

<details><summary>All hits</summary>

- [github, closed] unused-function-argument not silenced by prefixed underscore if variable name is "次" — https://github.com/astral-sh/ruff/issues/28721
- [github, closed] Formatter: Bug with Pragma Comment for pyright being moved to invalid position — https://github.com/astral-sh/ruff/issues/8481
- [github, closed] Fixed code unformatted — https://github.com/astral-sh/ruff/issues/8619
- [github, closed] Add option to disable preview warnings — https://github.com/astral-sh/ruff/issues/8620
- [github, closed] UP011 is incorrectly replacing `lru_cache(max_size)=None` — https://github.com/astral-sh/ruff/issues/1913
- [github, closed] Implement pyupgrade — https://github.com/astral-sh/ruff/issues/827

</details>
