[ty] Reject ellipsis literals in odd places in type/annotation expressions#23611
[ty] Reject ellipsis literals in odd places in type/annotation expressions#23611AlexWaygood merged 6 commits intomainfrom
Conversation
Ellipsis literals inside a `Callable` parameter list (e.g., `Callable[[...], int]`) are now rejected with an `invalid-type-form` diagnostic. The valid gradual form `Callable[..., int]` (bare ellipsis as the first argument) is unaffected. https://claude.ai/code/session_01KDdJ3EZbcBiVeEADaLjPdy
Move the ellipsis rejection from the Callable-specific parameter list loop to `infer_type_expression_no_store`, where it applies to all type expression contexts. This is analogous to how int/bool/float literals are rejected as type expressions. To avoid double-reporting or false positives, also: - Skip `infer_type_expression` for `...` in the `Concatenate` argument loop (the trailing `...` is valid there) - Skip `infer_type_expression` for `...` in the tuple multi-element and single-element invalid-position handlers (they already report their own specific tuple error) - Handle `EllipsisLiteral` in `infer_annotation_expression_impl` to preserve the existing behavior for type alias bodies (`type Foo = ...`) https://claude.ai/code/session_01KDdJ3EZbcBiVeEADaLjPdy
Typing conformance resultsThe percentage of diagnostics emitted that were expected errors decreased from 85.39% to 85.32%. The percentage of expected errors that received a diagnostic increased from 75.46% to 75.55%. Summary
True positives addedDetails
|
|
Memory usage reportMemory usage unchanged ✅ |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-type-form |
89 | 0 | 0 |
invalid-await |
40 | 0 | 0 |
invalid-argument-type |
0 | 1 | 0 |
invalid-return-type |
1 | 0 | 0 |
| Total | 130 | 1 | 0 |
Change from "Ellipsis literal (`...`) is not allowed in this context in a type expression" to the shorter "`...` is not allowed in this context in a type expression". The subdiagnostic linking to the type-expression grammar is already included via `report_invalid_type_expression` → `add_type_expression_reference_link`. https://claude.ai/code/session_01KDdJ3EZbcBiVeEADaLjPdy
When `...` is the sole element inside the parameter list of a `Callable` type (i.e. `Callable[[...], int]`), add a subdiagnostic suggesting the correct gradual form: `Callable[..., <return type>]`. This is the only ellipsis-in-type-expression case where the user's intent is unambiguous enough to warrant a suggestion. Other cases (e.g. `Callable[[int, ...], R]`, `Union[int, ...]`, `list[...]`) are left without suggestions since the intended type is unclear. https://claude.ai/code/session_01KDdJ3EZbcBiVeEADaLjPdy
|
There's a really surprising number of ecosystem hits here, but I think they're all true positives. The ones in django-stubs and xarray look odd, but it's because they're doing this: class F:
def method(self) -> tuple[float, ...]: ...
def tuple(self): ...We interpret The mypy hits are because we don't understand the |
c566bdc to
d86e0d9
Compare
|
The conformance results here say we are adding a false positive -- but then don't list any details for that. |
Okay, I added some debug prints to the conformance script. We are indeed adding a new false positive, but the reason why the script got confused is that it's a line on which we already have a false-positive error (we now have 2 on that line, where we previously had only 1): from typing import Callable
type GoodAlias2[S1, *S2, **S3] = Callable[S3, S1] | tuple[*S2]
# main: 1 false positive:
#
# error[invalid-type-arguments]: Too many type arguments: expected 2, got 3`
#
# this PR adds:
#
# error[invalid-type-form]: `...` is not allowed in this context in a type expression`
#
type GoodAlias3 = GoodAlias2[int, tuple[int, str], ...]But the underlying cause of both these false positives is missing support for from typing import Callable
type GoodAlias2[S1, **S3] = Callable[S3, S1]
type GoodAlias3 = GoodAlias2[int, ...] |
|
For reference, the debug prints I added were: diff --git a/scripts/conformance.py b/scripts/conformance.py
index a3680619da..87f96beab8 100644
--- a/scripts/conformance.py
+++ b/scripts/conformance.py
@@ -842,6 +842,12 @@ def main():
python_version=args.python_version,
extra_search_paths=extra_search_paths,
)
+ import pprint
+ with open("old.txt", "w") as f:
+ pprint.pp(old, stream=f)
+
+ print()
+ print()
new = collect_ty_diagnostics(
ty_path=args.new_ty,
@@ -850,6 +856,8 @@ def main():
python_version=args.python_version,
extra_search_paths=extra_search_paths,
)
+ with open("new.txt", "w") as f:
+ pprint.pp([diag.__replace__(source=Source.OLD) for diag in new], stream=f)
grouped = group_diagnostics_by_key(
old=old,and then after running the script with that change applied, I ran |
* main: [ty] Move binary expression logic out of `builder.rs` (#23649) [ty] Limit recursion depth when displaying self-referential function types (#23647) [ty] Move comparison logic out of `builder.rs` (#23646) Avoid inserting redundant `None` elements in UP045 (#23459) [ty] Add more ParamSpec validation for `P.args` and `P.kwargs` (#23640) [ty] Sync vendored typeshed stubs (#23642) [ty] Fix inference of `t.__mro__` if `t` is an instance of `type[Any]` (#23632) [ty] Detect inconsistent generic base class specializations (#23615) [ty] Validate type variable defaults don't reference later type parameters or type parameters out of scope (#23623) [ty] Ban nested `Required`/`NotRequired`, and ban them both outside of `TypedDict` fields (#23627) [ty] Reject generic metaclasses parameterized by type variables (#23628) Update default Python version examples (#23605) fix binops with NewType of float and Unknown (#23620) [ty] Reject functions with PEP-695 type parameters that shadow type parameters from enclosing scopes (#23619) [ty] Reject ellipsis literals in odd places in type/annotation expressions (#23611) [ty] hash-cons `UseDefMap` fields (#23283)
Summary
More low-hanging fruit to improve our conformance score
Test plan
mdtests