Skip to content

Commit c566bdc

Browse files
committed
improve hint
1 parent 6889518 commit c566bdc

2 files changed

Lines changed: 60 additions & 43 deletions

File tree

crates/ty_python_semantic/resources/mdtest/annotations/callable.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ Or, when an ellipsis literal is used as a parameter type in the list (note that
6363
form uses `...` as the entire first argument, not inside a list):
6464

6565
```py
66-
# error: [invalid-type-form] "`...` is not allowed in this context in a type expression: Did you mean `Callable[..., <return type>]`?"
66+
# error: [invalid-type-form] "`[...]` is not a valid parameter list for `Callable`: Did you mean `Callable[..., int]`?"
6767
def _(c: Callable[[...], int]):
68-
reveal_type(c) # revealed: (Unknown, /) -> int
68+
reveal_type(c) # revealed: (...) -> int
6969
```
7070

7171
```py

crates/ty_python_semantic/src/types/infer/builder/type_expression.rs

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
6565
fn report_invalid_type_expression(
6666
&self,
6767
expression: &ast::Expr,
68-
message: std::fmt::Arguments,
68+
message: impl std::fmt::Display,
6969
) -> Option<LintDiagnosticGuard<'_, '_>> {
7070
self.context
7171
.report_lint(&INVALID_TYPE_FORM, expression)
@@ -1292,26 +1292,53 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
12921292

12931293
let return_type = arguments.next().map(|arg| self.infer_type_expression(arg));
12941294

1295-
let correct_argument_number = if let Some(third_argument) = arguments.next() {
1296-
self.infer_type_expression(third_argument);
1297-
for argument in arguments {
1298-
self.infer_type_expression(argument);
1295+
let callable_type = if parameters.is_none()
1296+
&& let Some(first_argument) = first_argument
1297+
&& let ast::Expr::List(list) = first_argument
1298+
&& let [single_param] = &list.elts[..]
1299+
&& single_param.is_ellipsis_literal_expr()
1300+
{
1301+
self.store_expression_type(single_param, Type::unknown());
1302+
if let Some(mut diagnostic) = self.report_invalid_type_expression(
1303+
first_argument,
1304+
"`[...]` is not a valid parameter list for `Callable`",
1305+
) {
1306+
if let Some(returns) = return_type {
1307+
diagnostic.set_primary_message(format_args!(
1308+
"Did you mean `Callable[..., {}]`?",
1309+
returns.display(db)
1310+
));
1311+
}
12991312
}
1300-
false
1313+
Type::single_callable(
1314+
db,
1315+
Signature::new(
1316+
Parameters::unknown(),
1317+
return_type.unwrap_or_else(Type::unknown),
1318+
),
1319+
)
13011320
} else {
1302-
return_type.is_some()
1303-
};
1321+
let correct_argument_number = if let Some(third_argument) = arguments.next() {
1322+
self.infer_type_expression(third_argument);
1323+
for argument in arguments {
1324+
self.infer_type_expression(argument);
1325+
}
1326+
false
1327+
} else {
1328+
return_type.is_some()
1329+
};
13041330

1305-
if !correct_argument_number {
1306-
report_invalid_arguments_to_callable(&self.context, subscript);
1307-
}
1331+
if !correct_argument_number {
1332+
report_invalid_arguments_to_callable(&self.context, subscript);
1333+
}
13081334

1309-
let callable_type = if let (Some(parameters), Some(return_type), true) =
1310-
(parameters, return_type, correct_argument_number)
1311-
{
1312-
Type::single_callable(db, Signature::new(parameters, return_type))
1313-
} else {
1314-
Type::Callable(CallableType::unknown(db))
1335+
if correct_argument_number
1336+
&& let (Some(parameters), Some(return_type)) = (parameters, return_type)
1337+
{
1338+
Type::single_callable(db, Signature::new(parameters, return_type))
1339+
} else {
1340+
Type::Callable(CallableType::unknown(db))
1341+
}
13151342
};
13161343

13171344
// `Signature` / `Parameters` are not a `Type` variant, so we're storing
@@ -1863,35 +1890,25 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
18631890
return Some(Parameters::gradual_form());
18641891
}
18651892
ast::Expr::List(ast::ExprList { elts: params, .. }) => {
1893+
if let [ast::Expr::EllipsisLiteral(_)] = &params[..] {
1894+
// Return `None` here so that we emit a specific diagnostic at the callsite.
1895+
return None;
1896+
}
1897+
18661898
let mut parameter_types = Vec::with_capacity(params.len());
18671899

18681900
// Whether to infer `Todo` for the parameters
18691901
let mut return_todo = false;
18701902

1871-
// `Callable[[...], R]` — the user almost certainly meant `Callable[..., R]`
1872-
if let [sole_param] = &params[..]
1873-
&& sole_param.is_ellipsis_literal_expr()
1874-
{
1875-
if let Some(mut diagnostic) = self.report_invalid_type_expression(
1876-
sole_param,
1877-
format_args!("`...` is not allowed in this context in a type expression"),
1878-
) {
1879-
diagnostic
1880-
.set_primary_message("Did you mean `Callable[..., <return type>]`?");
1881-
}
1882-
self.store_expression_type(sole_param, Type::unknown());
1883-
parameter_types.push(Type::unknown());
1884-
} else {
1885-
for param in params {
1886-
let param_type = self.infer_type_expression(param);
1887-
// This is similar to what we currently do for inferring tuple type expression.
1888-
// We currently infer `Todo` for the parameters to avoid invalid diagnostics
1889-
// when trying to check for assignability or any other relation. For example,
1890-
// `*tuple[int, str]`, `Unpack[]`, etc. are not yet supported.
1891-
return_todo |= param_type.is_todo()
1892-
&& matches!(param, ast::Expr::Starred(_) | ast::Expr::Subscript(_));
1893-
parameter_types.push(param_type);
1894-
}
1903+
for param in params {
1904+
let param_type = self.infer_type_expression(param);
1905+
// This is similar to what we currently do for inferring tuple type expression.
1906+
// We currently infer `Todo` for the parameters to avoid invalid diagnostics
1907+
// when trying to check for assignability or any other relation. For example,
1908+
// `*tuple[int, str]`, `Unpack[]`, etc. are not yet supported.
1909+
return_todo |= param_type.is_todo()
1910+
&& matches!(param, ast::Expr::Starred(_) | ast::Expr::Subscript(_));
1911+
parameter_types.push(param_type);
18951912
}
18961913

18971914
return Some(if return_todo {

0 commit comments

Comments
 (0)