ruff
8fbdca7a - [`pyupgrade`] Skip fix when a defaulted `TypeVar` precedes a non-defaulted one (`UP040`, `UP046`, `UP047`) (#27133)

Commit
71 days ago
[`pyupgrade`] Skip fix when a defaulted `TypeVar` precedes a non-defaulted one (`UP040`, `UP046`, `UP047`) (#27133) ## Summary Fixes #27021. In a PEP 695 type-parameter list, a non-defaulted parameter cannot follow a defaulted one, but the UP040/UP046/UP047 fixes generated exactly that when the source declared e.g. `T = TypeVar("T", default=int)` before `S = TypeVar("S")` — producing `type Pair[T = int, S] = tuple[T, S]`, which ruff itself then rejects as invalid syntax. Reordering the parameters would not be an equivalent fix: parameter order determines how positional arguments bind when subscripting the alias/class/function (`Pair[int, str]`). So, per the issue's suggestion and following the existing precedent in these rules (duplicate TypeVars and defaults on unsupported targets also suppress the diagnostic; `NonPEP695TypeAlias` declares `FixAvailability::Always`, so a fixless diagnostic isn't an option), the diagnostic is skipped when a non-defaulted TypeVar follows a defaulted one. The guard lives at both choke points (`check_type_vars` for UP046/UP047 and `create_diagnostic` for both UP040 paths, including `TypeAliasType(..., type_params=...)`), so all three rules are covered by one helper. ## Test Plan - New fixture cases in `UP040.py`, `UP046_0.py`, `UP047_0.py`: defaulted-then-non-defaulted (no diagnostic), non-defaulted-first and all-defaulted (still fixed correctly); snapshots regenerated. - `cargo test -p ruff_linter` — all pass; `cargo fmt --check` and `cargo clippy -p ruff_linter --no-deps -- -D warnings` clean. - Manually verified the issue's repro: before, ruff emitted the invalid fix and then flagged its own output; after, no diagnostic on the bad ordering while valid orderings still convert. --- Developed with AI assistance (Claude Code); I reviewed the change and can speak to it.
Parents
Loading