[ty] Fix false positives for legacy ParamSpecs inside Callable type expressions (#18426)

This commit is contained in:
Alex Waygood 2025-06-02 14:10:00 +01:00 committed by GitHub
parent e2d96df501
commit 47698883ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 30 deletions

View file

@ -264,10 +264,9 @@ from typing_extensions import ParamSpec
P2 = ParamSpec("P2") P2 = ParamSpec("P2")
# TODO: Not an error; remove once `ParamSpec` is supported # TODO: argument list should not be `...` (requires `ParamSpec` support)
# error: [invalid-type-form]
def _(c: Callable[P2, int]): def _(c: Callable[P2, int]):
reveal_type(c) # revealed: (...) -> Unknown reveal_type(c) # revealed: (...) -> int
``` ```
## Using `typing.Unpack` ## Using `typing.Unpack`

View file

@ -119,7 +119,8 @@ use super::string_annotation::{
}; };
use super::subclass_of::SubclassOfInner; use super::subclass_of::SubclassOfInner;
use super::{ use super::{
BoundSuperError, BoundSuperType, ClassBase, add_inferred_python_version_hint_to_diagnostic, BoundSuperError, BoundSuperType, ClassBase, NominalInstanceType,
add_inferred_python_version_hint_to_diagnostic,
}; };
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope. /// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
@ -9131,9 +9132,9 @@ impl<'db> TypeInferenceBuilder<'db> {
&mut self, &mut self,
parameters: &ast::Expr, parameters: &ast::Expr,
) -> Option<Parameters<'db>> { ) -> Option<Parameters<'db>> {
Some(match parameters { match parameters {
ast::Expr::EllipsisLiteral(ast::ExprEllipsisLiteral { .. }) => { ast::Expr::EllipsisLiteral(ast::ExprEllipsisLiteral { .. }) => {
Parameters::gradual_form() return Some(Parameters::gradual_form());
} }
ast::Expr::List(ast::ExprList { elts: params, .. }) => { ast::Expr::List(ast::ExprList { elts: params, .. }) => {
let mut parameter_types = Vec::with_capacity(params.len()); let mut parameter_types = Vec::with_capacity(params.len());
@ -9152,43 +9153,48 @@ impl<'db> TypeInferenceBuilder<'db> {
parameter_types.push(param_type); parameter_types.push(param_type);
} }
if return_todo { return Some(if return_todo {
// TODO: `Unpack` // TODO: `Unpack`
Parameters::todo() Parameters::todo()
} else { } else {
Parameters::new(parameter_types.iter().map(|param_type| { Parameters::new(parameter_types.iter().map(|param_type| {
Parameter::positional_only(None).with_annotated_type(*param_type) Parameter::positional_only(None).with_annotated_type(*param_type)
})) }))
} });
} }
ast::Expr::Subscript(_) => { ast::Expr::Subscript(_) => {
// TODO: Support `Concatenate[...]` // TODO: Support `Concatenate[...]`
Parameters::todo() return Some(Parameters::todo());
} }
ast::Expr::Name(name) if name.is_invalid() => { ast::Expr::Name(name) => {
if name.is_invalid() {
// This is a special case to avoid raising the error suggesting what the first // This is a special case to avoid raising the error suggesting what the first
// argument should be. This only happens when there's already a syntax error like // argument should be. This only happens when there's already a syntax error like
// `Callable[]`. // `Callable[]`.
return None; return None;
} }
ast::Expr::Name(name) match self.infer_name_load(name) {
if self.infer_name_load(name) Type::Dynamic(DynamicType::TodoPEP695ParamSpec) => {
== Type::Dynamic(DynamicType::TodoPEP695ParamSpec) => return Some(Parameters::todo());
}
Type::NominalInstance(NominalInstanceType { class, .. })
if class.is_known(self.db(), KnownClass::ParamSpec) =>
{ {
return Some(Parameters::todo()); return Some(Parameters::todo());
} }
_ => { _ => {}
}
}
_ => {}
}
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) {
let diag = builder.into_diagnostic(format_args!( let diag = builder.into_diagnostic(format_args!(
"The first argument to `Callable` \ "The first argument to `Callable` must be either a list of types, \
must be either a list of types, \
ParamSpec, Concatenate, or `...`", ParamSpec, Concatenate, or `...`",
)); ));
diagnostic::add_type_expression_reference_link(diag); diagnostic::add_type_expression_reference_link(diag);
} }
return None; None
}
})
} }
} }