Compare commits

...

5 Commits

Author SHA1 Message Date
Brent Westbrook
55a335165a add implicitly concatenated annotation tests 2026-01-12 17:55:55 -05:00
Brent Westbrook
e4602d7d0d Merge branch 'main' into brent/up045 2026-01-12 17:35:04 -05:00
Brent Westbrook
dfd02426a2 test TypeAlias too 2025-12-31 17:35:52 -05:00
Brent Westbrook
29e089781d fix regression test 2025-12-31 17:27:23 -05:00
Brent Westbrook
8ccea64ed4 add regression test 2025-12-31 16:59:12 -05:00
4 changed files with 132 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
"""
Regression test for https://github.com/astral-sh/ruff/issues/20096
"""
from __future__ import annotations
from typing import Optional, cast, TypeAlias
x: Optional[str] # UP045
x: "Optional[str]" # UP045
cast("Optional[str]", None) # UP045
cast(Optional[str], None) # okay, str | None is a runtime error
x: TypeAlias = "Optional[str]" # UP045
x: TypeAlias = Optional[str] # okay
# complex (implicitly concatenated) annotations
x: (
"Optional"
"["
"str"
"]"
)
cast((
"Optional"
"["
"str"
"]"
), None)
x: TypeAlias = (
"Optional"
"["
"str"
"]"
)

View File

@@ -54,7 +54,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|| checker.target_version() >= PythonVersion::PY310
|| (checker.target_version() >= PythonVersion::PY37
&& checker.semantic.future_annotations_or_stub()
&& checker.semantic.in_annotation()
&& (checker.semantic.in_annotation()
|| checker.semantic.in_string_type_definition())
&& !checker.settings().pyupgrade.keep_runtime_typing)
{
pyupgrade::rules::non_pep604_annotation(checker, expr, slice, operator);

View File

@@ -452,4 +452,17 @@ mod tests {
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}
#[test]
fn up045_future_annotations_py39() -> Result<()> {
let diagnostics = test_path(
Path::new("pyupgrade/UP045_py39.py"),
&settings::LinterSettings {
unresolved_target_version: PythonVersion::PY39.into(),
..settings::LinterSettings::for_rule(Rule::NonPEP604AnnotationOptional)
},
)?;
assert_diagnostics!(diagnostics);
Ok(())
}
}

View File

@@ -0,0 +1,82 @@
---
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
---
UP045 [*] Use `X | None` for type annotations
--> UP045_py39.py:10:4
|
10 | x: Optional[str] # UP045
| ^^^^^^^^^^^^^
11 | x: "Optional[str]" # UP045
12 | cast("Optional[str]", None) # UP045
|
help: Convert to `X | None`
7 | from typing import Optional, cast, TypeAlias
8 |
9 |
- x: Optional[str] # UP045
10 + x: str | None # UP045
11 | x: "Optional[str]" # UP045
12 | cast("Optional[str]", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
note: This is an unsafe fix and may change runtime behavior
UP045 [*] Use `X | None` for type annotations
--> UP045_py39.py:11:5
|
10 | x: Optional[str] # UP045
11 | x: "Optional[str]" # UP045
| ^^^^^^^^^^^^^
12 | cast("Optional[str]", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
|
help: Convert to `X | None`
8 |
9 |
10 | x: Optional[str] # UP045
- x: "Optional[str]" # UP045
11 + x: "str | None" # UP045
12 | cast("Optional[str]", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
14 | x: TypeAlias = "Optional[str]" # UP045
note: This is an unsafe fix and may change runtime behavior
UP045 [*] Use `X | None` for type annotations
--> UP045_py39.py:12:7
|
10 | x: Optional[str] # UP045
11 | x: "Optional[str]" # UP045
12 | cast("Optional[str]", None) # UP045
| ^^^^^^^^^^^^^
13 | cast(Optional[str], None) # okay, str | None is a runtime error
14 | x: TypeAlias = "Optional[str]" # UP045
|
help: Convert to `X | None`
9 |
10 | x: Optional[str] # UP045
11 | x: "Optional[str]" # UP045
- cast("Optional[str]", None) # UP045
12 + cast("str | None", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
14 | x: TypeAlias = "Optional[str]" # UP045
15 | x: TypeAlias = Optional[str] # okay
note: This is an unsafe fix and may change runtime behavior
UP045 [*] Use `X | None` for type annotations
--> UP045_py39.py:14:17
|
12 | cast("Optional[str]", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
14 | x: TypeAlias = "Optional[str]" # UP045
| ^^^^^^^^^^^^^
15 | x: TypeAlias = Optional[str] # okay
|
help: Convert to `X | None`
11 | x: "Optional[str]" # UP045
12 | cast("Optional[str]", None) # UP045
13 | cast(Optional[str], None) # okay, str | None is a runtime error
- x: TypeAlias = "Optional[str]" # UP045
14 + x: TypeAlias = "str | None" # UP045
15 | x: TypeAlias = Optional[str] # okay
16 |
17 | # complex (implicitly concatenated) annotations
note: This is an unsafe fix and may change runtime behavior