Conversation
Diagnostic diff on typing conformance testsChanges were detected when running ty on typing conformance tests--- old-output.txt 2025-11-25 18:04:28.872627110 +0000
+++ new-output.txt 2025-11-25 18:04:32.604642480 +0000
@@ -194,6 +194,10 @@
classes_classvar.py:77:8: error[invalid-type-form] `ClassVar` annotations are only allowed in class-body scopes
classes_classvar.py:111:1: error[invalid-attribute-access] Cannot assign to ClassVar `stats` from an instance of type `Starship`
classes_classvar.py:140:13: error[invalid-assignment] Object of type `ProtoAImpl` is not assignable to `ProtoA`
+classes_override.py:53:9: error[invalid-explicit-override] Method `method3` is decorated with `@override` but does not override anything
+classes_override.py:65:9: error[invalid-explicit-override] Method `method4` is decorated with `@override` but does not override anything
+classes_override.py:79:9: error[invalid-explicit-override] Method `static_method1` is decorated with `@override` but does not override anything
+classes_override.py:84:9: error[invalid-explicit-override] Method `class_method1` is decorated with `@override` but does not override anything
constructors_call_init.py:21:13: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `int`, found `float`
constructors_call_init.py:25:1: error[type-assertion-failure] Type `Class1[int | float]` does not match asserted type `Class1[float]`
constructors_call_init.py:56:1: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `Class4[int]`, found `Class4[str]`
@@ -776,6 +780,7 @@
overloads_definitions.py:159:46: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | str`
overloads_definitions.py:167:44: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | str`
overloads_definitions.py:183:10: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | str`
+overloads_definitions.py:198:9: error[invalid-explicit-override] Method `bad_override` is decorated with `@override` but does not override anything
overloads_definitions.py:198:45: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | str`
overloads_definitions.py:215:46: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `int | str`
overloads_definitions.py:229:9: error[invalid-overload] `@override` decorator should be applied only to the overload implementation
@@ -786,6 +791,7 @@
overloads_definitions_stub.pyi:44:9: error[invalid-overload] Overloaded function `func6` does not use the `@classmethod` decorator consistently
overloads_definitions_stub.pyi:76:9: error[invalid-overload] `@final` decorator should be applied only to the first overload
overloads_definitions_stub.pyi:86:9: error[invalid-overload] `@final` decorator should be applied only to the first overload
+overloads_definitions_stub.pyi:122:9: error[invalid-explicit-override] Method `bad_override` is decorated with `@override` but does not override anything
overloads_definitions_stub.pyi:147:9: error[invalid-overload] `@override` decorator should be applied only to the first overload
overloads_evaluation.py:38:1: error[no-matching-overload] No overload of function `example1_1` matches arguments
overloads_evaluation.py:46:15: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]`
@@ -1005,5 +1011,5 @@
typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor
typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie`: Unknown key "title"
typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions
-Found 1007 diagnostics
+Found 1013 diagnostics
WARN A fatal error occurred while checking some files. Not all project files were analyzed. See the diagnostics list above for details.
|
|
|
e3fc8f1 to
5442f81
Compare
5442f81 to
ca53612
Compare
0a1b839 to
23246c9
Compare
23246c9 to
375709b
Compare
|
The typing conformance suite results are all good -- all the places where we're newly emitting diagnostics are marked with The ecosystem results are also excellent -- this is one of the few new diagnostics where you'd really expect no new ecosystem hits, and indeed that's what we have! |
| /// @override | ||
| /// def foo(self): ... # fine: overrides `A.foo` | ||
| /// ``` | ||
| pub(crate) static EXPLICIT_OVERRIDE = { |
There was a problem hiding this comment.
I think I'd prefer invalid-override or invalid-explicit-override. explicit-override sounds like explicit overrides are a bad thing.
And incorrect-override for an override that doesn't match the signature of the parent class
There was a problem hiding this comment.
yeah, I wasn't sure what to call this. I don't want to call it invalid-override because that implies a Liskov violation to me (e.g. the name for the "bad method override" diagnostic is invalid-method-override).
invalid-explicit-override is okay, I can switch to that. (Edit: I pushed this rename.)
There was a problem hiding this comment.
Hmm yeah, this is tricky. invalid-explicit-override is a bit long.
Should invalid-method-override be incorrect-method-override to make the distinction clearer? What name do you have in mind for the strict enforcement?
- Pyrefly: bad-override
There was a problem hiding this comment.
invalid-explicit-overrideis a bit long.
not as long as some of the ones we already have, such as subclass-of-final-class 😄
Should
invalid-method-overridebeincorrect-method-overrideto make the distinction clearer?
I'm not sure that would make the distinction much clearer. Applying @override to a method that doesn't override anything is just as incorrect as overriding a method in a way that violates the Liskov Substitution Principle.
What name do you have in mind for the strict enforcement?
The one I'm using in my head right now is invalid-explicit-override-strict or similar. But it's not great.
Another option here would be for the strict enforcement would actually just be the same error code, but we'd have a tool.ty.explicit-override-enforcement = true configuration setting (etc.) that allows you to opt into the stricter checks. That would obviate the need for a separate error code, which is nice. But the strict enforcement is really detecting a conceptually pretty different (and far more pedantic) problem -- so a separate error code does feel appropriate in some ways.
Do you have any suggestions?
There was a problem hiding this comment.
Other possible names for the Liskov violation code might be:
unsound-method-overrideliskov-violating-overrideincompatible-method-override
But I think even if we went with any of those for the Liskov violation error, I would still want the term "explicit" somewhere in the error code for invalid uses of typing.override, so that we're clear that this error code isn't about overrides in general, it's about overrides that are demarcated with typing.override.
There was a problem hiding this comment.
I do also think that the most important thing is to get this check in soon. It's very easy to change the name of an error code later, especially while we still declare ty to be alpha or beta software
There was a problem hiding this comment.
IMO invalid-explicit-override is fine for this code. I think for the strict check the right name would be invalid-implicit-override, since the nature of the strict check is "you failed to use @override here, but you are required to" -- that is, you are making an implicit override, but overrides are required to be explicit.
| if has_typeddict_in_mro { | ||
| if !KnownClass::TypedDictFallback | ||
| .to_instance(db) | ||
| .member(db, &member.name) | ||
| .place | ||
| .is_undefined() | ||
| { | ||
| subclass_overrides_superclass_declaration = true; | ||
| } |
There was a problem hiding this comment.
methods on TypedDict subclasses are invalid (as noted in comments in liskov.md)... but they exist in the wild anyway, and some of them are decorated with @override: https://github.com/Toufool/AutoSplit/blob/d9c5e3b48eeda52a13c08e75b5ffd61873ce5b8f/src/user_profile.py#L50-L53
| In a stub file, `@override` should always be applied to the first overload. Even if it isn't, we | ||
| always emit `invalid-explicit-override` diagnostics on the first overload. |
There was a problem hiding this comment.
It's a bit annoying to implement this but it means that we pass several typing conformance-suite tests that we'd otherwise fail, so I think it's worth it
f2ff136 to
a25e9b1
Compare
| # TODO: this raises an exception at runtime (which we should emit a diagnostic for). | ||
| # It shouldn't be an `invalid-explicit-override` diagnostic, however. |
There was a problem hiding this comment.
I don't think an invalid-explicit-override would be wrong here, exactly -- NamedTuple isn't really a base class, so you aren't overriding anything here. But I think it's fine if we don't emit it.
| /// @override | ||
| /// def foo(self): ... # fine: overrides `A.foo` | ||
| /// ``` | ||
| pub(crate) static EXPLICIT_OVERRIDE = { |
There was a problem hiding this comment.
IMO invalid-explicit-override is fine for this code. I think for the strict check the right name would be invalid-implicit-override, since the nature of the strict check is "you failed to use @override here, but you are required to" -- that is, you are making an implicit override, but overrides are required to be explicit.
* main: [ty] Implement `typing.override` (astral-sh#21627) [ty] Avoid expression reinference for diagnostics (astral-sh#21267) [ty] Improve autocomplete suppressions of keywords in variable bindings [ty] Only suggest completions based on text before the cursor Implement goto-definition and find-references for global/nonlocal statements (astral-sh#21616) [ty] Inlay Hint edit follow up (astral-sh#21621) [ty] Implement lsp support for string annotations (astral-sh#21577) [ty] Add 'remove unused ignore comment' code action (astral-sh#21582) [ty] Refactor `CheckSuppressionContext` to use `DiagnosticGuard` (astral-sh#21587) [ty] Improve several "Did you mean?" suggestions (astral-sh#21597) [ty] Add more and update existing projects in `ty_benchmark` (astral-sh#21536) [ty] fix ty playground initialization and vite optimization issues (astral-sh#21471)
Summary
Part of astral-sh/ty#155. This implements the basic check (
@override-decorated methods should override things!), but not the strict check specified in https://typing.python.org/en/latest/spec/class-compat.html#strict-enforcement-per-project, which should be a separate error code.Test Plan
mdtests and snapshots