Skip to content

Commit 3a9341f

Browse files
authored
[ty] Remove false positives when subscripting Generic or Protocol with a ParamSpec or TypeVarTuple (#19749)
1 parent 739c94f commit 3a9341f

File tree

2 files changed

+37
-14
lines changed

2 files changed

+37
-14
lines changed

crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,8 +1052,7 @@ class FooLegacy(Generic[T]):
10521052
class Bar[T, **P]:
10531053
def __call__(self): ...
10541054

1055-
# TODO: should not error
1056-
class BarLegacy(Generic[T, P]): # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
1055+
class BarLegacy(Generic[T, P]):
10571056
def __call__(self): ...
10581057

10591058
static_assert(is_assignable_to(Foo, Callable[..., Any]))
@@ -1064,9 +1063,7 @@ static_assert(is_assignable_to(BarLegacy, Callable[..., Any]))
10641063
class Spam[T]: ...
10651064
class SpamLegacy(Generic[T]): ...
10661065
class Eggs[T, **P]: ...
1067-
1068-
# TODO: should not error
1069-
class EggsLegacy(Generic[T, P]): ... # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
1066+
class EggsLegacy(Generic[T, P]): ...
10701067

10711068
static_assert(not is_assignable_to(Spam, Callable[..., Any]))
10721069
static_assert(not is_assignable_to(SpamLegacy, Callable[..., Any]))

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8563,7 +8563,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
85638563

85648564
fn infer_subscript_expression_types(
85658565
&self,
8566-
value_node: &ast::Expr,
8566+
value_node: &'ast ast::Expr,
85678567
value_ty: Type<'db>,
85688568
slice_ty: Type<'db>,
85698569
expr_context: ExprContext,
@@ -8732,7 +8732,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
87328732
.map(|context| {
87338733
Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context))
87348734
})
8735-
.unwrap_or_else(Type::unknown),
8735+
.unwrap_or_else(GenericContextError::into_type),
87368736
// TODO: emit a diagnostic
87378737
TupleSpec::Variable(_) => Type::unknown(),
87388738
})
@@ -8745,7 +8745,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
87458745
LegacyGenericBase::Protocol,
87468746
)
87478747
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context)))
8748-
.unwrap_or_else(Type::unknown),
8748+
.unwrap_or_else(GenericContextError::into_type),
87498749
),
87508750

87518751
(Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(_)), _) => {
@@ -8764,7 +8764,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
87648764
.map(|context| {
87658765
Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context))
87668766
})
8767-
.unwrap_or_else(Type::unknown),
8767+
.unwrap_or_else(GenericContextError::into_type),
87688768
// TODO: emit a diagnostic
87698769
TupleSpec::Variable(_) => Type::unknown(),
87708770
})
@@ -8777,7 +8777,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
87778777
LegacyGenericBase::Generic,
87788778
)
87798779
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context)))
8780-
.unwrap_or_else(Type::unknown),
8780+
.unwrap_or_else(GenericContextError::into_type),
87818781
),
87828782

87838783
(Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(_)), _) => {
@@ -8946,11 +8946,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
89468946
value_node: &ast::Expr,
89478947
typevars: &[Type<'db>],
89488948
origin: LegacyGenericBase,
8949-
) -> Option<GenericContext<'db>> {
8950-
let typevars: Option<FxOrderSet<_>> = typevars
8949+
) -> Result<GenericContext<'db>, GenericContextError> {
8950+
let typevars: Result<FxOrderSet<_>, GenericContextError> = typevars
89518951
.iter()
89528952
.map(|typevar| match typevar {
8953-
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Some(*typevar),
8953+
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Ok(*typevar),
8954+
Type::NominalInstance(NominalInstanceType { class, .. })
8955+
if matches!(
8956+
class.known(self.db()),
8957+
Some(KnownClass::TypeVarTuple | KnownClass::ParamSpec)
8958+
) =>
8959+
{
8960+
Err(GenericContextError::NotYetSupported)
8961+
}
89548962
_ => {
89558963
if let Some(builder) =
89568964
self.context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
@@ -8960,7 +8968,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
89608968
typevar.display(self.db()),
89618969
));
89628970
}
8963-
None
8971+
Err(GenericContextError::InvalidArgument)
89648972
}
89658973
})
89668974
.collect();
@@ -10860,6 +10868,24 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
1086010868
}
1086110869
}
1086210870

10871+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10872+
enum GenericContextError {
10873+
/// It's invalid to subscript `Generic` or `Protocol` with this type
10874+
InvalidArgument,
10875+
/// It's valid to subscribe `Generic` or `Protocol` with this type,
10876+
/// but the type is not yet supported.
10877+
NotYetSupported,
10878+
}
10879+
10880+
impl GenericContextError {
10881+
const fn into_type<'db>(self) -> Type<'db> {
10882+
match self {
10883+
GenericContextError::InvalidArgument => Type::unknown(),
10884+
GenericContextError::NotYetSupported => todo_type!("ParamSpecs and TypeVarTuples"),
10885+
}
10886+
}
10887+
}
10888+
1086310889
/// The deferred state of a specific expression in an inference region.
1086410890
#[derive(Default, Debug, Clone, Copy)]
1086510891
enum DeferredExpressionState {

0 commit comments

Comments
 (0)