mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00
[red-knot] infer types for PEP695 typevars (#14182)
## Summary Create definitions and infer types for PEP 695 type variables. This just gives us the type of the type variable itself (the type of `T` as a runtime object in the body of `def f[T](): ...`), with special handling for its attributes `__name__`, `__bound__`, `__constraints__`, and `__default__`. Mostly the support for these attributes exists because it is easy to implement and allows testing that we are internally representing the typevar correctly. This PR doesn't yet have support for interpreting a typevar as a type annotation, which is of course the primary use of a typevar. But the information we store in the typevar's type in this PR gives us everything we need to handle it correctly in a future PR when the typevar appears in an annotation. ## Test Plan Added mdtest.
This commit is contained in:
parent
1430f21283
commit
645ce7e5ec
8 changed files with 395 additions and 80 deletions
|
@ -6,13 +6,9 @@ Basic PEP 695 generics
|
||||||
|
|
||||||
```py
|
```py
|
||||||
class MyBox[T]:
|
class MyBox[T]:
|
||||||
# TODO: `T` is defined here
|
|
||||||
# error: [unresolved-reference] "Name `T` used when not defined"
|
|
||||||
data: T
|
data: T
|
||||||
box_model_number = 695
|
box_model_number = 695
|
||||||
|
|
||||||
# TODO: `T` is defined here
|
|
||||||
# error: [unresolved-reference] "Name `T` used when not defined"
|
|
||||||
def __init__(self, data: T):
|
def __init__(self, data: T):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
@ -31,17 +27,12 @@ reveal_type(MyBox.box_model_number) # revealed: Literal[695]
|
||||||
|
|
||||||
```py
|
```py
|
||||||
class MyBox[T]:
|
class MyBox[T]:
|
||||||
# TODO: `T` is defined here
|
|
||||||
# error: [unresolved-reference] "Name `T` used when not defined"
|
|
||||||
data: T
|
data: T
|
||||||
|
|
||||||
# TODO: `T` is defined here
|
|
||||||
# error: [unresolved-reference] "Name `T` used when not defined"
|
|
||||||
def __init__(self, data: T):
|
def __init__(self, data: T):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
# TODO not error on the subscripting or the use of type param
|
# TODO not error on the subscripting
|
||||||
# error: [unresolved-reference] "Name `T` used when not defined"
|
|
||||||
# error: [non-subscriptable]
|
# error: [non-subscriptable]
|
||||||
class MySecureBox[T](MyBox[T]): ...
|
class MySecureBox[T](MyBox[T]): ...
|
||||||
|
|
||||||
|
@ -66,3 +57,55 @@ class S[T](Seq[S]): ... # error: [non-subscriptable]
|
||||||
|
|
||||||
reveal_type(S) # revealed: Literal[S]
|
reveal_type(S) # revealed: Literal[S]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Type params
|
||||||
|
|
||||||
|
A PEP695 type variable defines a value of type `typing.TypeVar` with attributes `__name__`,
|
||||||
|
`__bounds__`, `__constraints__`, and `__default__` (the latter three all lazily evaluated):
|
||||||
|
|
||||||
|
```py
|
||||||
|
def f[T, U: A, V: (A, B), W = A, X: A = A1]():
|
||||||
|
reveal_type(T) # revealed: TypeVar
|
||||||
|
reveal_type(T.__name__) # revealed: Literal["T"]
|
||||||
|
reveal_type(T.__bound__) # revealed: None
|
||||||
|
reveal_type(T.__constraints__) # revealed: tuple[()]
|
||||||
|
reveal_type(T.__default__) # revealed: NoDefault
|
||||||
|
|
||||||
|
reveal_type(U) # revealed: TypeVar
|
||||||
|
reveal_type(U.__name__) # revealed: Literal["U"]
|
||||||
|
reveal_type(U.__bound__) # revealed: type[A]
|
||||||
|
reveal_type(U.__constraints__) # revealed: tuple[()]
|
||||||
|
reveal_type(U.__default__) # revealed: NoDefault
|
||||||
|
|
||||||
|
reveal_type(V) # revealed: TypeVar
|
||||||
|
reveal_type(V.__name__) # revealed: Literal["V"]
|
||||||
|
reveal_type(V.__bound__) # revealed: None
|
||||||
|
reveal_type(V.__constraints__) # revealed: tuple[type[A], type[B]]
|
||||||
|
reveal_type(V.__default__) # revealed: NoDefault
|
||||||
|
|
||||||
|
reveal_type(W) # revealed: TypeVar
|
||||||
|
reveal_type(W.__name__) # revealed: Literal["W"]
|
||||||
|
reveal_type(W.__bound__) # revealed: None
|
||||||
|
reveal_type(W.__constraints__) # revealed: tuple[()]
|
||||||
|
reveal_type(W.__default__) # revealed: type[A]
|
||||||
|
|
||||||
|
reveal_type(X) # revealed: TypeVar
|
||||||
|
reveal_type(X.__name__) # revealed: Literal["X"]
|
||||||
|
reveal_type(X.__bound__) # revealed: type[A]
|
||||||
|
reveal_type(X.__constraints__) # revealed: tuple[()]
|
||||||
|
reveal_type(X.__default__) # revealed: type[A1]
|
||||||
|
|
||||||
|
class A: ...
|
||||||
|
class B: ...
|
||||||
|
class A1(A): ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Minimum two constraints
|
||||||
|
|
||||||
|
A typevar with less than two constraints emits a diagnostic and is treated as unconstrained:
|
||||||
|
|
||||||
|
```py
|
||||||
|
# error: [invalid-typevar-constraints] "TypeVar must have at least two constrained types"
|
||||||
|
def f[T: (int,)]():
|
||||||
|
reveal_type(T.__constraints__) # revealed: tuple[()]
|
||||||
|
```
|
||||||
|
|
|
@ -373,6 +373,11 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||||
if let Some(default) = default {
|
if let Some(default) = default {
|
||||||
self.visit_expr(default);
|
self.visit_expr(default);
|
||||||
}
|
}
|
||||||
|
match type_param {
|
||||||
|
ast::TypeParam::TypeVar(node) => self.add_definition(symbol, node),
|
||||||
|
ast::TypeParam::ParamSpec(node) => self.add_definition(symbol, node),
|
||||||
|
ast::TypeParam::TypeVarTuple(node) => self.add_definition(symbol, node),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,9 @@ pub(crate) enum DefinitionNodeRef<'a> {
|
||||||
WithItem(WithItemDefinitionNodeRef<'a>),
|
WithItem(WithItemDefinitionNodeRef<'a>),
|
||||||
MatchPattern(MatchPatternDefinitionNodeRef<'a>),
|
MatchPattern(MatchPatternDefinitionNodeRef<'a>),
|
||||||
ExceptHandler(ExceptHandlerDefinitionNodeRef<'a>),
|
ExceptHandler(ExceptHandlerDefinitionNodeRef<'a>),
|
||||||
|
TypeVar(&'a ast::TypeParamTypeVar),
|
||||||
|
ParamSpec(&'a ast::TypeParamParamSpec),
|
||||||
|
TypeVarTuple(&'a ast::TypeParamTypeVarTuple),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a ast::StmtFunctionDef> for DefinitionNodeRef<'a> {
|
impl<'a> From<&'a ast::StmtFunctionDef> for DefinitionNodeRef<'a> {
|
||||||
|
@ -130,6 +133,24 @@ impl<'a> From<&'a ast::Alias> for DefinitionNodeRef<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a ast::TypeParamTypeVar> for DefinitionNodeRef<'a> {
|
||||||
|
fn from(value: &'a ast::TypeParamTypeVar) -> Self {
|
||||||
|
Self::TypeVar(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a ast::TypeParamParamSpec> for DefinitionNodeRef<'a> {
|
||||||
|
fn from(value: &'a ast::TypeParamParamSpec) -> Self {
|
||||||
|
Self::ParamSpec(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a ast::TypeParamTypeVarTuple> for DefinitionNodeRef<'a> {
|
||||||
|
fn from(value: &'a ast::TypeParamTypeVarTuple) -> Self {
|
||||||
|
Self::TypeVarTuple(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<ImportFromDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
|
impl<'a> From<ImportFromDefinitionNodeRef<'a>> for DefinitionNodeRef<'a> {
|
||||||
fn from(node_ref: ImportFromDefinitionNodeRef<'a>) -> Self {
|
fn from(node_ref: ImportFromDefinitionNodeRef<'a>) -> Self {
|
||||||
Self::ImportFrom(node_ref)
|
Self::ImportFrom(node_ref)
|
||||||
|
@ -317,6 +338,15 @@ impl<'db> DefinitionNodeRef<'db> {
|
||||||
handler: AstNodeRef::new(parsed, handler),
|
handler: AstNodeRef::new(parsed, handler),
|
||||||
is_star,
|
is_star,
|
||||||
}),
|
}),
|
||||||
|
DefinitionNodeRef::TypeVar(node) => {
|
||||||
|
DefinitionKind::TypeVar(AstNodeRef::new(parsed, node))
|
||||||
|
}
|
||||||
|
DefinitionNodeRef::ParamSpec(node) => {
|
||||||
|
DefinitionKind::ParamSpec(AstNodeRef::new(parsed, node))
|
||||||
|
}
|
||||||
|
DefinitionNodeRef::TypeVarTuple(node) => {
|
||||||
|
DefinitionKind::TypeVarTuple(AstNodeRef::new(parsed, node))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,6 +386,9 @@ impl<'db> DefinitionNodeRef<'db> {
|
||||||
identifier.into()
|
identifier.into()
|
||||||
}
|
}
|
||||||
Self::ExceptHandler(ExceptHandlerDefinitionNodeRef { handler, .. }) => handler.into(),
|
Self::ExceptHandler(ExceptHandlerDefinitionNodeRef { handler, .. }) => handler.into(),
|
||||||
|
Self::TypeVar(node) => node.into(),
|
||||||
|
Self::ParamSpec(node) => node.into(),
|
||||||
|
Self::TypeVarTuple(node) => node.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,6 +445,9 @@ pub enum DefinitionKind<'db> {
|
||||||
WithItem(WithItemDefinitionKind),
|
WithItem(WithItemDefinitionKind),
|
||||||
MatchPattern(MatchPatternDefinitionKind),
|
MatchPattern(MatchPatternDefinitionKind),
|
||||||
ExceptHandler(ExceptHandlerDefinitionKind),
|
ExceptHandler(ExceptHandlerDefinitionKind),
|
||||||
|
TypeVar(AstNodeRef<ast::TypeParamTypeVar>),
|
||||||
|
ParamSpec(AstNodeRef<ast::TypeParamParamSpec>),
|
||||||
|
TypeVarTuple(AstNodeRef<ast::TypeParamTypeVarTuple>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefinitionKind<'_> {
|
impl DefinitionKind<'_> {
|
||||||
|
@ -421,7 +457,10 @@ impl DefinitionKind<'_> {
|
||||||
DefinitionKind::Function(_)
|
DefinitionKind::Function(_)
|
||||||
| DefinitionKind::Class(_)
|
| DefinitionKind::Class(_)
|
||||||
| DefinitionKind::Import(_)
|
| DefinitionKind::Import(_)
|
||||||
| DefinitionKind::ImportFrom(_) => DefinitionCategory::DeclarationAndBinding,
|
| DefinitionKind::ImportFrom(_)
|
||||||
|
| DefinitionKind::TypeVar(_)
|
||||||
|
| DefinitionKind::ParamSpec(_)
|
||||||
|
| DefinitionKind::TypeVarTuple(_) => DefinitionCategory::DeclarationAndBinding,
|
||||||
// a parameter always binds a value, but is only a declaration if annotated
|
// a parameter always binds a value, but is only a declaration if annotated
|
||||||
DefinitionKind::Parameter(parameter) => {
|
DefinitionKind::Parameter(parameter) => {
|
||||||
if parameter.annotation.is_some() {
|
if parameter.annotation.is_some() {
|
||||||
|
@ -696,3 +735,21 @@ impl From<&ast::ExceptHandlerExceptHandler> for DefinitionNodeKey {
|
||||||
Self(NodeKey::from_node(handler))
|
Self(NodeKey::from_node(handler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&ast::TypeParamTypeVar> for DefinitionNodeKey {
|
||||||
|
fn from(value: &ast::TypeParamTypeVar) -> Self {
|
||||||
|
Self(NodeKey::from_node(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ast::TypeParamParamSpec> for DefinitionNodeKey {
|
||||||
|
fn from(value: &ast::TypeParamParamSpec) -> Self {
|
||||||
|
Self(NodeKey::from_node(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ast::TypeParamTypeVarTuple> for DefinitionNodeKey {
|
||||||
|
fn from(value: &ast::TypeParamTypeVarTuple) -> Self {
|
||||||
|
Self(NodeKey::from_node(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -307,7 +307,7 @@ fn declarations_ty<'db>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a type: a set of possible values at runtime.
|
/// Representation of a type: a set of possible values at runtime.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub enum Type<'db> {
|
pub enum Type<'db> {
|
||||||
/// The dynamic type: a statically unknown set of values
|
/// The dynamic type: a statically unknown set of values
|
||||||
Any,
|
Any,
|
||||||
|
@ -336,7 +336,7 @@ pub enum Type<'db> {
|
||||||
/// The set of Python objects with the given class in their __class__'s method resolution order
|
/// The set of Python objects with the given class in their __class__'s method resolution order
|
||||||
Instance(InstanceType<'db>),
|
Instance(InstanceType<'db>),
|
||||||
/// A single Python object that requires special treatment in the type system
|
/// A single Python object that requires special treatment in the type system
|
||||||
KnownInstance(KnownInstanceType),
|
KnownInstance(KnownInstanceType<'db>),
|
||||||
/// The set of objects in any of the types in the union
|
/// The set of objects in any of the types in the union
|
||||||
Union(UnionType<'db>),
|
Union(UnionType<'db>),
|
||||||
/// The set of objects in all of the types in the intersection
|
/// The set of objects in all of the types in the intersection
|
||||||
|
@ -657,17 +657,23 @@ impl<'db> Type<'db> {
|
||||||
// TODO: Once we have support for final classes, we can establish that
|
// TODO: Once we have support for final classes, we can establish that
|
||||||
// `Type::SubclassOf('FinalClass')` is equivalent to `Type::ClassLiteral('FinalClass')`.
|
// `Type::SubclassOf('FinalClass')` is equivalent to `Type::ClassLiteral('FinalClass')`.
|
||||||
|
|
||||||
// TODO: The following is a workaround that is required to unify the two different
|
// TODO: The following is a workaround that is required to unify the two different versions
|
||||||
// versions of `NoneType` in typeshed. This should not be required anymore once we
|
// of `NoneType` and `NoDefaultType` in typeshed. This should not be required anymore once
|
||||||
// understand `sys.version_info` branches.
|
// we understand `sys.version_info` branches.
|
||||||
self == other
|
self == other
|
||||||
|| matches!((self, other),
|
|| matches!((self, other),
|
||||||
(
|
(
|
||||||
Type::Instance(InstanceType { class: self_class }),
|
Type::Instance(InstanceType { class: self_class }),
|
||||||
Type::Instance(InstanceType { class: target_class })
|
Type::Instance(InstanceType { class: target_class })
|
||||||
)
|
)
|
||||||
if self_class.is_known(db, KnownClass::NoneType) &&
|
if (
|
||||||
target_class.is_known(db, KnownClass::NoneType))
|
self_class.is_known(db, KnownClass::NoneType) &&
|
||||||
|
target_class.is_known(db, KnownClass::NoneType)
|
||||||
|
) || (
|
||||||
|
self_class.is_known(db, KnownClass::NoDefaultType) &&
|
||||||
|
target_class.is_known(db, KnownClass::NoDefaultType)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if this type and `other` have no common elements.
|
/// Return true if this type and `other` have no common elements.
|
||||||
|
@ -907,8 +913,11 @@ impl<'db> Type<'db> {
|
||||||
| Type::ModuleLiteral(..)
|
| Type::ModuleLiteral(..)
|
||||||
| Type::KnownInstance(..) => true,
|
| Type::KnownInstance(..) => true,
|
||||||
Type::Instance(InstanceType { class }) => {
|
Type::Instance(InstanceType { class }) => {
|
||||||
// TODO some more instance types can be singleton types (EllipsisType, NotImplementedType)
|
if let Some(known_class) = class.known(db) {
|
||||||
matches!(class.known(db), Some(KnownClass::NoneType))
|
known_class.is_singleton()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Type::Tuple(..) => {
|
Type::Tuple(..) => {
|
||||||
// The empty tuple is a singleton on CPython and PyPy, but not on other Python
|
// The empty tuple is a singleton on CPython and PyPy, but not on other Python
|
||||||
|
@ -961,7 +970,7 @@ impl<'db> Type<'db> {
|
||||||
.all(|elem| elem.is_single_valued(db)),
|
.all(|elem| elem.is_single_valued(db)),
|
||||||
|
|
||||||
Type::Instance(InstanceType { class }) => match class.known(db) {
|
Type::Instance(InstanceType { class }) => match class.known(db) {
|
||||||
Some(KnownClass::NoneType) => true,
|
Some(KnownClass::NoneType | KnownClass::NoDefaultType) => true,
|
||||||
Some(
|
Some(
|
||||||
KnownClass::Bool
|
KnownClass::Bool
|
||||||
| KnownClass::Object
|
| KnownClass::Object
|
||||||
|
@ -978,7 +987,8 @@ impl<'db> Type<'db> {
|
||||||
| KnownClass::GenericAlias
|
| KnownClass::GenericAlias
|
||||||
| KnownClass::ModuleType
|
| KnownClass::ModuleType
|
||||||
| KnownClass::FunctionType
|
| KnownClass::FunctionType
|
||||||
| KnownClass::SpecialForm,
|
| KnownClass::SpecialForm
|
||||||
|
| KnownClass::TypeVar,
|
||||||
) => false,
|
) => false,
|
||||||
None => false,
|
None => false,
|
||||||
},
|
},
|
||||||
|
@ -1049,9 +1059,7 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
Type::ClassLiteral(class_ty) => class_ty.member(db, name),
|
Type::ClassLiteral(class_ty) => class_ty.member(db, name),
|
||||||
Type::SubclassOf(subclass_of_ty) => subclass_of_ty.member(db, name),
|
Type::SubclassOf(subclass_of_ty) => subclass_of_ty.member(db, name),
|
||||||
Type::KnownInstance(known_instance) => {
|
Type::KnownInstance(known_instance) => known_instance.member(db, name),
|
||||||
known_instance.instance_fallback(db).member(db, name)
|
|
||||||
}
|
|
||||||
Type::Instance(_) => {
|
Type::Instance(_) => {
|
||||||
// TODO MRO? get_own_instance_member, get_instance_member
|
// TODO MRO? get_own_instance_member, get_instance_member
|
||||||
Type::Todo.into()
|
Type::Todo.into()
|
||||||
|
@ -1442,7 +1450,7 @@ impl<'db> Type<'db> {
|
||||||
Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db),
|
Type::IntLiteral(_) | Type::BooleanLiteral(_) => self.repr(db),
|
||||||
Type::StringLiteral(_) | Type::LiteralString => *self,
|
Type::StringLiteral(_) | Type::LiteralString => *self,
|
||||||
Type::KnownInstance(known_instance) => {
|
Type::KnownInstance(known_instance) => {
|
||||||
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr()))
|
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db)))
|
||||||
}
|
}
|
||||||
// TODO: handle more complex types
|
// TODO: handle more complex types
|
||||||
_ => KnownClass::Str.to_instance(db),
|
_ => KnownClass::Str.to_instance(db),
|
||||||
|
@ -1464,7 +1472,7 @@ impl<'db> Type<'db> {
|
||||||
})),
|
})),
|
||||||
Type::LiteralString => Type::LiteralString,
|
Type::LiteralString => Type::LiteralString,
|
||||||
Type::KnownInstance(known_instance) => {
|
Type::KnownInstance(known_instance) => {
|
||||||
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr()))
|
Type::StringLiteral(StringLiteralType::new(db, known_instance.repr(db)))
|
||||||
}
|
}
|
||||||
// TODO: handle more complex types
|
// TODO: handle more complex types
|
||||||
_ => KnownClass::Str.to_instance(db),
|
_ => KnownClass::Str.to_instance(db),
|
||||||
|
@ -1514,7 +1522,10 @@ pub enum KnownClass {
|
||||||
FunctionType,
|
FunctionType,
|
||||||
// Typeshed
|
// Typeshed
|
||||||
NoneType, // Part of `types` for Python >= 3.10
|
NoneType, // Part of `types` for Python >= 3.10
|
||||||
|
// Typing
|
||||||
SpecialForm,
|
SpecialForm,
|
||||||
|
TypeVar,
|
||||||
|
NoDefaultType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> KnownClass {
|
impl<'db> KnownClass {
|
||||||
|
@ -1537,6 +1548,8 @@ impl<'db> KnownClass {
|
||||||
Self::FunctionType => "FunctionType",
|
Self::FunctionType => "FunctionType",
|
||||||
Self::NoneType => "NoneType",
|
Self::NoneType => "NoneType",
|
||||||
Self::SpecialForm => "_SpecialForm",
|
Self::SpecialForm => "_SpecialForm",
|
||||||
|
Self::TypeVar => "TypeVar",
|
||||||
|
Self::NoDefaultType => "_NoDefaultType",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1561,8 +1574,34 @@ impl<'db> KnownClass {
|
||||||
Self::GenericAlias | Self::ModuleType | Self::FunctionType => {
|
Self::GenericAlias | Self::ModuleType | Self::FunctionType => {
|
||||||
types_symbol(db, self.as_str()).unwrap_or_unknown()
|
types_symbol(db, self.as_str()).unwrap_or_unknown()
|
||||||
}
|
}
|
||||||
Self::SpecialForm => typing_symbol(db, self.as_str()).unwrap_or_unknown(),
|
|
||||||
Self::NoneType => typeshed_symbol(db, self.as_str()).unwrap_or_unknown(),
|
Self::NoneType => typeshed_symbol(db, self.as_str()).unwrap_or_unknown(),
|
||||||
|
Self::SpecialForm => typing_symbol(db, self.as_str()).unwrap_or_unknown(),
|
||||||
|
Self::TypeVar => typing_symbol(db, self.as_str()).unwrap_or_unknown(),
|
||||||
|
Self::NoDefaultType => typing_extensions_symbol(db, self.as_str()).unwrap_or_unknown(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_singleton(self) -> bool {
|
||||||
|
// TODO there are other singleton types (EllipsisType, NotImplementedType)
|
||||||
|
match self {
|
||||||
|
Self::NoneType | Self::NoDefaultType => true,
|
||||||
|
Self::Bool
|
||||||
|
| Self::Object
|
||||||
|
| Self::Bytes
|
||||||
|
| Self::Tuple
|
||||||
|
| Self::Int
|
||||||
|
| Self::Float
|
||||||
|
| Self::Str
|
||||||
|
| Self::Set
|
||||||
|
| Self::Dict
|
||||||
|
| Self::List
|
||||||
|
| Self::Type
|
||||||
|
| Self::Slice
|
||||||
|
| Self::GenericAlias
|
||||||
|
| Self::ModuleType
|
||||||
|
| Self::FunctionType
|
||||||
|
| Self::SpecialForm
|
||||||
|
| Self::TypeVar => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1597,6 +1636,7 @@ impl<'db> KnownClass {
|
||||||
"ModuleType" => Some(Self::ModuleType),
|
"ModuleType" => Some(Self::ModuleType),
|
||||||
"FunctionType" => Some(Self::FunctionType),
|
"FunctionType" => Some(Self::FunctionType),
|
||||||
"_SpecialForm" => Some(Self::SpecialForm),
|
"_SpecialForm" => Some(Self::SpecialForm),
|
||||||
|
"_NoDefaultType" => Some(Self::NoDefaultType),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1621,7 +1661,7 @@ impl<'db> KnownClass {
|
||||||
| Self::Slice => module.name() == "builtins",
|
| Self::Slice => module.name() == "builtins",
|
||||||
Self::GenericAlias | Self::ModuleType | Self::FunctionType => module.name() == "types",
|
Self::GenericAlias | Self::ModuleType | Self::FunctionType => module.name() == "types",
|
||||||
Self::NoneType => matches!(module.name().as_str(), "_typeshed" | "types"),
|
Self::NoneType => matches!(module.name().as_str(), "_typeshed" | "types"),
|
||||||
Self::SpecialForm => {
|
Self::SpecialForm | Self::TypeVar | Self::NoDefaultType => {
|
||||||
matches!(module.name().as_str(), "typing" | "typing_extensions")
|
matches!(module.name().as_str(), "typing" | "typing_extensions")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1629,17 +1669,20 @@ impl<'db> KnownClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumeration of specific runtime that are special enough to be considered their own type.
|
/// Enumeration of specific runtime that are special enough to be considered their own type.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub enum KnownInstanceType {
|
pub enum KnownInstanceType<'db> {
|
||||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||||
Literal,
|
Literal,
|
||||||
|
/// A single instance of `typing.TypeVar`
|
||||||
|
TypeVar(TypeVarInstance<'db>),
|
||||||
// TODO: fill this enum out with more special forms, etc.
|
// TODO: fill this enum out with more special forms, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KnownInstanceType {
|
impl<'db> KnownInstanceType<'db> {
|
||||||
pub const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
KnownInstanceType::Literal => "Literal",
|
KnownInstanceType::Literal => "Literal",
|
||||||
|
KnownInstanceType::TypeVar(_) => "TypeVar",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1647,13 +1690,15 @@ impl KnownInstanceType {
|
||||||
pub const fn bool(self) -> Truthiness {
|
pub const fn bool(self) -> Truthiness {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => Truthiness::AlwaysTrue,
|
Self::Literal => Truthiness::AlwaysTrue,
|
||||||
|
Self::TypeVar(_) => Truthiness::AlwaysTrue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the repr of the symbol at runtime
|
/// Return the repr of the symbol at runtime
|
||||||
pub const fn repr(self) -> &'static str {
|
pub fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => "typing.Literal",
|
Self::Literal => "typing.Literal",
|
||||||
|
Self::TypeVar(typevar) => typevar.name(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1661,6 +1706,7 @@ impl KnownInstanceType {
|
||||||
pub const fn class(self) -> KnownClass {
|
pub const fn class(self) -> KnownClass {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal => KnownClass::SpecialForm,
|
Self::Literal => KnownClass::SpecialForm,
|
||||||
|
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1682,6 +1728,93 @@ impl KnownInstanceType {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||||
|
match (self, name) {
|
||||||
|
(Self::TypeVar(typevar), "__name__") => Symbol::Type(
|
||||||
|
Type::StringLiteral(StringLiteralType::new(db, typevar.name(db).as_str())),
|
||||||
|
Boundness::Bound,
|
||||||
|
),
|
||||||
|
(Self::TypeVar(typevar), "__bound__") => Symbol::Type(
|
||||||
|
typevar
|
||||||
|
.upper_bound(db)
|
||||||
|
.map(|ty| ty.to_meta_type(db))
|
||||||
|
.unwrap_or(KnownClass::NoneType.to_instance(db)),
|
||||||
|
Boundness::Bound,
|
||||||
|
),
|
||||||
|
(Self::TypeVar(typevar), "__constraints__") => Symbol::Type(
|
||||||
|
Type::Tuple(TupleType::new(
|
||||||
|
db,
|
||||||
|
typevar
|
||||||
|
.constraints(db)
|
||||||
|
.map(|constraints| {
|
||||||
|
constraints
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.to_meta_type(db))
|
||||||
|
.collect::<Box<_>>()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| std::iter::empty().collect::<Box<_>>()),
|
||||||
|
)),
|
||||||
|
Boundness::Bound,
|
||||||
|
),
|
||||||
|
(Self::TypeVar(typevar), "__default__") => Symbol::Type(
|
||||||
|
typevar
|
||||||
|
.default_ty(db)
|
||||||
|
.map(|ty| ty.to_meta_type(db))
|
||||||
|
.unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
|
||||||
|
Boundness::Bound,
|
||||||
|
),
|
||||||
|
_ => self.instance_fallback(db).member(db, name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data regarding a single type variable.
|
||||||
|
///
|
||||||
|
/// This is referenced by `KnownInstanceType::TypeVar` (to represent the singleton type of the
|
||||||
|
/// runtime `typing.TypeVar` object itself). In the future, it will also be referenced also by a
|
||||||
|
/// new `Type` variant to represent the type that this typevar represents as an annotation: that
|
||||||
|
/// is, an unknown set of objects, constrained by the upper-bound/constraints on this type var,
|
||||||
|
/// defaulting to the default type of this type var when not otherwise bound to a type.
|
||||||
|
///
|
||||||
|
/// This must be a tracked struct, not an interned one, because typevar equivalence is by identity,
|
||||||
|
/// not by value. Two typevars that have the same name, bound/constraints, and default, are still
|
||||||
|
/// different typevars: if used in the same scope, they may be bound to different types.
|
||||||
|
#[salsa::tracked]
|
||||||
|
pub struct TypeVarInstance<'db> {
|
||||||
|
/// The name of this TypeVar (e.g. `T`)
|
||||||
|
#[return_ref]
|
||||||
|
name: ast::name::Name,
|
||||||
|
|
||||||
|
/// The upper bound or constraint on the type of this TypeVar
|
||||||
|
bound_or_constraints: Option<TypeVarBoundOrConstraints<'db>>,
|
||||||
|
|
||||||
|
/// The default type for this TypeVar
|
||||||
|
default_ty: Option<Type<'db>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> TypeVarInstance<'db> {
|
||||||
|
pub(crate) fn upper_bound(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||||
|
if let Some(TypeVarBoundOrConstraints::UpperBound(ty)) = self.bound_or_constraints(db) {
|
||||||
|
Some(ty)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&[Type<'db>]> {
|
||||||
|
if let Some(TypeVarBoundOrConstraints::Constraints(tuple)) = self.bound_or_constraints(db) {
|
||||||
|
Some(tuple.elements(db))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update)]
|
||||||
|
pub enum TypeVarBoundOrConstraints<'db> {
|
||||||
|
UpperBound(Type<'db>),
|
||||||
|
Constraints(TupleType<'db>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -2499,7 +2632,7 @@ impl<T: Hash + Eq> SeenSet<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A singleton type representing a single class object at runtime.
|
/// A singleton type representing a single class object at runtime.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub struct ClassLiteralType<'db> {
|
pub struct ClassLiteralType<'db> {
|
||||||
class: Class<'db>,
|
class: Class<'db>,
|
||||||
}
|
}
|
||||||
|
@ -2521,7 +2654,7 @@ impl<'db> From<ClassLiteralType<'db>> for Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that represents `type[C]`, i.e. the class literal `C` and class literals that are subclasses of `C`.
|
/// A type that represents `type[C]`, i.e. the class literal `C` and class literals that are subclasses of `C`.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||||
pub struct SubclassOfType<'db> {
|
pub struct SubclassOfType<'db> {
|
||||||
class: Class<'db>,
|
class: Class<'db>,
|
||||||
}
|
}
|
||||||
|
@ -2533,7 +2666,7 @@ impl<'db> SubclassOfType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type representing the set of runtime objects which are instances of a certain class.
|
/// A type representing the set of runtime objects which are instances of a certain class.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
|
||||||
pub struct InstanceType<'db> {
|
pub struct InstanceType<'db> {
|
||||||
class: Class<'db>,
|
class: Class<'db>,
|
||||||
}
|
}
|
||||||
|
@ -2746,9 +2879,10 @@ mod tests {
|
||||||
// BuiltinInstance("str") corresponds to an instance of the builtin `str` class
|
// BuiltinInstance("str") corresponds to an instance of the builtin `str` class
|
||||||
BuiltinInstance(&'static str),
|
BuiltinInstance(&'static str),
|
||||||
TypingInstance(&'static str),
|
TypingInstance(&'static str),
|
||||||
KnownInstance(KnownInstanceType),
|
TypingLiteral,
|
||||||
// BuiltinClassLiteral("str") corresponds to the builtin `str` class object itself
|
// BuiltinClassLiteral("str") corresponds to the builtin `str` class object itself
|
||||||
BuiltinClassLiteral(&'static str),
|
BuiltinClassLiteral(&'static str),
|
||||||
|
KnownClassInstance(KnownClass),
|
||||||
Union(Vec<Ty>),
|
Union(Vec<Ty>),
|
||||||
Intersection { pos: Vec<Ty>, neg: Vec<Ty> },
|
Intersection { pos: Vec<Ty>, neg: Vec<Ty> },
|
||||||
Tuple(Vec<Ty>),
|
Tuple(Vec<Ty>),
|
||||||
|
@ -2769,8 +2903,9 @@ mod tests {
|
||||||
Ty::BytesLiteral(s) => Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes())),
|
Ty::BytesLiteral(s) => Type::BytesLiteral(BytesLiteralType::new(db, s.as_bytes())),
|
||||||
Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db),
|
Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db),
|
||||||
Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db),
|
Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db),
|
||||||
Ty::KnownInstance(known_instance) => Type::KnownInstance(known_instance),
|
Ty::TypingLiteral => Type::KnownInstance(KnownInstanceType::Literal),
|
||||||
Ty::BuiltinClassLiteral(s) => builtins_symbol(db, s).expect_type(),
|
Ty::BuiltinClassLiteral(s) => builtins_symbol(db, s).expect_type(),
|
||||||
|
Ty::KnownClassInstance(known_class) => known_class.to_instance(db),
|
||||||
Ty::Union(tys) => {
|
Ty::Union(tys) => {
|
||||||
UnionType::from_elements(db, tys.into_iter().map(|ty| ty.into_type(db)))
|
UnionType::from_elements(db, tys.into_iter().map(|ty| ty.into_type(db)))
|
||||||
}
|
}
|
||||||
|
@ -2876,14 +3011,8 @@ mod tests {
|
||||||
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("str")], neg: vec![Ty::StringLiteral("foo")]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]})]
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("str")], neg: vec![Ty::StringLiteral("foo")]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]})]
|
||||||
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("int"))]
|
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("int"))]
|
||||||
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinInstance("object"))]
|
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinInstance("object"))]
|
||||||
#[test_case(
|
#[test_case(Ty::TypingLiteral, Ty::TypingInstance("_SpecialForm"))]
|
||||||
Ty::KnownInstance(KnownInstanceType::Literal),
|
#[test_case(Ty::TypingLiteral, Ty::BuiltinInstance("object"))]
|
||||||
Ty::TypingInstance("_SpecialForm")
|
|
||||||
)]
|
|
||||||
#[test_case(
|
|
||||||
Ty::KnownInstance(KnownInstanceType::Literal),
|
|
||||||
Ty::BuiltinInstance("object")
|
|
||||||
)]
|
|
||||||
fn is_subtype_of(from: Ty, to: Ty) {
|
fn is_subtype_of(from: Ty, to: Ty) {
|
||||||
let db = setup_db();
|
let db = setup_db();
|
||||||
assert!(from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
|
assert!(from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
|
||||||
|
@ -3126,6 +3255,7 @@ mod tests {
|
||||||
#[test_case(Ty::None)]
|
#[test_case(Ty::None)]
|
||||||
#[test_case(Ty::BooleanLiteral(true))]
|
#[test_case(Ty::BooleanLiteral(true))]
|
||||||
#[test_case(Ty::BooleanLiteral(false))]
|
#[test_case(Ty::BooleanLiteral(false))]
|
||||||
|
#[test_case(Ty::KnownClassInstance(KnownClass::NoDefaultType))]
|
||||||
fn is_singleton(from: Ty) {
|
fn is_singleton(from: Ty) {
|
||||||
let db = setup_db();
|
let db = setup_db();
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,11 @@ impl Display for DisplayRepresentation<'_> {
|
||||||
{
|
{
|
||||||
f.write_str("None")
|
f.write_str("None")
|
||||||
}
|
}
|
||||||
|
Type::Instance(InstanceType { class })
|
||||||
|
if class.is_known(self.db, KnownClass::NoDefaultType) =>
|
||||||
|
{
|
||||||
|
f.write_str("NoDefault")
|
||||||
|
}
|
||||||
// `[Type::Todo]`'s display should be explicit that is not a valid display of
|
// `[Type::Todo]`'s display should be explicit that is not a valid display of
|
||||||
// any other type
|
// any other type
|
||||||
Type::Todo => f.write_str("@Todo"),
|
Type::Todo => f.write_str("@Todo"),
|
||||||
|
|
|
@ -59,7 +59,8 @@ use crate::types::{
|
||||||
Boundness, BytesLiteralType, Class, ClassLiteralType, FunctionType, InstanceType,
|
Boundness, BytesLiteralType, Class, ClassLiteralType, FunctionType, InstanceType,
|
||||||
IntersectionBuilder, IntersectionType, IterationOutcome, KnownClass, KnownFunction,
|
IntersectionBuilder, IntersectionType, IterationOutcome, KnownClass, KnownFunction,
|
||||||
KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, StringLiteralType,
|
KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, StringLiteralType,
|
||||||
Symbol, Truthiness, TupleType, Type, TypeArrayDisplay, UnionBuilder, UnionType,
|
Symbol, Truthiness, TupleType, Type, TypeArrayDisplay, TypeVarBoundOrConstraints,
|
||||||
|
TypeVarInstance, UnionBuilder, UnionType,
|
||||||
};
|
};
|
||||||
use crate::unpack::Unpack;
|
use crate::unpack::Unpack;
|
||||||
use crate::util::subscript::{PyIndex, PySlice};
|
use crate::util::subscript::{PyIndex, PySlice};
|
||||||
|
@ -642,6 +643,15 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
DefinitionKind::ExceptHandler(except_handler_definition) => {
|
DefinitionKind::ExceptHandler(except_handler_definition) => {
|
||||||
self.infer_except_handler_definition(except_handler_definition, definition);
|
self.infer_except_handler_definition(except_handler_definition, definition);
|
||||||
}
|
}
|
||||||
|
DefinitionKind::TypeVar(node) => {
|
||||||
|
self.infer_typevar_definition(node, definition);
|
||||||
|
}
|
||||||
|
DefinitionKind::ParamSpec(node) => {
|
||||||
|
self.infer_paramspec_definition(node, definition);
|
||||||
|
}
|
||||||
|
DefinitionKind::TypeVarTuple(node) => {
|
||||||
|
self.infer_typevartuple_definition(node, definition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1352,6 +1362,82 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_typevar_definition(
|
||||||
|
&mut self,
|
||||||
|
node: &ast::TypeParamTypeVar,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) {
|
||||||
|
let ast::TypeParamTypeVar {
|
||||||
|
range: _,
|
||||||
|
name,
|
||||||
|
bound,
|
||||||
|
default,
|
||||||
|
} = node;
|
||||||
|
let bound_or_constraint = match bound.as_deref() {
|
||||||
|
Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => {
|
||||||
|
if elts.len() < 2 {
|
||||||
|
self.diagnostics.add(
|
||||||
|
expr.into(),
|
||||||
|
"invalid-typevar-constraints",
|
||||||
|
format_args!("TypeVar must have at least two constrained types"),
|
||||||
|
);
|
||||||
|
self.infer_expression(expr);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let tuple = TupleType::new(
|
||||||
|
self.db,
|
||||||
|
elts.iter()
|
||||||
|
.map(|expr| self.infer_type_expression(expr))
|
||||||
|
.collect::<Box<_>>(),
|
||||||
|
);
|
||||||
|
let constraints = TypeVarBoundOrConstraints::Constraints(tuple);
|
||||||
|
self.store_expression_type(expr, Type::Tuple(tuple));
|
||||||
|
Some(constraints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(expr) => Some(TypeVarBoundOrConstraints::UpperBound(
|
||||||
|
self.infer_type_expression(expr),
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let default_ty = self.infer_optional_type_expression(default.as_deref());
|
||||||
|
let ty = Type::KnownInstance(KnownInstanceType::TypeVar(TypeVarInstance::new(
|
||||||
|
self.db,
|
||||||
|
name.id.clone(),
|
||||||
|
bound_or_constraint,
|
||||||
|
default_ty,
|
||||||
|
)));
|
||||||
|
self.add_declaration_with_binding(node.into(), definition, ty, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_paramspec_definition(
|
||||||
|
&mut self,
|
||||||
|
node: &ast::TypeParamParamSpec,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) {
|
||||||
|
let ast::TypeParamParamSpec {
|
||||||
|
range: _,
|
||||||
|
name: _,
|
||||||
|
default,
|
||||||
|
} = node;
|
||||||
|
self.infer_optional_expression(default.as_deref());
|
||||||
|
self.add_declaration_with_binding(node.into(), definition, Type::Todo, Type::Todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infer_typevartuple_definition(
|
||||||
|
&mut self,
|
||||||
|
node: &ast::TypeParamTypeVarTuple,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) {
|
||||||
|
let ast::TypeParamTypeVarTuple {
|
||||||
|
range: _,
|
||||||
|
name: _,
|
||||||
|
default,
|
||||||
|
} = node;
|
||||||
|
self.infer_optional_expression(default.as_deref());
|
||||||
|
self.add_declaration_with_binding(node.into(), definition, Type::Todo, Type::Todo);
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_match_statement(&mut self, match_statement: &ast::StmtMatch) {
|
fn infer_match_statement(&mut self, match_statement: &ast::StmtMatch) {
|
||||||
let ast::StmtMatch {
|
let ast::StmtMatch {
|
||||||
range: _,
|
range: _,
|
||||||
|
@ -2013,13 +2099,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
expression.map(|expr| self.infer_expression(expr))
|
expression.map(|expr| self.infer_expression(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_optional_annotation_expression(
|
|
||||||
&mut self,
|
|
||||||
expr: Option<&ast::Expr>,
|
|
||||||
) -> Option<Type<'db>> {
|
|
||||||
expr.map(|expr| self.infer_annotation_expression(expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn infer_expression(&mut self, expression: &ast::Expr) -> Type<'db> {
|
fn infer_expression(&mut self, expression: &ast::Expr) -> Type<'db> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -3912,32 +3991,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
} = type_parameters;
|
} = type_parameters;
|
||||||
for type_param in type_params {
|
for type_param in type_params {
|
||||||
match type_param {
|
match type_param {
|
||||||
ast::TypeParam::TypeVar(typevar) => {
|
ast::TypeParam::TypeVar(node) => self.infer_definition(node),
|
||||||
let ast::TypeParamTypeVar {
|
ast::TypeParam::ParamSpec(node) => self.infer_definition(node),
|
||||||
range: _,
|
ast::TypeParam::TypeVarTuple(node) => self.infer_definition(node),
|
||||||
name: _,
|
|
||||||
bound,
|
|
||||||
default,
|
|
||||||
} = typevar;
|
|
||||||
self.infer_optional_expression(bound.as_deref());
|
|
||||||
self.infer_optional_expression(default.as_deref());
|
|
||||||
}
|
|
||||||
ast::TypeParam::ParamSpec(param_spec) => {
|
|
||||||
let ast::TypeParamParamSpec {
|
|
||||||
range: _,
|
|
||||||
name: _,
|
|
||||||
default,
|
|
||||||
} = param_spec;
|
|
||||||
self.infer_optional_expression(default.as_deref());
|
|
||||||
}
|
|
||||||
ast::TypeParam::TypeVarTuple(typevar_tuple) => {
|
|
||||||
let ast::TypeParamTypeVarTuple {
|
|
||||||
range: _,
|
|
||||||
name: _,
|
|
||||||
default,
|
|
||||||
} = typevar_tuple;
|
|
||||||
self.infer_optional_expression(default.as_deref());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3971,6 +4027,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.store_expression_type(expression, annotation_ty);
|
self.store_expression_type(expression, annotation_ty);
|
||||||
annotation_ty
|
annotation_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_optional_annotation_expression(
|
||||||
|
&mut self,
|
||||||
|
expr: Option<&ast::Expr>,
|
||||||
|
) -> Option<Type<'db>> {
|
||||||
|
expr.map(|expr| self.infer_annotation_expression(expr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type expressions
|
/// Type expressions
|
||||||
|
@ -4145,6 +4208,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_optional_type_expression(
|
||||||
|
&mut self,
|
||||||
|
opt_expression: Option<&ast::Expr>,
|
||||||
|
) -> Option<Type<'db>> {
|
||||||
|
opt_expression.map(|expr| self.infer_type_expression(expr))
|
||||||
|
}
|
||||||
|
|
||||||
/// Given the slice of a `tuple[]` annotation, return the type that the annotation represents
|
/// Given the slice of a `tuple[]` annotation, return the type that the annotation represents
|
||||||
fn infer_tuple_type_expression(&mut self, tuple_slice: &ast::Expr) -> Type<'db> {
|
fn infer_tuple_type_expression(&mut self, tuple_slice: &ast::Expr) -> Type<'db> {
|
||||||
/// In most cases, if a subelement of the tuple is inferred as `Todo`,
|
/// In most cases, if a subelement of the tuple is inferred as `Todo`,
|
||||||
|
@ -4262,6 +4332,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
KnownInstanceType::TypeVar(_) => Type::Todo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,7 @@ impl<'db> ClassBase<'db> {
|
||||||
| Type::SubclassOf(_) => None,
|
| Type::SubclassOf(_) => None,
|
||||||
Type::KnownInstance(known_instance) => match known_instance {
|
Type::KnownInstance(known_instance) => match known_instance {
|
||||||
KnownInstanceType::Literal => None,
|
KnownInstanceType::Literal => None,
|
||||||
|
KnownInstanceType::TypeVar(_) => None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
def foo[T: (str, bytes)](x: T) -> T:
|
def foo[T: (str, bytes)](x: T) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
def bar[T: (str,)](x: T) -> T:
|
||||||
|
...
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue