Compare commits
13 Commits
micha/ty-p
...
david/gene
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96e7ebb588 | ||
|
|
54c88b599d | ||
|
|
8ed96b04e4 | ||
|
|
0a2536736b | ||
|
|
6aaa9d784a | ||
|
|
d85469e94c | ||
|
|
f184132d69 | ||
|
|
96c491099f | ||
|
|
c1e6ecccc0 | ||
|
|
343c6b6287 | ||
|
|
f40ab81093 | ||
|
|
eee6f25f2e | ||
|
|
013d43a2dd |
@@ -79,9 +79,8 @@ async def main():
|
||||
task("B"),
|
||||
)
|
||||
|
||||
# TODO: these should be `int`
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
reveal_type(a) # revealed: int
|
||||
reveal_type(b) # revealed: int
|
||||
```
|
||||
|
||||
## Under the hood
|
||||
|
||||
@@ -191,13 +191,13 @@ def _(
|
||||
reveal_type(int_or_callable) # revealed: int | ((str, /) -> bytes)
|
||||
reveal_type(callable_or_int) # revealed: ((str, /) -> bytes) | int
|
||||
# TODO should be Unknown | int
|
||||
reveal_type(type_var_or_int) # revealed: typing.TypeVar | int
|
||||
reveal_type(type_var_or_int) # revealed: T@TypeVarOrInt | int
|
||||
# TODO should be int | Unknown
|
||||
reveal_type(int_or_type_var) # revealed: int | typing.TypeVar
|
||||
reveal_type(int_or_type_var) # revealed: int | T@IntOrTypeVar
|
||||
# TODO should be Unknown | None
|
||||
reveal_type(type_var_or_none) # revealed: typing.TypeVar | None
|
||||
reveal_type(type_var_or_none) # revealed: T@TypeVarOrNone | None
|
||||
# TODO should be None | Unknown
|
||||
reveal_type(none_or_type_var) # revealed: None | typing.TypeVar
|
||||
reveal_type(none_or_type_var) # revealed: None | T@NoneOrTypeVar
|
||||
```
|
||||
|
||||
If a type is unioned with itself in a value expression, the result is just that type. No
|
||||
@@ -366,7 +366,9 @@ def g(obj: Y):
|
||||
reveal_type(obj) # revealed: list[int | str]
|
||||
```
|
||||
|
||||
## Generic types
|
||||
## Generic implicit type aliases
|
||||
|
||||
### Functionality
|
||||
|
||||
Implicit type aliases can also be generic:
|
||||
|
||||
@@ -388,24 +390,25 @@ ListOrTuple = list[T] | tuple[T, ...]
|
||||
ListOrTupleLegacy = Union[list[T], tuple[T, ...]]
|
||||
MyCallable = Callable[P, T]
|
||||
AnnotatedType = Annotated[T, "tag"]
|
||||
TransparentAlias = T
|
||||
MyOptional = T | None
|
||||
|
||||
# TODO: Consider displaying this as `<class 'list[T]'>`, … instead? (and similar for some others below)
|
||||
reveal_type(MyList) # revealed: <class 'list[typing.TypeVar]'>
|
||||
reveal_type(MyDict) # revealed: <class 'dict[typing.TypeVar, typing.TypeVar]'>
|
||||
reveal_type(MyList) # revealed: <class 'list[T@MyList]'>
|
||||
reveal_type(MyDict) # revealed: <class 'dict[T@MyDict, U@MyDict]'>
|
||||
reveal_type(MyType) # revealed: GenericAlias
|
||||
reveal_type(IntAndType) # revealed: <class 'tuple[int, typing.TypeVar]'>
|
||||
reveal_type(Pair) # revealed: <class 'tuple[typing.TypeVar, typing.TypeVar]'>
|
||||
reveal_type(Sum) # revealed: <class 'tuple[typing.TypeVar, typing.TypeVar]'>
|
||||
reveal_type(IntAndType) # revealed: <class 'tuple[int, T@IntAndType]'>
|
||||
reveal_type(Pair) # revealed: <class 'tuple[T@Pair, T@Pair]'>
|
||||
reveal_type(Sum) # revealed: <class 'tuple[T@Sum, U@Sum]'>
|
||||
reveal_type(ListOrTuple) # revealed: types.UnionType
|
||||
reveal_type(ListOrTupleLegacy) # revealed: types.UnionType
|
||||
reveal_type(MyCallable) # revealed: GenericAlias
|
||||
reveal_type(AnnotatedType) # revealed: <typing.Annotated special form>
|
||||
reveal_type(TransparentAlias) # revealed: typing.TypeVar
|
||||
reveal_type(MyOptional) # revealed: types.UnionType
|
||||
|
||||
def _(
|
||||
list_of_ints: MyList[int],
|
||||
dict_str_to_int: MyDict[str, int],
|
||||
# TODO: no error here
|
||||
# error: [invalid-type-form] "`typing.TypeVar` is not a generic class"
|
||||
subclass_of_int: MyType[int],
|
||||
int_and_str: IntAndType[str],
|
||||
pair_of_ints: Pair[int],
|
||||
@@ -413,48 +416,40 @@ def _(
|
||||
list_or_tuple: ListOrTuple[int],
|
||||
list_or_tuple_legacy: ListOrTupleLegacy[int],
|
||||
# TODO: no error here
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
|
||||
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `tuple[str, bytes]`?"
|
||||
my_callable: MyCallable[[str, bytes], int],
|
||||
annotated_int: AnnotatedType[int],
|
||||
transparent_alias: TransparentAlias[int],
|
||||
optional_int: MyOptional[int],
|
||||
):
|
||||
# TODO: This should be `list[int]`
|
||||
reveal_type(list_of_ints) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: This should be `dict[str, int]`
|
||||
reveal_type(dict_str_to_int) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: This should be `type[int]`
|
||||
reveal_type(subclass_of_int) # revealed: Unknown
|
||||
# TODO: This should be `tuple[int, str]`
|
||||
reveal_type(int_and_str) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: This should be `tuple[int, int]`
|
||||
reveal_type(pair_of_ints) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: This should be `tuple[int, bytes]`
|
||||
reveal_type(int_and_bytes) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: This should be `list[int] | tuple[int, ...]`
|
||||
reveal_type(list_or_tuple) # revealed: @Todo(Generic specialization of types.UnionType)
|
||||
# TODO: This should be `list[int] | tuple[int, ...]`
|
||||
reveal_type(list_or_tuple_legacy) # revealed: @Todo(Generic specialization of types.UnionType)
|
||||
reveal_type(list_of_ints) # revealed: list[int]
|
||||
reveal_type(dict_str_to_int) # revealed: dict[str, int]
|
||||
reveal_type(subclass_of_int) # revealed: type[int]
|
||||
reveal_type(int_and_str) # revealed: tuple[int, str]
|
||||
reveal_type(pair_of_ints) # revealed: tuple[int, int]
|
||||
reveal_type(int_and_bytes) # revealed: tuple[int, bytes]
|
||||
reveal_type(list_or_tuple) # revealed: list[int] | tuple[int, ...]
|
||||
reveal_type(list_or_tuple_legacy) # revealed: list[int] | tuple[int, ...]
|
||||
reveal_type(list_or_tuple_legacy) # revealed: list[int] | tuple[int, ...]
|
||||
# TODO: This should be `(str, bytes) -> int`
|
||||
reveal_type(my_callable) # revealed: @Todo(Generic specialization of typing.Callable)
|
||||
# TODO: This should be `int`
|
||||
reveal_type(annotated_int) # revealed: @Todo(Generic specialization of typing.Annotated)
|
||||
reveal_type(my_callable) # revealed: Unknown
|
||||
reveal_type(annotated_int) # revealed: int
|
||||
reveal_type(transparent_alias) # revealed: int
|
||||
reveal_type(optional_int) # revealed: int | None
|
||||
```
|
||||
|
||||
Generic implicit type aliases can be partially specialized:
|
||||
|
||||
```py
|
||||
U = TypeVar("U")
|
||||
|
||||
DictStrTo = MyDict[str, U]
|
||||
|
||||
reveal_type(DictStrTo) # revealed: GenericAlias
|
||||
reveal_type(DictStrTo) # revealed: <class 'dict[str, U@DictStrTo]'>
|
||||
|
||||
def _(
|
||||
# TODO: No error here
|
||||
# error: [invalid-type-form] "Invalid subscript of object of type `GenericAlias` in type expression"
|
||||
dict_str_to_int: DictStrTo[int],
|
||||
):
|
||||
# TODO: This should be `dict[str, int]`
|
||||
reveal_type(dict_str_to_int) # revealed: Unknown
|
||||
reveal_type(dict_str_to_int) # revealed: dict[str, int]
|
||||
```
|
||||
|
||||
Using specializations of generic implicit type aliases in other implicit type aliases works as
|
||||
@@ -464,26 +459,65 @@ expected:
|
||||
IntsOrNone = MyList[int] | None
|
||||
IntsOrStrs = Pair[int] | Pair[str]
|
||||
ListOfPairs = MyList[Pair[str]]
|
||||
ListOrTupleOfInts = ListOrTuple[int]
|
||||
AnnotatedInt = AnnotatedType[int]
|
||||
SubclassOfInt = MyType[int]
|
||||
# TODO: No error here
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
|
||||
# error: [invalid-type-form] "List literals are not allowed in this context in a type expression: Did you mean `list[int]`?"
|
||||
CallableIntToStr = MyCallable[[int], str]
|
||||
|
||||
reveal_type(IntsOrNone) # revealed: UnionType
|
||||
reveal_type(IntsOrStrs) # revealed: UnionType
|
||||
reveal_type(ListOfPairs) # revealed: GenericAlias
|
||||
reveal_type(IntsOrNone) # revealed: types.UnionType
|
||||
reveal_type(IntsOrStrs) # revealed: types.UnionType
|
||||
reveal_type(ListOfPairs) # revealed: <class 'list[tuple[str, str]]'>
|
||||
reveal_type(ListOrTupleOfInts) # revealed: types.UnionType
|
||||
reveal_type(AnnotatedInt) # revealed: <typing.Annotated special form>
|
||||
reveal_type(SubclassOfInt) # revealed: GenericAlias
|
||||
reveal_type(CallableIntToStr) # revealed: Unknown
|
||||
|
||||
def _(
|
||||
# TODO: This should not be an error
|
||||
# error: [invalid-type-form] "Variable of type `UnionType` is not allowed in a type expression"
|
||||
ints_or_none: IntsOrNone,
|
||||
# TODO: This should not be an error
|
||||
# error: [invalid-type-form] "Variable of type `UnionType` is not allowed in a type expression"
|
||||
ints_or_strs: IntsOrStrs,
|
||||
list_of_pairs: ListOfPairs,
|
||||
list_or_tuple_of_ints: ListOrTupleOfInts,
|
||||
annotated_int: AnnotatedInt,
|
||||
subclass_of_int: SubclassOfInt,
|
||||
callable_int_to_str: CallableIntToStr,
|
||||
):
|
||||
# TODO: This should be `list[int] | None`
|
||||
reveal_type(ints_or_none) # revealed: Unknown
|
||||
# TODO: This should be `tuple[int, int] | tuple[str, str]`
|
||||
reveal_type(ints_or_strs) # revealed: Unknown
|
||||
# TODO: This should be `list[tuple[str, str]]`
|
||||
reveal_type(list_of_pairs) # revealed: @Todo(Support for `typing.GenericAlias` instances in type expressions)
|
||||
reveal_type(ints_or_none) # revealed: list[int] | None
|
||||
reveal_type(ints_or_strs) # revealed: tuple[int, int] | tuple[str, str]
|
||||
reveal_type(list_of_pairs) # revealed: list[tuple[str, str]]
|
||||
reveal_type(list_or_tuple_of_ints) # revealed: list[int] | tuple[int, ...]
|
||||
reveal_type(annotated_int) # revealed: int
|
||||
reveal_type(subclass_of_int) # revealed: type[int]
|
||||
# TODO: This should be `(int, /) -> str`
|
||||
reveal_type(callable_int_to_str) # revealed: Unknown
|
||||
```
|
||||
|
||||
A generic implicit type alias can also be used in another generic implicit type alias:
|
||||
|
||||
```py
|
||||
from typing_extensions import Any
|
||||
|
||||
B = TypeVar("B", bound=int)
|
||||
|
||||
MyOtherList = MyList[T]
|
||||
MyOtherType = MyType[T]
|
||||
TypeOrList = MyType[B] | MyList[B]
|
||||
|
||||
reveal_type(MyOtherList) # revealed: <class 'list[T@MyOtherList]'>
|
||||
reveal_type(MyOtherType) # revealed: GenericAlias
|
||||
reveal_type(TypeOrList) # revealed: types.UnionType
|
||||
|
||||
def _(
|
||||
list_of_ints: MyOtherList[int],
|
||||
subclass_of_int: MyOtherType[int],
|
||||
type_or_list: TypeOrList[Any],
|
||||
):
|
||||
reveal_type(list_of_ints) # revealed: list[int]
|
||||
reveal_type(subclass_of_int) # revealed: type[int]
|
||||
# TODO: Should be `type[Any] | list[Any]`
|
||||
reveal_type(type_or_list) # revealed: @Todo(type[T] for typevar T) | list[Any]
|
||||
```
|
||||
|
||||
If a generic implicit type alias is used unspecialized in a type expression, we treat it as an
|
||||
@@ -496,11 +530,11 @@ def _(
|
||||
my_callable: MyCallable,
|
||||
):
|
||||
# TODO: Should be `list[Unknown]`
|
||||
reveal_type(my_list) # revealed: list[typing.TypeVar]
|
||||
reveal_type(my_list) # revealed: list[T@MyList]
|
||||
# TODO: Should be `dict[Unknown, Unknown]`
|
||||
reveal_type(my_dict) # revealed: dict[typing.TypeVar, typing.TypeVar]
|
||||
reveal_type(my_dict) # revealed: dict[T@MyDict, U@MyDict]
|
||||
# TODO: Should be `(...) -> Unknown`
|
||||
reveal_type(my_callable) # revealed: (...) -> typing.TypeVar
|
||||
reveal_type(my_callable) # revealed: (...) -> T@MyCallable
|
||||
```
|
||||
|
||||
(Generic) implicit type aliases can be used as base classes:
|
||||
@@ -522,37 +556,182 @@ reveal_mro(Derived1)
|
||||
|
||||
GenericBaseAlias = GenericBase[T]
|
||||
|
||||
# TODO: No error here
|
||||
# error: [non-subscriptable] "Cannot subscript object of type `<class 'GenericBase[typing.TypeVar]'>` with no `__class_getitem__` method"
|
||||
class Derived2(GenericBaseAlias[int]):
|
||||
pass
|
||||
```
|
||||
|
||||
### Imported aliases
|
||||
|
||||
Generic implicit type aliases can be imported from other modules and specialized:
|
||||
|
||||
`my_types.py`:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
MyList = list[T]
|
||||
```
|
||||
|
||||
`main.py`:
|
||||
|
||||
```py
|
||||
from my_types import MyList
|
||||
import my_types as mt
|
||||
|
||||
def _(
|
||||
list_of_ints1: MyList[int],
|
||||
list_of_ints2: mt.MyList[int],
|
||||
):
|
||||
reveal_type(list_of_ints1) # revealed: list[int]
|
||||
reveal_type(list_of_ints2) # revealed: list[int]
|
||||
```
|
||||
|
||||
### In stringified annotations
|
||||
|
||||
Generic implicit type aliases can be specialized in stringified annotations:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
MyList = list[T]
|
||||
|
||||
def _(
|
||||
list_of_ints: "MyList[int]",
|
||||
):
|
||||
reveal_type(list_of_ints) # revealed: list[int]
|
||||
```
|
||||
|
||||
### Error cases
|
||||
|
||||
A generic alias that is already fully specialized cannot be specialized again:
|
||||
|
||||
```py
|
||||
ListOfInts = list[int]
|
||||
|
||||
# TODO: this should be an error
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments: expected 0, got 1"
|
||||
def _(doubly_specialized: ListOfInts[int]):
|
||||
# TODO: this should be `Unknown`
|
||||
reveal_type(doubly_specialized) # revealed: @Todo(specialized generic alias in type expression)
|
||||
reveal_type(doubly_specialized) # revealed: Unknown
|
||||
```
|
||||
|
||||
Specializing a generic implicit type alias with an incorrect number of type arguments also results
|
||||
in an error:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
|
||||
MyList = list[T]
|
||||
MyDict = dict[T, U]
|
||||
|
||||
def _(
|
||||
# TODO: this should be an error
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments: expected 1, got 2"
|
||||
list_too_many_args: MyList[int, str],
|
||||
# TODO: this should be an error
|
||||
# error: [missing-argument] "No argument provided for required parameter `U`"
|
||||
dict_too_few_args: MyDict[int],
|
||||
):
|
||||
# TODO: this should be `Unknown`
|
||||
reveal_type(list_too_many_args) # revealed: @Todo(specialized generic alias in type expression)
|
||||
# TODO: this should be `Unknown`
|
||||
reveal_type(dict_too_few_args) # revealed: @Todo(specialized generic alias in type expression)
|
||||
reveal_type(list_too_many_args) # revealed: Unknown
|
||||
reveal_type(dict_too_few_args) # revealed: Unknown
|
||||
```
|
||||
|
||||
Trying to specialize a non-name node results in an error:
|
||||
|
||||
```py
|
||||
from ty_extensions import TypeOf
|
||||
|
||||
IntOrStr = int | str
|
||||
|
||||
def this_does_not_work() -> TypeOf[IntOrStr]:
|
||||
raise NotImplementedError()
|
||||
|
||||
def _(
|
||||
# TODO: Better error message? `invalid-type-form`
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments: expected 0, got 1"
|
||||
specialized: this_does_not_work()[int],
|
||||
):
|
||||
reveal_type(specialized) # revealed: Unknown
|
||||
```
|
||||
|
||||
Similarly, if you try to specialize a union type without a binding context, we emit an error:
|
||||
|
||||
```py
|
||||
# error: [invalid-type-form] "`types.UnionType` is not subscriptable"
|
||||
x: (list[T] | set[T])[int]
|
||||
|
||||
def _():
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
||||
### Multiple definitions
|
||||
|
||||
#### Shadowed definitions
|
||||
|
||||
When a generic type alias shadows a definition from an outer scope, the inner definition is used:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
MyAlias = list[T]
|
||||
|
||||
def outer():
|
||||
MyAlias = set[T]
|
||||
|
||||
def _(x: MyAlias[int]):
|
||||
reveal_type(x) # revealed: set[int]
|
||||
```
|
||||
|
||||
#### Statically known conditions
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
if True:
|
||||
MyAlias1 = list[T]
|
||||
else:
|
||||
MyAlias1 = set[T]
|
||||
|
||||
if False:
|
||||
MyAlias2 = list[T]
|
||||
else:
|
||||
MyAlias2 = set[T]
|
||||
|
||||
def _(
|
||||
x1: MyAlias1[int],
|
||||
x2: MyAlias2[int],
|
||||
):
|
||||
reveal_type(x1) # revealed: list[int]
|
||||
reveal_type(x2) # revealed: set[int]
|
||||
```
|
||||
|
||||
#### Statically unknown conditions
|
||||
|
||||
If several definitions are visible, we emit an error:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
if flag():
|
||||
MyAlias = list[T]
|
||||
else:
|
||||
MyAlias = set[T]
|
||||
|
||||
# error: [invalid-type-form] "Invalid subscript of object of type `<class 'list[T@MyAlias]'> | <class 'set[T@MyAlias]'>` in type expression"
|
||||
def _(x: MyAlias[int]):
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
||||
## `Literal`s
|
||||
@@ -642,8 +821,7 @@ Deprecated = Annotated[T, "deprecated attribute"]
|
||||
class C:
|
||||
old: Deprecated[int]
|
||||
|
||||
# TODO: Should be `int`
|
||||
reveal_type(C().old) # revealed: @Todo(Generic specialization of typing.Annotated)
|
||||
reveal_type(C().old) # revealed: int
|
||||
```
|
||||
|
||||
If the metadata argument is missing, we emit an error (because this code fails at runtime), but
|
||||
@@ -1298,3 +1476,14 @@ def _(
|
||||
reveal_type(recursive_dict3) # revealed: dict[Divergent, int]
|
||||
reveal_type(recursive_dict4) # revealed: dict[Divergent, int]
|
||||
```
|
||||
|
||||
### Self-referential generic implicit type aliases
|
||||
|
||||
<!-- expect-panic: execute: too many cycle iterations -->
|
||||
|
||||
```py
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
NestedDict = dict[str, "NestedDict[T] | T"]
|
||||
```
|
||||
|
||||
@@ -96,6 +96,24 @@ def _(x: MyAlias):
|
||||
reveal_type(x) # revealed: int | ((str, /) -> int)
|
||||
```
|
||||
|
||||
## Generic aliases
|
||||
|
||||
```py
|
||||
from typing import TypeAlias, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
MyList: TypeAlias = list[T]
|
||||
ListOrSet: TypeAlias = list[T] | set[T]
|
||||
|
||||
reveal_type(MyList) # revealed: <class 'list[T]'>
|
||||
reveal_type(ListOrSet) # revealed: types.UnionType
|
||||
|
||||
def _(list_of_int: MyList[int], list_or_set_of_str: ListOrSet[str]):
|
||||
reveal_type(list_of_int) # revealed: list[int]
|
||||
reveal_type(list_or_set_of_str) # revealed: list[str] | set[str]
|
||||
```
|
||||
|
||||
## Subscripted generic alias in union
|
||||
|
||||
```py
|
||||
@@ -107,8 +125,7 @@ Alias1: TypeAlias = list[T] | set[T]
|
||||
MyAlias: TypeAlias = int | Alias1[str]
|
||||
|
||||
def _(x: MyAlias):
|
||||
# TODO: int | list[str] | set[str]
|
||||
reveal_type(x) # revealed: int | @Todo(Specialization of union type alias)
|
||||
reveal_type(x) # revealed: int | list[str] | set[str]
|
||||
```
|
||||
|
||||
## Imported
|
||||
|
||||
@@ -90,6 +90,12 @@ impl<'db> Definition<'db> {
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
DefinitionKind::Assignment(assignment) => {
|
||||
let target_node = assignment.target.node(&module);
|
||||
target_node
|
||||
.as_name_expr()
|
||||
.map(|name_expr| name_expr.id.as_str().to_string())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,6 +906,13 @@ impl<'db> Type<'db> {
|
||||
matches!(self, Type::GenericAlias(_))
|
||||
}
|
||||
|
||||
// pub(crate) fn as_generic_alias(&self) -> Option<GenericAlias<'db>> {
|
||||
// match self {
|
||||
// Type::GenericAlias(alias) => Some(*alias),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
|
||||
const fn is_dynamic(&self) -> bool {
|
||||
matches!(self, Type::Dynamic(_))
|
||||
}
|
||||
@@ -6792,7 +6799,7 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
KnownInstanceType::Literal(ty) => Ok(ty.inner(db)),
|
||||
KnownInstanceType::Annotated(ty) => Ok(ty.inner(db)),
|
||||
KnownInstanceType::TypeGenericAlias(ty) => {
|
||||
KnownInstanceType::TypeGenericAlias(instance) => {
|
||||
// When `type[…]` appears in a value position (e.g. in an implicit type alias),
|
||||
// we infer its argument as a type expression. This ensures that we can emit
|
||||
// diagnostics for invalid type expressions, and more importantly, that we can
|
||||
@@ -6801,9 +6808,26 @@ impl<'db> Type<'db> {
|
||||
// (`int` -> instance of `int` -> subclass of `int`) can be lossy, but it is
|
||||
// okay for all valid arguments to `type[…]`.
|
||||
|
||||
Ok(ty.inner(db).to_meta_type(db))
|
||||
let ty = instance.inner(db);
|
||||
|
||||
if ty.is_type_var() {
|
||||
// TODO:
|
||||
// This is a temporary workaround until we have proper support for type[T].
|
||||
// If we pass a typevar to `.to_meta_type()`, we currently get `type[B]`,
|
||||
// where `B` is the upper bound of `T`. However, we really need `type[T]`
|
||||
// here. Otherwise, when we specialize a generic implicit type alias like
|
||||
// `TypeOrList[T] = type[T] | list[T]` using `TypeOrList[Any]`, we would get
|
||||
// `type[B] | list[Any]`, which leads to a lot of false positives for numpy-
|
||||
// users.
|
||||
Ok(todo_type!("type[T] for typevar T"))
|
||||
} else {
|
||||
Ok(ty.to_meta_type(db))
|
||||
}
|
||||
}
|
||||
KnownInstanceType::GenericAlias(instance) => Ok(instance.inner(db)),
|
||||
KnownInstanceType::Callable(instance) => {
|
||||
Ok(Type::Callable(instance.callable_type(db)))
|
||||
}
|
||||
KnownInstanceType::Callable(callable) => Ok(Type::Callable(*callable)),
|
||||
KnownInstanceType::LiteralStringAlias(ty) => Ok(ty.inner(db)),
|
||||
},
|
||||
|
||||
@@ -7198,17 +7222,83 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
||||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
|
||||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::TypeVar(typevar) => {
|
||||
match type_mapping {
|
||||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
|
||||
}
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::ReplaceSelf { .. } |
|
||||
TypeMapping::Materialize(_) |
|
||||
TypeMapping::ReplaceParameterDefaults => self,
|
||||
}
|
||||
}
|
||||
TypeMapping::Specialization(_) |
|
||||
TypeMapping::PartialSpecialization(_) |
|
||||
TypeMapping::PromoteLiterals(_) |
|
||||
TypeMapping::BindSelf(_) |
|
||||
TypeMapping::ReplaceSelf { .. } |
|
||||
TypeMapping::Materialize(_) |
|
||||
TypeMapping::ReplaceParameterDefaults => self,
|
||||
KnownInstanceType::UnionType(instance) => {
|
||||
if let Ok(union_type) = instance.union_type(db) {
|
||||
Type::KnownInstance(KnownInstanceType::UnionType(
|
||||
UnionTypeInstance::new(
|
||||
db,
|
||||
instance._value_expr_types(db),
|
||||
Ok(union_type.apply_type_mapping_impl(db, type_mapping, tcx, visitor)),
|
||||
instance.binding_context(db),
|
||||
)
|
||||
))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
},
|
||||
KnownInstanceType::Annotated(instance) => {
|
||||
Type::KnownInstance(KnownInstanceType::Annotated(
|
||||
TypeInContext::new(
|
||||
db,
|
||||
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
instance.binding_context(db),
|
||||
)
|
||||
))
|
||||
},
|
||||
KnownInstanceType::TypeGenericAlias(instance) => {
|
||||
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||
TypeInContext::new(
|
||||
db,
|
||||
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
instance.binding_context(db),
|
||||
)
|
||||
))
|
||||
},
|
||||
KnownInstanceType::GenericAlias(instance) => {
|
||||
Type::KnownInstance(KnownInstanceType::GenericAlias(
|
||||
TypeInContext::new(
|
||||
db,
|
||||
instance.inner(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
instance.binding_context(db),
|
||||
)
|
||||
))
|
||||
},
|
||||
KnownInstanceType::Callable(instance) => {
|
||||
Type::KnownInstance(KnownInstanceType::Callable(CallableTypeInstance::new(
|
||||
db,
|
||||
instance.callable_type(db).apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
instance.binding_context(db),
|
||||
)))
|
||||
},
|
||||
KnownInstanceType::SubscriptedProtocol(_) |
|
||||
KnownInstanceType::SubscriptedGeneric(_) |
|
||||
KnownInstanceType::TypeAliasType(_) |
|
||||
KnownInstanceType::Deprecated(_) |
|
||||
KnownInstanceType::Field(_) |
|
||||
KnownInstanceType::ConstraintSet(_) |
|
||||
KnownInstanceType::GenericContext(_) |
|
||||
KnownInstanceType::Specialization(_) |
|
||||
KnownInstanceType::Literal(_) |
|
||||
KnownInstanceType::LiteralStringAlias(_) |
|
||||
KnownInstanceType::NewType(_) => {
|
||||
// TODO: For some of these, we may need to apply the type mapping to inner types.
|
||||
self
|
||||
},
|
||||
}
|
||||
|
||||
Type::FunctionLiteral(function) => {
|
||||
@@ -7369,8 +7459,7 @@ impl<'db> Type<'db> {
|
||||
// some other generic context's specialization is applied to it.
|
||||
| Type::ClassLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_) => self,
|
||||
| Type::SpecialForm(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7507,6 +7596,53 @@ impl<'db> Type<'db> {
|
||||
});
|
||||
}
|
||||
|
||||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::UnionType(instance) => {
|
||||
if let Ok(union_type) = instance.union_type(db) {
|
||||
union_type.find_legacy_typevars_impl(
|
||||
db,
|
||||
binding_context,
|
||||
typevars,
|
||||
visitor,
|
||||
);
|
||||
}
|
||||
}
|
||||
KnownInstanceType::Annotated(ty) => {
|
||||
ty.inner(db)
|
||||
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
KnownInstanceType::Callable(instance) => {
|
||||
instance.callable_type(db).find_legacy_typevars_impl(
|
||||
db,
|
||||
binding_context,
|
||||
typevars,
|
||||
visitor,
|
||||
);
|
||||
}
|
||||
KnownInstanceType::TypeGenericAlias(ty) => {
|
||||
ty.inner(db)
|
||||
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
KnownInstanceType::GenericAlias(ty) => {
|
||||
ty.inner(db)
|
||||
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
KnownInstanceType::SubscriptedProtocol(_)
|
||||
| KnownInstanceType::SubscriptedGeneric(_)
|
||||
| KnownInstanceType::TypeVar(_)
|
||||
| KnownInstanceType::TypeAliasType(_)
|
||||
| KnownInstanceType::Deprecated(_)
|
||||
| KnownInstanceType::Field(_)
|
||||
| KnownInstanceType::ConstraintSet(_)
|
||||
| KnownInstanceType::GenericContext(_)
|
||||
| KnownInstanceType::Specialization(_)
|
||||
| KnownInstanceType::Literal(_)
|
||||
| KnownInstanceType::LiteralStringAlias(_)
|
||||
| KnownInstanceType::NewType(_) => {
|
||||
// TODO: For some of these, we may need to try to find legacy typevars in inner types.
|
||||
}
|
||||
},
|
||||
|
||||
Type::Dynamic(_)
|
||||
| Type::Never
|
||||
| Type::AlwaysTruthy
|
||||
@@ -7534,7 +7670,6 @@ impl<'db> Type<'db> {
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::TypedDict(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -8075,13 +8210,15 @@ pub enum KnownInstanceType<'db> {
|
||||
Literal(InternedType<'db>),
|
||||
|
||||
/// A single instance of `typing.Annotated`
|
||||
Annotated(InternedType<'db>),
|
||||
Annotated(TypeInContext<'db>),
|
||||
|
||||
/// An instance of `typing.GenericAlias` representing a `type[...]` expression.
|
||||
TypeGenericAlias(InternedType<'db>),
|
||||
TypeGenericAlias(TypeInContext<'db>),
|
||||
|
||||
GenericAlias(TypeInContext<'db>),
|
||||
|
||||
/// An instance of `typing.GenericAlias` representing a `Callable[...]` expression.
|
||||
Callable(CallableType<'db>),
|
||||
Callable(CallableTypeInstance<'db>),
|
||||
|
||||
/// A literal string which is the right-hand side of a PEP 613 `TypeAlias`.
|
||||
LiteralStringAlias(InternedType<'db>),
|
||||
@@ -8123,14 +8260,16 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
visitor.visit_type(db, *union_type);
|
||||
}
|
||||
}
|
||||
KnownInstanceType::Literal(ty)
|
||||
| KnownInstanceType::Annotated(ty)
|
||||
| KnownInstanceType::TypeGenericAlias(ty)
|
||||
| KnownInstanceType::LiteralStringAlias(ty) => {
|
||||
KnownInstanceType::Annotated(instance)
|
||||
| KnownInstanceType::TypeGenericAlias(instance)
|
||||
| KnownInstanceType::GenericAlias(instance) => {
|
||||
visitor.visit_type(db, instance.inner(db));
|
||||
}
|
||||
KnownInstanceType::Literal(ty) | KnownInstanceType::LiteralStringAlias(ty) => {
|
||||
visitor.visit_type(db, ty.inner(db));
|
||||
}
|
||||
KnownInstanceType::Callable(callable) => {
|
||||
visitor.visit_callable_type(db, callable);
|
||||
KnownInstanceType::Callable(instance) => {
|
||||
visitor.visit_callable_type(db, instance.callable_type(db));
|
||||
}
|
||||
KnownInstanceType::NewType(newtype) => {
|
||||
if let ClassType::Generic(generic_alias) = newtype.base_class_type(db) {
|
||||
@@ -8169,6 +8308,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
|
||||
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
|
||||
Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)),
|
||||
Self::GenericAlias(ty) => Self::GenericAlias(ty.normalized_impl(db, visitor)),
|
||||
Self::Callable(callable) => Self::Callable(callable.normalized_impl(db, visitor)),
|
||||
Self::LiteralStringAlias(ty) => {
|
||||
Self::LiteralStringAlias(ty.normalized_impl(db, visitor))
|
||||
@@ -8207,6 +8347,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||
Self::Literal(_)
|
||||
| Self::Annotated(_)
|
||||
| Self::TypeGenericAlias(_)
|
||||
| Self::GenericAlias(_)
|
||||
| Self::Callable(_) => KnownClass::GenericAlias,
|
||||
Self::LiteralStringAlias(_) => KnownClass::Str,
|
||||
Self::NewType(_) => KnownClass::NewType,
|
||||
@@ -8312,6 +8453,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||
KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => {
|
||||
f.write_str("GenericAlias")
|
||||
}
|
||||
KnownInstanceType::GenericAlias(_) => f.write_str("GenericAlias(…)"), //TODO
|
||||
KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"),
|
||||
KnownInstanceType::NewType(declaration) => {
|
||||
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
|
||||
@@ -9494,6 +9636,11 @@ pub struct UnionTypeInstance<'db> {
|
||||
/// contains the first encountered error.
|
||||
#[returns(ref)]
|
||||
union_type: Result<Type<'db>, InvalidTypeExpressionError<'db>>,
|
||||
|
||||
/// The typevar binding context in which this union type instance was created.
|
||||
/// For an implicit type alias like `ListOrSet = list[T] | set[T]`, this would
|
||||
/// be the definition of `ListOrSet`.
|
||||
binding_context: Option<Definition<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for UnionTypeInstance<'_> {}
|
||||
@@ -9513,7 +9660,12 @@ impl<'db> UnionTypeInstance<'db> {
|
||||
Ok(ty) => builder.add_in_place(ty),
|
||||
Err(error) => {
|
||||
return Type::KnownInstance(KnownInstanceType::UnionType(
|
||||
UnionTypeInstance::new(db, Some(value_expr_types), Err(error)),
|
||||
UnionTypeInstance::new(
|
||||
db,
|
||||
Some(value_expr_types),
|
||||
Err(error),
|
||||
typevar_binding_context,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -9523,6 +9675,7 @@ impl<'db> UnionTypeInstance<'db> {
|
||||
db,
|
||||
Some(value_expr_types),
|
||||
Ok(builder.build()),
|
||||
typevar_binding_context,
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -9568,7 +9721,7 @@ impl<'db> UnionTypeInstance<'db> {
|
||||
.clone()
|
||||
.map(|ty| ty.normalized_impl(db, visitor));
|
||||
|
||||
Self::new(db, value_expr_types, union_type)
|
||||
Self::new(db, value_expr_types, union_type, self.binding_context(db))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9591,6 +9744,56 @@ impl<'db> InternedType<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A salsa-interned `Type` with an associated binding context for type variables.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the context's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the context was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TypeInContext<'db> {
|
||||
inner: Type<'db>,
|
||||
/// The typevar binding context in which this type was inferred.
|
||||
binding_context: Option<Definition<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for TypeInContext<'_> {}
|
||||
|
||||
impl<'db> TypeInContext<'db> {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||
TypeInContext::new(
|
||||
db,
|
||||
self.inner(db).normalized_impl(db, visitor),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of `typing.GenericAlias` representing a `Callable[...]` expression.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the context's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the context was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct CallableTypeInstance<'db> {
|
||||
callable_type: CallableType<'db>,
|
||||
/// The binding context in which this callable type instance was inferred.
|
||||
binding_context: Option<Definition<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for CallableTypeInstance<'_> {}
|
||||
|
||||
impl<'db> CallableTypeInstance<'db> {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||
CallableTypeInstance::new(
|
||||
db,
|
||||
self.callable_type(db).normalized_impl(db, visitor),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned if a type is not awaitable.
|
||||
#[derive(Debug)]
|
||||
enum AwaitError<'db> {
|
||||
|
||||
@@ -185,6 +185,9 @@ impl<'db> ClassBase<'db> {
|
||||
KnownInstanceType::TypeGenericAlias(_) => {
|
||||
Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass)
|
||||
}
|
||||
KnownInstanceType::GenericAlias(instance) => {
|
||||
Self::try_from_type(db, instance.inner(db), subclass)
|
||||
}
|
||||
KnownInstanceType::Annotated(ty) => {
|
||||
// Unions are not supported in this position, so we only need to support
|
||||
// something like `class C(Annotated[Base, "metadata"]): ...`, which we
|
||||
|
||||
@@ -101,14 +101,14 @@ use crate::types::typed_dict::{
|
||||
};
|
||||
use crate::types::visitor::any_over_type;
|
||||
use crate::types::{
|
||||
CallDunderError, CallableBinding, CallableType, CallableTypes, ClassLiteral, ClassType,
|
||||
DataclassParams, DynamicType, InternedType, IntersectionBuilder, IntersectionType, KnownClass,
|
||||
KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate,
|
||||
PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType, SubclassOfType,
|
||||
TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers, TypeContext,
|
||||
TypeQualifiers, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
|
||||
TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
|
||||
UnionTypeInstance, binding_type, liskov, todo_type,
|
||||
CallDunderError, CallableBinding, CallableType, CallableTypeInstance, CallableTypes,
|
||||
ClassLiteral, ClassType, DataclassParams, DynamicType, InternedType, IntersectionBuilder,
|
||||
IntersectionType, KnownClass, KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy,
|
||||
MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType,
|
||||
SubclassOfType, TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers,
|
||||
TypeContext, TypeInContext, TypeQualifiers, TypeVarBoundOrConstraintsEvaluation,
|
||||
TypeVarDefaultEvaluation, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
|
||||
TypedDictType, UnionBuilder, UnionType, UnionTypeInstance, binding_type, liskov, todo_type,
|
||||
};
|
||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||
use crate::unpack::{EvaluationMode, UnpackPosition};
|
||||
@@ -4739,6 +4739,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
unpacked.expression_type(target)
|
||||
}
|
||||
TargetKind::Single => {
|
||||
// This could be an implicit type alias (OptionalList = list[T] | None). Use the definition
|
||||
// of `OptionalList` as the binding context while inferring the RHS (`list[T] | None`), in
|
||||
// order to bind `T` to `OptionalList`.
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(definition);
|
||||
|
||||
let value_ty = if let Some(standalone_expression) = self.index.try_expression(value)
|
||||
{
|
||||
self.infer_standalone_expression_impl(value, standalone_expression, tcx)
|
||||
@@ -4777,6 +4783,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
self.infer_expression(value, tcx)
|
||||
};
|
||||
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
|
||||
// `TYPE_CHECKING` is a special variable that should only be assigned `False`
|
||||
// at runtime, but is always considered `True` in type checking.
|
||||
// See mdtest/known_constants.md#user-defined-type_checking for details.
|
||||
@@ -5523,11 +5531,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
self.deferred_state = DeferredExpressionState::Deferred;
|
||||
}
|
||||
|
||||
// This might be a PEP-613 type alias (`OptionalList: TypeAlias = list[T] | None`). Use
|
||||
// the definition of `OptionalList` as the binding context while inferring the
|
||||
// RHS (`list[T] | None`), in order to bind `T` to `OptionalList`.
|
||||
let previous_typevar_binding_context = self.typevar_binding_context.replace(definition);
|
||||
|
||||
let inferred_ty = self.infer_maybe_standalone_expression(
|
||||
value,
|
||||
TypeContext::new(Some(declared.inner_type())),
|
||||
);
|
||||
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
|
||||
self.deferred_state = previous_deferred_state;
|
||||
|
||||
self.dataclass_field_specifiers.clear();
|
||||
@@ -10794,7 +10809,52 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
fn infer_subscript_load(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
||||
let value_ty = self.infer_expression(&subscript.value, TypeContext::default());
|
||||
self.infer_subscript_load_impl(value_ty, subscript)
|
||||
|
||||
// If we have an implicit type alias like `MyList = list[T]`, and if `MyList` is being
|
||||
// used in another implicit type alias like `Numbers = MyList[int]`, then we infer the
|
||||
// right hand side as a value expression, and need to handle the specialization here.
|
||||
if let Type::KnownInstance(KnownInstanceType::GenericAlias(alias)) = value_ty {
|
||||
return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
|
||||
self.db(),
|
||||
self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
alias.binding_context(self.db()),
|
||||
false,
|
||||
),
|
||||
self.typevar_binding_context,
|
||||
)));
|
||||
}
|
||||
|
||||
// if let Type::GenericAlias(alias) = value_ty {
|
||||
// return Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
|
||||
// self.db(),
|
||||
// self.infer_explicitly_specialized_type_alias(
|
||||
// subscript,
|
||||
// value_ty,
|
||||
// Some(alias.definition(self.db())),
|
||||
// false,
|
||||
// ),
|
||||
// self.typevar_binding_context,
|
||||
// )));
|
||||
// }
|
||||
|
||||
let result_ty = self.infer_subscript_load_impl(value_ty, subscript);
|
||||
|
||||
// let result_ty = if result_ty.is_generic_alias() {
|
||||
// Type::KnownInstance(KnownInstanceType::GenericAlias(TypeInContext::new(
|
||||
// self.db(),
|
||||
// result_ty,
|
||||
// self.typevar_binding_context,
|
||||
// )))
|
||||
// } else {
|
||||
// result_ty
|
||||
// };
|
||||
|
||||
// eprintln!("Subscripting type: {}", value_ty.display(self.db()));
|
||||
// eprintln!("Inferred subscript type: {}", result_ty.display(self.db()));
|
||||
|
||||
result_ty
|
||||
}
|
||||
|
||||
fn infer_subscript_load_impl(
|
||||
@@ -10848,7 +10908,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
} else if class.is_known(self.db(), KnownClass::Type) {
|
||||
let argument_ty = self.infer_type_expression(slice);
|
||||
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||
InternedType::new(self.db(), argument_ty),
|
||||
TypeInContext::new(self.db(), argument_ty, self.typevar_binding_context),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -10926,9 +10986,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
let ty = self.infer_type_expression(type_expr);
|
||||
|
||||
return Type::KnownInstance(KnownInstanceType::Annotated(InternedType::new(
|
||||
return Type::KnownInstance(KnownInstanceType::Annotated(TypeInContext::new(
|
||||
self.db(),
|
||||
ty,
|
||||
self.typevar_binding_context,
|
||||
)));
|
||||
}
|
||||
Type::SpecialForm(SpecialFormType::Optional) => {
|
||||
@@ -10959,6 +11020,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
match **slice {
|
||||
ast::Expr::Tuple(ref tuple) => {
|
||||
let typevar_binding_context = self.typevar_binding_context;
|
||||
let mut elements = tuple
|
||||
.elts
|
||||
.iter()
|
||||
@@ -10971,6 +11033,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
db,
|
||||
None,
|
||||
Ok(UnionType::from_elements(db, elements)),
|
||||
typevar_binding_context,
|
||||
),
|
||||
));
|
||||
|
||||
@@ -10995,7 +11058,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// Similar to the branch above that handles `type[…]`, handle `typing.Type[…]`
|
||||
let argument_ty = self.infer_type_expression(slice);
|
||||
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||
InternedType::new(self.db(), argument_ty),
|
||||
TypeInContext::new(self.db(), argument_ty, self.typevar_binding_context),
|
||||
));
|
||||
}
|
||||
Type::SpecialForm(SpecialFormType::Callable) => {
|
||||
@@ -11004,7 +11067,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
.as_callable()
|
||||
.expect("always returns Type::Callable");
|
||||
|
||||
return Type::KnownInstance(KnownInstanceType::Callable(callable));
|
||||
return Type::KnownInstance(KnownInstanceType::Callable(
|
||||
CallableTypeInstance::new(self.db(), callable, self.typevar_binding_context),
|
||||
));
|
||||
}
|
||||
// `typing` special forms with a single generic argument
|
||||
Type::SpecialForm(
|
||||
@@ -11095,8 +11160,32 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
.map(Type::from)
|
||||
.unwrap_or_else(Type::unknown);
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::UnionType(_)) => {
|
||||
return todo_type!("Specialization of union type alias");
|
||||
Type::KnownInstance(KnownInstanceType::UnionType(instance)) => {
|
||||
return self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
Type::KnownInstance(
|
||||
KnownInstanceType::Annotated(instance)
|
||||
| KnownInstanceType::TypeGenericAlias(instance),
|
||||
) => {
|
||||
return self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::Callable(instance)) => {
|
||||
return self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ use itertools::Either;
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
use super::{DeferredExpressionState, TypeInferenceBuilder};
|
||||
use crate::FxOrderSet;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::diagnostic::{
|
||||
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
|
||||
report_invalid_arguments_to_callable,
|
||||
@@ -11,9 +13,10 @@ use crate::types::string_annotation::parse_string_annotation;
|
||||
use crate::types::tuple::{TupleSpecBuilder, TupleType};
|
||||
use crate::types::visitor::any_over_type;
|
||||
use crate::types::{
|
||||
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
|
||||
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
|
||||
TypeAliasType, TypeContext, TypeIsType, UnionBuilder, UnionType, todo_type,
|
||||
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
|
||||
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
|
||||
Type, TypeAliasType, TypeContext, TypeInContext, TypeIsType, TypeMapping, UnionBuilder,
|
||||
UnionType, todo_type,
|
||||
};
|
||||
|
||||
/// Type expressions
|
||||
@@ -750,6 +753,84 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Infer the type of an explicitly specialized generic type alias (implicit or PEP 613).
|
||||
pub(crate) fn infer_explicitly_specialized_type_alias(
|
||||
&mut self,
|
||||
subscript: &ast::ExprSubscript,
|
||||
mut value_ty: Type<'db>,
|
||||
typevar_binding_context: Option<Definition<'db>>,
|
||||
in_type_expression: bool,
|
||||
) -> Type<'db> {
|
||||
let db = self.db();
|
||||
|
||||
let Some(typevar_binding_context) = typevar_binding_context else {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
let mut diag = builder.into_diagnostic(format_args!(
|
||||
"`{}` is not subscriptable",
|
||||
value_ty.display(db)
|
||||
));
|
||||
diag.info("Consider creating a type alias to create a binding context for the type variable(s)");
|
||||
}
|
||||
return Type::unknown();
|
||||
};
|
||||
|
||||
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = value_ty
|
||||
&& let Some(definition) = typevar.definition(db)
|
||||
{
|
||||
value_ty = value_ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::BindLegacyTypevars(BindingContext::Definition(definition)),
|
||||
TypeContext::default(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut variables = FxOrderSet::default();
|
||||
value_ty.find_legacy_typevars(db, Some(typevar_binding_context), &mut variables);
|
||||
let generic_context = GenericContext::from_typevar_instances(db, variables);
|
||||
|
||||
let scope_id = self.scope();
|
||||
let current_typevar_binding_context = self.typevar_binding_context;
|
||||
let specialize = |types: &[Option<Type<'db>>]| {
|
||||
let specialized = value_ty.apply_specialization(
|
||||
db,
|
||||
generic_context.specialize_partial(db, types.iter().copied()),
|
||||
);
|
||||
|
||||
if in_type_expression {
|
||||
specialized
|
||||
.in_type_expression(db, scope_id, current_typevar_binding_context)
|
||||
.unwrap_or_else(|_| Type::unknown())
|
||||
} else {
|
||||
// Update the binding context
|
||||
match specialized {
|
||||
// Type::GenericAlias(alias) => Type::GenericAlias(GenericAlias::new(
|
||||
// db,
|
||||
// alias.origin(db),
|
||||
// alias.specialization(db),
|
||||
// current_typevar_binding_context,
|
||||
// )),
|
||||
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(instance)) => {
|
||||
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||
TypeInContext::new(
|
||||
db,
|
||||
instance.inner(db),
|
||||
current_typevar_binding_context,
|
||||
),
|
||||
))
|
||||
}
|
||||
_ => specialized,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.infer_explicit_callable_specialization(
|
||||
subscript,
|
||||
value_ty,
|
||||
generic_context,
|
||||
specialize,
|
||||
)
|
||||
}
|
||||
|
||||
fn infer_subscript_type_expression(
|
||||
&mut self,
|
||||
subscript: &ast::ExprSubscript,
|
||||
@@ -840,10 +921,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::TypeVar(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("TypeVar annotations")
|
||||
}
|
||||
KnownInstanceType::TypeAliasType(type_alias @ TypeAliasType::PEP695(_)) => {
|
||||
match type_alias.generic_context(self.db()) {
|
||||
Some(generic_context) => {
|
||||
@@ -886,11 +963,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic stringified PEP-613 type alias")
|
||||
}
|
||||
KnownInstanceType::UnionType(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic specialization of types.UnionType")
|
||||
}
|
||||
KnownInstanceType::Literal(ty) | KnownInstanceType::TypeGenericAlias(ty) => {
|
||||
KnownInstanceType::Literal(ty) => {
|
||||
self.infer_type_expression(slice);
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
@@ -900,14 +973,36 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::Callable(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic specialization of typing.Callable")
|
||||
}
|
||||
KnownInstanceType::Annotated(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic specialization of typing.Annotated")
|
||||
}
|
||||
KnownInstanceType::TypeVar(instance) => self
|
||||
.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.definition(self.db()),
|
||||
false,
|
||||
),
|
||||
KnownInstanceType::UnionType(instance) => self
|
||||
.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
true,
|
||||
),
|
||||
KnownInstanceType::Annotated(instance)
|
||||
| KnownInstanceType::TypeGenericAlias(instance)
|
||||
| KnownInstanceType::GenericAlias(instance) => self
|
||||
.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
true,
|
||||
),
|
||||
KnownInstanceType::Callable(instance) => self
|
||||
.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
true,
|
||||
),
|
||||
KnownInstanceType::NewType(newtype) => {
|
||||
self.infer_type_expression(&subscript.slice);
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
@@ -948,13 +1043,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::GenericAlias(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
// If the generic alias is already fully specialized, this is an error. But it
|
||||
// could have been specialized with another typevar (e.g. a type alias like `MyList
|
||||
// = list[T]`), in which case it's later valid to do `MyList[int]`.
|
||||
todo_type!("specialized generic alias in type expression")
|
||||
}
|
||||
// Type::GenericAlias(alias) => {
|
||||
// self.infer_explicitly_specialized_type_alias(subscript, value_ty, None, true)
|
||||
// }
|
||||
Type::StringLiteral(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
// For stringified TypeAlias; remove once properly supported
|
||||
@@ -963,6 +1054,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
_ => {
|
||||
self.infer_type_expression(slice);
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
dbg!(&value_ty);
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Invalid subscript of object of type `{}` in type expression",
|
||||
value_ty.display(self.db())
|
||||
|
||||
@@ -100,7 +100,13 @@ impl<'db> Mro<'db> {
|
||||
if original_bases.contains(&Type::SpecialForm(SpecialFormType::Protocol)) {
|
||||
return;
|
||||
}
|
||||
if remaining_bases.iter().any(Type::is_generic_alias) {
|
||||
if remaining_bases.iter().any(|ty| {
|
||||
matches!(
|
||||
ty,
|
||||
Type::GenericAlias(..)
|
||||
| Type::KnownInstance(KnownInstanceType::GenericAlias(_))
|
||||
)
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
resolved_bases.push(ClassBase::Generic);
|
||||
|
||||
Reference in New Issue
Block a user