Compare commits
14 Commits
alex/subsc
...
david/gene
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e609da0c72 | ||
|
|
7818186fcc | ||
|
|
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
||||
use crate::suppression::check_suppressions;
|
||||
use crate::types::bound_super::BoundSuperType;
|
||||
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
||||
use crate::types::class::GenericAliasInstance;
|
||||
pub(crate) use crate::types::class_base::ClassBase;
|
||||
use crate::types::constraints::{
|
||||
ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension,
|
||||
@@ -759,7 +760,7 @@ pub enum Type<'db> {
|
||||
/// A specific class object
|
||||
ClassLiteral(ClassLiteral<'db>),
|
||||
/// A specialization of a generic class
|
||||
GenericAlias(GenericAlias<'db>),
|
||||
GenericAlias(GenericAliasInstance<'db>),
|
||||
/// The set of all class objects that are subclasses of the given class (C), spelled `type[C]`.
|
||||
SubclassOf(SubclassOfType<'db>),
|
||||
/// The set of Python objects with the given class in their __class__'s method resolution order.
|
||||
@@ -906,6 +907,13 @@ impl<'db> Type<'db> {
|
||||
matches!(self, Type::GenericAlias(_))
|
||||
}
|
||||
|
||||
pub(crate) fn as_generic_alias(&self) -> Option<GenericAliasInstance<'db>> {
|
||||
match self {
|
||||
Type::GenericAlias(alias) => Some(*alias),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_dynamic(&self) -> bool {
|
||||
matches!(self, Type::Dynamic(_))
|
||||
}
|
||||
@@ -1152,7 +1160,7 @@ impl<'db> Type<'db> {
|
||||
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> Option<ClassType<'db>> {
|
||||
match self {
|
||||
Type::ClassLiteral(class_literal) => Some(class_literal.default_specialization(db)),
|
||||
Type::GenericAlias(alias) => Some(ClassType::Generic(alias)),
|
||||
Type::GenericAlias(instance) => Some(ClassType::Generic(instance.alias(db))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1276,8 +1284,8 @@ impl<'db> Type<'db> {
|
||||
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
|
||||
}
|
||||
|
||||
pub(crate) fn typed_dict(defining_class: impl Into<ClassType<'db>>) -> Self {
|
||||
Self::TypedDict(TypedDictType::new(defining_class.into()))
|
||||
pub(crate) fn typed_dict(defining_class: ClassType<'db>) -> Self {
|
||||
Self::TypedDict(TypedDictType::new(defining_class))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -1562,7 +1570,9 @@ impl<'db> Type<'db> {
|
||||
Some(class_literal.default_specialization(db).into_callable(db))
|
||||
}
|
||||
|
||||
Type::GenericAlias(alias) => Some(ClassType::Generic(alias).into_callable(db)),
|
||||
Type::GenericAlias(instance) => {
|
||||
Some(ClassType::Generic(instance.alias(db)).into_callable(db))
|
||||
}
|
||||
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
Type::instance(db, newtype.base_class_type(db)).try_upcast_to_callable(db)
|
||||
@@ -2409,20 +2419,22 @@ impl<'db> Type<'db> {
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
|
||||
(Type::GenericAlias(alias), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty
|
||||
.subclass_of()
|
||||
.into_class()
|
||||
.map(|subclass_of_class| {
|
||||
ClassType::Generic(alias).has_relation_to_impl(
|
||||
db,
|
||||
subclass_of_class,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
|
||||
(Type::GenericAlias(instance), Type::SubclassOf(target_subclass_ty)) => {
|
||||
target_subclass_ty
|
||||
.subclass_of()
|
||||
.into_class()
|
||||
.map(|subclass_of_class| {
|
||||
ClassType::Generic(instance.alias(db)).has_relation_to_impl(
|
||||
db,
|
||||
subclass_of_class,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability()))
|
||||
}
|
||||
|
||||
// This branch asks: given two types `type[T]` and `type[S]`, is `type[T]` a subtype of `type[S]`?
|
||||
(Type::SubclassOf(self_subclass_ty), Type::SubclassOf(target_subclass_ty)) => {
|
||||
@@ -2449,7 +2461,7 @@ impl<'db> Type<'db> {
|
||||
disjointness_visitor,
|
||||
)
|
||||
}
|
||||
(Type::GenericAlias(alias), _) => ClassType::from(alias)
|
||||
(Type::GenericAlias(instance), _) => ClassType::Generic(instance.alias(db))
|
||||
.metaclass_instance_type(db)
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
@@ -3173,7 +3185,7 @@ impl<'db> Type<'db> {
|
||||
| (Type::GenericAlias(alias_b), Type::SubclassOf(subclass_of_ty)) => {
|
||||
match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
|
||||
SubclassOfInner::Class(class_a) => ClassType::from(alias_b)
|
||||
SubclassOfInner::Class(class_a) => ClassType::Generic(alias_b.alias(db))
|
||||
.when_subclass_of(db, class_a, inferable)
|
||||
.negate(db),
|
||||
}
|
||||
@@ -3288,9 +3300,9 @@ impl<'db> Type<'db> {
|
||||
.metaclass_instance_type(db)
|
||||
.when_subtype_of(db, instance, inferable)
|
||||
.negate(db),
|
||||
(Type::GenericAlias(alias), instance @ Type::NominalInstance(_))
|
||||
| (instance @ Type::NominalInstance(_), Type::GenericAlias(alias)) => {
|
||||
ClassType::from(alias)
|
||||
(Type::GenericAlias(generic), instance @ Type::NominalInstance(_))
|
||||
| (instance @ Type::NominalInstance(_), Type::GenericAlias(generic)) => {
|
||||
ClassType::Generic(generic.alias(db))
|
||||
.metaclass_instance_type(db)
|
||||
.has_relation_to_impl(
|
||||
db,
|
||||
@@ -3815,13 +3827,21 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
Type::GenericAlias(alias) if alias.is_typed_dict(db) => {
|
||||
Some(alias.origin(db).typed_dict_member(db, None, name, policy))
|
||||
}
|
||||
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => Some(
|
||||
instance
|
||||
.alias(db)
|
||||
.origin(db)
|
||||
.typed_dict_member(db, None, name, policy),
|
||||
),
|
||||
|
||||
Type::GenericAlias(alias) => {
|
||||
let attr = Some(ClassType::from(*alias).class_member(db, name, policy));
|
||||
match alias.specialization(db).materialization_kind(db) {
|
||||
Type::GenericAlias(instance) => {
|
||||
let attr =
|
||||
Some(ClassType::Generic(instance.alias(db)).class_member(db, name, policy));
|
||||
match instance
|
||||
.alias(db)
|
||||
.specialization(db)
|
||||
.materialization_kind(db)
|
||||
{
|
||||
None => attr,
|
||||
Some(materialization_kind) => attr.map(|attr| {
|
||||
attr.materialize(
|
||||
@@ -5025,14 +5045,16 @@ impl<'db> Type<'db> {
|
||||
.metaclass_instance_type(db)
|
||||
.try_bool_impl(db, allow_short_circuit, visitor)?
|
||||
}
|
||||
Type::GenericAlias(alias) => ClassType::from(*alias)
|
||||
Type::GenericAlias(generic) => ClassType::Generic(generic.alias(db))
|
||||
.metaclass_instance_type(db)
|
||||
.try_bool_impl(db, allow_short_circuit, visitor)?,
|
||||
|
||||
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => Truthiness::Ambiguous,
|
||||
SubclassOfInner::Class(class) => {
|
||||
Type::from(class).try_bool_impl(db, allow_short_circuit, visitor)?
|
||||
class
|
||||
.into_type(db)
|
||||
.try_bool_impl(db, allow_short_circuit, visitor)?
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5763,7 +5785,7 @@ impl<'db> Type<'db> {
|
||||
// getting the signature here. This signature can still be used in some cases (e.g.
|
||||
// evaluating callable subtyping). TODO improve this definition (intersection of
|
||||
// `__new__` and `__init__` signatures? and respect metaclass `__call__`).
|
||||
SubclassOfInner::Class(class) => Type::from(class).bindings(db),
|
||||
SubclassOfInner::Class(class) => class.into_type(db).bindings(db),
|
||||
},
|
||||
|
||||
Type::NominalInstance(_) | Type::ProtocolInstance(_) | Type::NewTypeInstance(_) => {
|
||||
@@ -5965,7 +5987,7 @@ impl<'db> Type<'db> {
|
||||
match ty {
|
||||
Type::NominalInstance(nominal) => nominal.tuple_spec(db),
|
||||
Type::NewTypeInstance(newtype) => non_async_special_case(db, Type::instance(db, newtype.base_class_type(db))),
|
||||
Type::GenericAlias(alias) if alias.origin(db).is_tuple(db) => {
|
||||
Type::GenericAlias(instance) if instance.alias(db).origin(db).is_tuple(db) => {
|
||||
Some(Cow::Owned(TupleSpec::homogeneous(todo_type!(
|
||||
"*tuple[] annotations"
|
||||
))))
|
||||
@@ -6411,7 +6433,7 @@ impl<'db> Type<'db> {
|
||||
// It is important that identity_specialization specializes the class with
|
||||
// _inferable_ typevars, so that our specialization inference logic will
|
||||
// try to find a specialization for them.
|
||||
Type::from(class.identity_specialization(db)),
|
||||
class.identity_specialization(db).into_type(db),
|
||||
),
|
||||
_ => (None, None, self),
|
||||
},
|
||||
@@ -6592,7 +6614,7 @@ impl<'db> Type<'db> {
|
||||
.map(|specialization| {
|
||||
Type::instance(
|
||||
db,
|
||||
generic_origin.apply_specialization(db, |_| specialization),
|
||||
generic_origin.apply_specialization(db, |_| specialization, None),
|
||||
)
|
||||
})
|
||||
.unwrap_or(instance_ty);
|
||||
@@ -6625,7 +6647,9 @@ impl<'db> Type<'db> {
|
||||
match self {
|
||||
Type::Dynamic(_) | Type::Never => Some(self),
|
||||
Type::ClassLiteral(class) => Some(Type::instance(db, class.default_specialization(db))),
|
||||
Type::GenericAlias(alias) => Some(Type::instance(db, ClassType::from(alias))),
|
||||
Type::GenericAlias(instance) => {
|
||||
Some(Type::instance(db, ClassType::Generic(instance.alias(db))))
|
||||
}
|
||||
Type::SubclassOf(subclass_of_ty) => Some(subclass_of_ty.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => {
|
||||
Some(Type::NewTypeInstance(newtype))
|
||||
@@ -6707,8 +6731,12 @@ impl<'db> Type<'db> {
|
||||
};
|
||||
Ok(ty)
|
||||
}
|
||||
Type::GenericAlias(alias) if alias.is_typed_dict(db) => Ok(Type::typed_dict(*alias)),
|
||||
Type::GenericAlias(alias) => Ok(Type::instance(db, ClassType::from(*alias))),
|
||||
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => {
|
||||
Ok(Type::typed_dict(ClassType::Generic(instance.alias(db))))
|
||||
}
|
||||
Type::GenericAlias(instance) => {
|
||||
Ok(Type::instance(db, ClassType::Generic(instance.alias(db))))
|
||||
}
|
||||
|
||||
Type::SubclassOf(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
@@ -6792,7 +6820,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 +6829,25 @@ 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::Callable(instance) => {
|
||||
Ok(Type::Callable(instance.callable_type(db)))
|
||||
}
|
||||
KnownInstanceType::Callable(callable) => Ok(Type::Callable(*callable)),
|
||||
KnownInstanceType::LiteralStringAlias(ty) => Ok(ty.inner(db)),
|
||||
},
|
||||
|
||||
@@ -7050,7 +7094,7 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
Type::ClassLiteral(class) => class.metaclass(db),
|
||||
Type::GenericAlias(alias) => ClassType::from(alias).metaclass(db),
|
||||
Type::GenericAlias(instance) => ClassType::Generic(instance.alias(db)).metaclass(db),
|
||||
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Dynamic(_) => self,
|
||||
SubclassOfInner::Class(class) => SubclassOfType::from(
|
||||
@@ -7075,7 +7119,7 @@ impl<'db> Type<'db> {
|
||||
// understand a more specific meta type in order to correctly handle `__getitem__`.
|
||||
Type::TypedDict(typed_dict) => SubclassOfType::from(db, typed_dict.defining_class()),
|
||||
Type::TypeAlias(alias) => alias.value_type(db).to_meta_type(db),
|
||||
Type::NewTypeInstance(newtype) => Type::from(newtype.base_class_type(db)),
|
||||
Type::NewTypeInstance(newtype) => newtype.base_class_type(db).into_type(db),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7088,8 +7132,12 @@ impl<'db> Type<'db> {
|
||||
pub(crate) fn dunder_class(self, db: &'db dyn Db) -> Type<'db> {
|
||||
if self.is_typed_dict() {
|
||||
return KnownClass::Dict
|
||||
.to_specialized_class_type(db, [KnownClass::Str.to_instance(db), Type::object()])
|
||||
.map(Type::from)
|
||||
.to_specialized_class_type(
|
||||
db,
|
||||
[KnownClass::Str.to_instance(db), Type::object()],
|
||||
None,
|
||||
)
|
||||
.map(|class| class.into_type(db))
|
||||
// Guard against user-customized typesheds with a broken `dict` class
|
||||
.unwrap_or_else(Type::unknown);
|
||||
}
|
||||
@@ -7198,17 +7246,74 @@ 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::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 +7474,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 +7611,49 @@ 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::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 +7681,6 @@ impl<'db> Type<'db> {
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::TypedDict(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -7749,7 +7895,7 @@ impl<'db> Type<'db> {
|
||||
|
||||
pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option<ClassLiteral<'db>> {
|
||||
match self {
|
||||
Type::GenericAlias(generic) => Some(generic.origin(db)),
|
||||
Type::GenericAlias(instance) => Some(instance.origin(db)),
|
||||
Type::NominalInstance(instance) => {
|
||||
if let ClassType::Generic(generic) = instance.class(db) {
|
||||
Some(generic.origin(db))
|
||||
@@ -7809,7 +7955,7 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
|
||||
Type::NominalInstance(nominal_instance_type) => {
|
||||
nominal_instance_type.variance_of(db, typevar)
|
||||
}
|
||||
Type::GenericAlias(generic_alias) => generic_alias.variance_of(db, typevar),
|
||||
Type::GenericAlias(instance) => instance.alias(db).variance_of(db, typevar),
|
||||
Type::Callable(callable_type) => callable_type.signatures(db).variance_of(db, typevar),
|
||||
// A type variable is always covariant in itself.
|
||||
Type::TypeVar(other_typevar) if other_typevar == typevar => {
|
||||
@@ -8075,13 +8221,13 @@ 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>),
|
||||
|
||||
/// 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 +8269,14 @@ 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) => {
|
||||
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) {
|
||||
@@ -9494,6 +9640,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 +9664,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 +9679,7 @@ impl<'db> UnionTypeInstance<'db> {
|
||||
db,
|
||||
Some(value_expr_types),
|
||||
Ok(builder.build()),
|
||||
typevar_binding_context,
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -9568,7 +9725,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 +9748,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> {
|
||||
|
||||
@@ -76,9 +76,11 @@ impl<'db> BoundSuperError<'db> {
|
||||
BoundSuperError::InvalidPivotClassType { pivot_class } => {
|
||||
if let Some(builder) = context.report_lint(&INVALID_SUPER_ARGUMENT, node) {
|
||||
match pivot_class {
|
||||
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
|
||||
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
|
||||
"`types.GenericAlias` instance `{}` is not a valid class",
|
||||
alias.display_with(context.db(), DisplaySettings::default()),
|
||||
instance
|
||||
.alias(context.db())
|
||||
.display_with(context.db(), DisplaySettings::default()),
|
||||
)),
|
||||
_ => builder.into_diagnostic(format_args!(
|
||||
"`{pivot_class}` is not a valid class",
|
||||
@@ -209,13 +211,11 @@ impl<'db> SuperOwnerKind<'db> {
|
||||
SuperOwnerKind::Instance(instance) => Some(instance.class(db)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<SuperOwnerKind<'db>> for Type<'db> {
|
||||
fn from(owner: SuperOwnerKind<'db>) -> Self {
|
||||
match owner {
|
||||
pub fn into_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
SuperOwnerKind::Dynamic(dynamic) => Type::Dynamic(dynamic),
|
||||
SuperOwnerKind::Class(class) => class.into(),
|
||||
SuperOwnerKind::Class(class) => class.into_type(db),
|
||||
SuperOwnerKind::Instance(instance) => instance.into(),
|
||||
}
|
||||
}
|
||||
@@ -236,8 +236,8 @@ pub(super) fn walk_bound_super_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
bound_super: BoundSuperType<'db>,
|
||||
visitor: &V,
|
||||
) {
|
||||
visitor.visit_type(db, Type::from(bound_super.pivot_class(db)));
|
||||
visitor.visit_type(db, Type::from(bound_super.owner(db)));
|
||||
visitor.visit_type(db, bound_super.pivot_class(db).into_type(db));
|
||||
visitor.visit_type(db, bound_super.owner(db).into_type(db));
|
||||
}
|
||||
|
||||
impl<'db> BoundSuperType<'db> {
|
||||
@@ -515,12 +515,12 @@ impl<'db> BoundSuperType<'db> {
|
||||
db,
|
||||
attribute,
|
||||
Type::none(db),
|
||||
Type::from(owner),
|
||||
owner.into_type(db),
|
||||
)
|
||||
.0,
|
||||
),
|
||||
SuperOwnerKind::Instance(_) => {
|
||||
let owner = Type::from(owner);
|
||||
let owner = owner.into_type(db);
|
||||
Some(
|
||||
Type::try_call_dunder_get_on_attribute(
|
||||
db,
|
||||
|
||||
@@ -103,7 +103,7 @@ fn try_mro_cycle_initial<'db>(
|
||||
) -> Result<Mro<'db>, MroError<'db>> {
|
||||
Err(MroError::cycle(
|
||||
db,
|
||||
self_.apply_optional_specialization(db, specialization),
|
||||
self_.apply_optional_specialization(db, specialization, None),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -296,9 +296,60 @@ impl<'db> GenericAlias<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<GenericAlias<'db>> for Type<'db> {
|
||||
fn from(alias: GenericAlias<'db>) -> Type<'db> {
|
||||
Type::GenericAlias(alias)
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericAliasInstance<'db> {
|
||||
pub alias: GenericAlias<'db>,
|
||||
pub binding_context: Option<Definition<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for GenericAliasInstance<'_> {}
|
||||
|
||||
impl<'db> GenericAliasInstance<'db> {
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.alias(db).normalized_impl(db, visitor),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
|
||||
self.alias(db).definition(db)
|
||||
}
|
||||
|
||||
pub(crate) fn origin(self, db: &'db dyn Db) -> ClassLiteral<'db> {
|
||||
self.alias(db).origin(db)
|
||||
}
|
||||
|
||||
pub(crate) fn specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||
self.alias(db).specialization(db)
|
||||
}
|
||||
|
||||
pub(super) fn apply_type_mapping_impl<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
type_mapping: &TypeMapping<'a, 'db>,
|
||||
tcx: TypeContext<'db>,
|
||||
visitor: &ApplyTypeMappingVisitor<'db>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.alias(db)
|
||||
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
self.binding_context(db),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn find_legacy_typevars_impl(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
|
||||
visitor: &FindLegacyTypeVarsVisitor<'db>,
|
||||
) {
|
||||
self.alias(db)
|
||||
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,10 +436,19 @@ impl<'db> ClassType<'db> {
|
||||
matches!(self, Self::Generic(_))
|
||||
}
|
||||
|
||||
pub(super) const fn into_generic_alias(self) -> Option<GenericAlias<'db>> {
|
||||
pub(super) fn into_generic_alias(self, db: &'db dyn Db) -> Option<GenericAlias<'db>> {
|
||||
match self {
|
||||
Self::NonGeneric(_) => None,
|
||||
Self::Generic(generic) => Some(generic),
|
||||
Self::Generic(alias) => Some(alias),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
Self::NonGeneric(class) => class.into(),
|
||||
Self::Generic(instance) => {
|
||||
Type::GenericAlias(GenericAliasInstance::new(db, instance, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,7 +1118,7 @@ impl<'db> ClassType<'db> {
|
||||
/// constructor signature of this class.
|
||||
#[salsa::tracked(cycle_initial=into_callable_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(super) fn into_callable(self, db: &'db dyn Db) -> CallableTypes<'db> {
|
||||
let self_ty = Type::from(self);
|
||||
let self_ty = self.into_type(db);
|
||||
let metaclass_dunder_call_function_symbol = self_ty
|
||||
.member_lookup_with_policy(
|
||||
db,
|
||||
@@ -1222,26 +1282,11 @@ fn into_callable_cycle_initial<'db>(
|
||||
CallableTypes::one(CallableType::bottom(db))
|
||||
}
|
||||
|
||||
impl<'db> From<GenericAlias<'db>> for ClassType<'db> {
|
||||
fn from(generic: GenericAlias<'db>) -> ClassType<'db> {
|
||||
ClassType::Generic(generic)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassType<'db>> for Type<'db> {
|
||||
fn from(class: ClassType<'db>) -> Type<'db> {
|
||||
match class {
|
||||
ClassType::NonGeneric(non_generic) => non_generic.into(),
|
||||
ClassType::Generic(generic) => generic.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for ClassType<'db> {
|
||||
fn variance_of(self, db: &'db dyn Db, typevar: BoundTypeVarInstance<'db>) -> TypeVarVariance {
|
||||
match self {
|
||||
Self::NonGeneric(class) => class.variance_of(db, typevar),
|
||||
Self::Generic(generic) => generic.variance_of(db, typevar),
|
||||
Self::Generic(alias) => alias.variance_of(db, typevar),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1529,6 +1574,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
) -> ClassType<'db> {
|
||||
match self.generic_context(db) {
|
||||
None => ClassType::NonGeneric(self),
|
||||
@@ -1554,48 +1600,63 @@ impl<'db> ClassLiteral<'db> {
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: Option<Specialization<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
) -> ClassType<'db> {
|
||||
self.apply_specialization(db, |generic_context| {
|
||||
specialization
|
||||
.unwrap_or_else(|| generic_context.default_specialization(db, self.known(db)))
|
||||
})
|
||||
self.apply_specialization(
|
||||
db,
|
||||
|generic_context| {
|
||||
specialization
|
||||
.unwrap_or_else(|| generic_context.default_specialization(db, self.known(db)))
|
||||
},
|
||||
binding_context,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn top_materialization(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
self.apply_specialization(db, |generic_context| {
|
||||
generic_context
|
||||
.default_specialization(db, self.known(db))
|
||||
.materialize_impl(
|
||||
db,
|
||||
MaterializationKind::Top,
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
)
|
||||
})
|
||||
self.apply_specialization(
|
||||
db,
|
||||
|generic_context| {
|
||||
generic_context
|
||||
.default_specialization(db, self.known(db))
|
||||
.materialize_impl(
|
||||
db,
|
||||
MaterializationKind::Top,
|
||||
&ApplyTypeMappingVisitor::default(),
|
||||
)
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the default specialization of this class. For non-generic classes, the class is
|
||||
/// returned unchanged. For a non-specialized generic class, we return a generic alias that
|
||||
/// applies the default specialization to the class's typevars.
|
||||
pub(crate) fn default_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
self.apply_specialization(db, |generic_context| {
|
||||
generic_context.default_specialization(db, self.known(db))
|
||||
})
|
||||
self.apply_specialization(
|
||||
db,
|
||||
|generic_context| generic_context.default_specialization(db, self.known(db)),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the unknown specialization of this class. For non-generic classes, the class is
|
||||
/// returned unchanged. For a non-specialized generic class, we return a generic alias that
|
||||
/// maps each of the class's typevars to `Unknown`.
|
||||
pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
self.apply_specialization(db, |generic_context| {
|
||||
generic_context.unknown_specialization(db)
|
||||
})
|
||||
self.apply_specialization(
|
||||
db,
|
||||
|generic_context| generic_context.unknown_specialization(db),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a specialization of this class where each typevar is mapped to itself.
|
||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
self.apply_specialization(db, |generic_context| {
|
||||
generic_context.identity_specialization(db)
|
||||
})
|
||||
self.apply_specialization(
|
||||
db,
|
||||
|generic_context| generic_context.identity_specialization(db),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return an iterator over the inferred types of this class's *explicit* bases.
|
||||
@@ -1626,7 +1687,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
|
||||
Box::new([
|
||||
definition_expression_type(db, class_definition, &class_stmt.bases()[0]),
|
||||
Type::from(tuple_type.to_class_type(db)),
|
||||
tuple_type.to_class_type(db, None).into_type(db),
|
||||
])
|
||||
} else {
|
||||
class_stmt
|
||||
@@ -1963,7 +2024,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
|
||||
let (metaclass_literal, _) = candidate.metaclass.class_literal(db);
|
||||
Ok((
|
||||
candidate.metaclass.into(),
|
||||
candidate.metaclass.into_type(db),
|
||||
metaclass_literal.dataclass_transformer_params(db),
|
||||
))
|
||||
}
|
||||
@@ -2041,7 +2102,7 @@ impl<'db> ClassLiteral<'db> {
|
||||
// Note: calling `Type::from(superclass).member()` would be incorrect here.
|
||||
// What we'd really want is a `Type::Any.own_class_member()` method,
|
||||
// but adding such a method wouldn't make much sense -- it would always return `Any`!
|
||||
dynamic_type_to_intersect_with.get_or_insert(Type::from(superclass));
|
||||
dynamic_type_to_intersect_with.get_or_insert(superclass.into_type(db));
|
||||
}
|
||||
ClassBase::Class(class) => {
|
||||
let known = class.known(db);
|
||||
@@ -2271,8 +2332,10 @@ impl<'db> ClassLiteral<'db> {
|
||||
|| transformer_params.is_some_and(|params| params.flags(db).contains(param))
|
||||
};
|
||||
|
||||
let instance_ty =
|
||||
Type::instance(db, self.apply_optional_specialization(db, specialization));
|
||||
let instance_ty = Type::instance(
|
||||
db,
|
||||
self.apply_optional_specialization(db, specialization, None),
|
||||
);
|
||||
|
||||
let signature_from_fields = |mut parameters: Vec<_>, return_ty: Option<Type<'db>>| {
|
||||
for (field_name, field) in self.fields(db, specialization, field_policy) {
|
||||
@@ -4787,6 +4850,7 @@ impl KnownClass {
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
specialization: impl IntoIterator<Item = Type<'db>>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
) -> Option<ClassType<'db>> {
|
||||
let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else {
|
||||
return None;
|
||||
@@ -4808,7 +4872,11 @@ impl KnownClass {
|
||||
return Some(class_literal.default_specialization(db));
|
||||
}
|
||||
|
||||
Some(class_literal.apply_specialization(db, |_| generic_context.specialize(db, types)))
|
||||
Some(class_literal.apply_specialization(
|
||||
db,
|
||||
|_| generic_context.specialize(db, types),
|
||||
binding_context,
|
||||
))
|
||||
}
|
||||
|
||||
/// Lookup a [`KnownClass`] in typeshed and return a [`Type`]
|
||||
@@ -4827,8 +4895,8 @@ impl KnownClass {
|
||||
KnownClass::Tuple,
|
||||
"Use `Type::heterogeneous_tuple` or `Type::homogeneous_tuple` to create `tuple` instances"
|
||||
);
|
||||
self.to_specialized_class_type(db, specialization)
|
||||
.and_then(|class_type| Type::from(class_type).to_instance(db))
|
||||
self.to_specialized_class_type(db, specialization, None)
|
||||
.and_then(|class_type| class_type.into_type(db).to_instance(db))
|
||||
.unwrap_or_else(Type::unknown)
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,9 @@ impl<'db> ClassBase<'db> {
|
||||
match ty {
|
||||
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
|
||||
Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
|
||||
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
|
||||
Type::GenericAlias(instance) => {
|
||||
Some(Self::Class(ClassType::Generic(instance.alias(db))))
|
||||
}
|
||||
Type::NominalInstance(instance)
|
||||
if instance.has_known_class(db, KnownClass::GenericAlias) =>
|
||||
{
|
||||
@@ -239,8 +241,8 @@ impl<'db> ClassBase<'db> {
|
||||
db,
|
||||
fields.values().map(|field| field.declared_ty),
|
||||
)?
|
||||
.to_class_type(db)
|
||||
.into(),
|
||||
.to_class_type(db, None)
|
||||
.into_type(db),
|
||||
subclass,
|
||||
)
|
||||
}
|
||||
@@ -383,7 +385,7 @@ impl<'db> ClassBase<'db> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.base {
|
||||
ClassBase::Dynamic(dynamic) => dynamic.fmt(f),
|
||||
ClassBase::Class(class) => Type::from(class).display(self.db).fmt(f),
|
||||
ClassBase::Class(class) => class.into_type(self.db).display(self.db).fmt(f),
|
||||
ClassBase::Protocol => f.write_str("typing.Protocol"),
|
||||
ClassBase::Generic => f.write_str("typing.Generic"),
|
||||
ClassBase::TypedDict => f.write_str("typing.TypedDict"),
|
||||
@@ -393,19 +395,11 @@ impl<'db> ClassBase<'db> {
|
||||
|
||||
ClassBaseDisplay { db, base: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
||||
fn from(value: ClassType<'db>) -> Self {
|
||||
ClassBase::Class(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassBase<'db>> for Type<'db> {
|
||||
fn from(value: ClassBase<'db>) -> Self {
|
||||
match value {
|
||||
pub fn into_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic),
|
||||
ClassBase::Class(class) => class.into(),
|
||||
ClassBase::Class(class) => class.into_type(db),
|
||||
ClassBase::Protocol => Type::SpecialForm(SpecialFormType::Protocol),
|
||||
ClassBase::Generic => Type::SpecialForm(SpecialFormType::Generic),
|
||||
ClassBase::TypedDict => Type::SpecialForm(SpecialFormType::TypedDict),
|
||||
@@ -413,9 +407,9 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<&ClassBase<'db>> for Type<'db> {
|
||||
fn from(value: &ClassBase<'db>) -> Self {
|
||||
Self::from(*value)
|
||||
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
||||
fn from(value: ClassType<'db>) -> Self {
|
||||
ClassBase::Class(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2506,9 +2506,9 @@ pub(super) fn report_possibly_missing_attribute(
|
||||
"Attribute `{attribute}` may be missing on class `{}`",
|
||||
class.name(db),
|
||||
)),
|
||||
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
|
||||
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` may be missing on class `{}`",
|
||||
alias.display(db),
|
||||
instance.alias(db).display(db),
|
||||
)),
|
||||
_ => builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` may be missing on object of type `{}`",
|
||||
|
||||
@@ -416,11 +416,11 @@ impl<'db> super::visitor::TypeVisitor<'db> for AmbiguousClassCollector<'db> {
|
||||
Type::ProtocolInstance(ProtocolInstanceType {
|
||||
inner: Protocol::FromClass(class),
|
||||
..
|
||||
}) => return self.visit_type(db, Type::from(class)),
|
||||
}) => return self.visit_type(db, class.into_type(db)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let visitor::TypeKind::NonAtomic(t) = visitor::TypeKind::from(ty) {
|
||||
if let visitor::TypeKind::NonAtomic(t) = visitor::TypeKind::from_type(db, ty) {
|
||||
if !self.visited_types.borrow_mut().insert(ty) {
|
||||
// If we have already seen this type, we can skip it.
|
||||
return;
|
||||
@@ -599,7 +599,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
|
||||
(ClassType::NonGeneric(class), _) => {
|
||||
class.display_with(self.db, self.settings.clone()).fmt_detailed(f)
|
||||
},
|
||||
(ClassType::Generic(alias), _) => alias.display_with(self.db, self.settings.clone()).fmt_detailed(f),
|
||||
(ClassType::Generic(instance), _) => instance.display_with(self.db, self.settings.clone()).fmt_detailed(f),
|
||||
}
|
||||
}
|
||||
Type::ProtocolInstance(protocol) => match protocol.inner {
|
||||
@@ -607,7 +607,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
|
||||
ClassType::NonGeneric(class) => class
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f),
|
||||
ClassType::Generic(alias) => alias
|
||||
ClassType::Generic(instance) => instance
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f),
|
||||
},
|
||||
@@ -653,6 +653,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
|
||||
let mut f = f.with_type(self.ty);
|
||||
f.write_str("<class '")?;
|
||||
generic
|
||||
.alias(self.db)
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(&mut f)?;
|
||||
f.write_str("'>")
|
||||
@@ -667,11 +668,11 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
|
||||
.fmt_detailed(f)?;
|
||||
f.write_char(']')
|
||||
}
|
||||
SubclassOfInner::Class(ClassType::Generic(alias)) => {
|
||||
SubclassOfInner::Class(ClassType::Generic(instance)) => {
|
||||
f.with_type(KnownClass::Type.to_class_literal(self.db))
|
||||
.write_str("type")?;
|
||||
f.write_char('[')?;
|
||||
alias
|
||||
instance
|
||||
.display_with(self.db, self.settings.clone())
|
||||
.fmt_detailed(f)?;
|
||||
f.write_char(']')
|
||||
@@ -861,11 +862,15 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
|
||||
Type::BoundSuper(bound_super) => {
|
||||
f.set_invalid_syntax();
|
||||
f.write_str("<super: ")?;
|
||||
Type::from(bound_super.pivot_class(self.db))
|
||||
bound_super
|
||||
.pivot_class(self.db)
|
||||
.into_type(self.db)
|
||||
.display_with(self.db, self.settings.singleline())
|
||||
.fmt_detailed(f)?;
|
||||
f.write_str(", ")?;
|
||||
Type::from(bound_super.owner(self.db))
|
||||
bound_super
|
||||
.owner(self.db)
|
||||
.into_type(self.db)
|
||||
.display_with(self.db, self.settings.singleline())
|
||||
.fmt_detailed(f)?;
|
||||
f.write_str(">")
|
||||
|
||||
@@ -154,7 +154,7 @@ pub(crate) fn enum_metadata<'db>(
|
||||
.skip(1)
|
||||
.filter_map(ClassBase::into_class)
|
||||
.filter(|class| {
|
||||
!Type::from(*class).is_subtype_of(
|
||||
!class.into_type(db).is_subtype_of(
|
||||
db,
|
||||
KnownClass::Enum.to_subclass_of(db),
|
||||
)
|
||||
|
||||
@@ -1578,7 +1578,7 @@ impl KnownFunction {
|
||||
let mut good_argument = true;
|
||||
let classes = match param_type {
|
||||
Type::ClassLiteral(class) => vec![ClassType::NonGeneric(*class)],
|
||||
Type::GenericAlias(generic_alias) => vec![ClassType::Generic(*generic_alias)],
|
||||
Type::GenericAlias(generic) => vec![ClassType::Generic(generic.alias(db))],
|
||||
Type::Union(union) => {
|
||||
let elements = union.elements(db);
|
||||
let mut classes = Vec::with_capacity(elements.len());
|
||||
@@ -1587,8 +1587,8 @@ impl KnownFunction {
|
||||
Type::ClassLiteral(class) => {
|
||||
classes.push(ClassType::NonGeneric(*class));
|
||||
}
|
||||
Type::GenericAlias(generic_alias) => {
|
||||
classes.push(ClassType::Generic(*generic_alias));
|
||||
Type::GenericAlias(generic) => {
|
||||
classes.push(ClassType::Generic(generic.alias(db)));
|
||||
}
|
||||
_ => {
|
||||
good_argument = false;
|
||||
|
||||
@@ -1609,7 +1609,7 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
// Extract formal_alias if this is a generic class
|
||||
let formal_alias = match formal {
|
||||
Type::NominalInstance(formal_nominal) => {
|
||||
formal_nominal.class(self.db).into_generic_alias()
|
||||
formal_nominal.class(self.db).into_generic_alias(self.db)
|
||||
}
|
||||
// TODO: This will only handle classes that explicit implement a generic protocol
|
||||
// by listing it as a base class. To handle classes that implicitly implement a
|
||||
@@ -1618,7 +1618,7 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
Type::ProtocolInstance(ProtocolInstanceType {
|
||||
inner: Protocol::FromClass(class),
|
||||
..
|
||||
}) => class.into_generic_alias(),
|
||||
}) => class.into_generic_alias(self.db),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ impl<'db> AllMembers<'db> {
|
||||
self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db));
|
||||
}
|
||||
|
||||
Type::GenericAlias(generic_alias) if generic_alias.is_typed_dict(db) => {
|
||||
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => {
|
||||
self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db));
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
@@ -686,7 +686,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
continue;
|
||||
}
|
||||
Type::ClassLiteral(class) => ClassType::NonGeneric(*class),
|
||||
Type::GenericAlias(class) => ClassType::Generic(*class),
|
||||
Type::GenericAlias(generic) => ClassType::Generic(generic.alias(self.db())),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
@@ -7485,9 +7500,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
}
|
||||
|
||||
let class_type =
|
||||
class_literal.apply_specialization(self.db(), |_| builder.build(generic_context));
|
||||
class_literal.apply_specialization(self.db(), |_| builder.build(generic_context), None);
|
||||
|
||||
Type::from(class_type).to_instance(self.db())
|
||||
class_type.into_type(self.db()).to_instance(self.db())
|
||||
}
|
||||
|
||||
/// Infer the type of the `iter` expression of the first comprehension.
|
||||
@@ -8070,7 +8085,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
let class = match callable_type {
|
||||
Type::ClassLiteral(class) => Some(ClassType::NonGeneric(class)),
|
||||
Type::GenericAlias(generic) => Some(ClassType::Generic(generic)),
|
||||
Type::GenericAlias(generic) => Some(ClassType::Generic(generic.alias(self.db()))),
|
||||
Type::SubclassOf(subclass) => subclass.subclass_of().into_class(),
|
||||
_ => None,
|
||||
};
|
||||
@@ -9132,9 +9147,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
"Class `{}` has no attribute `{attr_name}`",
|
||||
class.name(db),
|
||||
)),
|
||||
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
|
||||
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
|
||||
"Class `{}` has no attribute `{attr_name}`",
|
||||
alias.display(db),
|
||||
instance.alias(db).display(db),
|
||||
)),
|
||||
Type::FunctionLiteral(function) => builder.into_diagnostic(format_args!(
|
||||
"Function `{}` has no attribute `{attr_name}`",
|
||||
@@ -10794,6 +10809,22 @@ 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());
|
||||
|
||||
// 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 Some(instance) = value_ty.as_generic_alias() {
|
||||
let return_ty = self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
// alias.binding_context(self.db()),
|
||||
instance.binding_context(self.db()),
|
||||
false,
|
||||
);
|
||||
|
||||
return return_ty;
|
||||
}
|
||||
|
||||
self.infer_subscript_load_impl(value_ty, subscript)
|
||||
}
|
||||
|
||||
@@ -10830,9 +10861,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
}
|
||||
}
|
||||
|
||||
let tuple_generic_alias = |db: &'db dyn Db, tuple: Option<TupleType<'db>>| {
|
||||
let db = self.db();
|
||||
let typevar_binding_context = self.typevar_binding_context;
|
||||
let tuple_generic_alias = |tuple: Option<TupleType<'db>>| {
|
||||
let tuple = tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()));
|
||||
Type::from(tuple.to_class_type(db))
|
||||
tuple
|
||||
.to_class_type(db, typevar_binding_context)
|
||||
.into_type(db)
|
||||
};
|
||||
|
||||
match value_ty {
|
||||
@@ -10844,11 +10879,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// updating all of the subscript logic below to use custom callables for all of the _other_
|
||||
// special cases, too.
|
||||
if class.is_tuple(self.db()) {
|
||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||
return tuple_generic_alias(self.infer_tuple_type_expression(slice));
|
||||
} 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),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -10872,7 +10907,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
}
|
||||
}
|
||||
Type::SpecialForm(SpecialFormType::Tuple) => {
|
||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||
return tuple_generic_alias(self.infer_tuple_type_expression(slice));
|
||||
}
|
||||
Type::SpecialForm(SpecialFormType::Literal) => {
|
||||
match self.infer_literal_parameter_type(slice) {
|
||||
@@ -10926,9 +10961,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 +10995,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 +11008,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
db,
|
||||
None,
|
||||
Ok(UnionType::from_elements(db, elements)),
|
||||
typevar_binding_context,
|
||||
),
|
||||
));
|
||||
|
||||
@@ -10995,7 +11033,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 +11042,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(
|
||||
@@ -11033,8 +11073,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
.expect("A known stdlib class is available");
|
||||
|
||||
return class
|
||||
.to_specialized_class_type(self.db(), [element_ty])
|
||||
.map(Type::from)
|
||||
.to_specialized_class_type(
|
||||
self.db(),
|
||||
[element_ty],
|
||||
self.typevar_binding_context,
|
||||
)
|
||||
.map(|class_type| class_type.into_type(self.db()))
|
||||
.unwrap_or_else(Type::unknown);
|
||||
}
|
||||
// `typing` special forms with two generic arguments
|
||||
@@ -11091,12 +11135,40 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
.expect("Stdlib class available");
|
||||
|
||||
return class
|
||||
.to_specialized_class_type(self.db(), [first_ty, second_ty])
|
||||
.map(Type::from)
|
||||
.to_specialized_class_type(
|
||||
self.db(),
|
||||
[first_ty, second_ty],
|
||||
self.typevar_binding_context,
|
||||
)
|
||||
.map(|class_type| class_type.into_type(self.db()))
|
||||
.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,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -11114,10 +11186,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
generic_context: GenericContext<'db>,
|
||||
) -> Type<'db> {
|
||||
let db = self.db();
|
||||
let typevar_binding_context = self.typevar_binding_context;
|
||||
let specialize = |types: &[Option<Type<'db>>]| {
|
||||
Type::from(generic_class.apply_specialization(db, |_| {
|
||||
generic_context.specialize_partial(db, types.iter().copied())
|
||||
}))
|
||||
generic_class
|
||||
.apply_specialization(
|
||||
db,
|
||||
|_| generic_context.specialize_partial(db, types.iter().copied()),
|
||||
typevar_binding_context,
|
||||
)
|
||||
.into_type(db)
|
||||
};
|
||||
|
||||
self.infer_explicit_callable_specialization(
|
||||
|
||||
@@ -2,6 +2,9 @@ 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::class::GenericAliasInstance;
|
||||
use crate::types::diagnostic::{
|
||||
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
|
||||
report_invalid_arguments_to_callable,
|
||||
@@ -11,9 +14,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, GenericAlias, GenericContext, IntersectionBuilder,
|
||||
KnownClass, KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType,
|
||||
SubclassOfType, Type, TypeAliasType, TypeContext, TypeInContext, TypeIsType, TypeMapping,
|
||||
UnionBuilder, UnionType, todo_type,
|
||||
};
|
||||
|
||||
/// Type expressions
|
||||
@@ -710,13 +714,20 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
match class_literal.generic_context(self.db()) {
|
||||
Some(generic_context) => {
|
||||
let db = self.db();
|
||||
let typevar_binding_context = self.typevar_binding_context;
|
||||
let specialize = |types: &[Option<Type<'db>>]| {
|
||||
SubclassOfType::from(
|
||||
db,
|
||||
class_literal.apply_specialization(db, |_| {
|
||||
generic_context
|
||||
.specialize_partial(db, types.iter().copied())
|
||||
}),
|
||||
class_literal.apply_specialization(
|
||||
db,
|
||||
|_| {
|
||||
generic_context.specialize_partial(
|
||||
db,
|
||||
types.iter().copied(),
|
||||
)
|
||||
},
|
||||
typevar_binding_context,
|
||||
),
|
||||
)
|
||||
};
|
||||
self.infer_explicit_callable_specialization(
|
||||
@@ -750,6 +761,83 @@ 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(GenericAliasInstance::new(
|
||||
db,
|
||||
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 +928,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 +970,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 +980,35 @@ 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) => 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 +1049,12 @@ 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(instance) => self.infer_explicitly_specialized_type_alias(
|
||||
subscript,
|
||||
value_ty,
|
||||
instance.binding_context(self.db()),
|
||||
true,
|
||||
),
|
||||
Type::StringLiteral(_) => {
|
||||
self.infer_type_expression(slice);
|
||||
// For stringified TypeAlias; remove once properly supported
|
||||
|
||||
@@ -207,13 +207,13 @@ pub(super) fn walk_nominal_instance_type<'db, V: super::visitor::TypeVisitor<'db
|
||||
nominal: NominalInstanceType<'db>,
|
||||
visitor: &V,
|
||||
) {
|
||||
visitor.visit_type(db, nominal.class(db).into());
|
||||
visitor.visit_type(db, nominal.class(db).into_type(db));
|
||||
}
|
||||
|
||||
impl<'db> NominalInstanceType<'db> {
|
||||
pub(super) fn class(&self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
match self.0 {
|
||||
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db),
|
||||
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db, None),
|
||||
NominalInstanceInner::NonTuple(class) => class,
|
||||
NominalInstanceInner::Object => KnownClass::Object
|
||||
.try_to_class_literal(db)
|
||||
@@ -224,7 +224,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||
|
||||
pub(super) fn class_literal(&self, db: &'db dyn Db) -> ClassLiteral<'db> {
|
||||
let class = match self.0 {
|
||||
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db),
|
||||
NominalInstanceInner::ExactTuple(tuple) => tuple.to_class_type(db, None),
|
||||
NominalInstanceInner::NonTuple(class) => class,
|
||||
NominalInstanceInner::Object => {
|
||||
return KnownClass::Object
|
||||
@@ -279,7 +279,7 @@ impl<'db> NominalInstanceType<'db> {
|
||||
}
|
||||
KnownClass::Tuple => Some(
|
||||
class
|
||||
.into_generic_alias()
|
||||
.into_generic_alias(db)
|
||||
.and_then(|alias| {
|
||||
Some(Cow::Borrowed(alias.specialization(db).tuple(db)?))
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<'db> Mro<'db> {
|
||||
class_literal: ClassLiteral<'db>,
|
||||
specialization: Option<Specialization<'db>>,
|
||||
) -> Result<Self, MroError<'db>> {
|
||||
let class = class_literal.apply_optional_specialization(db, specialization);
|
||||
let class = class_literal.apply_optional_specialization(db, specialization, None);
|
||||
// Special-case `NotImplementedType`: typeshed says that it inherits from `Any`,
|
||||
// but this causes more problems than it fixes.
|
||||
if class_literal.is_known(db, KnownClass::NotImplementedType) {
|
||||
@@ -412,10 +412,11 @@ impl<'db> Iterator for MroIterator<'db> {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.first_element_yielded {
|
||||
self.first_element_yielded = true;
|
||||
return Some(ClassBase::Class(
|
||||
self.class
|
||||
.apply_optional_specialization(self.db, self.specialization),
|
||||
));
|
||||
return Some(ClassBase::Class(self.class.apply_optional_specialization(
|
||||
self.db,
|
||||
self.specialization,
|
||||
None,
|
||||
)));
|
||||
}
|
||||
self.full_mro_except_first_element().next()
|
||||
}
|
||||
|
||||
@@ -144,6 +144,10 @@ impl<'db> ProtocolClass<'db> {
|
||||
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
self.0.into_type(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> Deref for ProtocolClass<'db> {
|
||||
@@ -154,12 +158,6 @@ impl<'db> Deref for ProtocolClass<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ProtocolClass<'db>> for Type<'db> {
|
||||
fn from(value: ProtocolClass<'db>) -> Self {
|
||||
Self::from(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The interface of a protocol: the members of that protocol, and the types of those members.
|
||||
///
|
||||
/// # Ordering
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(super) fn walk_subclass_of_type<'db, V: super::visitor::TypeVisitor<'db> + ?
|
||||
subclass_of: SubclassOfType<'db>,
|
||||
visitor: &V,
|
||||
) {
|
||||
visitor.visit_type(db, Type::from(subclass_of.subclass_of));
|
||||
visitor.visit_type(db, subclass_of.subclass_of.into_type(db));
|
||||
}
|
||||
|
||||
impl<'db> SubclassOfType<'db> {
|
||||
@@ -47,7 +47,7 @@ impl<'db> SubclassOfType<'db> {
|
||||
SubclassOfInner::Dynamic(_) => Type::SubclassOf(Self { subclass_of }),
|
||||
SubclassOfInner::Class(class) => {
|
||||
if class.is_final(db) {
|
||||
Type::from(class)
|
||||
class.into_type(db)
|
||||
} else if class.is_object(db) {
|
||||
KnownClass::Type.to_instance(db)
|
||||
} else {
|
||||
@@ -129,7 +129,9 @@ impl<'db> SubclassOfType<'db> {
|
||||
name: &str,
|
||||
policy: MemberLookupPolicy,
|
||||
) -> Option<PlaceAndQualifiers<'db>> {
|
||||
Type::from(self.subclass_of).find_name_in_mro_with_policy(db, name, policy)
|
||||
self.subclass_of
|
||||
.into_type(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
}
|
||||
|
||||
/// Return `true` if `self` has a certain relation to `other`.
|
||||
@@ -271,11 +273,18 @@ impl<'db> SubclassOfInner<'db> {
|
||||
match ty {
|
||||
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
|
||||
Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
|
||||
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
|
||||
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic.alias(db)))),
|
||||
Type::SpecialForm(SpecialFormType::Any) => Some(Self::Dynamic(DynamicType::Any)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
SubclassOfInner::Dynamic(dynamic) => Type::Dynamic(dynamic),
|
||||
SubclassOfInner::Class(class) => class.into_type(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassType<'db>> for SubclassOfInner<'db> {
|
||||
@@ -295,12 +304,3 @@ impl<'db> From<ProtocolClass<'db>> for SubclassOfInner<'db> {
|
||||
SubclassOfInner::Class(*value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<SubclassOfInner<'db>> for Type<'db> {
|
||||
fn from(value: SubclassOfInner<'db>) -> Self {
|
||||
match value {
|
||||
SubclassOfInner::Dynamic(dynamic) => Type::Dynamic(dynamic),
|
||||
SubclassOfInner::Class(class) => class.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,19 +202,27 @@ impl<'db> TupleType<'db> {
|
||||
// `static-frame` as part of a mypy_primer run! This is because it's called
|
||||
// from `NominalInstanceType::class()`, which is a very hot method.
|
||||
#[salsa::tracked(cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
pub(crate) fn to_class_type(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
) -> ClassType<'db> {
|
||||
let tuple_class = KnownClass::Tuple
|
||||
.try_to_class_literal(db)
|
||||
.expect("Typeshed should always have a `tuple` class in `builtins.pyi`");
|
||||
|
||||
tuple_class.apply_specialization(db, |generic_context| {
|
||||
if generic_context.variables(db).len() == 1 {
|
||||
let element_type = self.tuple(db).homogeneous_element_type(db);
|
||||
generic_context.specialize_tuple(db, element_type, self)
|
||||
} else {
|
||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
||||
}
|
||||
})
|
||||
tuple_class.apply_specialization(
|
||||
db,
|
||||
|generic_context| {
|
||||
if generic_context.variables(db).len() == 1 {
|
||||
let element_type = self.tuple(db).homogeneous_element_type(db);
|
||||
generic_context.specialize_tuple(db, element_type, self)
|
||||
} else {
|
||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
||||
}
|
||||
},
|
||||
binding_context,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a normalized version of `self`.
|
||||
@@ -294,18 +302,23 @@ fn to_class_type_cycle_initial<'db>(
|
||||
db: &'db dyn Db,
|
||||
_id: salsa::Id,
|
||||
self_: TupleType<'db>,
|
||||
binding_context: Option<Definition<'db>>,
|
||||
) -> ClassType<'db> {
|
||||
let tuple_class = KnownClass::Tuple
|
||||
.try_to_class_literal(db)
|
||||
.expect("Typeshed should always have a `tuple` class in `builtins.pyi`");
|
||||
|
||||
tuple_class.apply_specialization(db, |generic_context| {
|
||||
if generic_context.variables(db).len() == 1 {
|
||||
generic_context.specialize_tuple(db, Type::Never, self_)
|
||||
} else {
|
||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
||||
}
|
||||
})
|
||||
tuple_class.apply_specialization(
|
||||
db,
|
||||
|generic_context| {
|
||||
if generic_context.variables(db).len() == 1 {
|
||||
generic_context.specialize_tuple(db, Type::Never, self_)
|
||||
} else {
|
||||
generic_context.default_specialization(db, Some(KnownClass::Tuple))
|
||||
}
|
||||
},
|
||||
binding_context,
|
||||
)
|
||||
}
|
||||
|
||||
/// A tuple spec describes the contents of a tuple type, which might be fixed- or variable-length.
|
||||
|
||||
@@ -253,7 +253,7 @@ pub(crate) fn walk_typed_dict_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
typed_dict: TypedDictType<'db>,
|
||||
visitor: &V,
|
||||
) {
|
||||
visitor.visit_type(db, typed_dict.defining_class.into());
|
||||
visitor.visit_type(db, typed_dict.defining_class.into_type(db));
|
||||
}
|
||||
|
||||
pub(super) fn typed_dict_params_from_class_def(class_stmt: &StmtClassDef) -> TypedDictParams {
|
||||
|
||||
@@ -144,8 +144,8 @@ pub(super) enum TypeKind<'db> {
|
||||
NonAtomic(NonAtomicType<'db>),
|
||||
}
|
||||
|
||||
impl<'db> From<Type<'db>> for TypeKind<'db> {
|
||||
fn from(ty: Type<'db>) -> Self {
|
||||
impl<'db> TypeKind<'db> {
|
||||
pub(super) fn from_type(db: &'db dyn Db, ty: Type<'db>) -> Self {
|
||||
match ty {
|
||||
Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
@@ -180,7 +180,9 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
|
||||
TypeKind::NonAtomic(NonAtomicType::MethodWrapper(method_wrapper))
|
||||
}
|
||||
Type::Callable(callable) => TypeKind::NonAtomic(NonAtomicType::Callable(callable)),
|
||||
Type::GenericAlias(alias) => TypeKind::NonAtomic(NonAtomicType::GenericAlias(alias)),
|
||||
Type::GenericAlias(instance) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::GenericAlias(instance.alias(db)))
|
||||
}
|
||||
Type::KnownInstance(known_instance) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::KnownInstance(known_instance))
|
||||
}
|
||||
@@ -260,7 +262,7 @@ pub(crate) fn walk_type_with_recursion_guard<'db>(
|
||||
visitor: &impl TypeVisitor<'db>,
|
||||
recursion_guard: &TypeCollector<'db>,
|
||||
) {
|
||||
match TypeKind::from(ty) {
|
||||
match TypeKind::from_type(db, ty) {
|
||||
TypeKind::Atomic => {}
|
||||
TypeKind::NonAtomic(non_atomic_type) => {
|
||||
if recursion_guard.type_was_already_seen(ty) {
|
||||
@@ -349,7 +351,7 @@ fn specialization_depth(db: &dyn Db, ty: Type<'_>) -> usize {
|
||||
}
|
||||
|
||||
fn visit_type(&self, db: &'db dyn Db, ty: Type<'db>) {
|
||||
match TypeKind::from(ty) {
|
||||
match TypeKind::from_type(db, ty) {
|
||||
TypeKind::Atomic => {
|
||||
if ty.is_divergent() {
|
||||
self.max_depth.set(usize::MAX);
|
||||
|
||||
Reference in New Issue
Block a user