[pyupgrade] Ensure we do not rename two type parameters to the same name (UP049) (#16038)

Fixes #16024

## Summary

This PR adds proper isolation for `UP049` fixes so that two type
parameters are not renamed to the same name, which would introduce
invalid syntax. E.g. for this:

```py
class Foo[_T, __T]: ...
```

we cannot apply two autofixes to the class, as that would produce
invalid syntax -- this:

```py
class Foo[T, T]: ...
```

The "isolation" here means that Ruff won't apply more than one fix to
the same type-parameter list in a single iteration of the loop it does
to apply all autofixes. This means that after the first autofix has been
done, the semantic model will have recalculated which variables are
available in the scope, meaning that the diagnostic for the second
parameter will be deemed unfixable since it collides with an existing
name in the same scope (the name we autofixed the first parameter to in
an earlier iteration of the autofix loop).

Cc. @ntBre, for interest!

## Test Plan

I added an integration test that reproduces the bug on `main`.
This commit is contained in:
Alex Waygood 2025-02-08 15:44:04 +00:00 committed by GitHub
parent a04ddf2a55
commit 22728808aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 1 deletions

View file

@ -159,7 +159,8 @@ pub(crate) fn private_type_parameter(checker: &Checker, binding: &Binding) -> Op
Applicability::Safe
};
Ok(Fix::applicable_edits(first, rest, applicability))
let fix_isolation = Checker::isolation(binding.source);
Ok(Fix::applicable_edits(first, rest, applicability).isolate(fix_isolation))
});
Some(diagnostic)