mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Remove false positives when subscripting Generic
or Protocol
with a ParamSpec
or TypeVarTuple
(#19749)
This commit is contained in:
parent
739c94f95a
commit
3a9341f7be
2 changed files with 37 additions and 14 deletions
|
@ -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]))
|
||||
|
|
|
@ -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<GenericContext<'db>> {
|
||||
let typevars: Option<FxOrderSet<_>> = typevars
|
||||
) -> Result<GenericContext<'db>, GenericContextError> {
|
||||
let typevars: Result<FxOrderSet<_>, 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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue