diff --git a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md index 65c4f98865..b6a3dccfb4 100644 --- a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md @@ -138,3 +138,12 @@ def get_name() -> str: # error: [invalid-type-alias-type] "The name of a `typing.TypeAlias` must be a string literal" IntOrStr = TypeAliasType(get_name(), int | str) ``` + +## Recursive type aliases + +```py +type Recursive = dict[str, "Recursive"] + +# TODO: this should not be an error +r: Recursive = {"key": {}} # error: [invalid-assignment] +``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md index ffa1e0fad0..7d14b042bb 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md @@ -396,10 +396,11 @@ type LiteralInt = TypeOf[int] type LiteralStr = TypeOf[str] type LiteralObject = TypeOf[object] -assert_type(bool, LiteralBool) -assert_type(int, LiteralInt) -assert_type(str, LiteralStr) -assert_type(object, LiteralObject) +# TODO: these should not be errors +assert_type(bool, LiteralBool) # error: [type-assertion-failure] +assert_type(int, LiteralInt) # error: [type-assertion-failure] +assert_type(str, LiteralStr) # error: [type-assertion-failure] +assert_type(object, LiteralObject) # error: [type-assertion-failure] # bool @@ -462,9 +463,10 @@ type LiteralBase = TypeOf[Base] type LiteralDerived = TypeOf[Derived] type LiteralUnrelated = TypeOf[Unrelated] -assert_type(Base, LiteralBase) -assert_type(Derived, LiteralDerived) -assert_type(Unrelated, LiteralUnrelated) +# TODO: these should not be errors +assert_type(Base, LiteralBase) # error: [type-assertion-failure] +assert_type(Derived, LiteralDerived) # error: [type-assertion-failure] +assert_type(Unrelated, LiteralUnrelated) # error: [type-assertion-failure] static_assert(is_subtype_of(LiteralBase, type)) static_assert(is_subtype_of(LiteralBase, object)) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index af81ff210b..43bb25c3d4 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -992,7 +992,7 @@ impl<'db> Type<'db> { #[must_use] pub fn normalized(self, db: &'db dyn Db) -> Self { match self { - Type::TypeAliasRef(_) => todo!(), + Type::TypeAliasRef(alias) => alias.value_type(db).normalized(db), Type::Union(union) => Type::Union(union.normalized(db)), Type::Intersection(intersection) => Type::Intersection(intersection.normalized(db)), Type::Tuple(tuple) => Type::Tuple(tuple.normalized(db)), @@ -1069,9 +1069,8 @@ impl<'db> Type<'db> { (Type::Never, _) => true, (_, Type::Never) => false, - (_, Type::TypeAliasRef(_)) | (Type::TypeAliasRef(_), _) => { - todo!() - } + (left, Type::TypeAliasRef(right)) => left.is_subtype_of(db, right.value_type(db)), + (Type::TypeAliasRef(left), right) => left.value_type(db).is_subtype_of(db, right), // Everything is a subtype of `object`. (_, Type::NominalInstance(instance)) if instance.class.is_object(db) => true, @@ -1799,7 +1798,8 @@ impl<'db> Type<'db> { (Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => false, - (Type::TypeAliasRef(_), _) | (_, Type::TypeAliasRef(_)) => todo!(), + (Type::TypeAliasRef(left), right) => left.value_type(db).is_disjoint_from(db, right), + (left, Type::TypeAliasRef(right)) => left.is_disjoint_from(db, right.value_type(db)), // A typevar is never disjoint from itself, since all occurrences of the typevar must // be specialized to the same type. (This is an important difference between typevars @@ -2249,7 +2249,7 @@ impl<'db> Type<'db> { /// Returns true if the type does not contain any gradual forms (as a sub-part). pub(crate) fn is_fully_static(&self, db: &'db dyn Db) -> bool { match self { - Type::TypeAliasRef(_) => todo!(), + Type::TypeAliasRef(alias) => alias.value_type(db).is_fully_static(db), Type::Dynamic(_) => false, Type::Never | Type::FunctionLiteral(..) @@ -5000,7 +5000,7 @@ impl<'db> Type<'db> { }), Type::KnownInstance(known_instance) => match known_instance { - KnownInstanceType::TypeAliasType(alias) => Ok(alias.value_type(db)), + KnownInstanceType::TypeAliasType(alias) => Ok(Type::TypeAliasRef(*alias)), KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)), KnownInstanceType::SubscriptedProtocol(_) => Err(InvalidTypeExpressionError { invalid_expressions: smallvec::smallvec![InvalidTypeExpression::Protocol], @@ -5315,7 +5315,7 @@ impl<'db> Type<'db> { type_mapping: &TypeMapping<'a, 'db>, ) -> Type<'db> { match self { - Type::TypeAliasRef(_) => todo!(), + Type::TypeAliasRef(alias) => alias.value_type(db).apply_type_mapping(db, type_mapping), Type::TypeVar(typevar) => match type_mapping { TypeMapping::Specialization(specialization) => { specialization.get(db, typevar).unwrap_or(self) diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 6f8587804d..b80adec287 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -67,7 +67,7 @@ struct DisplayRepresentation<'db> { impl Display for DisplayRepresentation<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self.ty { - Type::TypeAliasRef(_) => todo!(), + Type::TypeAliasRef(alias) => f.write_str(alias.name(self.db)), Type::Dynamic(dynamic) => dynamic.fmt(f), Type::Never => f.write_str("Never"), Type::NominalInstance(instance) => { diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 6c581b312b..cf7812fbea 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -6177,7 +6177,20 @@ impl<'db> TypeInferenceBuilder<'db> { } match (left_ty, right_ty, op) { - (Type::TypeAliasRef(_), _, _) | (_, Type::TypeAliasRef(_), _) => todo!(), + (Type::TypeAliasRef(alias), _, _) => self.infer_binary_expression_type( + node, + emitted_division_by_zero_diagnostic, + alias.value_type(self.db()), + right_ty, + op, + ), + (_, Type::TypeAliasRef(alias), _) => self.infer_binary_expression_type( + node, + emitted_division_by_zero_diagnostic, + left_ty, + alias.value_type(self.db()), + op, + ), (Type::Union(lhs_union), rhs, _) => { let mut union = UnionBuilder::new(self.db());