mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
[ty] Promote float and complex when promoting literals (#22215)
## Summary Resolve https://github.com/astral-sh/ty/issues/2226 We need to add a special case in `apply_type_mapping` instead of directly in `promote_literals_impl` because we do not reach this with non generic non tuple nominal instances. We still ensure we apply the normal mapping if we do not see `float` or `complex` instances. ## Test Plan Update existing mdtest and add a new case to `literal_promotion.md`
This commit is contained in:
@@ -2204,7 +2204,7 @@ mod tests {
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r#"
|
||||
a[: list[Unknown | int]] = [1, 2]
|
||||
b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
c[: list[Unknown | bool]] = [True, False]
|
||||
d[: list[Unknown | None]] = [None, None]
|
||||
e[: list[Unknown | str]] = ["hel", "lo"]
|
||||
@@ -2229,7 +2229,7 @@ mod tests {
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
| ^^^^
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
|
|
||||
|
||||
@@ -2247,7 +2247,7 @@ mod tests {
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
| ^^^^^^^
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
|
|
||||
|
||||
@@ -2265,7 +2265,7 @@ mod tests {
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
| ^^^
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
|
|
||||
|
||||
@@ -2281,7 +2281,7 @@ mod tests {
|
||||
--> main2.py:3:5
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
| ^^^^
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
@@ -2300,12 +2300,31 @@ mod tests {
|
||||
--> main2.py:3:10
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
| ^^^^^^^
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
|
|
||||
|
||||
info[inlay-hint-location]: Inlay Hint Target
|
||||
--> stdlib/builtins.pyi:348:7
|
||||
|
|
||||
347 | @disjoint_base
|
||||
348 | class int:
|
||||
| ^^^
|
||||
349 | """int([x]) -> integer
|
||||
350 | int(x, base=10) -> integer
|
||||
|
|
||||
info: Source
|
||||
--> main2.py:3:20
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
| ^^^
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
|
|
||||
|
||||
info[inlay-hint-location]: Inlay Hint Target
|
||||
--> stdlib/builtins.pyi:661:7
|
||||
|
|
||||
@@ -2315,11 +2334,11 @@ mod tests {
|
||||
662 | """Convert a string or number to a floating-point number, if possible."""
|
||||
|
|
||||
info: Source
|
||||
--> main2.py:3:20
|
||||
--> main2.py:3:26
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
| ^^^^^
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
| ^^^^^
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
|
|
||||
@@ -2336,7 +2355,7 @@ mod tests {
|
||||
--> main2.py:4:5
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
| ^^^^
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
@@ -2356,7 +2375,7 @@ mod tests {
|
||||
--> main2.py:4:10
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
| ^^^^^^^
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
@@ -2376,7 +2395,7 @@ mod tests {
|
||||
--> main2.py:4:20
|
||||
|
|
||||
2 | a[: list[Unknown | int]] = [1, 2]
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
| ^^^^
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
@@ -2394,7 +2413,7 @@ mod tests {
|
||||
info: Source
|
||||
--> main2.py:5:5
|
||||
|
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
| ^^^^
|
||||
@@ -2414,7 +2433,7 @@ mod tests {
|
||||
info: Source
|
||||
--> main2.py:5:10
|
||||
|
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
| ^^^^^^^
|
||||
@@ -2434,7 +2453,7 @@ mod tests {
|
||||
info: Source
|
||||
--> main2.py:5:20
|
||||
|
|
||||
3 | b[: list[Unknown | float]] = [1.0, 2.0]
|
||||
3 | b[: list[Unknown | int | float]] = [1.0, 2.0]
|
||||
4 | c[: list[Unknown | bool]] = [True, False]
|
||||
5 | d[: list[Unknown | None]] = [None, None]
|
||||
| ^^^^
|
||||
@@ -2885,7 +2904,7 @@ mod tests {
|
||||
info: Source
|
||||
|
||||
a: list[Unknown | int] = [1, 2]
|
||||
b: list[Unknown | float] = [1.0, 2.0]
|
||||
b: list[Unknown | int | float] = [1.0, 2.0]
|
||||
c: list[Unknown | bool] = [True, False]
|
||||
d: list[Unknown | None] = [None, None]
|
||||
e: list[Unknown | str] = ["hel", "lo"]
|
||||
|
||||
@@ -7,6 +7,9 @@ python-version = "3.12"
|
||||
|
||||
There are certain places where we promote literals to their common supertype.
|
||||
|
||||
We also promote `float` to `int | float` and `complex` to `int | float | complex`, even when not in
|
||||
a type annotation.
|
||||
|
||||
## All literal types are promotable
|
||||
|
||||
```py
|
||||
@@ -31,6 +34,9 @@ def _(
|
||||
reveal_type(promote(lit3)) # revealed: list[bool]
|
||||
reveal_type(promote(lit4)) # revealed: list[bytes]
|
||||
reveal_type(promote(lit5)) # revealed: list[MyEnum]
|
||||
|
||||
reveal_type(promote(3.14)) # revealed: list[int | float]
|
||||
reveal_type(promote(3.14j)) # revealed: list[int | float | complex]
|
||||
```
|
||||
|
||||
Function types are also promoted to their `Callable` form:
|
||||
|
||||
@@ -251,7 +251,7 @@ reveal_type(LegacyProperty("height", 42)) # revealed: LegacyProperty[int]
|
||||
reveal_type(LegacyProperty.value) # revealed: property
|
||||
reveal_type(LegacyProperty.value.fget) # revealed: (self, /) -> Unknown
|
||||
reveal_type(LegacyProperty[str].value.fget) # revealed: (self, /) -> str
|
||||
reveal_type(LegacyProperty("height", 3.4).value) # revealed: float
|
||||
reveal_type(LegacyProperty("height", 3.4).value) # revealed: int | float
|
||||
```
|
||||
|
||||
## Attributes on `NamedTuple`
|
||||
|
||||
@@ -7948,6 +7948,14 @@ impl<'db> Type<'db> {
|
||||
method.self_instance(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
)),
|
||||
|
||||
Type::NominalInstance(instance) if matches!(type_mapping, TypeMapping::PromoteLiterals(PromoteLiteralsMode::On)) => {
|
||||
match instance.known_class(db) {
|
||||
Some(KnownClass::Complex) => KnownUnion::Complex.to_type(db),
|
||||
Some(KnownClass::Float) => KnownUnion::Float.to_type(db),
|
||||
_ => instance.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
}
|
||||
}
|
||||
|
||||
Type::NominalInstance(instance) => {
|
||||
instance.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user