mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
Initial implementation of single starred arg in overload call
This commit is contained in:
parent
990202768e
commit
4cd7de1fa1
2 changed files with 77 additions and 64 deletions
|
@ -139,8 +139,7 @@ reveal_type(f(A())) # revealed: A
|
|||
reveal_type(f(*(A(),))) # revealed: A
|
||||
|
||||
reveal_type(f(B())) # revealed: A
|
||||
# TODO: revealed: A
|
||||
reveal_type(f(*(B(),))) # revealed: Unknown
|
||||
reveal_type(f(*(B(),))) # revealed: A
|
||||
|
||||
# But, in this case, the arity check filters out the first overload, so we only have one match:
|
||||
reveal_type(f(B(), 1)) # revealed: B
|
||||
|
@ -551,16 +550,13 @@ from overloaded import MyEnumSubclass, ActualEnum, f
|
|||
|
||||
def _(actual_enum: ActualEnum, my_enum_instance: MyEnumSubclass):
|
||||
reveal_type(f(actual_enum)) # revealed: Both
|
||||
# TODO: revealed: Both
|
||||
reveal_type(f(*(actual_enum,))) # revealed: Unknown
|
||||
reveal_type(f(*(actual_enum,))) # revealed: Both
|
||||
|
||||
reveal_type(f(ActualEnum.A)) # revealed: OnlyA
|
||||
# TODO: revealed: OnlyA
|
||||
reveal_type(f(*(ActualEnum.A,))) # revealed: Unknown
|
||||
reveal_type(f(*(ActualEnum.A,))) # revealed: OnlyA
|
||||
|
||||
reveal_type(f(ActualEnum.B)) # revealed: OnlyB
|
||||
# TODO: revealed: OnlyB
|
||||
reveal_type(f(*(ActualEnum.B,))) # revealed: Unknown
|
||||
reveal_type(f(*(ActualEnum.B,))) # revealed: OnlyB
|
||||
|
||||
reveal_type(f(my_enum_instance)) # revealed: MyEnumSubclass
|
||||
reveal_type(f(*(my_enum_instance,))) # revealed: MyEnumSubclass
|
||||
|
@ -969,12 +965,10 @@ reveal_type(f(*(1,))) # revealed: str
|
|||
|
||||
def _(list_int: list[int], list_any: list[Any]):
|
||||
reveal_type(f(list_int)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*(list_int,))) # revealed: Unknown
|
||||
reveal_type(f(*(list_int,))) # revealed: int
|
||||
|
||||
reveal_type(f(list_any)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*(list_any,))) # revealed: Unknown
|
||||
reveal_type(f(*(list_any,))) # revealed: int
|
||||
```
|
||||
|
||||
### Single list argument (ambiguous)
|
||||
|
@ -1008,8 +1002,7 @@ def _(list_int: list[int], list_any: list[Any]):
|
|||
# All materializations of `list[int]` are assignable to `list[int]`, so it matches the first
|
||||
# overload.
|
||||
reveal_type(f(list_int)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*(list_int,))) # revealed: Unknown
|
||||
reveal_type(f(*(list_int,))) # revealed: int
|
||||
|
||||
# All materializations of `list[Any]` are assignable to `list[int]` and `list[Any]`, but the
|
||||
# return type of first and second overloads are not equivalent, so the overload matching
|
||||
|
@ -1042,25 +1035,21 @@ reveal_type(f("a")) # revealed: str
|
|||
reveal_type(f(*("a",))) # revealed: str
|
||||
|
||||
reveal_type(f((1, "b"))) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*((1, "b"),))) # revealed: Unknown
|
||||
reveal_type(f(*((1, "b"),))) # revealed: int
|
||||
|
||||
reveal_type(f((1, 2))) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*((1, 2),))) # revealed: Unknown
|
||||
reveal_type(f(*((1, 2),))) # revealed: int
|
||||
|
||||
def _(int_str: tuple[int, str], int_any: tuple[int, Any], any_any: tuple[Any, Any]):
|
||||
# All materializations are assignable to first overload, so second and third overloads are
|
||||
# eliminated
|
||||
reveal_type(f(int_str)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*(int_str,))) # revealed: Unknown
|
||||
reveal_type(f(*(int_str,))) # revealed: int
|
||||
|
||||
# All materializations are assignable to second overload, so the third overload is eliminated;
|
||||
# the return type of first and second overload is equivalent
|
||||
reveal_type(f(int_any)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*(int_any,))) # revealed: Unknown
|
||||
reveal_type(f(*(int_any,))) # revealed: int
|
||||
|
||||
# All materializations of `tuple[Any, Any]` are assignable to the parameters of all the
|
||||
# overloads, but the return types aren't equivalent, so the overload matching is ambiguous
|
||||
|
@ -1188,8 +1177,7 @@ from overloaded import f
|
|||
|
||||
def _(literal: LiteralString, string: str, any: Any):
|
||||
reveal_type(f(literal)) # revealed: LiteralString
|
||||
# TODO: revealed: LiteralString
|
||||
reveal_type(f(*(literal,))) # revealed: Unknown
|
||||
reveal_type(f(*(literal,))) # revealed: LiteralString
|
||||
|
||||
reveal_type(f(string)) # revealed: str
|
||||
reveal_type(f(*(string,))) # revealed: str
|
||||
|
@ -1227,12 +1215,10 @@ from overloaded import f
|
|||
|
||||
def _(list_int: list[int], list_str: list[str], list_any: list[Any], any: Any):
|
||||
reveal_type(f(list_int)) # revealed: A
|
||||
# TODO: revealed: A
|
||||
reveal_type(f(*(list_int,))) # revealed: Unknown
|
||||
reveal_type(f(*(list_int,))) # revealed: A
|
||||
|
||||
reveal_type(f(list_str)) # revealed: str
|
||||
# TODO: Should be `str`
|
||||
reveal_type(f(*(list_str,))) # revealed: Unknown
|
||||
reveal_type(f(*(list_str,))) # revealed: str
|
||||
|
||||
reveal_type(f(list_any)) # revealed: Unknown
|
||||
reveal_type(f(*(list_any,))) # revealed: Unknown
|
||||
|
@ -1433,12 +1419,10 @@ def _(any: Any):
|
|||
reveal_type(f(*(any,), flag=False)) # revealed: str
|
||||
|
||||
def _(args: tuple[Any, Literal[True]]):
|
||||
# TODO: revealed: int
|
||||
reveal_type(f(*args)) # revealed: Unknown
|
||||
reveal_type(f(*args)) # revealed: int
|
||||
|
||||
def _(args: tuple[Any, Literal[False]]):
|
||||
# TODO: revealed: str
|
||||
reveal_type(f(*args)) # revealed: Unknown
|
||||
reveal_type(f(*args)) # revealed: str
|
||||
```
|
||||
|
||||
### Argument type expansion
|
||||
|
|
|
@ -1522,50 +1522,79 @@ impl<'db> CallableBinding<'db> {
|
|||
arguments: &CallArguments<'_, 'db>,
|
||||
matching_overload_indexes: &[usize],
|
||||
) {
|
||||
// These are the parameter indexes that matches the arguments that participate in the
|
||||
// filtering process.
|
||||
//
|
||||
// The parameter types at these indexes have at least one overload where the type isn't
|
||||
// gradual equivalent to the parameter types at the same index for other overloads.
|
||||
// These are the parameter indexes that will participate in the filtering process. The
|
||||
// parameter types at these indexes have at least one overload where the type isn't
|
||||
// equivalent to the parameter types at the same index for other overloads. For example, if
|
||||
// all parameter types at a specific index for the remaining overloads are equivalent, they
|
||||
// aren't useful to filter the overloads, so we don't include them here.
|
||||
let mut participating_parameter_indexes = HashSet::new();
|
||||
let mut current_parameter_index = 0;
|
||||
|
||||
loop {
|
||||
// TODO: Should the parameter kind be considered here? For example, even though the
|
||||
// types are equivalent, what if the parameter kinds aren't?
|
||||
let mut first_parameter_type: Option<Type<'db>> = None;
|
||||
|
||||
for &overload_index in matching_overload_indexes {
|
||||
let overload = &self.overloads[overload_index];
|
||||
let Some(parameter) = overload.signature.parameters().get(current_parameter_index)
|
||||
else {
|
||||
// There's no parameter at this index for this overload, but we can't stop here
|
||||
// because other overloads might have a parameter at this index.
|
||||
continue;
|
||||
};
|
||||
// TODO: For an unannotated `self` / `cls` parameter, the type should be
|
||||
// `typing.Self` / `type[typing.Self]`
|
||||
let parameter_type = parameter.annotated_type().unwrap_or(Type::unknown());
|
||||
if let Some(first_parameter_type) = first_parameter_type {
|
||||
if !first_parameter_type.is_equivalent_to(db, parameter_type) {
|
||||
participating_parameter_indexes.insert(current_parameter_index);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
first_parameter_type = Some(parameter_type);
|
||||
}
|
||||
}
|
||||
|
||||
current_parameter_index += 1;
|
||||
|
||||
if first_parameter_type.is_none() {
|
||||
// None of the overloads had a parameter at this index, so we can stop here.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if participating_parameter_indexes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// These only contain the top materialized argument types for the corresponding
|
||||
// participating parameter indexes.
|
||||
let mut top_materialized_argument_types = vec![];
|
||||
|
||||
for (argument_index, argument_type) in arguments.iter_types().enumerate() {
|
||||
let mut first_parameter_type: Option<Type<'db>> = None;
|
||||
let mut participating_parameter_index = None;
|
||||
|
||||
'overload: for overload_index in matching_overload_indexes {
|
||||
let overload = &self.overloads[*overload_index];
|
||||
for parameter_index in &overload.argument_matches[argument_index].parameters {
|
||||
// TODO: For an unannotated `self` / `cls` parameter, the type should be
|
||||
// `typing.Self` / `type[typing.Self]`
|
||||
let current_parameter_type = overload.signature.parameters()[*parameter_index]
|
||||
.annotated_type()
|
||||
.unwrap_or(Type::unknown());
|
||||
if let Some(first_parameter_type) = first_parameter_type {
|
||||
if !first_parameter_type.is_equivalent_to(db, current_parameter_type) {
|
||||
participating_parameter_index = Some(*parameter_index);
|
||||
break 'overload;
|
||||
}
|
||||
} else {
|
||||
first_parameter_type = Some(current_parameter_type);
|
||||
for (argument_index, (argument, argument_type)) in arguments.iter().enumerate() {
|
||||
let argument_type = argument_type.unwrap_or_else(Type::unknown);
|
||||
// TODO: Add support for `**kwargs`. Should we avoid doing any filtering if `**kwargs`
|
||||
// is present to any incorrect filtering?
|
||||
if matches!(argument, Argument::Variadic(_)) {
|
||||
for (index, &unpacked_argument_type) in
|
||||
argument_type.iterate(db).all_elements().enumerate()
|
||||
{
|
||||
let adjusted_index = argument_index + index;
|
||||
if !participating_parameter_indexes.contains(&adjusted_index) {
|
||||
continue;
|
||||
}
|
||||
top_materialized_argument_types
|
||||
.push(unpacked_argument_type.top_materialization(db));
|
||||
}
|
||||
} else {
|
||||
if !participating_parameter_indexes.contains(&argument_index) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(parameter_index) = participating_parameter_index {
|
||||
participating_parameter_indexes.insert(parameter_index);
|
||||
top_materialized_argument_types.push(argument_type.top_materialization(db));
|
||||
}
|
||||
}
|
||||
|
||||
if top_materialized_argument_types.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let top_materialized_argument_type =
|
||||
Type::heterogeneous_tuple(db, top_materialized_argument_types);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue