Skip to content

Commit 20cd78f

Browse files
committed
add more tests
1 parent c4dbd36 commit 20cd78f

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

crates/ty_python_semantic/resources/mdtest/call/builtins.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ s = SubclassOfA()
152152
reveal_type(isinstance(s, SubclassOfA)) # revealed: Literal[True]
153153
reveal_type(isinstance(s, A)) # revealed: Literal[True]
154154

155-
def _(x: A | B):
155+
def _(x: A | B, y: list[int]):
156+
reveal_type(isinstance(y, list)) # revealed: Literal[True]
156157
reveal_type(isinstance(x, A)) # revealed: bool
157158

158159
if isinstance(x, A):

crates/ty_python_semantic/resources/mdtest/exhaustiveness_checking.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,103 @@ def match_non_exhaustive(x: A | B | C):
238238
# this diagnostic is correct: the inferred type of `x` is `B & ~A & ~C`
239239
assert_never(x) # error: [type-assertion-failure]
240240
```
241+
242+
## `isinstance` checks with generics
243+
244+
```toml
245+
[environment]
246+
python-version = "3.12"
247+
```
248+
249+
```py
250+
from typing import assert_never
251+
252+
class A[T]: ...
253+
class ASub[T](A[T]): ...
254+
class B[T]: ...
255+
class C[T]: ...
256+
class D: ...
257+
class E: ...
258+
class F: ...
259+
260+
def if_else_exhaustive(x: A[D] | B[E] | C[F]):
261+
if isinstance(x, A):
262+
pass
263+
elif isinstance(x, B):
264+
pass
265+
elif isinstance(x, C):
266+
pass
267+
else:
268+
# TODO: both of these are false positives (https://github.com/astral-sh/ty/issues/456)
269+
no_diagnostic_here # error: [unresolved-reference]
270+
assert_never(x) # error: [type-assertion-failure]
271+
272+
# TODO: false-positive diagnostic (https://github.com/astral-sh/ty/issues/456)
273+
def if_else_exhaustive_no_assertion(x: A[D] | B[E] | C[F]) -> int: # error: [invalid-return-type]
274+
if isinstance(x, A):
275+
return 0
276+
elif isinstance(x, B):
277+
return 1
278+
elif isinstance(x, C):
279+
return 2
280+
281+
def if_else_non_exhaustive(x: A[D] | B[E] | C[F]):
282+
if isinstance(x, A):
283+
pass
284+
elif isinstance(x, C):
285+
pass
286+
else:
287+
this_should_be_an_error # error: [unresolved-reference]
288+
289+
# this diagnostic is correct: the inferred type of `x` is `B[E] & ~A[D] & ~C[F]`
290+
assert_never(x) # error: [type-assertion-failure]
291+
292+
def match_exhaustive(x: A[D] | B[E] | C[F]):
293+
match x:
294+
case A():
295+
pass
296+
case B():
297+
pass
298+
case C():
299+
pass
300+
case _:
301+
# TODO: both of these are false positives (https://github.com/astral-sh/ty/issues/456)
302+
no_diagnostic_here # error: [unresolved-reference]
303+
assert_never(x) # error: [type-assertion-failure]
304+
305+
# TODO: false-positive diagnostic (https://github.com/astral-sh/ty/issues/456)
306+
def match_exhaustive_no_assertion(x: A[D] | B[E] | C[F]) -> int: # error: [invalid-return-type]
307+
match x:
308+
case A():
309+
return 0
310+
case B():
311+
return 1
312+
case C():
313+
return 2
314+
315+
def match_non_exhaustive(x: A[D] | B[E] | C[F]):
316+
match x:
317+
case A():
318+
pass
319+
case C():
320+
pass
321+
case _:
322+
this_should_be_an_error # error: [unresolved-reference]
323+
324+
# this diagnostic is correct: the inferred type of `x` is `B[E] & ~A[D] & ~C[F]`
325+
assert_never(x) # error: [type-assertion-failure]
326+
327+
# This function might seem a bit silly, but it's a pattern that exists in real-world code!
328+
# see https://github.com/bokeh/bokeh/blob/adef0157284696ce86961b2089c75fddda53c15c/src/bokeh/core/property/container.py#L130-L140
329+
def no_invalid_return_diagnostic_here_either[T](x: A[T]) -> ASub[T]:
330+
if isinstance(x, A):
331+
if isinstance(x, ASub):
332+
return x
333+
else:
334+
return ASub()
335+
else:
336+
# We *would* emit a diagnostic here complaining that it's an invalid `return` statement
337+
# ...except that we (correctly) infer that this branch is unreachable, so the complaint
338+
# is null and void (and therefore we don't emit a diagnostic)
339+
return x
340+
```

0 commit comments

Comments
 (0)