Skip to content

Commit 3756987

Browse files
committed
[pyupgrade] No fix offer for Optional[None] in UP045/UP007
1 parent 86e5a31 commit 3756987

File tree

3 files changed

+26
-4
lines changed

3 files changed

+26
-4
lines changed

crates/ruff_linter/resources/test/fixtures/pyupgrade/UP045.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,8 @@ class ServiceRefOrValue:
4242
# Regression test for: https://github.com/astral-sh/ruff/issues/7201
4343
class ServiceRefOrValue:
4444
service_specification: Optional[str]is not True = None
45+
46+
47+
# Test for: https://github.com/astral-sh/ruff/issues/18508
48+
# Optional[None] should not be offered a fix
49+
foo: Optional[None] = None

crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ impl Violation for NonPEP604AnnotationUnion {
100100
/// ```
101101
///
102102
/// ## Fix safety
103-
/// This rule's fix is marked as unsafe, as it may lead to runtime errors when
104-
/// alongside libraries that rely on runtime type annotations, like Pydantic,
103+
/// This rule's fix is marked as unsafe, as it may lead to runtime errors
104+
/// using libraries that rely on runtime type annotations, like Pydantic,
105105
/// on Python versions prior to Python 3.10. It may also lead to runtime errors
106106
/// in unusual and likely incorrect type annotations where the type does not
107107
/// support the `|` operator.
@@ -138,7 +138,8 @@ pub(crate) fn non_pep604_annotation(
138138
// lead to invalid syntax.
139139
let fixable = checker.semantic().in_type_definition()
140140
&& !checker.semantic().in_complex_string_type_definition()
141-
&& is_allowed_value(slice);
141+
&& is_allowed_value(slice)
142+
&& !is_optional_none(operator, slice);
142143

143144
let applicability = if checker.target_version() >= PythonVersion::PY310 {
144145
Applicability::Safe
@@ -276,3 +277,8 @@ fn is_allowed_value(expr: &Expr) -> bool {
276277
| Expr::IpyEscapeCommand(_) => false,
277278
}
278279
}
280+
281+
/// Return `true` if this is an `Optional[None]` annotation.
282+
fn is_optional_none(operator: Pep604Operator, slice: &Expr) -> bool {
283+
matches!(operator, Pep604Operator::Optional) && matches!(slice, Expr::NoneLiteral(_))
284+
}

crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP045.py.snap

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
3-
snapshot_kind: text
43
---
54
UP045.py:5:10: UP007 [*] Use `X | Y` for type annotations
65
|
@@ -149,3 +148,15 @@ UP045.py:44:28: UP007 [*] Use `X | Y` for type annotations
149148
43 43 | class ServiceRefOrValue:
150149
44 |- service_specification: Optional[str]is not True = None
151150
44 |+ service_specification: str | None is not True = None
151+
45 45 |
152+
46 46 |
153+
47 47 | # Test for: https://github.com/astral-sh/ruff/issues/18508
154+
155+
UP045.py:49:6: UP007 Use `X | Y` for type annotations
156+
|
157+
47 | # Test for: https://github.com/astral-sh/ruff/issues/18508
158+
48 | # Optional[None] should not be offered a fix
159+
49 | foo: Optional[None] = None
160+
| ^^^^^^^^^^^^^^ UP007
161+
|
162+
= help: Convert to `X | Y`

0 commit comments

Comments
 (0)