mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:21 +00:00
[ty] Fix false positives for legacy ParamSpec
s inside Callable
type expressions (#18426)
This commit is contained in:
parent
e2d96df501
commit
47698883ae
2 changed files with 35 additions and 30 deletions
|
@ -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`
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
ast::Expr::Name(name) if name.is_invalid() => {
|
|
||||||
// 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
|
|
||||||
// `Callable[]`.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
ast::Expr::Name(name)
|
|
||||||
if self.infer_name_load(name)
|
|
||||||
== Type::Dynamic(DynamicType::TodoPEP695ParamSpec) =>
|
|
||||||
{
|
|
||||||
return Some(Parameters::todo());
|
return Some(Parameters::todo());
|
||||||
}
|
}
|
||||||
_ => {
|
ast::Expr::Name(name) => {
|
||||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) {
|
if name.is_invalid() {
|
||||||
let diag = builder.into_diagnostic(format_args!(
|
// This is a special case to avoid raising the error suggesting what the first
|
||||||
"The first argument to `Callable` \
|
// argument should be. This only happens when there's already a syntax error like
|
||||||
must be either a list of types, \
|
// `Callable[]`.
|
||||||
ParamSpec, Concatenate, or `...`",
|
return None;
|
||||||
));
|
}
|
||||||
diagnostic::add_type_expression_reference_link(diag);
|
match self.infer_name_load(name) {
|
||||||
|
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 None;
|
|
||||||
}
|
}
|
||||||
})
|
_ => {}
|
||||||
|
}
|
||||||
|
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) {
|
||||||
|
let diag = builder.into_diagnostic(format_args!(
|
||||||
|
"The first argument to `Callable` must be either a list of types, \
|
||||||
|
ParamSpec, Concatenate, or `...`",
|
||||||
|
));
|
||||||
|
diagnostic::add_type_expression_reference_link(diag);
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue