[ty] Infer class and mapping pattern bindings#25941
Conversation
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 94.47%. The percentage of expected errors that received a diagnostic held steady at 89.19%. The number of fully passing files held steady at 95/134. |
Memory usage reportMemory usage unchanged ✅ |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
unresolved-attribute |
8 | 0 | 0 |
invalid-argument-type |
6 | 0 | 0 |
invalid-assignment |
0 | 0 | 3 |
type-assertion-failure |
0 | 1 | 2 |
invalid-return-type |
1 | 1 | 0 |
no-matching-overload |
1 | 0 | 0 |
| Total | 16 | 2 | 5 |
Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.
Raw diff (23 changes)
cwltool (https://github.com/common-workflow-language/cwltool)
+ cwltool/main.py:244:57 error[no-matching-overload] No overload of bound method `str.join` matches arguments
+ cwltool/validate_js.py:101:29 error[invalid-argument-type] Method `__getitem__` of type `bound method Top[MutableMapping[Unknown, Unknown]].__getitem__(key: Never, /) -> object` cannot be called with key of type `str` on object of type `Top[MutableMapping[Unknown, Unknown]]`
+ cwltool/validate_js.py:101:29 error[invalid-argument-type] Method `__getitem__` of type `bound method str.__getitem__(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str` cannot be called with key of type `str` on object of type `str`
egglog-python (https://github.com/egraphs-good/egglog-python)
- python/egglog/egraph_state.py:378:25 error[type-assertion-failure] Type `Unknown & ~Literal["delete"] & ~Literal["subsume"]` is not equivalent to `Never`
- python/egglog/egraph_state.py:623:25 error[type-assertion-failure] Type `Unknown & ~None` is not equivalent to `Never`
+ python/egglog/egraph_state.py:623:25 error[type-assertion-failure] Type `Value` is not equivalent to `Never`
- python/egglog/pretty.py:309:17 error[type-assertion-failure] Type `Unknown & ~None` is not equivalent to `Never`
+ python/egglog/pretty.py:309:17 error[type-assertion-failure] Type `Value` is not equivalent to `Never`
+ python/egglog/thunk.py:69:28 error[invalid-return-type] Return type does not match returned value: expected `T@__call__`, found `TypeVar`
jax (https://github.com/google/jax)
- jax/experimental/mosaic/gpu/constraints.py:211:6 error[invalid-return-type] Function can implicitly return `None`, which is not assignable to return type `Variable | RegisterLayout | TMEMLayout | ... omitted 6 union elements`
pylint (https://github.com/pycqa/pylint)
+ pylint/checkers/match_statements_checker.py:164:25 error[unresolved-attribute] Attribute `value` is not defined on `NodeNG` in union `NodeNG | Proxy`
+ pylint/checkers/classes/class_checker.py:946:54 error[unresolved-attribute] Attribute `name` is not defined on `NodeNG` in union `NodeNG | UninferableBase | Proxy`
+ pylint/checkers/refactoring/recommendation_checker.py:261:29 error[unresolved-attribute] Attribute `name` is not defined on `Attribute` in union `Attribute | Name`
+ pylint/checkers/refactoring/recommendation_checker.py:265:29 error[unresolved-attribute] Attribute `attrname` is not defined on `Name` in union `Attribute | Name`
+ pylint/checkers/refactoring/refactoring_checker.py:652:16 error[unresolved-attribute] Object of type `NodeNG | None` has no attribute `value`
+ pylint/checkers/refactoring/refactoring_checker.py:1178:12 error[unresolved-attribute] Object of type `NodeNG` has no attribute `name`
+ pylint/checkers/refactoring/refactoring_checker.py:1830:32 error[unresolved-attribute] Attribute `name` is not defined on `NodeNG` in union `NodeNG | Proxy`
+ pylint/checkers/refactoring/refactoring_checker.py:1841:38 error[unresolved-attribute] Attribute `name` is not defined on `NodeNG` in union `NodeNG | Proxy`
+ pylint/checkers/utils.py:1303:83 error[invalid-argument-type] Argument to function `has_known_bases` is incorrect: Expected `ClassDef`, found `(ClassDef & BaseInstance) | (FunctionDef & BaseInstance) | (Lambda & BaseInstance) | (UnboundMethod & BaseInstance)`
+ pylint/checkers/utils.py:1304:38 error[invalid-argument-type] Argument is incorrect: Expected `NodeNG`, found `(ClassDef & BaseInstance) | (FunctionDef & BaseInstance) | (Lambda & BaseInstance) | (UnboundMethod & BaseInstance)`
pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/assertion/rewrite.py:1012:25 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `expr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1012:25 error[invalid-assignment] Invalid subscript assignment with key of type `str` and value of type `expr` on object of type `dict[str, str]`
- src/_pytest/assertion/rewrite.py:1110:17 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `NamedExpr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1110:17 error[invalid-assignment] Invalid subscript assignment with key of type `str` and value of type `NamedExpr` on object of type `dict[str, str]`
- src/_pytest/assertion/rewrite.py:1128:21 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `NamedExpr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1128:21 error[invalid-assignment] Invalid subscript assignment with key of type `Any & str` and value of type `NamedExpr` on object of type `dict[str, str]`
schema_salad (https://github.com/common-workflow-language/schema_salad)
+ src/schema_salad/typescript_codegen.py:408:25 error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Any | str | Literal[False]`
typeshed-stats (https://github.com/AlexWaygood/typeshed-stats)
+ src/typeshed_stats/gather.py:520:27 error[invalid-argument-type] Argument to function `sorted` is incorrect: Expected `Iterable[str]`, found `Top[list[Unknown]]`5721b8d to
7bd8d49
Compare
b453dd7 to
54e1f3b
Compare
7bd8d49 to
78629a8
Compare
54e1f3b to
2c2d601
Compare
78629a8 to
cd30fbb
Compare
60429ac to
846bfac
Compare
cd30fbb to
5919bfb
Compare
846bfac to
6b9cf4e
Compare
0fd6816 to
c35e207
Compare
6b9cf4e to
e42c4f5
Compare
c35e207 to
0636474
Compare
f1558fa to
9ca3ba0
Compare
4011765 to
2a91303
Compare
9ca3ba0 to
7f028a9
Compare
2a91303 to
279abdd
Compare
25345ad to
faa393d
Compare
a1afac5 to
f81d5bb
Compare
faa393d to
0cd4aba
Compare
f81d5bb to
a1661c5
Compare
86059d1 to
54e4971
Compare
83f5d2d to
90ec895
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
c797a45 to
9f2473e
Compare
9fa61a9 to
304fe53
Compare
9f2473e to
4956fb6
Compare
304fe53 to
8d89b66
Compare
3c2f4af to
66be638
Compare
8d89b66 to
ee71ae3
Compare
66be638 to
9837919
Compare
ee71ae3 to
966ed8b
Compare
9837919 to
d32c160
Compare
966ed8b to
2ac5a92
Compare
d32c160 to
05c004c
Compare
2ac5a92 to
bce8e8c
Compare
05c004c to
6a9e995
Compare
bce8e8c to
05d8601
Compare
6a9e995 to
7a9bec2
Compare
05d8601 to
220f8b4
Compare
7a9bec2 to
5592892
Compare
| def test_widened_match_args_does_not_select_an_attribute(value: WidenedMatchArgs) -> None: | ||
| match value: | ||
| case WidenedMatchArgs(item): | ||
| reveal_type(item) # revealed: Unknown |
There was a problem hiding this comment.
A bit questionable, but consistent, I think.
There was a problem hiding this comment.
Consistent with other type checkers?
I agree here though.
| def test_match_nested_generic_subclass_capture(value: GenericPatternBase[int]) -> list[int]: | ||
| match value: | ||
| case GenericPatternChild(items=items): | ||
| reveal_type(items) # revealed: Unknown |
There was a problem hiding this comment.
Should there be a TODO here given that this should be list[int] instead of Unknown?
| def test_widened_match_args_does_not_select_an_attribute(value: WidenedMatchArgs) -> None: | ||
| match value: | ||
| case WidenedMatchArgs(item): | ||
| reveal_type(item) # revealed: Unknown |
There was a problem hiding this comment.
Consistent with other type checkers?
I agree here though.
| case MissingClassPatternAttribute(missing=item) | (int() as item): | ||
| reveal_type(item) # revealed: int |
There was a problem hiding this comment.
This is cool given that other type checkers reveal Unknown | int (or Any | int) which is incorrect (because of @final) given that the first pattern can never be matched here.
| def test_definite_class_alternative_removes_later_bindings(value: DefiniteFirst) -> None: | ||
| match value: | ||
| case (DefiniteFirst() as item) | UnreachableLater(payload=item): | ||
| reveal_type(item) # revealed: DefiniteFirst |
There was a problem hiding this comment.
When I switch the order here as follows it reveals str | DefiniteFirst which I think is correct given that DefiniteFirst is not a final class in this example. Can we add both of these test cases here? One where the order is switched and the other which additionally also sets the DefiniteFirst as a final class?
def test_definite_class_alternative_removes_later_bindings(value: DefiniteFirst) -> None:
match value:
case UnreachableLater(payload=item) | (DefiniteFirst() as item):
reveal_type(item) # revealed: str | DefiniteFirst| class OverlapCaptureB: | ||
| member: int | ||
|
|
||
| class OverlapCaptureC(OverlapCaptureA, OverlapCaptureB): ... |
There was a problem hiding this comment.
The OverlapCaptureC seems unused here?
Summary
We already infer the types of captures and aliases in sequence patterns by following the values passed from each pattern node to its children. This PR extends the same recursive analysis to class and mapping patterns.
A class pattern passes the selected instance member to each child pattern. Positional patterns resolve that member through
__match_args__; keyword patterns use the named attribute. A mapping pattern passes the value associated with each matched key, while**restreceives the new dictionary built from the remaining entries.