mirror of https://github.com/astral-sh/ruff
[red-knot] Support overloads for callable equivalence (#17698)
## Summary Part of #15383, this PR adds `is_equivalent_to` support for overloaded callables. This is mainly done by delegating it to the subtyping check in that two types A and B are considered equivalent if A is a subtype of B and B is a subtype of A. ## Test Plan Add test cases for overloaded callables in `is_equivalent_to.md`
This commit is contained in:
parent
549ab74bd6
commit
f11d9cb509
|
|
@ -256,6 +256,65 @@ static_assert(is_equivalent_to(int | Callable[[int | str], None], Callable[[str
|
||||||
|
|
||||||
### Overloads
|
### Overloads
|
||||||
|
|
||||||
TODO
|
#### One overload
|
||||||
|
|
||||||
|
`overloaded.pyi`:
|
||||||
|
|
||||||
|
```pyi
|
||||||
|
from typing import overload
|
||||||
|
|
||||||
|
class Grandparent: ...
|
||||||
|
class Parent(Grandparent): ...
|
||||||
|
class Child(Parent): ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def overloaded(a: Child) -> None: ...
|
||||||
|
@overload
|
||||||
|
def overloaded(a: Parent) -> None: ...
|
||||||
|
@overload
|
||||||
|
def overloaded(a: Grandparent) -> None: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
from knot_extensions import CallableTypeOf, is_equivalent_to, static_assert
|
||||||
|
from overloaded import Grandparent, Parent, Child, overloaded
|
||||||
|
|
||||||
|
def grandparent(a: Grandparent) -> None: ...
|
||||||
|
|
||||||
|
static_assert(is_equivalent_to(CallableTypeOf[grandparent], CallableTypeOf[overloaded]))
|
||||||
|
static_assert(is_equivalent_to(CallableTypeOf[overloaded], CallableTypeOf[grandparent]))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Both overloads
|
||||||
|
|
||||||
|
`overloaded.pyi`:
|
||||||
|
|
||||||
|
```pyi
|
||||||
|
from typing import overload
|
||||||
|
|
||||||
|
class Grandparent: ...
|
||||||
|
class Parent(Grandparent): ...
|
||||||
|
class Child(Parent): ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def pg(a: Parent) -> None: ...
|
||||||
|
@overload
|
||||||
|
def pg(a: Grandparent) -> None: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def cpg(a: Child) -> None: ...
|
||||||
|
@overload
|
||||||
|
def cpg(a: Parent) -> None: ...
|
||||||
|
@overload
|
||||||
|
def cpg(a: Grandparent) -> None: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
from knot_extensions import CallableTypeOf, is_equivalent_to, static_assert
|
||||||
|
from overloaded import pg, cpg
|
||||||
|
|
||||||
|
static_assert(is_equivalent_to(CallableTypeOf[pg], CallableTypeOf[cpg]))
|
||||||
|
static_assert(is_equivalent_to(CallableTypeOf[cpg], CallableTypeOf[pg]))
|
||||||
|
```
|
||||||
|
|
||||||
[the equivalence relation]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
[the equivalence relation]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
||||||
|
|
|
||||||
|
|
@ -6984,11 +6984,22 @@ impl<'db> CallableType<'db> {
|
||||||
fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
match (&**self.signatures(db), &**other.signatures(db)) {
|
match (&**self.signatures(db), &**other.signatures(db)) {
|
||||||
([self_signature], [other_signature]) => {
|
([self_signature], [other_signature]) => {
|
||||||
|
// Common case: both callable types contain a single signature, use the custom
|
||||||
|
// equivalence check instead of delegating it to the subtype check.
|
||||||
self_signature.is_equivalent_to(db, other_signature)
|
self_signature.is_equivalent_to(db, other_signature)
|
||||||
}
|
}
|
||||||
_ => {
|
(self_signatures, other_signatures) => {
|
||||||
// TODO: overloads
|
if !self_signatures
|
||||||
false
|
.iter()
|
||||||
|
.chain(other_signatures.iter())
|
||||||
|
.all(|signature| signature.is_fully_static(db))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.is_subtype_of(db, other) && other.is_subtype_of(db, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue