diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index 02831c1104..5338b25ce8 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -1052,8 +1052,7 @@ class FooLegacy(Generic[T]): class Bar[T, **P]: def __call__(self): ... -# TODO: should not error -class BarLegacy(Generic[T, P]): # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`" +class BarLegacy(Generic[T, P]): def __call__(self): ... static_assert(is_assignable_to(Foo, Callable[..., Any])) @@ -1064,9 +1063,7 @@ static_assert(is_assignable_to(BarLegacy, Callable[..., Any])) class Spam[T]: ... class SpamLegacy(Generic[T]): ... class Eggs[T, **P]: ... - -# TODO: should not error -class EggsLegacy(Generic[T, P]): ... # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`" +class EggsLegacy(Generic[T, P]): ... static_assert(not is_assignable_to(Spam, Callable[..., Any])) static_assert(not is_assignable_to(SpamLegacy, Callable[..., Any])) diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index af56582a20..4b258bdd32 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -8563,7 +8563,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { fn infer_subscript_expression_types( &self, - value_node: &ast::Expr, + value_node: &'ast ast::Expr, value_ty: Type<'db>, slice_ty: Type<'db>, expr_context: ExprContext, @@ -8732,7 +8732,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .map(|context| { Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context)) }) - .unwrap_or_else(Type::unknown), + .unwrap_or_else(GenericContextError::into_type), // TODO: emit a diagnostic TupleSpec::Variable(_) => Type::unknown(), }) @@ -8745,7 +8745,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { LegacyGenericBase::Protocol, ) .map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context))) - .unwrap_or_else(Type::unknown), + .unwrap_or_else(GenericContextError::into_type), ), (Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(_)), _) => { @@ -8764,7 +8764,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .map(|context| { Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context)) }) - .unwrap_or_else(Type::unknown), + .unwrap_or_else(GenericContextError::into_type), // TODO: emit a diagnostic TupleSpec::Variable(_) => Type::unknown(), }) @@ -8777,7 +8777,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { LegacyGenericBase::Generic, ) .map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context))) - .unwrap_or_else(Type::unknown), + .unwrap_or_else(GenericContextError::into_type), ), (Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(_)), _) => { @@ -8946,11 +8946,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { value_node: &ast::Expr, typevars: &[Type<'db>], origin: LegacyGenericBase, - ) -> Option> { - let typevars: Option> = typevars + ) -> Result, GenericContextError> { + let typevars: Result, GenericContextError> = typevars .iter() .map(|typevar| match typevar { - Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Some(*typevar), + Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Ok(*typevar), + Type::NominalInstance(NominalInstanceType { class, .. }) + if matches!( + class.known(self.db()), + Some(KnownClass::TypeVarTuple | KnownClass::ParamSpec) + ) => + { + Err(GenericContextError::NotYetSupported) + } _ => { if let Some(builder) = self.context.report_lint(&INVALID_ARGUMENT_TYPE, value_node) @@ -8960,7 +8968,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { typevar.display(self.db()), )); } - None + Err(GenericContextError::InvalidArgument) } }) .collect(); @@ -10860,6 +10868,24 @@ impl<'db> TypeInferenceBuilder<'db, '_> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum GenericContextError { + /// It's invalid to subscript `Generic` or `Protocol` with this type + InvalidArgument, + /// It's valid to subscribe `Generic` or `Protocol` with this type, + /// but the type is not yet supported. + NotYetSupported, +} + +impl GenericContextError { + const fn into_type<'db>(self) -> Type<'db> { + match self { + GenericContextError::InvalidArgument => Type::unknown(), + GenericContextError::NotYetSupported => todo_type!("ParamSpecs and TypeVarTuples"), + } + } +} + /// The deferred state of a specific expression in an inference region. #[derive(Default, Debug, Clone, Copy)] enum DeferredExpressionState {