diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md new file mode 100644 index 0000000000000..cc5f2b3d04dff --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md @@ -0,0 +1,250 @@ +# Invalid assignment diagnostics + + + +```toml +[environment] +python-version = "3.12" +``` + +This file contains various scenarios of `invalid-assignment` (and related) diagnostics where we +(attempt to) do better than just report "type X is not assignable to type Y". + +## Basic + +Mainly for comparison: this is the most basic kind of `invalid-assignment` diagnostic: + +```py +def _(source: str): + target: bytes = source # error: [invalid-assignment] +``` + +## Unions + +Assigning a union to a non-union: + +```py +def _(source: str | None): + target: str = source # error: [invalid-assignment] +``` + +Assigning a non-union to a union: + +```py +def _(source: int): + target: str | None = source # error: [invalid-assignment] +``` + +Assigning a union to a union: + +```py +def _(source: str | None): + target: bytes | None = source # error: [invalid-assignment] +``` + +## Tuples + +Wrong element types: + +```py +def _(source: tuple[int, str, bool]): + target: tuple[int, bytes, bool] = source # error: [invalid-assignment] +``` + +Wrong number of elements: + +```py +def _(source: tuple[int, str]): + target: tuple[int, str, bool] = source # error: [invalid-assignment] +``` + +## `Callable` + +Assigning a function to a `Callable` + +```py +from typing import Any, Callable + +def source(x: int, y: str) -> None: + raise NotImplementedError + +target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] +``` + +Assigning a `Callable` to a `Callable` with wrong parameter type: + +```py +def _(source: Callable[[int, str], bool]): + target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] +``` + +Assigning a `Callable` to a `Callable` with wrong return type: + +```py +def _(source: Callable[[int, bytes], None]): + target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] +``` + +Assigning a `Callable` to a `Callable` with wrong number of parameters: + +```py +def _(source: Callable[[int, str], bool]): + target: Callable[[int], bool] = source # error: [invalid-assignment] +``` + +Assigning a class to a `Callable` + +```py +class Number: + def __init__(self, value: int): ... + +target: Callable[[str], Any] = Number # error: [invalid-assignment] +``` + +## Function assignability and overrides + +Liskov checks use function-to-function assignability. + +Wrong parameter type: + +```py +class Parent: + def method(self, x: str) -> bool: + raise NotImplementedError + +class Child1(Parent): + # error: [invalid-method-override] + def method(self, x: bytes) -> bool: + raise NotImplementedError +``` + +Wrong return type: + +```py +class Child2(Parent): + # error: [invalid-method-override] + def method(self, x: str) -> None: + raise NotImplementedError +``` + +Wrong non-positional-only parameter name: + +```py +class Child3(Parent): + # error: [invalid-method-override] + def method(self, y: str): + raise NotImplementedError +``` + +## `TypedDict` + +Incompatible field types: + +```py +from typing import Any, TypedDict + +class Person(TypedDict): + name: str + +class Other(TypedDict): + name: bytes + +def _(source: Person): + target: Other = source # error: [invalid-assignment] +``` + +Missing required fields: + +```py +class PersonWithAge(TypedDict): + name: str + age: int + +def _(source: Person): + target: PersonWithAge = source # error: [invalid-assignment] +``` + +Assigning a `TypedDict` to a `dict` + +```py +class Person(TypedDict): + name: str + +def _(source: Person): + target: dict[str, Any] = source # error: [invalid-assignment] +``` + +## Protocols + +Missing protocol members: + +```py +from typing import Protocol + +class SupportsCheck(Protocol): + def check(self, x: int, y: str) -> bool: ... + +class DoesNotHaveCheck: ... + +def _(source: DoesNotHaveCheck): + target: SupportsCheck = source # error: [invalid-assignment] +``` + +Incompatible types for protocol members: + +```py +class CheckWithWrongSignature: + def check(self, x: int, y: bytes) -> bool: + return False + +def _(source: CheckWithWrongSignature): + target: SupportsCheck = source # error: [invalid-assignment] +``` + +## Type aliases + +Type aliases should be expanded in diagnostics to understand the underlying incompatibilities: + +```py +from typing import Protocol + +class SupportsName(Protocol): + def name(self) -> str: ... + +class HasName: + def name(self) -> bytes: + return b"" + +type StringOrName = str | SupportsName + +def _(source: HasName): + target: SupportsName = source # error: [invalid-assignment] +``` + +## Deeply nested incompatibilities + +```py +from typing import Callable + +def source(x: tuple[int, str]) -> bool: + return False + +target: Callable[[tuple[int, bytes]], bool] = source # error: [invalid-assignment] +``` + +## Multiple nested incompatibilities + +```py +from typing import Protocol + +class SupportsCheck(Protocol): + def check1(self, x: str): ... + def check2(self, x: int) -> bool: ... + +class Incompatible: + def check1(self, x: bytes): ... + def check2(self, x: int) -> None: ... + +def _(source: Incompatible): + target: SupportsCheck = source # error: [invalid-assignment] +``` diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md similarity index 100% rename from crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md rename to crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Basic_(7e8ff12bff1e8ba1).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Basic_(7e8ff12bff1e8ba1).snap" new file mode 100644 index 0000000000000..abae1cebb867c --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Basic_(7e8ff12bff1e8ba1).snap" @@ -0,0 +1,34 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Basic +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` +1 | def _(source: str): +2 | target: bytes = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `str` is not assignable to `bytes` + --> src/mdtest_snippet.py:2:13 + | +1 | def _(source: str): +2 | target: bytes = source # error: [invalid-assignment] + | ----- ^^^^^^ Incompatible value of type `str` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Deeply_nested_incomp\342\200\246_(4771d5c9736f1df8).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Deeply_nested_incomp\342\200\246_(4771d5c9736f1df8).snap" new file mode 100644 index 0000000000000..490bacee0a54d --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Deeply_nested_incomp\342\200\246_(4771d5c9736f1df8).snap" @@ -0,0 +1,39 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Deeply nested incompatibilities +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` +1 | from typing import Callable +2 | +3 | def source(x: tuple[int, str]) -> bool: +4 | return False +5 | +6 | target: Callable[[tuple[int, bytes]], bool] = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `def source(x: tuple[int, str]) -> bool` is not assignable to `(tuple[int, bytes], /) -> bool` + --> src/mdtest_snippet.py:6:9 + | +4 | return False +5 | +6 | target: Callable[[tuple[int, bytes]], bool] = source # error: [invalid-assignment] + | ----------------------------------- ^^^^^^ Incompatible value of type `def source(x: tuple[int, str]) -> bool` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Function_assignabili\342\200\246_(c38a5ba9bdfd90e8).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Function_assignabili\342\200\246_(c38a5ba9bdfd90e8).snap" new file mode 100644 index 0000000000000..ba6141ea86724 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Function_assignabili\342\200\246_(c38a5ba9bdfd90e8).snap" @@ -0,0 +1,102 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Function assignability and overrides +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | class Parent: + 2 | def method(self, x: str) -> bool: + 3 | raise NotImplementedError + 4 | + 5 | class Child1(Parent): + 6 | # error: [invalid-method-override] + 7 | def method(self, x: bytes) -> bool: + 8 | raise NotImplementedError + 9 | class Child2(Parent): +10 | # error: [invalid-method-override] +11 | def method(self, x: str) -> None: +12 | raise NotImplementedError +13 | class Child3(Parent): +14 | # error: [invalid-method-override] +15 | def method(self, y: str): +16 | raise NotImplementedError +``` + +# Diagnostics + +``` +error[invalid-method-override]: Invalid override of method `method` + --> src/mdtest_snippet.py:7:9 + | +5 | class Child1(Parent): +6 | # error: [invalid-method-override] +7 | def method(self, x: bytes) -> bool: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Definition is incompatible with `Parent.method` +8 | raise NotImplementedError +9 | class Child2(Parent): + | + ::: src/mdtest_snippet.py:2:9 + | +1 | class Parent: +2 | def method(self, x: str) -> bool: + | ---------------------------- `Parent.method` defined here +3 | raise NotImplementedError + | +info: This violates the Liskov Substitution Principle +info: rule `invalid-method-override` is enabled by default + +``` + +``` +error[invalid-method-override]: Invalid override of method `method` + --> src/mdtest_snippet.py:11:9 + | + 9 | class Child2(Parent): +10 | # error: [invalid-method-override] +11 | def method(self, x: str) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Definition is incompatible with `Parent.method` +12 | raise NotImplementedError +13 | class Child3(Parent): + | + ::: src/mdtest_snippet.py:2:9 + | + 1 | class Parent: + 2 | def method(self, x: str) -> bool: + | ---------------------------- `Parent.method` defined here + 3 | raise NotImplementedError + | +info: This violates the Liskov Substitution Principle +info: rule `invalid-method-override` is enabled by default + +``` + +``` +error[invalid-method-override]: Invalid override of method `method` + --> src/mdtest_snippet.py:15:9 + | +13 | class Child3(Parent): +14 | # error: [invalid-method-override] +15 | def method(self, y: str): + | ^^^^^^^^^^^^^^^^^^^^ Definition is incompatible with `Parent.method` +16 | raise NotImplementedError + | + ::: src/mdtest_snippet.py:2:9 + | + 1 | class Parent: + 2 | def method(self, x: str) -> bool: + | ---------------------------- `Parent.method` defined here + 3 | raise NotImplementedError + | +info: This violates the Liskov Substitution Principle +info: rule `invalid-method-override` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_nested_inco\342\200\246_(9d79916b62cea322).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_nested_inco\342\200\246_(9d79916b62cea322).snap" new file mode 100644 index 0000000000000..9786a73cb64f0 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_nested_inco\342\200\246_(9d79916b62cea322).snap" @@ -0,0 +1,44 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Multiple nested incompatibilities +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | from typing import Protocol + 2 | + 3 | class SupportsCheck(Protocol): + 4 | def check1(self, x: str): ... + 5 | def check2(self, x: int) -> bool: ... + 6 | + 7 | class Incompatible: + 8 | def check1(self, x: bytes): ... + 9 | def check2(self, x: int) -> None: ... +10 | +11 | def _(source: Incompatible): +12 | target: SupportsCheck = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `Incompatible` is not assignable to `SupportsCheck` + --> src/mdtest_snippet.py:12:13 + | +11 | def _(source: Incompatible): +12 | target: SupportsCheck = source # error: [invalid-assignment] + | ------------- ^^^^^^ Incompatible value of type `Incompatible` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Protocols_(d6d4caa1b1180b74).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Protocols_(d6d4caa1b1180b74).snap" new file mode 100644 index 0000000000000..5456d31509621 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Protocols_(d6d4caa1b1180b74).snap" @@ -0,0 +1,63 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Protocols +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | from typing import Protocol + 2 | + 3 | class SupportsCheck(Protocol): + 4 | def check(self, x: int, y: str) -> bool: ... + 5 | + 6 | class DoesNotHaveCheck: ... + 7 | + 8 | def _(source: DoesNotHaveCheck): + 9 | target: SupportsCheck = source # error: [invalid-assignment] +10 | class CheckWithWrongSignature: +11 | def check(self, x: int, y: bytes) -> bool: +12 | return False +13 | +14 | def _(source: CheckWithWrongSignature): +15 | target: SupportsCheck = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `DoesNotHaveCheck` is not assignable to `SupportsCheck` + --> src/mdtest_snippet.py:9:13 + | + 8 | def _(source: DoesNotHaveCheck): + 9 | target: SupportsCheck = source # error: [invalid-assignment] + | ------------- ^^^^^^ Incompatible value of type `DoesNotHaveCheck` + | | + | Declared type +10 | class CheckWithWrongSignature: +11 | def check(self, x: int, y: bytes) -> bool: + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `CheckWithWrongSignature` is not assignable to `SupportsCheck` + --> src/mdtest_snippet.py:15:13 + | +14 | def _(source: CheckWithWrongSignature): +15 | target: SupportsCheck = source # error: [invalid-assignment] + | ------------- ^^^^^^ Incompatible value of type `CheckWithWrongSignature` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Tuples_(fe1bc35fec6e57b4).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Tuples_(fe1bc35fec6e57b4).snap" new file mode 100644 index 0000000000000..4dd01bdd7a769 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Tuples_(fe1bc35fec6e57b4).snap" @@ -0,0 +1,53 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Tuples +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` +1 | def _(source: tuple[int, str, bool]): +2 | target: tuple[int, bytes, bool] = source # error: [invalid-assignment] +3 | def _(source: tuple[int, str]): +4 | target: tuple[int, str, bool] = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `tuple[int, str, bool]` is not assignable to `tuple[int, bytes, bool]` + --> src/mdtest_snippet.py:2:13 + | +1 | def _(source: tuple[int, str, bool]): +2 | target: tuple[int, bytes, bool] = source # error: [invalid-assignment] + | ----------------------- ^^^^^^ Incompatible value of type `tuple[int, str, bool]` + | | + | Declared type +3 | def _(source: tuple[int, str]): +4 | target: tuple[int, str, bool] = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `tuple[int, str]` is not assignable to `tuple[int, str, bool]` + --> src/mdtest_snippet.py:4:13 + | +2 | target: tuple[int, bytes, bool] = source # error: [invalid-assignment] +3 | def _(source: tuple[int, str]): +4 | target: tuple[int, str, bool] = source # error: [invalid-assignment] + | --------------------- ^^^^^^ Incompatible value of type `tuple[int, str]` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Type_aliases_(8ab0fe5706e7da9e).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Type_aliases_(8ab0fe5706e7da9e).snap" new file mode 100644 index 0000000000000..d17a05058f8a1 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Type_aliases_(8ab0fe5706e7da9e).snap" @@ -0,0 +1,45 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Type aliases +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | from typing import Protocol + 2 | + 3 | class SupportsName(Protocol): + 4 | def name(self) -> str: ... + 5 | + 6 | class HasName: + 7 | def name(self) -> bytes: + 8 | return b"" + 9 | +10 | type StringOrName = str | SupportsName +11 | +12 | def _(source: HasName): +13 | target: SupportsName = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `HasName` is not assignable to `SupportsName` + --> src/mdtest_snippet.py:13:13 + | +12 | def _(source: HasName): +13 | target: SupportsName = source # error: [invalid-assignment] + | ------------ ^^^^^^ Incompatible value of type `HasName` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unions_(4434e7e4a696d6d5).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unions_(4434e7e4a696d6d5).snap" new file mode 100644 index 0000000000000..6d8821152bc27 --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unions_(4434e7e4a696d6d5).snap" @@ -0,0 +1,72 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - Unions +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` +1 | def _(source: str | None): +2 | target: str = source # error: [invalid-assignment] +3 | def _(source: int): +4 | target: str | None = source # error: [invalid-assignment] +5 | def _(source: str | None): +6 | target: bytes | None = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `str | None` is not assignable to `str` + --> src/mdtest_snippet.py:2:13 + | +1 | def _(source: str | None): +2 | target: str = source # error: [invalid-assignment] + | --- ^^^^^^ Incompatible value of type `str | None` + | | + | Declared type +3 | def _(source: int): +4 | target: str | None = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `int` is not assignable to `str | None` + --> src/mdtest_snippet.py:4:13 + | +2 | target: str = source # error: [invalid-assignment] +3 | def _(source: int): +4 | target: str | None = source # error: [invalid-assignment] + | ---------- ^^^^^^ Incompatible value of type `int` + | | + | Declared type +5 | def _(source: str | None): +6 | target: bytes | None = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `str | None` is not assignable to `bytes | None` + --> src/mdtest_snippet.py:6:13 + | +4 | target: str | None = source # error: [invalid-assignment] +5 | def _(source: str | None): +6 | target: bytes | None = source # error: [invalid-assignment] + | ------------ ^^^^^^ Incompatible value of type `str | None` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`Callable`_(d447753c67f673ad).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`Callable`_(d447753c67f673ad).snap" new file mode 100644 index 0000000000000..01f9a8477fc6b --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`Callable`_(d447753c67f673ad).snap" @@ -0,0 +1,117 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - `Callable` +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | from typing import Any, Callable + 2 | + 3 | def source(x: int, y: str) -> None: + 4 | raise NotImplementedError + 5 | + 6 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + 7 | def _(source: Callable[[int, str], bool]): + 8 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + 9 | def _(source: Callable[[int, bytes], None]): +10 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] +11 | def _(source: Callable[[int, str], bool]): +12 | target: Callable[[int], bool] = source # error: [invalid-assignment] +13 | class Number: +14 | def __init__(self, value: int): ... +15 | +16 | target: Callable[[str], Any] = Number # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `def source(x: int, y: str) -> None` is not assignable to `(int, bytes, /) -> bool` + --> src/mdtest_snippet.py:6:9 + | +4 | raise NotImplementedError +5 | +6 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + | ---------------------------- ^^^^^^ Incompatible value of type `def source(x: int, y: str) -> None` + | | + | Declared type +7 | def _(source: Callable[[int, str], bool]): +8 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `(int, str, /) -> bool` is not assignable to `(int, bytes, /) -> bool` + --> src/mdtest_snippet.py:8:13 + | + 6 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + 7 | def _(source: Callable[[int, str], bool]): + 8 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + | ---------------------------- ^^^^^^ Incompatible value of type `(int, str, /) -> bool` + | | + | Declared type + 9 | def _(source: Callable[[int, bytes], None]): +10 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `(int, bytes, /) -> None` is not assignable to `(int, bytes, /) -> bool` + --> src/mdtest_snippet.py:10:13 + | + 8 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + 9 | def _(source: Callable[[int, bytes], None]): +10 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] + | ---------------------------- ^^^^^^ Incompatible value of type `(int, bytes, /) -> None` + | | + | Declared type +11 | def _(source: Callable[[int, str], bool]): +12 | target: Callable[[int], bool] = source # error: [invalid-assignment] + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `(int, str, /) -> bool` is not assignable to `(int, /) -> bool` + --> src/mdtest_snippet.py:12:13 + | +10 | target: Callable[[int, bytes], bool] = source # error: [invalid-assignment] +11 | def _(source: Callable[[int, str], bool]): +12 | target: Callable[[int], bool] = source # error: [invalid-assignment] + | --------------------- ^^^^^^ Incompatible value of type `(int, str, /) -> bool` + | | + | Declared type +13 | class Number: +14 | def __init__(self, value: int): ... + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `` is not assignable to `(str, /) -> Any` + --> src/mdtest_snippet.py:16:9 + | +14 | def __init__(self, value: int): ... +15 | +16 | target: Callable[[str], Any] = Number # error: [invalid-assignment] + | -------------------- ^^^^^^ Incompatible value of type `` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`TypedDict`_(c8d8ad73050ae4d7).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`TypedDict`_(c8d8ad73050ae4d7).snap" new file mode 100644 index 0000000000000..5a3ec8d1fc1ce --- /dev/null +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_d\342\200\246_-_Invalid_assignment_d\342\200\246_-_`TypedDict`_(c8d8ad73050ae4d7).snap" @@ -0,0 +1,85 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- + +--- +mdtest name: invalid_assignment_details.md - Invalid assignment diagnostics - `TypedDict` +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_details.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | from typing import Any, TypedDict + 2 | + 3 | class Person(TypedDict): + 4 | name: str + 5 | + 6 | class Other(TypedDict): + 7 | name: bytes + 8 | + 9 | def _(source: Person): +10 | target: Other = source # error: [invalid-assignment] +11 | class PersonWithAge(TypedDict): +12 | name: str +13 | age: int +14 | +15 | def _(source: Person): +16 | target: PersonWithAge = source # error: [invalid-assignment] +17 | class Person(TypedDict): +18 | name: str +19 | +20 | def _(source: Person): +21 | target: dict[str, Any] = source # error: [invalid-assignment] +``` + +# Diagnostics + +``` +error[invalid-assignment]: Object of type `Person` is not assignable to `Other` + --> src/mdtest_snippet.py:10:13 + | + 9 | def _(source: Person): +10 | target: Other = source # error: [invalid-assignment] + | ----- ^^^^^^ Incompatible value of type `Person` + | | + | Declared type +11 | class PersonWithAge(TypedDict): +12 | name: str + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `Person` is not assignable to `PersonWithAge` + --> src/mdtest_snippet.py:16:13 + | +15 | def _(source: Person): +16 | target: PersonWithAge = source # error: [invalid-assignment] + | ------------- ^^^^^^ Incompatible value of type `Person` + | | + | Declared type +17 | class Person(TypedDict): +18 | name: str + | +info: rule `invalid-assignment` is enabled by default + +``` + +``` +error[invalid-assignment]: Object of type `Person` is not assignable to `dict[str, Any]` + --> src/mdtest_snippet.py:21:13 + | +20 | def _(source: Person): +21 | target: dict[str, Any] = source # error: [invalid-assignment] + | -------------- ^^^^^^ Incompatible value of type `Person` + | | + | Declared type + | +info: rule `invalid-assignment` is enabled by default + +``` diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(4b799ca1eeb857b9).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(b0568dbda1e94374).snap" similarity index 79% rename from "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(4b799ca1eeb857b9).snap" rename to "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(b0568dbda1e94374).snap" index 76a1dade8c94a..c176128c34df4 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(4b799ca1eeb857b9).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Annotated_assignment_(b0568dbda1e94374).snap" @@ -4,8 +4,8 @@ expression: snapshot --- --- -mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Annotated assignment -mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md +mdtest name: invalid_assignment_syntactic_variants.md - Invalid assignment diagnostics - Annotated assignment +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md --- # Python source files diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(f316976ffe72c6c7).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(429392d5a8842ca6).snap" similarity index 82% rename from "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(f316976ffe72c6c7).snap" rename to "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(429392d5a8842ca6).snap" index a1e4da5e36d83..62c41660e9425 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(f316976ffe72c6c7).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiline_expression\342\200\246_(429392d5a8842ca6).snap" @@ -4,8 +4,8 @@ expression: snapshot --- --- -mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Multiline expressions -mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md +mdtest name: invalid_assignment_syntactic_variants.md - Invalid assignment diagnostics - Multiline expressions +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md --- # Python source files diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(e20ddfd7a91affb0).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(655e9238f07236b2).snap" similarity index 88% rename from "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(e20ddfd7a91affb0).snap" rename to "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(655e9238f07236b2).snap" index ebf3b8179962d..55bbf76bece8e 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(e20ddfd7a91affb0).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Multiple_targets_(655e9238f07236b2).snap" @@ -4,8 +4,8 @@ expression: snapshot --- --- -mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Multiple targets -mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md +mdtest name: invalid_assignment_syntactic_variants.md - Invalid assignment diagnostics - Multiple targets +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md --- # Python source files diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(35c120b3bd9929f8).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(f3e81bd84a3c9ca3).snap" similarity index 80% rename from "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(35c120b3bd9929f8).snap" rename to "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(f3e81bd84a3c9ca3).snap" index d285720f31c4a..74f76cd2aef5d 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(35c120b3bd9929f8).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Named_expression_(f3e81bd84a3c9ca3).snap" @@ -4,8 +4,8 @@ expression: snapshot --- --- -mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Named expression -mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md +mdtest name: invalid_assignment_syntactic_variants.md - Invalid assignment diagnostics - Named expression +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md --- # Python source files diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(67e4b9239d5681a).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(9ca7498412f218b3).snap" similarity index 79% rename from "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(67e4b9239d5681a).snap" rename to "crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(9ca7498412f218b3).snap" index 3099de022c7eb..ab58fda9af5d8 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(67e4b9239d5681a).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment_s\342\200\246_-_Invalid_assignment_d\342\200\246_-_Unannotated_assignme\342\200\246_(9ca7498412f218b3).snap" @@ -4,8 +4,8 @@ expression: snapshot --- --- -mdtest name: invalid_assignment.md - Invalid assignment diagnostics - Unannotated assignment -mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment.md +mdtest name: invalid_assignment_syntactic_variants.md - Invalid assignment diagnostics - Unannotated assignment +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assignment_syntactic_variants.md --- # Python source files