mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Proper assignability/subtyping checks for protocols with method members (#20165)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This commit is contained in:
parent
bb9be263c7
commit
33b3d44ebd
5 changed files with 104 additions and 113 deletions
|
@ -170,10 +170,10 @@ reveal_type(len(ZeroOrOne())) # revealed: Literal[0, 1]
|
|||
reveal_type(len(ZeroOrTrue())) # revealed: Literal[0, 1]
|
||||
reveal_type(len(OneOrFalse())) # revealed: Literal[1, 0]
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
# error: [invalid-argument-type] "Argument to function `len` is incorrect: Expected `Sized`, found `OneOrFoo`"
|
||||
reveal_type(len(OneOrFoo())) # revealed: int
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
# error: [invalid-argument-type] "Argument to function `len` is incorrect: Expected `Sized`, found `ZeroOrStr`"
|
||||
reveal_type(len(ZeroOrStr())) # revealed: int
|
||||
```
|
||||
|
||||
|
@ -194,46 +194,6 @@ reveal_type(len(LiteralTrue())) # revealed: Literal[1]
|
|||
reveal_type(len(LiteralFalse())) # revealed: Literal[0]
|
||||
```
|
||||
|
||||
### Enums
|
||||
|
||||
```py
|
||||
from enum import Enum, auto
|
||||
from typing import Literal
|
||||
|
||||
class SomeEnum(Enum):
|
||||
AUTO = auto()
|
||||
INT = 2
|
||||
STR = "4"
|
||||
TUPLE = (8, "16")
|
||||
INT_2 = 3_2
|
||||
|
||||
class Auto:
|
||||
def __len__(self) -> Literal[SomeEnum.AUTO]:
|
||||
return SomeEnum.AUTO
|
||||
|
||||
class Int:
|
||||
def __len__(self) -> Literal[SomeEnum.INT]:
|
||||
return SomeEnum.INT
|
||||
|
||||
class Str:
|
||||
def __len__(self) -> Literal[SomeEnum.STR]:
|
||||
return SomeEnum.STR
|
||||
|
||||
class Tuple:
|
||||
def __len__(self) -> Literal[SomeEnum.TUPLE]:
|
||||
return SomeEnum.TUPLE
|
||||
|
||||
class IntUnion:
|
||||
def __len__(self) -> Literal[SomeEnum.INT, SomeEnum.INT_2]:
|
||||
return SomeEnum.INT
|
||||
|
||||
reveal_type(len(Auto())) # revealed: int
|
||||
reveal_type(len(Int())) # revealed: int
|
||||
reveal_type(len(Str())) # revealed: int
|
||||
reveal_type(len(Tuple())) # revealed: int
|
||||
reveal_type(len(IntUnion())) # revealed: int
|
||||
```
|
||||
|
||||
### Negative integers
|
||||
|
||||
```py
|
||||
|
@ -263,8 +223,8 @@ class SecondRequiredArgument:
|
|||
# this is fine: the call succeeds at runtime since the second argument is optional
|
||||
reveal_type(len(SecondOptionalArgument())) # revealed: Literal[0]
|
||||
|
||||
# TODO: Emit a diagnostic
|
||||
reveal_type(len(SecondRequiredArgument())) # revealed: Literal[1]
|
||||
# error: [invalid-argument-type] "Argument to function `len` is incorrect: Expected `Sized`, found `SecondRequiredArgument`"
|
||||
reveal_type(len(SecondRequiredArgument())) # revealed: int
|
||||
```
|
||||
|
||||
### No `__len__`
|
||||
|
|
|
@ -1766,9 +1766,7 @@ class DefinitelyNotSubtype:
|
|||
|
||||
static_assert(is_subtype_of(NominalSubtype, P))
|
||||
static_assert(not is_subtype_of(DefinitelyNotSubtype, P))
|
||||
|
||||
# TODO: should pass
|
||||
static_assert(not is_subtype_of(NotSubtype, P)) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NotSubtype, P))
|
||||
```
|
||||
|
||||
A callable instance attribute is not sufficient for a type to satisfy a protocol with a method
|
||||
|
@ -1924,27 +1922,22 @@ static_assert(is_assignable_to(NominalGeneric, LegacyClassScoped[int]))
|
|||
# and there exist fully static materializations of `NewStyleClassScoped[Unknown]`
|
||||
# where `Nominal` would not be a subtype of the given materialization,
|
||||
# hence there is no subtyping relation:
|
||||
#
|
||||
# TODO: these should pass
|
||||
static_assert(not is_subtype_of(NominalConcrete, NewStyleClassScoped)) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalConcrete, LegacyClassScoped)) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalConcrete, NewStyleClassScoped))
|
||||
static_assert(not is_subtype_of(NominalConcrete, LegacyClassScoped))
|
||||
|
||||
# Similarly, `NominalGeneric` is implicitly `NominalGeneric[Unknown`]
|
||||
#
|
||||
# TODO: these should pass
|
||||
static_assert(not is_subtype_of(NominalGeneric, NewStyleClassScoped[int])) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalGeneric, LegacyClassScoped[int])) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalGeneric, NewStyleClassScoped[int]))
|
||||
static_assert(not is_subtype_of(NominalGeneric, LegacyClassScoped[int]))
|
||||
|
||||
static_assert(is_subtype_of(NominalConcrete, NewStyleClassScoped[int]))
|
||||
static_assert(is_subtype_of(NominalConcrete, LegacyClassScoped[int]))
|
||||
static_assert(is_subtype_of(NominalGeneric[int], NewStyleClassScoped[int]))
|
||||
static_assert(is_subtype_of(NominalGeneric[int], LegacyClassScoped[int]))
|
||||
|
||||
# TODO: these should pass
|
||||
static_assert(not is_assignable_to(NominalConcrete, NewStyleClassScoped[str])) # error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(NominalConcrete, LegacyClassScoped[str])) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalGeneric[int], NewStyleClassScoped[str])) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(NominalGeneric[int], LegacyClassScoped[str])) # error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(NominalConcrete, NewStyleClassScoped[str]))
|
||||
static_assert(not is_assignable_to(NominalConcrete, LegacyClassScoped[str]))
|
||||
static_assert(not is_subtype_of(NominalGeneric[int], NewStyleClassScoped[str]))
|
||||
static_assert(not is_subtype_of(NominalGeneric[int], LegacyClassScoped[str]))
|
||||
```
|
||||
|
||||
And they can also have generic contexts scoped to the method:
|
||||
|
@ -2219,24 +2212,24 @@ class Foo(Protocol):
|
|||
static_assert(is_subtype_of(Callable[[int], str], Foo))
|
||||
static_assert(is_assignable_to(Callable[[int], str], Foo))
|
||||
|
||||
# TODO: these should pass
|
||||
static_assert(not is_subtype_of(Callable[[str], str], Foo)) # error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(Callable[[str], str], Foo)) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(Callable[[CallMeMaybe, int], str], Foo)) # error: [static-assert-error]
|
||||
static_assert(not is_assignable_to(Callable[[CallMeMaybe, int], str], Foo)) # error: [static-assert-error]
|
||||
static_assert(not is_subtype_of(Callable[[str], str], Foo))
|
||||
static_assert(not is_assignable_to(Callable[[str], str], Foo))
|
||||
static_assert(not is_subtype_of(Callable[[CallMeMaybe, int], str], Foo))
|
||||
static_assert(not is_assignable_to(Callable[[CallMeMaybe, int], str], Foo))
|
||||
|
||||
def h(obj: Callable[[int], str], obj2: Foo, obj3: Callable[[str], str]):
|
||||
obj2 = obj
|
||||
|
||||
# TODO: we should emit [invalid-assignment] here because the signature of `obj3` is not assignable
|
||||
# to the declared type of `obj2`
|
||||
# error: [invalid-assignment] "Object of type `(str, /) -> str` is not assignable to `Foo`"
|
||||
obj2 = obj3
|
||||
|
||||
def satisfies_foo(x: int) -> str:
|
||||
return "foo"
|
||||
|
||||
static_assert(is_subtype_of(TypeOf[satisfies_foo], Foo))
|
||||
static_assert(is_assignable_to(TypeOf[satisfies_foo], Foo))
|
||||
|
||||
# TODO: this should pass
|
||||
static_assert(is_subtype_of(TypeOf[satisfies_foo], Foo)) # error: [static-assert-error]
|
||||
```
|
||||
|
||||
## Nominal subtyping of protocols
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue