From bc01ce321de12abcc684d4aa97cfd13acfd954bd Mon Sep 17 00:00:00 2001 From: Zanie Date: Fri, 6 Oct 2023 10:46:57 -0500 Subject: [PATCH] Update `non-pep695-type-alias` to require `--unsafe-fixes` outside of stub files --- .../test/fixtures/pyupgrade/UP040.pyi | 7 ++++ crates/ruff_linter/src/rules/pyupgrade/mod.rs | 1 + .../pyupgrade/rules/use_pep695_type_alias.rs | 14 ++++++- ...er__rules__pyupgrade__tests__UP040.py.snap | 22 +++++------ ...r__rules__pyupgrade__tests__UP040.pyi.snap | 38 +++++++++++++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.pyi create mode 100644 crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.pyi.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.pyi b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.pyi new file mode 100644 index 0000000000000..e2a18694bd86e --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.pyi @@ -0,0 +1,7 @@ +import typing +from typing import TypeAlias + +# UP040 +# Fixes in type stub files should be safe to apply unlike in regular code where runtime behavior could change +x: typing.TypeAlias = int +x: TypeAlias = int diff --git a/crates/ruff_linter/src/rules/pyupgrade/mod.rs b/crates/ruff_linter/src/rules/pyupgrade/mod.rs index bff478382baaa..54181e3e085c5 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/mod.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/mod.rs @@ -83,6 +83,7 @@ mod tests { #[test_case(Rule::YieldInForLoop, Path::new("UP028_0.py"))] #[test_case(Rule::YieldInForLoop, Path::new("UP028_1.py"))] #[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.py"))] + #[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.pyi"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = path.to_string_lossy().to_string(); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs index 8cb7fc1aeba34..f8531a0204c89 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs @@ -132,7 +132,7 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) }) }; - diagnostic.set_fix(Fix::always_applies(Edit::range_replacement( + let edit = Edit::range_replacement( checker.generator().stmt(&Stmt::from(StmtTypeAlias { range: TextRange::default(), name: target.clone(), @@ -140,7 +140,17 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) value: value.clone(), })), stmt.range(), - ))); + ); + + // The fix is only safe in a type stub because new-style aliases have different runtime behavior + // See https://github.com/astral-sh/ruff/issues/6434 + let fix = if checker.source_type.is_stub() { + Fix::always_applies(edit) + } else { + Fix::sometimes_applies(edit) + }; + + diagnostic.set_fix(fix); } checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap index 955401aad3aa3..d470fe80ff81e 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap @@ -10,7 +10,7 @@ UP040.py:5:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of th | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 2 2 | from typing import TypeAlias 3 3 | 4 4 | # UP040 @@ -31,7 +31,7 @@ UP040.py:6:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of th | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 3 3 | 4 4 | # UP040 5 5 | x: typing.TypeAlias = int @@ -52,7 +52,7 @@ UP040.py:10:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 7 7 | 8 8 | # UP040 simple generic 9 9 | T = typing.TypeVar["T"] @@ -73,7 +73,7 @@ UP040.py:14:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 11 11 | 12 12 | # UP040 call style generic 13 13 | T = typing.TypeVar("T") @@ -94,7 +94,7 @@ UP040.py:18:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 15 15 | 16 16 | # UP040 bounded generic 17 17 | T = typing.TypeVar("T", bound=int) @@ -115,7 +115,7 @@ UP040.py:22:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 19 19 | 20 20 | # UP040 constrained generic 21 21 | T = typing.TypeVar("T", int, str) @@ -136,7 +136,7 @@ UP040.py:26:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 23 23 | 24 24 | # UP040 contravariant generic 25 25 | T = typing.TypeVar("T", contravariant=True) @@ -157,7 +157,7 @@ UP040.py:30:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 27 27 | 28 28 | # UP040 covariant generic 29 29 | T = typing.TypeVar("T", covariant=True) @@ -178,7 +178,7 @@ UP040.py:36:5: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 33 33 | T = typing.TypeVar["T"] 34 34 | class Foo: 35 35 | # reference to global variable @@ -199,7 +199,7 @@ UP040.py:40:5: UP040 [*] Type alias `y` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 37 37 | 38 38 | # reference to class variable 39 39 | TCLS = typing.TypeVar["TCLS"] @@ -220,7 +220,7 @@ UP040.py:44:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of t | = help: Use the `type` keyword -ℹ Fix +ℹ Suggested fix 41 41 | 42 42 | # UP040 wont add generics in fix 43 43 | T = typing.TypeVar(*args) diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.pyi.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.pyi.snap new file mode 100644 index 0000000000000..cc46a87dbcc8e --- /dev/null +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.pyi.snap @@ -0,0 +1,38 @@ +--- +source: crates/ruff_linter/src/rules/pyupgrade/mod.rs +--- +UP040.pyi:6:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keyword + | +4 | # UP040 +5 | # Fixes in type stub files should be safe to apply unlike in regular code where runtime behavior could change +6 | x: typing.TypeAlias = int + | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP040 +7 | x: TypeAlias = int + | + = help: Use the `type` keyword + +ℹ Fix +3 3 | +4 4 | # UP040 +5 5 | # Fixes in type stub files should be safe to apply unlike in regular code where runtime behavior could change +6 |-x: typing.TypeAlias = int + 6 |+type x = int +7 7 | x: TypeAlias = int + +UP040.pyi:7:1: UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keyword + | +5 | # Fixes in type stub files should be safe to apply unlike in regular code where runtime behavior could change +6 | x: typing.TypeAlias = int +7 | x: TypeAlias = int + | ^^^^^^^^^^^^^^^^^^ UP040 + | + = help: Use the `type` keyword + +ℹ Fix +4 4 | # UP040 +5 5 | # Fixes in type stub files should be safe to apply unlike in regular code where runtime behavior could change +6 6 | x: typing.TypeAlias = int +7 |-x: TypeAlias = int + 7 |+type x = int + +