diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md
index 1c28473a8993a..f3551881334a4 100644
--- a/crates/ty/docs/rules.md
+++ b/crates/ty/docs/rules.md
@@ -8,7 +8,7 @@
Default level: error ·
Added in 0.0.13 ·
Related issues ·
-View source
+View source
@@ -49,7 +49,7 @@ class Derived(Base): # Error: `Derived` does not implement `method`
Default level: warn ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -90,7 +90,7 @@ class SubProto(BaseProto, Protocol):
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -126,7 +126,7 @@ def _(x: int):
Default level: error ·
Preview (since 0.0.16) ·
Related issues ·
-View source
+View source
@@ -175,7 +175,7 @@ Foo.method() # Error: cannot call abstract classmethod
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -199,7 +199,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
Default level: error ·
Added in 0.0.7 ·
Related issues ·
-View source
+View source
@@ -230,7 +230,7 @@ def f(x: object):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -262,7 +262,7 @@ f(int) # error
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -293,7 +293,7 @@ a = 1
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -325,7 +325,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -357,7 +357,7 @@ class B(A): ...
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -385,7 +385,7 @@ type B = A
Default level: error ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -417,7 +417,7 @@ class Example:
Default level: warn ·
Added in 0.0.1-alpha.16 ·
Related issues ·
-View source
+View source
@@ -444,7 +444,7 @@ old_func() # emits [deprecated] diagnostic
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -473,7 +473,7 @@ false positives it can produce.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -500,7 +500,7 @@ class B(A, A): ...
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -538,7 +538,7 @@ class A: # Crash at runtime
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -609,7 +609,7 @@ def foo() -> "intt\b": ...
Default level: error ·
Added in 0.0.20 ·
Related issues ·
-View source
+View source
@@ -641,7 +641,7 @@ def my_function() -> int:
Default level: error ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -736,7 +736,7 @@ def test(): -> "Literal[5]":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -766,7 +766,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -792,7 +792,7 @@ t[3] # IndexError: tuple index out of range
Default level: warn ·
Added in 0.0.1-alpha.33 ·
Related issues ·
-View source
+View source
@@ -826,7 +826,7 @@ class MyClass: ...
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -915,7 +915,7 @@ an atypical memory layout.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -942,7 +942,7 @@ func("foo") # error: [invalid-argument-type]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -970,7 +970,7 @@ a: int = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1004,7 +1004,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -1040,7 +1040,7 @@ asyncio.run(main())
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1064,7 +1064,7 @@ class A(42): ... # error: [invalid-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1091,7 +1091,7 @@ with 1:
Default level: error ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -1128,7 +1128,7 @@ class Foo(NamedTuple):
Default level: error ·
Added in 0.0.13 ·
Related issues ·
-View source
+View source
@@ -1160,7 +1160,7 @@ class A:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1189,7 +1189,7 @@ a: str
Default level: warn ·
Added in 0.0.20 ·
Related issues ·
-View source
+View source
@@ -1238,7 +1238,7 @@ class Pet(Enum):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1282,7 +1282,7 @@ except ZeroDivisionError:
Default level: error ·
Added in 0.0.1-alpha.28 ·
Related issues ·
-View source
+View source
@@ -1324,7 +1324,7 @@ class D(A):
Default level: error ·
Added in 0.0.1-alpha.35 ·
Related issues ·
-View source
+View source
@@ -1368,7 +1368,7 @@ class NonFrozenChild(FrozenBase): # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1406,7 +1406,7 @@ class D(Generic[U, T]): ...
Default level: error ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -1485,7 +1485,7 @@ a = 20 / 0 # type: ignore
Default level: error ·
Added in 0.0.1-alpha.17 ·
Related issues ·
-View source
+View source
@@ -1524,7 +1524,7 @@ carol = Person(name="Carol", aeg=25) # typo!
Default level: warn ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -1585,7 +1585,7 @@ def f(x, y, /): # Python 3.8+ syntax
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1620,7 +1620,7 @@ def f(t: TypeVar("U")): ...
Default level: error ·
Added in 0.0.18 ·
Related issues ·
-View source
+View source
@@ -1648,7 +1648,7 @@ match x:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1682,7 +1682,7 @@ class B(metaclass=f): ...
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1789,7 +1789,7 @@ Correct use of `@override` is enforced by ty's [`invalid-explicit-override`](#in
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -1843,7 +1843,7 @@ AttributeError: Cannot overwrite NamedTuple attribute _asdict
Default level: warn ·
Added in 0.0.31 ·
Related issues ·
-View source
+View source
@@ -1884,7 +1884,7 @@ admin[0] # "Alice"
Default level: error ·
Added in 0.0.1-alpha.27 ·
Related issues ·
-View source
+View source
@@ -1914,7 +1914,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1964,7 +1964,7 @@ def foo(x: int) -> int: ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1990,7 +1990,7 @@ def f(a: int = ''): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2021,7 +2021,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2055,7 +2055,7 @@ TypeError: Protocols can only inherit from other protocols, got
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2104,7 +2104,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2133,7 +2133,7 @@ def func() -> int:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2229,7 +2229,7 @@ class C: ...
Default level: error ·
Added in 0.0.10 ·
Related issues ·
-View source
+View source
@@ -2275,7 +2275,7 @@ class MyClass:
Default level: error ·
Added in 0.0.1-alpha.6 ·
Related issues ·
-View source
+View source
@@ -2302,7 +2302,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -2349,7 +2349,7 @@ Bar[int] # error: too few arguments
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2379,7 +2379,7 @@ TYPE_CHECKING = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2409,7 +2409,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -2443,7 +2443,7 @@ f(10) # Error
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -2477,7 +2477,7 @@ class C:
Default level: error ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -2508,7 +2508,7 @@ def g[U, T: U](): ... # error: [invalid-type-variable-bound]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2555,7 +2555,7 @@ U = TypeVar('U', list[int], int) # valid constrained Type
Default level: error ·
Added in 0.0.16 ·
Related issues ·
-View source
+View source
@@ -2587,7 +2587,7 @@ U = TypeVar("U", int, str, default=bytes) # error: [invalid-type-variable-defau
Default level: error ·
Added in 0.0.28 ·
Related issues ·
-View source
+View source
@@ -2618,7 +2618,7 @@ class Child(Base):
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -2653,7 +2653,7 @@ def f(x: dict):
Default level: error ·
Added in 0.0.9 ·
Related issues ·
-View source
+View source
@@ -2684,7 +2684,7 @@ class Foo(TypedDict):
Default level: error ·
Added in 0.0.25 ·
Related issues ·
-View source
+View source
@@ -2715,7 +2715,7 @@ def gen() -> Iterator[int]:
Default level: error ·
Added in 0.0.14 ·
Related issues ·
-View source
+View source
@@ -2770,7 +2770,7 @@ def h(arg2: type):
Default level: error ·
Added in 0.0.15 ·
Related issues ·
-View source
+View source
@@ -2813,7 +2813,7 @@ def g(arg: object):
Default level: warn ·
Added in 0.0.30 ·
Related issues ·
-View source
+View source
@@ -2851,7 +2851,7 @@ Movie = TypedDict("Film", {"title": str}) # error: [mismatched-type-name]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2876,7 +2876,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -2909,7 +2909,7 @@ alice["age"] # KeyError
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2938,7 +2938,7 @@ func("string") # error: [no-matching-overload]
Default level: error ·
Added in 0.0.30 ·
Related issues ·
-View source
+View source
@@ -2971,7 +2971,7 @@ class Sub(Super): ... # error: [non-callable-init-subclass]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2997,7 +2997,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3021,7 +3021,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -3054,7 +3054,7 @@ class B(A):
Default level: error ·
Added in 0.0.16 ·
Related issues ·
-View source
+View source
@@ -3087,7 +3087,7 @@ class B(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3114,7 +3114,7 @@ f(1, x=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -3141,7 +3141,7 @@ f(x=1) # Error raised here
Default level: ignore ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -3174,7 +3174,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -3206,7 +3206,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: ignore ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -3243,7 +3243,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: warn ·
Added in 0.0.23 ·
Related issues ·
-View source
+View source
@@ -3270,7 +3270,7 @@ html.parser # AttributeError: module 'html' has no attribute 'parser'
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3334,7 +3334,7 @@ def test(): -> "int":
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3361,7 +3361,7 @@ cast(int, f()) # Redundant
Default level: warn ·
Added in 0.0.18 ·
Related issues ·
-View source
+View source
@@ -3393,7 +3393,7 @@ class C:
Default level: error ·
Added in 0.0.20 ·
Related issues ·
-View source
+View source
@@ -3427,7 +3427,7 @@ class Outer[T]:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3457,7 +3457,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3486,7 +3486,7 @@ class B(A): ... # Error raised here
Default level: error ·
Added in 0.0.1-alpha.30 ·
Related issues ·
-View source
+View source
@@ -3520,7 +3520,7 @@ class F(NamedTuple):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3547,7 +3547,7 @@ f("foo") # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3575,7 +3575,7 @@ def _(x: int):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3621,7 +3621,7 @@ class A:
Default level: error ·
Added in 0.0.20 ·
Related issues ·
-View source
+View source
@@ -3658,7 +3658,7 @@ class C(Generic[T]):
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3682,7 +3682,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3709,7 +3709,7 @@ f(x=1, y=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3737,7 +3737,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: warn ·
Added in 0.0.1-alpha.15 ·
Related issues ·
-View source
+View source
@@ -3795,7 +3795,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3820,7 +3820,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3845,7 +3845,7 @@ print(x) # NameError: name 'x' is not defined
Default level: warn ·
Added in 0.0.1-alpha.7 ·
Related issues ·
-View source
+View source
@@ -3884,7 +3884,7 @@ class D(C): ... # error: [unsupported-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3921,7 +3921,7 @@ b1 < b2 < b1 # exception raised here
Default level: ignore ·
Added in 0.0.12 ·
Related issues ·
-View source
+View source
@@ -3961,7 +3961,7 @@ def factory(base: type[Base]) -> type:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -3989,7 +3989,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
Default level: warn ·
Preview (since 0.0.21) ·
Related issues ·
-View source
+View source
@@ -4095,7 +4095,7 @@ to `false`.
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -4158,7 +4158,7 @@ def foo(x: int | str) -> int | str:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
diff --git a/crates/ty/tests/cli/rule_selection.rs b/crates/ty/tests/cli/rule_selection.rs
index ec71fbc18a9f2..02300dc28274a 100644
--- a/crates/ty/tests/cli/rule_selection.rs
+++ b/crates/ty/tests/cli/rule_selection.rs
@@ -1105,15 +1105,17 @@ fn configuration_all_rules_with_rule_sorting_before_all() -> anyhow::Result<()>
exit_code: 1
----- stdout -----
error[abstract-method-in-final-class]: Final class `Derived` has unimplemented abstract methods
- --> test.py:11:7
+ --> test.py:6:5
|
- 11 | class Derived(Base):
- | ^^^^^^^ `foo` is unimplemented
- |
- ::: test.py:7:9
- |
- 7 | def foo(self) -> int:
- | --- `foo` declared as abstract on superclass `Base`
+ 6 | / @abstractmethod
+ 7 | | def foo(self) -> int:
+ | |________________________- `foo` declared as abstract on superclass `Base`
+ 8 | raise NotImplementedError
+ 9 |
+ 10 | @final
+ | ------
+ 11 | class Derived(Base):
+ | ^^^^^^^ `foo` is unimplemented
|
info: rule `abstract-method-in-final-class` was selected in the configuration file
@@ -1166,15 +1168,17 @@ fn overrides_all_rules_with_rule_sorting_before_all() -> anyhow::Result<()> {
exit_code: 1
----- stdout -----
error[abstract-method-in-final-class]: Final class `Derived` has unimplemented abstract methods
- --> src/test.py:11:7
- |
- 11 | class Derived(Base):
- | ^^^^^^^ `foo` is unimplemented
- |
- ::: src/test.py:7:9
+ --> src/test.py:6:5
|
- 7 | def foo(self) -> int:
- | --- `foo` declared as abstract on superclass `Base`
+ 6 | / @abstractmethod
+ 7 | | def foo(self) -> int:
+ | |________________________- `foo` declared as abstract on superclass `Base`
+ 8 | raise NotImplementedError
+ 9 |
+ 10 | @final
+ | ------
+ 11 | class Derived(Base):
+ | ^^^^^^^ `foo` is unimplemented
|
info: rule `abstract-method-in-final-class` was selected in the configuration file
diff --git a/crates/ty_python_semantic/resources/mdtest/call/abstract_method.md b/crates/ty_python_semantic/resources/mdtest/call/abstract_method.md
index a2f7d9c99447b..6509b63edf230 100644
--- a/crates/ty_python_semantic/resources/mdtest/call/abstract_method.md
+++ b/crates/ty_python_semantic/resources/mdtest/call/abstract_method.md
@@ -18,16 +18,16 @@ Foo.method()
```snapshot
error[call-abstract-method]: Cannot call `method` on class object
- --> src/mdtest_snippet.py:9:1
+ --> src/mdtest_snippet.py:4:5
|
-9 | Foo.method()
- | ^^^^^^^^^^^^ `method` is an abstract classmethod with a trivial body
- |
-info: Method `method` defined here
- --> src/mdtest_snippet.py:6:9
- |
-6 | def method(cls) -> int: ...
- | ^^^^^^
+4 | / @classmethod
+5 | | @abstractmethod
+6 | | def method(cls) -> int: ...
+ | |_______________________________- Method `method` defined here
+7 |
+8 | # snapshot: call-abstract-method
+9 | Foo.method()
+ | ^^^^^^^^^^^^ `method` is an abstract classmethod with a trivial body
|
```
diff --git a/crates/ty_python_semantic/resources/mdtest/call/overloads.md b/crates/ty_python_semantic/resources/mdtest/call/overloads.md
index 995f05839396a..b9656ca57b7d5 100644
--- a/crates/ty_python_semantic/resources/mdtest/call/overloads.md
+++ b/crates/ty_python_semantic/resources/mdtest/call/overloads.md
@@ -956,10 +956,11 @@ error[no-matching-overload]: No overload of function `f` matches arguments
|
info: Limit of argument type expansion reached at argument 9
info: First overload defined here
- --> src/overloaded.pyi:8:5
+ --> src/overloaded.pyi:7:1
|
-8 | def f() -> None: ...
- | ^^^^^^^^^^^
+7 | / @overload
+8 | | def f() -> None: ...
+ | |____________________^ First overload defined here
|
info: Possible overloads for function `f`:
info: () -> None
diff --git a/crates/ty_python_semantic/resources/mdtest/del.md b/crates/ty_python_semantic/resources/mdtest/del.md
index 3149a50829bc6..6ac52f41384f9 100644
--- a/crates/ty_python_semantic/resources/mdtest/del.md
+++ b/crates/ty_python_semantic/resources/mdtest/del.md
@@ -450,10 +450,15 @@ error[invalid-argument-type]: Cannot delete required key "name" from TypedDict `
| ^^^^^^
|
info: Field defined here
- --> src/mdtest_snippet.py:4:5
+ --> src/mdtest_snippet.py:3:7
|
+3 | class Movie(TypedDict):
+ | ---------------- `Movie` defined here
4 | name: str
- | --------- `name` declared as required here; consider making it `NotRequired`
+ | ---------
+ | |
+ | `name` declared as required here
+ | Consider making it `NotRequired`
|
info: Only keys marked as `NotRequired` (or in a TypedDict with `total=False`) can be deleted
```
@@ -485,10 +490,15 @@ error[invalid-argument-type]: Cannot delete required key "name" from TypedDict `
| ^^^^^^
|
info: Field defined here
- --> src/mdtest_snippet.py:12:5
+ --> src/mdtest_snippet.py:11:7
|
+11 | class MixedMovie(TypedDict):
+ | --------------------- `MixedMovie` defined here
12 | name: str
- | --------- `name` declared as required here; consider making it `NotRequired`
+ | ---------
+ | |
+ | `name` declared as required here
+ | Consider making it `NotRequired`
|
info: Only keys marked as `NotRequired` (or in a TypedDict with `total=False`) can be deleted
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Abstract_method_in_g\342\200\246_(6d8b024dda7ced11).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Abstract_method_in_g\342\200\246_(6d8b024dda7ced11).snap"
index d65494736608d..875d98df1357d 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Abstract_method_in_g\342\200\246_(6d8b024dda7ced11).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Abstract_method_in_g\342\200\246_(6d8b024dda7ced11).snap"
@@ -32,15 +32,19 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `Child` has unimplemented abstract methods
- --> src/mdtest_snippet.py:12:7
+ --> src/mdtest_snippet.py:5:5
|
-12 | class Child(Parent): # error: [abstract-method-in-final-class]
- | ^^^^^ `method` is unimplemented
- |
- ::: src/mdtest_snippet.py:6:9
- |
- 6 | def method(self) -> int: ...
- | ------ `method` declared as abstract on superclass `GrandParent`
+ 5 | / @abstractmethod
+ 6 | | def method(self) -> int: ...
+ | |________________________________- `method` declared as abstract on superclass `GrandParent`
+ 7 |
+ 8 | class Parent(GrandParent):
+ 9 | pass
+10 |
+11 | @final
+ | ------
+12 | class Child(Parent): # error: [abstract-method-in-final-class]
+ | ^^^^^ `method` is unimplemented
|
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Basic_case_with_ABC_(21e412599c45972a).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Basic_case_with_ABC_(21e412599c45972a).snap"
index dda5074f43a4c..77e677b06f64e 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Basic_case_with_ABC_(21e412599c45972a).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Basic_case_with_ABC_(21e412599c45972a).snap"
@@ -30,15 +30,17 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `Derived` has unimplemented abstract methods
- --> src/mdtest_snippet.py:10:7
+ --> src/mdtest_snippet.py:5:5
|
-10 | class Derived(Base): # error: [abstract-method-in-final-class] "Final class `Derived` has unimplemented abstract method `foo`"
- | ^^^^^^^ `foo` is unimplemented
- |
- ::: src/mdtest_snippet.py:6:9
- |
- 6 | def foo(self) -> int:
- | --- `foo` declared as abstract on superclass `Base`
+ 5 | / @abstractmethod
+ 6 | | def foo(self) -> int:
+ | |________________________- `foo` declared as abstract on superclass `Base`
+ 7 | raise NotImplementedError
+ 8 |
+ 9 | @final
+ | ------
+10 | class Derived(Base): # error: [abstract-method-in-final-class] "Final class `Derived` has unimplemented abstract method `foo`"
+ | ^^^^^^^ `foo` is unimplemented
|
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(ecae0f4510696c95).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(ecae0f4510696c95).snap"
index 34fcdbdb97f77..364cd63c677be 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(ecae0f4510696c95).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(ecae0f4510696c95).snap"
@@ -45,13 +45,16 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `Abstract` has unimplemented abstract methods
- --> src/mdtest_snippet.py:6:7
+ --> src/mdtest_snippet.py:4:1
|
-6 | class Abstract(ABC):
- | ^^^^^^^^ Abstract methods `aaaaaaaaaa`, `bbbbbbbb`, `cccccccc`, `ddddddddd`, `eeeeeeeee`, `ffffffff`, `ggggggg`, `hhhhhhhh`, `iiiiiiiii` and `kkkkkkkkkk` are unimplemented
-7 | @abstractmethod
-8 | def aaaaaaaaaa(self) -> int: ...
- | ---------- `aaaaaaaaaa` declared as abstract
+4 | @final
+ | ------
+5 | # error: [abstract-method-in-final-class] "Final class `Abstract` has unimplemented abstract methods `aaaaaaaaaa`, `bbbbbbbb`, `ccccccc…
+6 | class Abstract(ABC):
+ | ^^^^^^^^ Abstract methods `aaaaaaaaaa`, `bbbbbbbb`, `cccccccc`, `ddddddddd`, `eeeeeeeee`, `ffffffff`, `ggggggg`, `hhhhhhhh`, `iiiiiiiii` and `kkkkkkkkkk` are unimplemented
+7 | / @abstractmethod
+8 | | def aaaaaaaaaa(self) -> int: ...
+ | |____________________________________- `aaaaaaaaaa` declared as abstract
|
info: rule `abstract-method-in-final-class` is enabled by default
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(f807ff3716d8ab0d).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(f807ff3716d8ab0d).snap"
index 22724e278cf87..1bdc276848e1d 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(f807ff3716d8ab0d).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Diagnostic_when_ther\342\200\246_(f807ff3716d8ab0d).snap"
@@ -45,13 +45,16 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `Abstract` has unimplemented abstract methods
- --> src/mdtest_snippet.py:6:7
+ --> src/mdtest_snippet.py:4:1
|
-6 | class Abstract(ABC):
- | ^^^^^^^^ 10 abstract methods are unimplemented, including `aaaaaaaaaa`, `bbbbbbbb` and `cccccccc`
-7 | @abstractmethod
-8 | def aaaaaaaaaa(self) -> int: ...
- | ---------- `aaaaaaaaaa` declared as abstract
+4 | @final
+ | ------
+5 | # error: [abstract-method-in-final-class] "Final class `Abstract` has 10 unimplemented abstract methods, including `aaaaaaaaaa`, `bbbbb…
+6 | class Abstract(ABC):
+ | ^^^^^^^^ 10 abstract methods are unimplemented, including `aaaaaaaaaa`, `bbbbbbbb` and `cccccccc`
+7 | / @abstractmethod
+8 | | def aaaaaaaaaa(self) -> int: ...
+ | |____________________________________- `aaaaaaaaaa` declared as abstract
|
info: Use `--verbose` to see all 10 unimplemented abstract methods
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Multiple_abstract_me\342\200\246_(feafee9a4abbe8d1).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Multiple_abstract_me\342\200\246_(feafee9a4abbe8d1).snap"
index 46ea5dcbcadf2..dfc8d0464f2b2 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Multiple_abstract_me\342\200\246_(feafee9a4abbe8d1).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Multiple_abstract_me\342\200\246_(feafee9a4abbe8d1).snap"
@@ -41,30 +41,36 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `MissingAll` has unimplemented abstract methods
- --> src/mdtest_snippet.py:13:7
+ --> src/mdtest_snippet.py:12:1
|
-13 | class MissingAll(Base): # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^ Abstract methods `foo`, `bar` and `baz` are unimplemented
+12 | @final
+ | ------
+13 | class MissingAll(Base): # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^ Abstract methods `foo`, `bar` and `baz` are unimplemented
|
- ::: src/mdtest_snippet.py:6:9
+ ::: src/mdtest_snippet.py:5:5
|
- 6 | def foo(self) -> int: ...
- | --- `foo` declared as abstract on superclass `Base`
+ 5 | / @abstractmethod
+ 6 | | def foo(self) -> int: ...
+ | |_____________________________- `foo` declared as abstract on superclass `Base`
|
```
```
error[abstract-method-in-final-class]: Final class `PartiallyImplemented` has unimplemented abstract methods
- --> src/mdtest_snippet.py:17:7
+ --> src/mdtest_snippet.py:16:1
|
-17 | class PartiallyImplemented(Base): # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^^^^^^^^^^^ `baz` is unimplemented
+16 | @final
+ | ------
+17 | class PartiallyImplemented(Base): # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^^^^^^^^^^^ `baz` is unimplemented
|
- ::: src/mdtest_snippet.py:10:9
+ ::: src/mdtest_snippet.py:9:5
|
-10 | def baz(self) -> None: ...
- | --- `baz` declared as abstract on superclass `Base`
+ 9 | / @abstractmethod
+10 | | def baz(self) -> None: ...
+ | |______________________________- `baz` declared as abstract on superclass `Base`
|
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Protocol_with_implic\342\200\246_(e373f31c7a7d88e7).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Protocol_with_implic\342\200\246_(e373f31c7a7d88e7).snap"
index 055630997ead6..c421af29c49cc 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Protocol_with_implic\342\200\246_(e373f31c7a7d88e7).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_A_`@final`_class_mus\342\200\246_-_Protocol_with_implic\342\200\246_(e373f31c7a7d88e7).snap"
@@ -169,12 +169,13 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/final.md
```
error[abstract-method-in-final-class]: Final class `Q` has unimplemented abstract methods
- --> src/mdtest_snippet.py:11:9
+ --> src/mdtest_snippet.py:11:5
|
11 | def still_abstractmethod(self): ...
- | -------------------- `still_abstractmethod` declared as abstract on superclass `P`
+ | ----------------------------------- `still_abstractmethod` declared as abstract on superclass `P`
12 |
13 | @final
+ | ------
14 | class Q(P): ... # error: [abstract-method-in-final-class]
| ^ `still_abstractmethod` is unimplemented
|
@@ -190,12 +191,13 @@ help: Change the body of `still_abstractmethod` to `return` or `return None` if
```
error[abstract-method-in-final-class]: Final class `S` has unimplemented abstract methods
- --> src/mdtest_snippet.py:18:9
+ --> src/mdtest_snippet.py:18:5
|
18 | def also_still_abstractmethod(self) -> None: ...
- | ------------------------- `also_still_abstractmethod` declared as abstract on superclass `R`
+ | ------------------------------------------------ `also_still_abstractmethod` declared as abstract on superclass `R`
19 |
20 | @final
+ | ------
21 | class S(R): ... # error: [abstract-method-in-final-class]
| ^ `also_still_abstractmethod` is unimplemented
|
@@ -211,15 +213,16 @@ help: Change the body of `also_still_abstractmethod` to `return` or `return None
```
error[abstract-method-in-final-class]: Final class `RaisesSub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:28:7
+ --> src/mdtest_snippet.py:24:5
|
-28 | class RaisesSub(Raises): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^ `even_this_is_abstract` is unimplemented
- |
- ::: src/mdtest_snippet.py:24:9
- |
-24 | def even_this_is_abstract(self):
- | --------------------- `even_this_is_abstract` declared as abstract on superclass `Raises`
+24 | / def even_this_is_abstract(self):
+25 | | raise NotImplementedError
+ | |_________________________________- `even_this_is_abstract` declared as abstract on superclass `Raises`
+26 |
+27 | @final
+ | ------
+28 | class RaisesSub(Raises): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^ `even_this_is_abstract` is unimplemented
|
info: `Raises.even_this_is_abstract` is implicitly abstract because `Raises` is a `Protocol` class and `even_this_is_abstract` lacks an implementation
--> src/mdtest_snippet.py:23:7
@@ -232,15 +235,16 @@ info: `Raises.even_this_is_abstract` is implicitly abstract because `Raises` is
```
error[abstract-method-in-final-class]: Final class `AlsoRaisesSub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:35:7
- |
-35 | class AlsoRaisesSub(AlsoRaises): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^^^^ `also_abstractmethod` is unimplemented
+ --> src/mdtest_snippet.py:31:5
|
- ::: src/mdtest_snippet.py:31:9
- |
-31 | def also_abstractmethod(self) -> Never:
- | ------------------- `also_abstractmethod` declared as abstract on superclass `AlsoRaises`
+31 | / def also_abstractmethod(self) -> Never:
+32 | | raise NotImplementedError
+ | |_________________________________- `also_abstractmethod` declared as abstract on superclass `AlsoRaises`
+33 |
+34 | @final
+ | ------
+35 | class AlsoRaisesSub(AlsoRaises): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^^^^ `also_abstractmethod` is unimplemented
|
info: `AlsoRaises.also_abstractmethod` is implicitly abstract because `AlsoRaises` is a `Protocol` class and `also_abstractmethod` lacks an implementation
--> src/mdtest_snippet.py:30:7
@@ -253,15 +257,16 @@ info: `AlsoRaises.also_abstractmethod` is implicitly abstract because `AlsoRaise
```
error[abstract-method-in-final-class]: Final class `StrangeSub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:45:11
- |
-45 | class StrangeSub(Strange): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^ `weird_abstractmethod` is unimplemented
- |
- ::: src/mdtest_snippet.py:41:13
+ --> src/mdtest_snippet.py:41:9
|
-41 | def weird_abstractmethod(self):
- | -------------------- `weird_abstractmethod` declared as abstract on superclass `Strange`
+41 | / def weird_abstractmethod(self):
+42 | | raise x
+ | |___________________- `weird_abstractmethod` declared as abstract on superclass `Strange`
+43 |
+44 | @final
+ | ------
+45 | class StrangeSub(Strange): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^ `weird_abstractmethod` is unimplemented
|
info: `Strange.weird_abstractmethod` is implicitly abstract because `Strange` is a `Protocol` class and `weird_abstractmethod` lacks an implementation
--> src/mdtest_snippet.py:40:11
@@ -280,6 +285,7 @@ error[abstract-method-in-final-class]: Final class `HasOverloadSub` has unimplem
| --- `foo` declared as abstract on superclass `HasOverloads`
52 |
53 | @final
+ | ------
54 | class HasOverloadSub(HasOverloads): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^ `foo` is unimplemented
|
@@ -294,15 +300,17 @@ info: `HasOverloads.foo` is implicitly abstract because `HasOverloads` is a `Pro
```
error[abstract-method-in-final-class]: Final class `HasAbstractSub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:123:7
+ --> src/mdtest_snippet.py:122:1
|
+122 | @final
+ | ------
123 | class HasAbstractSub(HasAbstract): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:72:9
+ ::: src/mdtest_snippet.py:72:5
|
72 | def a(self) -> int: ...
- | - `a` declared as abstract on superclass `HasAbstract`
+ | ----------------------- `a` declared as abstract on superclass `HasAbstract`
|
info: `HasAbstract.a` is implicitly abstract because `HasAbstract` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:71:7
@@ -315,15 +323,18 @@ info: `HasAbstract.a` is implicitly abstract because `HasAbstract` is a `Protoco
```
error[abstract-method-in-final-class]: Final class `HasAbstract2Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:126:7
+ --> src/mdtest_snippet.py:125:1
|
-126 | class HasAbstract2Sub(HasAbstract2): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^^^^^^ `a` is unimplemented
+125 | @final
+ | ------
+126 | class HasAbstract2Sub(HasAbstract2): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:75:9
+ ::: src/mdtest_snippet.py:75:5
|
- 75 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract2`
+ 75 | / def a(self) -> int:
+ 76 | | pass
+ | |____________- `a` declared as abstract on superclass `HasAbstract2`
|
info: `HasAbstract2.a` is implicitly abstract because `HasAbstract2` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:74:7
@@ -336,15 +347,17 @@ info: `HasAbstract2.a` is implicitly abstract because `HasAbstract2` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract3Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:129:7
+ --> src/mdtest_snippet.py:128:1
|
+128 | @final
+ | ------
129 | class HasAbstract3Sub(HasAbstract4): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:83:9
+ ::: src/mdtest_snippet.py:83:5
|
83 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract4`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract4`
|
info: `HasAbstract4.a` is implicitly abstract because `HasAbstract4` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:82:7
@@ -357,15 +370,17 @@ info: `HasAbstract4.a` is implicitly abstract because `HasAbstract4` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract4Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:132:7
+ --> src/mdtest_snippet.py:131:1
|
+131 | @final
+ | ------
132 | class HasAbstract4Sub(HasAbstract4): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:83:9
+ ::: src/mdtest_snippet.py:83:5
|
83 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract4`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract4`
|
info: `HasAbstract4.a` is implicitly abstract because `HasAbstract4` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:82:7
@@ -378,15 +393,17 @@ info: `HasAbstract4.a` is implicitly abstract because `HasAbstract4` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract5Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:135:7
+ --> src/mdtest_snippet.py:134:1
|
+134 | @final
+ | ------
135 | class HasAbstract5Sub(HasAbstract5): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:88:9
+ ::: src/mdtest_snippet.py:88:5
|
88 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract5`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract5`
|
info: `HasAbstract5.a` is implicitly abstract because `HasAbstract5` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:87:7
@@ -399,15 +416,17 @@ info: `HasAbstract5.a` is implicitly abstract because `HasAbstract5` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract6Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:138:7
+ --> src/mdtest_snippet.py:137:1
|
+137 | @final
+ | ------
138 | class HasAbstract6Sub(HasAbstract6): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:93:9
+ ::: src/mdtest_snippet.py:93:5
|
93 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract6`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract6`
|
info: `HasAbstract6.a` is implicitly abstract because `HasAbstract6` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:92:7
@@ -420,15 +439,18 @@ info: `HasAbstract6.a` is implicitly abstract because `HasAbstract6` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract7Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:141:7
+ --> src/mdtest_snippet.py:140:1
|
-141 | class HasAbstract7Sub(HasAbstract7): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^^^^^^ `a` is unimplemented
+140 | @final
+ | ------
+141 | class HasAbstract7Sub(HasAbstract7): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:105:9
+ ::: src/mdtest_snippet.py:105:5
|
-105 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract7`
+105 | / def a(self) -> int:
+106 | | raise NotImplementedError
+ | |_________________________________- `a` declared as abstract on superclass `HasAbstract7`
|
info: `HasAbstract7.a` is implicitly abstract because `HasAbstract7` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:104:7
@@ -441,15 +463,18 @@ info: `HasAbstract7.a` is implicitly abstract because `HasAbstract7` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract8Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:144:7
+ --> src/mdtest_snippet.py:143:1
|
-144 | class HasAbstract8Sub(HasAbstract8): ... # error: [abstract-method-in-final-class]
- | ^^^^^^^^^^^^^^^ `a` is unimplemented
+143 | @final
+ | ------
+144 | class HasAbstract8Sub(HasAbstract8): ... # error: [abstract-method-in-final-class]
+ | ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:109:9
+ ::: src/mdtest_snippet.py:109:5
|
-109 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract8`
+109 | / def a(self) -> int:
+110 | | raise NotImplementedError()
+ | |___________________________________- `a` declared as abstract on superclass `HasAbstract8`
|
info: `HasAbstract8.a` is implicitly abstract because `HasAbstract8` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:108:7
@@ -462,15 +487,17 @@ info: `HasAbstract8.a` is implicitly abstract because `HasAbstract8` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract9Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:147:7
+ --> src/mdtest_snippet.py:146:1
|
+146 | @final
+ | ------
147 | class HasAbstract9Sub(HasAbstract9): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:113:9
+ ::: src/mdtest_snippet.py:113:5
|
113 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract9`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract9`
|
info: `HasAbstract9.a` is implicitly abstract because `HasAbstract9` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:112:7
@@ -483,15 +510,17 @@ info: `HasAbstract9.a` is implicitly abstract because `HasAbstract9` is a `Proto
```
error[abstract-method-in-final-class]: Final class `HasAbstract10Sub` has unimplemented abstract methods
- --> src/mdtest_snippet.py:150:7
+ --> src/mdtest_snippet.py:149:1
|
+149 | @final
+ | ------
150 | class HasAbstract10Sub(HasAbstract10): ... # error: [abstract-method-in-final-class]
| ^^^^^^^^^^^^^^^^ `a` is unimplemented
|
- ::: src/mdtest_snippet.py:118:9
+ ::: src/mdtest_snippet.py:118:5
|
118 | def a(self) -> int:
- | - `a` declared as abstract on superclass `HasAbstract10`
+ | ------------------ `a` declared as abstract on superclass `HasAbstract10`
|
info: `HasAbstract10.a` is implicitly abstract because `HasAbstract10` is a `Protocol` class and `a` lacks an implementation
--> src/mdtest_snippet.py:117:7
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_Overloaded_methods_d\342\200\246_(861757f48340ed92).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_Overloaded_methods_d\342\200\246_(861757f48340ed92).snap"
index 9daa93e8d247d..f7d51d3c021f3 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_Overloaded_methods_d\342\200\246_(861757f48340ed92).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/final.md_-_Tests_for_the_`@typi\342\200\246_-_Overloaded_methods_d\342\200\246_(861757f48340ed92).snap"
@@ -196,32 +196,34 @@ note: This is an unsafe fix and may change runtime behavior
```
error[invalid-overload]: `@final` decorator should be applied only to the first overload
- --> src/stub.pyi:27:9
+ --> src/stub.pyi:26:5
|
-27 | def bar(self, x: str) -> str: ...
- | --- First overload defined here
-28 | @overload
-29 | @final
- | ------
-30 | # error: [invalid-overload]
-31 | def bar(self, x: int) -> int: ...
- | ^^^
+26 | / @overload
+27 | | def bar(self, x: str) -> str: ...
+ | |_____________________________________- First overload defined here
+28 | @overload
+29 | @final
+ | ------
+30 | # error: [invalid-overload]
+31 | def bar(self, x: int) -> int: ...
+ | ^^^
|
```
```
error[invalid-overload]: `@final` decorator should be applied only to the first overload
- --> src/stub.pyi:33:9
+ --> src/stub.pyi:32:5
|
-33 | def baz(self, x: str) -> str: ...
- | --- First overload defined here
-34 | @final
- | ------
-35 | @overload
-36 | # error: [invalid-overload]
-37 | def baz(self, x: int) -> int: ...
- | ^^^
+32 | / @overload
+33 | | def baz(self, x: str) -> str: ...
+ | |_____________________________________- First overload defined here
+34 | @final
+ | ------
+35 | @overload
+36 | # error: [invalid-overload]
+37 | def baz(self, x: int) -> int: ...
+ | ^^^
|
```
@@ -322,8 +324,10 @@ note: This is an unsafe fix and may change runtime behavior
```
error[invalid-overload]: `@final` decorator should be applied only to the overload implementation
- --> src/main.py:24:5
+ --> src/main.py:23:5
|
+23 | @overload
+ | ---------
24 | @final
| ------
25 | def f(self, x: str) -> str: ... # error: [invalid-overload]
@@ -343,6 +347,7 @@ error[invalid-overload]: `@final` decorator should be applied only to the overlo
31 | @final
| ------
32 | @overload
+ | ---------
33 | def g(self, x: str) -> str: ... # error: [invalid-overload]
| ^
34 | @overload
@@ -355,8 +360,10 @@ error[invalid-overload]: `@final` decorator should be applied only to the overlo
```
error[invalid-overload]: `@final` decorator should be applied only to the overload implementation
- --> src/main.py:42:5
+ --> src/main.py:41:5
|
+41 | @overload
+ | ---------
42 | @final
| ------
43 | def h(self, x: int) -> int: ... # error: [invalid-overload]
@@ -374,6 +381,7 @@ error[invalid-overload]: `@final` decorator should be applied only to the overlo
49 | @final
| ------
50 | @overload
+ | ---------
51 | def i(self, x: int) -> int: ... # error: [invalid-overload]
| ^
52 | def i(self, x: int | str) -> int | str:
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_A_method_call_with_u\342\200\246_(31cb5f881221158e).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_A_method_call_with_u\342\200\246_(31cb5f881221158e).snap"
index e816a56a2b91a..d9ebde546e086 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_A_method_call_with_u\342\200\246_(31cb5f881221158e).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_A_method_call_with_u\342\200\246_(31cb5f881221158e).snap"
@@ -37,10 +37,11 @@ error[no-matching-overload]: No overload of bound method `Foo.bar` matches argum
| ^^^^^^^^^^^^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:5:9
+ --> src/mdtest_snippet.py:4:5
|
-5 | def bar(self, x: int) -> int: ...
- | ^^^^^^^^^^^^^^^^^^^^^^^^
+4 | / @overload
+5 | | def bar(self, x: int) -> int: ...
+ | |_____________________________________^ First overload defined here
|
info: Possible overloads for bound method `bar`:
info: (self, x: int) -> int
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(dd80c593d9136f35).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(dd80c593d9136f35).snap"
index 82bd2e9828655..589d5e1da11ec 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(dd80c593d9136f35).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(dd80c593d9136f35).snap"
@@ -74,10 +74,11 @@ error[no-matching-overload]: No overload of function `foo` matches arguments
| ^^^^^^^^^^^^^^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:6:5
+ --> src/mdtest_snippet.py:5:1
|
-6 | def foo(a: int, b: int, c: int): ...
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+5 | / @overload
+6 | | def foo(a: int, b: int, c: int): ...
+ | |____________________________________^ First overload defined here
|
info: Possible overloads for function `foo`:
info: (a: int, b: int, c: int) -> Unknown
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(f66e3a8a3977c472).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(f66e3a8a3977c472).snap"
index 4d6fc88ca0a3b..af1d883c02e3f 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(f66e3a8a3977c472).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Call_to_function_wit\342\200\246_(f66e3a8a3977c472).snap"
@@ -154,10 +154,11 @@ error[no-matching-overload]: No overload of function `foo` matches arguments
| ^^^^^^^^^^^^^^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:6:5
+ --> src/mdtest_snippet.py:5:1
|
-6 | def foo(a: int, b: int, c: int): ...
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+5 | / @overload
+6 | | def foo(a: int, b: int, c: int): ...
+ | |____________________________________^ First overload defined here
|
info: Possible overloads for function `foo`:
info: (a: int, b: int, c: int) -> Unknown
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(3553d085684e16a0).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(3553d085684e16a0).snap"
index 6813eff250181..3f5f5f7b277b4 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(3553d085684e16a0).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(3553d085684e16a0).snap"
@@ -35,10 +35,11 @@ error[no-matching-overload]: No overload of function `f` matches arguments
| ^^^^^^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:4:5
+ --> src/mdtest_snippet.py:3:1
|
-4 | def f(x: int) -> int: ...
- | ^^^^^^^^^^^^^^^^
+3 | / @overload
+4 | | def f(x: int) -> int: ...
+ | |_________________________^ First overload defined here
|
info: Possible overloads for function `f`:
info: (x: int) -> int
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(36814b28492c01d2).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(36814b28492c01d2).snap"
index 3bb2a264ed649..2fce9477dc835 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(36814b28492c01d2).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/no_matching_overload\342\200\246_-_No_matching_overload\342\200\246_-_Calls_to_overloaded_\342\200\246_(36814b28492c01d2).snap"
@@ -86,10 +86,10 @@ error[no-matching-overload]: No overload of function `f` matches arguments
| ^^^^^^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:4:5
+ --> src/mdtest_snippet.py:3:1
|
- 4 | def f(
- | _____^
+ 3 | / @overload
+ 4 | | def f(
5 | | lion: int,
6 | | turtle: int,
7 | | tortoise: int,
@@ -107,7 +107,7 @@ info: First overload defined here
19 | | leopard: int,
20 | | hyena: int,
21 | | ) -> int: ...
- | |________^
+ | |_____________^ First overload defined here
|
info: Possible overloads for function `f`:
info: (lion: int, turtle: int, tortoise: int, goat: int, capybara: int, chicken: int, ostrich: int, gorilla: int, giraffe: int, condor: int, kangaroo: int, anaconda: int, tarantula: int, millipede: int, leopard: int, hyena: int) -> int
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_At_least_two_overloa\342\200\246_(84dadf8abd8f2f2).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_At_least_two_overloa\342\200\246_(84dadf8abd8f2f2).snap"
index 80532b781a56a..1ab3a764ef52e 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_At_least_two_overloa\342\200\246_(84dadf8abd8f2f2).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_At_least_two_overloa\342\200\246_(84dadf8abd8f2f2).snap"
@@ -36,8 +36,11 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md
```
error[invalid-overload]: Overloaded function `func` requires at least two overloads
- --> src/mdtest_snippet.py:5:5
+ --> src/mdtest_snippet.py:3:1
|
+3 | @overload
+ | ---------
+4 | # error: [invalid-overload]
5 | def func(x: int) -> int: ...
| ^^^^ Only one overload defined here
|
@@ -46,8 +49,11 @@ error[invalid-overload]: Overloaded function `func` requires at least two overlo
```
error[invalid-overload]: Overloaded function `func` requires at least two overloads
- --> src/mdtest_snippet.pyi:5:5
+ --> src/mdtest_snippet.pyi:3:1
|
+3 | @overload
+ | ---------
+4 | # error: [invalid-overload]
5 | def func(x: int) -> int: ...
| ^^^^ Only one overload defined here
|
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@classmethod`_(aaa04d4cfa3adaba).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@classmethod`_(aaa04d4cfa3adaba).snap"
index a4d5da2d29351..c82a4d44671b0 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@classmethod`_(aaa04d4cfa3adaba).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@classmethod`_(aaa04d4cfa3adaba).snap"
@@ -75,8 +75,10 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md
```
error[invalid-overload]: Overloaded function `try_from1` does not use the `@classmethod` decorator consistently
- --> src/mdtest_snippet.py:13:9
+ --> src/mdtest_snippet.py:12:5
|
+12 | @overload
+ | ---------
13 | def try_from1(cls, x: str) -> None: ...
| --------- Missing here
14 | @classmethod
@@ -94,8 +96,10 @@ error[invalid-overload]: Overloaded function `try_from2` does not use the `@clas
28 | def try_from2(cls, x: int | str) -> CheckClassMethod | None:
| ^^^^^^^^^
|
- ::: src/mdtest_snippet.py:22:9
+ ::: src/mdtest_snippet.py:21:5
|
+21 | @overload
+ | ---------
22 | def try_from2(cls, x: int) -> CheckClassMethod: ...
| --------- Missing here
|
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@final`_(f8e529ec23a61665).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@final`_(f8e529ec23a61665).snap"
index e69c31f45020b..e8c70eabb2784 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@final`_(f8e529ec23a61665).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@final`_(f8e529ec23a61665).snap"
@@ -76,8 +76,10 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md
```
error[invalid-overload]: `@final` decorator should be applied only to the overload implementation
- --> src/mdtest_snippet.py:13:5
+ --> src/mdtest_snippet.py:12:5
|
+12 | @overload
+ | ---------
13 | @final
| ------
14 | # error: [invalid-overload]
@@ -93,8 +95,10 @@ error[invalid-overload]: `@final` decorator should be applied only to the overlo
```
error[invalid-overload]: `@final` decorator should be applied only to the overload implementation
- --> src/mdtest_snippet.py:24:5
+ --> src/mdtest_snippet.py:23:5
|
+23 | @overload
+ | ---------
24 | @final
| ------
25 | # error: [invalid-overload]
@@ -108,49 +112,52 @@ error[invalid-overload]: `@final` decorator should be applied only to the overlo
```
error[invalid-overload]: `@final` decorator should be applied only to the first overload
- --> src/mdtest_snippet.pyi:10:9
+ --> src/mdtest_snippet.pyi:9:5
|
-10 | def method2(self, x: int) -> int: ...
- | ------- First overload defined here
-11 | @final
- | ------
-12 | @overload
-13 | # error: [invalid-overload]
-14 | def method2(self, x: str) -> str: ...
- | ^^^^^^^
+ 9 | / @overload
+10 | | def method2(self, x: int) -> int: ...
+ | |_________________________________________- First overload defined here
+11 | @final
+ | ------
+12 | @overload
+13 | # error: [invalid-overload]
+14 | def method2(self, x: str) -> str: ...
+ | ^^^^^^^
|
```
```
error[invalid-overload]: `@final` decorator should be applied only to the first overload
- --> src/mdtest_snippet.pyi:16:9
+ --> src/mdtest_snippet.pyi:15:5
|
-16 | def method3(self, x: int) -> int: ...
- | ------- First overload defined here
-17 | @final
- | ------
-18 | @overload
-19 | def method3(self, x: str) -> int: ... # error: [invalid-overload]
- | ^^^^^^^
+15 | / @overload
+16 | | def method3(self, x: int) -> int: ...
+ | |_________________________________________- First overload defined here
+17 | @final
+ | ------
+18 | @overload
+19 | def method3(self, x: str) -> int: ... # error: [invalid-overload]
+ | ^^^^^^^
|
```
```
error[invalid-overload]: `@final` decorator should be applied only to the first overload
- --> src/mdtest_snippet.pyi:16:9
+ --> src/mdtest_snippet.pyi:15:5
|
-16 | def method3(self, x: int) -> int: ...
- | ------- First overload defined here
-17 | @final
-18 | @overload
-19 | def method3(self, x: str) -> int: ... # error: [invalid-overload]
-20 | @overload
-21 | @final
- | ------
-22 | def method3(self, x: bytes) -> bytes: ... # error: [invalid-overload]
- | ^^^^^^^
+15 | / @overload
+16 | | def method3(self, x: int) -> int: ...
+ | |_________________________________________- First overload defined here
+17 | @final
+18 | @overload
+19 | def method3(self, x: str) -> int: ... # error: [invalid-overload]
+20 | @overload
+21 | @final
+ | ------
+22 | def method3(self, x: bytes) -> bytes: ... # error: [invalid-overload]
+ | ^^^^^^^
|
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@override`_(2df210735ca532f9).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@override`_(2df210735ca532f9).snap"
index 99adaf5256dc1..9f163a71a4934 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@override`_(2df210735ca532f9).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/overloads.md_-_Overloads_-_Invalid_-_Inconsistent_decorat\342\200\246_-_`@override`_(2df210735ca532f9).snap"
@@ -84,8 +84,10 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/overloads.md
```
error[invalid-overload]: `@override` decorator should be applied only to the overload implementation
- --> src/mdtest_snippet.py:24:5
+ --> src/mdtest_snippet.py:23:5
|
+23 | @overload
+ | ---------
24 | @override
| ---------
25 | # error: [invalid-overload]
@@ -99,8 +101,10 @@ error[invalid-overload]: `@override` decorator should be applied only to the ove
```
error[invalid-overload]: `@override` decorator should be applied only to the overload implementation
- --> src/mdtest_snippet.py:32:5
+ --> src/mdtest_snippet.py:31:5
|
+31 | @overload
+ | ---------
32 | @override
| ---------
33 | # error: [invalid-overload]
@@ -116,16 +120,17 @@ error[invalid-overload]: `@override` decorator should be applied only to the ove
```
error[invalid-overload]: `@override` decorator should be applied only to the first overload
- --> src/mdtest_snippet.pyi:18:9
+ --> src/mdtest_snippet.pyi:17:5
|
-18 | def method(self, x: int) -> int: ...
- | ------ First overload defined here
-19 | @overload
-20 | @override
- | ---------
-21 | # error: [invalid-overload]
-22 | def method(self, x: str) -> str: ...
- | ^^^^^^
+17 | / @overload
+18 | | def method(self, x: int) -> int: ...
+ | |________________________________________- First overload defined here
+19 | @overload
+20 | @override
+ | ---------
+21 | # error: [invalid-overload]
+22 | def method(self, x: str) -> str: ...
+ | ^^^^^^
|
```
diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/union_call.md_-_Calling_a_union_of_f\342\200\246_-_Try_to_cover_all_pos\342\200\246_-_Cover_non-keyword_re\342\200\246_(707b284610419a54).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/union_call.md_-_Calling_a_union_of_f\342\200\246_-_Try_to_cover_all_pos\342\200\246_-_Cover_non-keyword_re\342\200\246_(707b284610419a54).snap"
index 4fda6b6a3cd60..65886dd963871 100644
--- "a/crates/ty_python_semantic/resources/mdtest/snapshots/union_call.md_-_Calling_a_union_of_f\342\200\246_-_Try_to_cover_all_pos\342\200\246_-_Cover_non-keyword_re\342\200\246_(707b284610419a54).snap"
+++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/union_call.md_-_Calling_a_union_of_f\342\200\246_-_Try_to_cover_all_pos\342\200\246_-_Cover_non-keyword_re\342\200\246_(707b284610419a54).snap"
@@ -121,10 +121,11 @@ error[no-matching-overload]: No overload of function `f6` matches arguments
| ^^^^
|
info: First overload defined here
- --> src/mdtest_snippet.py:24:5
+ --> src/mdtest_snippet.py:23:1
|
-24 | def f6() -> None: ...
- | ^^^^^^^^^^^^
+23 | / @overload
+24 | | def f6() -> None: ...
+ | |_____________________^ First overload defined here
|
info: Possible overloads for function `f6`:
info: () -> None
diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs
index 9c0d28f37e473..0d7cf02cb1467 100644
--- a/crates/ty_python_semantic/src/types/call/bind.rs
+++ b/crates/ty_python_semantic/src/types/call/bind.rs
@@ -58,7 +58,7 @@ use crate::types::{
TypeVarVariance, UnionBuilder, UnionType, WrapperDescriptorKind, enums, list_members,
};
use crate::{DisplaySettings, FxOrderSet, Program};
-use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
+use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
use ruff_python_ast::{self as ast, AnyNodeRef, ArgOrKeyword, PythonVersion};
use ty_module_resolver::KnownModule;
use ty_python_core::scope::NodeWithScopeKind;
@@ -3518,7 +3518,25 @@ impl<'db> CallableBinding<'db> {
SubDiagnosticSeverity::Info,
"First overload defined here",
);
- sub.annotate(Annotation::primary(overload.spans(context.db()).signature));
+ let file = function.file(context.db());
+ let module = parsed_module(context.db(), file).load(context.db());
+ let node =
+ overload.node(context.db(), function.file(context.db()), &module);
+ let range = if node.body.len() == 1 {
+ node.range()
+ } else {
+ TextRange::new(
+ node.start(),
+ node.returns
+ .as_deref()
+ .map(Ranged::end)
+ .unwrap_or_else(|| node.parameters.end()),
+ )
+ };
+ sub.annotate(
+ Annotation::primary(Span::from(file).with_range(range))
+ .message("First overload defined here"),
+ );
diag.sub(sub);
}
diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs
index ba182a1900d7d..fdd86b1dc9c84 100644
--- a/crates/ty_python_semantic/src/types/diagnostic.rs
+++ b/crates/ty_python_semantic/src/types/diagnostic.rs
@@ -30,9 +30,10 @@ use crate::types::{
ProtocolInstanceType, SpecialFormType, SubclassOfInner, Type, TypeContext, TypeVarVariance,
binding_type, protocol_class::ProtocolClass,
};
-use crate::types::{KnownInstanceType, MemberLookupPolicy, UnionType};
+use crate::types::{KnownInstanceType, MemberLookupPolicy, TypedDictType, UnionType};
use crate::{Db, DisplaySettings, FxIndexMap, Program, declare_lint};
use itertools::Itertools;
+use ruff_db::source::source_text;
use ruff_db::{
diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity},
parsed::parsed_module,
@@ -41,6 +42,7 @@ use ruff_diagnostics::{Edit, Fix, IsolationLevel};
use ruff_python_ast::name::Name;
use ruff_python_ast::token::parentheses_iterator;
use ruff_python_ast::{self as ast, AnyNodeRef, HasNodeIndex, PythonVersion, StringFlags};
+use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
use std::fmt::{self, Formatter};
@@ -4833,13 +4835,51 @@ pub(crate) fn report_call_to_abstract_method(
diag.set_primary_message(format_args!(
"`{name}` is an abstract {method_kind} with a trivial body"
));
- let spans = function.spans(db);
- let mut sub = SubDiagnostic::new(
- SubDiagnosticSeverity::Info,
- format_args!("Method `{name}` defined here"),
+ let span = abstract_method_span(
+ db,
+ function,
+ AbstractMethodAnnotationPolicy::AlwaysIncludeBody,
+ );
+ diag.annotate(
+ Annotation::secondary(span).message(format_args!("Method `{name}` defined here")),
);
- sub.annotate(Annotation::primary(spans.name));
- diag.sub(sub);
+}
+
+pub(super) fn abstract_method_span<'db>(
+ db: &'db dyn Db,
+ function: FunctionType<'db>,
+ policy: AbstractMethodAnnotationPolicy,
+) -> Span {
+ let (_, implementation) = function.overloads_and_implementation(db);
+
+ let Some(implementation) = implementation else {
+ return function.spans(db).name;
+ };
+
+ let file = function.file(db);
+ let module = parsed_module(db, file).load(db);
+ let node = implementation.node(db, file, &module);
+ let source_text = source_text(db, file);
+
+ if policy == AbstractMethodAnnotationPolicy::ExcludeVerboseBody
+ && source_text.line_start(node.name.end()) != source_text.line_start(node.end())
+ {
+ return implementation.spans(db).decorators_and_header;
+ }
+
+ if let [single_stmt] = &*node.body
+ && source_text.line_start(single_stmt.start()) == source_text.line_start(single_stmt.end())
+ {
+ Span::from(file).with_range(node.range())
+ } else {
+ implementation.spans(db).decorators_and_header
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub(super) enum AbstractMethodAnnotationPolicy {
+ AlwaysIncludeBody,
+ ExcludeVerboseBody,
}
pub(crate) fn report_undeclared_protocol_member(
@@ -5344,7 +5384,7 @@ pub(crate) enum TypedDictDeleteErrorKind {
pub(crate) fn report_cannot_delete_typed_dict_key<'db>(
context: &InferContext<'db, '_>,
key_node: AnyNodeRef,
- typed_dict_ty: Type<'db>,
+ typed_dict_ty: TypedDictType<'db>,
field_name: &str,
field: Option<&crate::types::typed_dict::TypedDictField<'db>>,
error_kind: TypedDictDeleteErrorKind,
@@ -5354,7 +5394,7 @@ pub(crate) fn report_cannot_delete_typed_dict_key<'db>(
return;
};
- let typed_dict_name = typed_dict_ty.display(db);
+ let typed_dict_name = Type::TypedDict(typed_dict_ty).display(db);
let mut diagnostic = match error_kind {
TypedDictDeleteErrorKind::RequiredKey => builder.into_diagnostic(format_args!(
@@ -5373,14 +5413,27 @@ pub(crate) fn report_cannot_delete_typed_dict_key<'db>(
let module = parsed_module(db, file).load(db);
let mut sub = SubDiagnostic::new(SubDiagnosticSeverity::Info, "Field defined here");
- sub.annotate(
- Annotation::secondary(
- Span::from(file).with_range(declaration.full_range(db, &module).range()),
- )
- .message(format_args!(
- "`{field_name}` declared as required here; consider making it `NotRequired`"
- )),
- );
+ for message in [
+ format_args!("`{field_name}` declared as required here"),
+ format_args!("Consider making it `NotRequired`"),
+ ] {
+ sub.annotate(
+ Annotation::secondary(
+ Span::from(file).with_range(declaration.full_range(db, &module).range()),
+ )
+ .message(message),
+ );
+ }
+
+ if let Some(class) = typed_dict_ty.defining_class() {
+ sub.annotate(
+ Annotation::secondary(
+ Span::from(file).with_range(class.class_literal(db).header_range(db)),
+ )
+ .message(format_args!("`{}` defined here", class.name(db))),
+ );
+ }
+
diagnostic.sub(sub);
}
@@ -6329,6 +6382,7 @@ pub(super) fn report_invalid_total_ordering(
"`{}` does not define `__lt__`, `__le__`, `__gt__`, or `__ge__`",
class.name(db)
));
+ diagnostic.annotate(context.secondary(class.header_range(db)));
diagnostic.info("The decorator will raise `ValueError` at runtime");
}
diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs
index dab6ab0f9eecd..8c93a8d114b15 100644
--- a/crates/ty_python_semantic/src/types/function.rs
+++ b/crates/ty_python_semantic/src/types/function.rs
@@ -109,6 +109,10 @@ pub(crate) struct FunctionSpans {
pub(crate) parameters: Span,
/// The span of the annotated return type, if present.
pub(crate) return_type: Option,
+ /// A span that starts at the beginning of the first decorator (if any),
+ /// and ends at the end of the function signature (either the last parameter,
+ /// or the return type if present).
+ pub(crate) decorators_and_header: Span,
}
bitflags! {
@@ -642,6 +646,7 @@ impl<'db> OverloadLiteral<'db> {
name: span.clone().with_range(func_def.name.range),
parameters: span.clone().with_range(func_def.parameters.range),
return_type: return_type_range.map(|range| span.clone().with_range(range)),
+ decorators_and_header: span.with_range(signature.cover_offset(func_def.start())),
}
}
}
diff --git a/crates/ty_python_semantic/src/types/infer/builder/post_inference/overloaded_function.rs b/crates/ty_python_semantic/src/types/infer/builder/post_inference/overloaded_function.rs
index 565d40c69b3d4..99bdff20c4d71 100644
--- a/crates/ty_python_semantic/src/types/infer/builder/post_inference/overloaded_function.rs
+++ b/crates/ty_python_semantic/src/types/infer/builder/post_inference/overloaded_function.rs
@@ -1,4 +1,7 @@
-use ruff_db::diagnostic::Annotation;
+use ruff_db::{
+ diagnostic::{Annotation, Span},
+ parsed::parsed_module,
+};
use ruff_text_size::Ranged;
use rustc_hash::FxHashSet;
@@ -92,6 +95,11 @@ pub(crate) fn check_overloaded_function<'db>(
&function_node.name
));
diagnostic.set_primary_message("Only one overload defined here");
+ if let Some(decorator) =
+ single_overload.find_known_decorator_span(db, KnownFunction::Overload)
+ {
+ diagnostic.annotate(Annotation::secondary(decorator));
+ }
}
}
@@ -130,9 +138,10 @@ pub(crate) fn check_overloaded_function<'db>(
let function_node = overloads[0].node(db, context.file(), context.module());
if let Some(builder) = context.report_lint(&INVALID_OVERLOAD, &function_node.name) {
let mut diagnostic = builder.into_diagnostic(format_args!(
- "Overloads for function `{}` must be followed by a non-`@overload`-decorated implementation function",
- &function_node.name
- ));
+ "Overloads for function `{}` must be followed by a \
+ non-`@overload`-decorated implementation function",
+ &function_node.name
+ ));
diagnostic.info(format_args!(
"Attempting to call `{}` will raise `TypeError` at runtime",
&function_node.name
@@ -178,7 +187,7 @@ pub(crate) fn check_overloaded_function<'db>(
if let Some(builder) = context.report_lint(&INVALID_OVERLOAD, &function_node.name) {
let mut diagnostic = builder.into_diagnostic(format_args!(
"Overloaded function `{}` does not use the `@{name}` decorator \
- consistently",
+ consistently",
&function_node.name
));
for function in decorator_missing {
@@ -187,11 +196,16 @@ pub(crate) fn check_overloaded_function<'db>(
.secondary(function.focus_range(db, context.module()))
.message(format_args!("Missing here")),
);
+ if let Some(decorator) =
+ function.find_known_decorator_span(db, KnownFunction::Overload)
+ {
+ diagnostic.annotate(Annotation::secondary(decorator));
+ }
}
}
}
- for (function, decorator) in [
+ for (known_function, decorator) in [
(KnownFunction::Final, FunctionDecorators::FINAL),
(KnownFunction::Override, FunctionDecorators::OVERRIDE),
] {
@@ -207,11 +221,14 @@ pub(crate) fn check_overloaded_function<'db>(
};
let mut diagnostic = builder.into_diagnostic(format_args!(
"`@{name}` decorator should be applied only to the \
- overload implementation",
- name = function.name()
+ overload implementation",
+ name = known_function.name()
));
- if let Some(decorator) = overload.find_known_decorator_span(db, function) {
- diagnostic.annotate(Annotation::secondary(decorator));
+ for known_function in [known_function, KnownFunction::Overload] {
+ if let Some(decorator) = overload.find_known_decorator_span(db, known_function)
+ {
+ diagnostic.annotate(Annotation::secondary(decorator));
+ }
}
diagnostic.annotate(
context
@@ -235,15 +252,22 @@ pub(crate) fn check_overloaded_function<'db>(
};
let mut diagnostic = builder.into_diagnostic(format_args!(
"`@{name}` decorator should be applied only to the \
- first overload",
- name = function.name()
+ first overload",
+ name = known_function.name()
));
- if let Some(decorator) = overload.find_known_decorator_span(db, function) {
+ if let Some(decorator) = overload.find_known_decorator_span(db, known_function) {
diagnostic.annotate(Annotation::secondary(decorator));
}
+ let file = function.file(db);
+ let module = parsed_module(db, file).load(db);
+ let node = first_overload.node(db, file, &module);
+ let span = if node.body.len() == 1 {
+ Span::from(file).with_range(node.range())
+ } else {
+ first_overload.spans(db).decorators_and_header
+ };
diagnostic.annotate(
- context
- .secondary(first_overload.focus_range(db, context.module()))
+ Annotation::secondary(span)
.message(format_args!("First overload defined here")),
);
}
diff --git a/crates/ty_python_semantic/src/types/infer/builder/post_inference/static_class.rs b/crates/ty_python_semantic/src/types/infer/builder/post_inference/static_class.rs
index 63690e3275b4d..89c3e4b89c710 100644
--- a/crates/ty_python_semantic/src/types/infer/builder/post_inference/static_class.rs
+++ b/crates/ty_python_semantic/src/types/infer/builder/post_inference/static_class.rs
@@ -1,7 +1,6 @@
use itertools::Itertools;
use ruff_db::{
- diagnostic::{Annotation, Span, SubDiagnostic, SubDiagnosticSeverity},
- parsed::parsed_module,
+ diagnostic::{Annotation, SubDiagnostic, SubDiagnosticSeverity},
source::source_text,
};
use ruff_diagnostics::{Edit, Fix};
@@ -9,7 +8,6 @@ use ruff_python_ast as ast;
use ruff_text_size::{Ranged, TextRange, TextSize};
use rustc_hash::FxHashMap;
-use crate::attribute_assignments;
use crate::{
TypeQualifiers,
diagnostic::format_enumeration,
@@ -17,7 +15,7 @@ use crate::{
types::{
CallArguments, ClassBase, ClassLiteral, ClassType, GenericAlias, KnownInstanceType,
MemberLookupPolicy, MetaclassCandidate, Parameters, Signature, SpecialFormType,
- StaticClassLiteral, Type,
+ StaticClassLiteral, Type, binding_type,
call::Argument,
class::{
AbstractMethod, CodeGeneratorKind, FieldKind, MetaclassErrorKind,
@@ -26,12 +24,12 @@ use crate::{
context::InferContext,
definition_expression_type,
diagnostic::{
- ABSTRACT_METHOD_IN_FINAL_CLASS, CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION,
- DATACLASS_FIELD_ORDER, DUPLICATE_KW_ONLY, FINAL_WITHOUT_VALUE, INCONSISTENT_MRO,
- INVALID_ARGUMENT_TYPE, INVALID_BASE, INVALID_DATACLASS, INVALID_GENERIC_CLASS,
- INVALID_GENERIC_ENUM, INVALID_METACLASS, INVALID_NAMED_TUPLE, INVALID_PROTOCOL,
- INVALID_TYPED_DICT_HEADER, IncompatibleBases, SUBCLASS_OF_FINAL_CLASS,
- UNKNOWN_ARGUMENT, report_bad_frozen_dataclass_inheritance,
+ ABSTRACT_METHOD_IN_FINAL_CLASS, AbstractMethodAnnotationPolicy, CONFLICTING_METACLASS,
+ CYCLIC_CLASS_DEFINITION, DATACLASS_FIELD_ORDER, DUPLICATE_KW_ONLY, FINAL_WITHOUT_VALUE,
+ INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE, INVALID_BASE, INVALID_DATACLASS,
+ INVALID_GENERIC_CLASS, INVALID_GENERIC_ENUM, INVALID_METACLASS, INVALID_NAMED_TUPLE,
+ INVALID_PROTOCOL, INVALID_TYPED_DICT_HEADER, IncompatibleBases,
+ SUBCLASS_OF_FINAL_CLASS, UNKNOWN_ARGUMENT, report_bad_frozen_dataclass_inheritance,
report_conflicting_metaclass_from_bases, report_duplicate_bases,
report_instance_layout_conflict, report_invalid_or_unsupported_base,
report_invalid_total_ordering, report_invalid_type_param_order,
@@ -53,6 +51,7 @@ use crate::{
visitor::find_over_type,
},
};
+use crate::{attribute_assignments, types::diagnostic::abstract_method_span};
use ty_python_core::{SemanticIndex, definition::DefinitionKind, scope::ScopeId};
/// Iterate over all static class definitions (created using `class` statements) to check that
@@ -1095,6 +1094,23 @@ fn check_final_class_abstract_methods<'db>(
"Final class `{class_name}` has unimplemented abstract methods",
));
+ let definition_types = infer_definition_types(db, class.definition(db));
+
+ if let Some(class_node) = class.body_scope(db).node(db).as_class()
+ && let Some(decorator) = class_node
+ .node(context.module())
+ .decorator_list
+ .iter()
+ .find(|decorator| {
+ definition_types
+ .expression_type(&decorator.expression)
+ .as_function_literal()
+ .is_some_and(|function| function.is_known(db, KnownFunction::Final))
+ })
+ {
+ diagnostic.annotate(context.secondary(decorator));
+ }
+
let num_abstract_methods = abstract_methods.len();
if num_abstract_methods == 1 {
@@ -1139,19 +1155,25 @@ fn check_final_class_abstract_methods<'db>(
kind,
} = abstract_method;
- let module = parsed_module(db, definition.file(db)).load(db);
- let span = Span::from(definition.focus_range(db, &module));
let defining_class_name = defining_class.name(db);
- let mut secondary_annotation = Annotation::secondary(span);
- secondary_annotation = if defining_class.class_literal(db) == ClassLiteral::Static(class) {
- secondary_annotation.message(format_args!("`{first_method_name}` declared as abstract"))
- } else {
- secondary_annotation.message(format_args!(
- "`{first_method_name}` declared as abstract on superclass `{defining_class_name}`",
- ))
- };
- diagnostic.annotate(secondary_annotation);
+ if let Type::FunctionLiteral(function) = binding_type(db, *definition) {
+ let policy = if kind.is_explicit() {
+ AbstractMethodAnnotationPolicy::ExcludeVerboseBody
+ } else {
+ AbstractMethodAnnotationPolicy::AlwaysIncludeBody
+ };
+ let secondary_span = abstract_method_span(db, function, policy);
+ let mut secondary_annotation = Annotation::secondary(secondary_span);
+ secondary_annotation = if defining_class.class_literal(db) == ClassLiteral::Static(class) {
+ secondary_annotation.message(format_args!("`{first_method_name}` declared as abstract"))
+ } else {
+ secondary_annotation.message(format_args!(
+ "`{first_method_name}` declared as abstract on superclass `{defining_class_name}`",
+ ))
+ };
+ diagnostic.annotate(secondary_annotation);
+ }
if !kind.is_explicit() {
let mut sub = SubDiagnostic::new(
diff --git a/crates/ty_python_semantic/src/types/infer/builder/subscript.rs b/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
index e3fb5f245900f..a6f3324cc3153 100644
--- a/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
+++ b/crates/ty_python_semantic/src/types/infer/builder/subscript.rs
@@ -1599,7 +1599,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
report_cannot_delete_typed_dict_key(
&self.context,
(&*target.slice).into(),
- object_ty,
+ typed_dict,
key,
Some(field),
TypedDictDeleteErrorKind::RequiredKey,
@@ -1609,7 +1609,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
report_cannot_delete_typed_dict_key(
&self.context,
(&*target.slice).into(),
- object_ty,
+ typed_dict,
key,
None,
TypedDictDeleteErrorKind::UnknownKey,