mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34: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]:
|
class Bar[T, **P]:
|
||||||
def __call__(self): ...
|
def __call__(self): ...
|
||||||
|
|
||||||
# TODO: should not error
|
class BarLegacy(Generic[T, P]):
|
||||||
class BarLegacy(Generic[T, P]): # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
|
|
||||||
def __call__(self): ...
|
def __call__(self): ...
|
||||||
|
|
||||||
static_assert(is_assignable_to(Foo, Callable[..., Any]))
|
static_assert(is_assignable_to(Foo, Callable[..., Any]))
|
||||||
|
@ -1064,9 +1063,7 @@ static_assert(is_assignable_to(BarLegacy, Callable[..., Any]))
|
||||||
class Spam[T]: ...
|
class Spam[T]: ...
|
||||||
class SpamLegacy(Generic[T]): ...
|
class SpamLegacy(Generic[T]): ...
|
||||||
class Eggs[T, **P]: ...
|
class Eggs[T, **P]: ...
|
||||||
|
class EggsLegacy(Generic[T, P]): ...
|
||||||
# TODO: should not error
|
|
||||||
class EggsLegacy(Generic[T, P]): ... # error: [invalid-argument-type] "`ParamSpec` is not a valid argument to `Generic`"
|
|
||||||
|
|
||||||
static_assert(not is_assignable_to(Spam, Callable[..., Any]))
|
static_assert(not is_assignable_to(Spam, Callable[..., Any]))
|
||||||
static_assert(not is_assignable_to(SpamLegacy, 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(
|
fn infer_subscript_expression_types(
|
||||||
&self,
|
&self,
|
||||||
value_node: &ast::Expr,
|
value_node: &'ast ast::Expr,
|
||||||
value_ty: Type<'db>,
|
value_ty: Type<'db>,
|
||||||
slice_ty: Type<'db>,
|
slice_ty: Type<'db>,
|
||||||
expr_context: ExprContext,
|
expr_context: ExprContext,
|
||||||
|
@ -8732,7 +8732,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context))
|
Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(Type::unknown),
|
.unwrap_or_else(GenericContextError::into_type),
|
||||||
// TODO: emit a diagnostic
|
// TODO: emit a diagnostic
|
||||||
TupleSpec::Variable(_) => Type::unknown(),
|
TupleSpec::Variable(_) => Type::unknown(),
|
||||||
})
|
})
|
||||||
|
@ -8745,7 +8745,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
LegacyGenericBase::Protocol,
|
LegacyGenericBase::Protocol,
|
||||||
)
|
)
|
||||||
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context)))
|
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(context)))
|
||||||
.unwrap_or_else(Type::unknown),
|
.unwrap_or_else(GenericContextError::into_type),
|
||||||
),
|
),
|
||||||
|
|
||||||
(Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(_)), _) => {
|
(Type::KnownInstance(KnownInstanceType::SubscriptedProtocol(_)), _) => {
|
||||||
|
@ -8764,7 +8764,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.map(|context| {
|
.map(|context| {
|
||||||
Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context))
|
Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(Type::unknown),
|
.unwrap_or_else(GenericContextError::into_type),
|
||||||
// TODO: emit a diagnostic
|
// TODO: emit a diagnostic
|
||||||
TupleSpec::Variable(_) => Type::unknown(),
|
TupleSpec::Variable(_) => Type::unknown(),
|
||||||
})
|
})
|
||||||
|
@ -8777,7 +8777,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
LegacyGenericBase::Generic,
|
LegacyGenericBase::Generic,
|
||||||
)
|
)
|
||||||
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context)))
|
.map(|context| Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(context)))
|
||||||
.unwrap_or_else(Type::unknown),
|
.unwrap_or_else(GenericContextError::into_type),
|
||||||
),
|
),
|
||||||
|
|
||||||
(Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(_)), _) => {
|
(Type::KnownInstance(KnownInstanceType::SubscriptedGeneric(_)), _) => {
|
||||||
|
@ -8946,11 +8946,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
value_node: &ast::Expr,
|
value_node: &ast::Expr,
|
||||||
typevars: &[Type<'db>],
|
typevars: &[Type<'db>],
|
||||||
origin: LegacyGenericBase,
|
origin: LegacyGenericBase,
|
||||||
) -> Option<GenericContext<'db>> {
|
) -> Result<GenericContext<'db>, GenericContextError> {
|
||||||
let typevars: Option<FxOrderSet<_>> = typevars
|
let typevars: Result<FxOrderSet<_>, GenericContextError> = typevars
|
||||||
.iter()
|
.iter()
|
||||||
.map(|typevar| match typevar {
|
.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) =
|
if let Some(builder) =
|
||||||
self.context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
|
self.context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
|
||||||
|
@ -8960,7 +8968,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
typevar.display(self.db()),
|
typevar.display(self.db()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None
|
Err(GenericContextError::InvalidArgument)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.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.
|
/// The deferred state of a specific expression in an inference region.
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
enum DeferredExpressionState {
|
enum DeferredExpressionState {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue