Skip to content

Commit cc47a66

Browse files
draft never handling
1 parent e474f36 commit cc47a66

File tree

9 files changed

+213
-63
lines changed

9 files changed

+213
-63
lines changed

crates/ty_python_semantic/resources/mdtest/directives/assert_never.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,53 @@
22

33
## Basic functionality
44

5-
<!-- snapshot-diagnostics -->
5+
`assert_never` makes sure that the type of the argument is `Never`.
66

7-
`assert_never` makes sure that the type of the argument is `Never`. If it is not, a
8-
`type-assertion-failure` diagnostic is emitted.
7+
### Correct usage
98

109
```py
1110
from typing_extensions import assert_never, Never, Any
1211
from ty_extensions import Unknown
1312

14-
def _(never: Never, any_: Any, unknown: Unknown, flag: bool):
13+
def _(never: Never):
1514
assert_never(never) # fine
15+
```
16+
17+
### Diagnostics
18+
19+
<!-- snapshot-diagnostics -->
1620

21+
If it is not, a `type-assertion-failure` diagnostic is emitted.
22+
23+
```py
24+
from typing_extensions import assert_never, Never, Any
25+
from ty_extensions import Unknown
26+
27+
def _():
1728
assert_never(0) # error: [type-assertion-failure]
29+
30+
def _():
1831
assert_never("") # error: [type-assertion-failure]
32+
33+
def _():
1934
assert_never(None) # error: [type-assertion-failure]
35+
36+
def _():
2037
assert_never([]) # error: [type-assertion-failure]
38+
39+
def _():
2140
assert_never({}) # error: [type-assertion-failure]
41+
42+
def _():
2243
assert_never(()) # error: [type-assertion-failure]
44+
45+
def _(flag: bool, never: Never):
2346
assert_never(1 if flag else never) # error: [type-assertion-failure]
2447

48+
def _(any_: Any):
2549
assert_never(any_) # error: [type-assertion-failure]
50+
51+
def _(unknown: Unknown):
2652
assert_never(unknown) # error: [type-assertion-failure]
2753
```
2854

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ source: crates/ty_test/src/lib.rs
33
expression: snapshot
44
---
55
---
6-
mdtest name: assert_never.md - `assert_never` - Basic functionality
6+
mdtest name: assert_never.md - `assert_never` - Basic functionality - Diagnostics
77
mdtest path: crates/ty_python_semantic/resources/mdtest/directives/assert_never.md
88
---
99

@@ -15,35 +15,47 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/directives/assert_never.
1515
1 | from typing_extensions import assert_never, Never, Any
1616
2 | from ty_extensions import Unknown
1717
3 |
18-
4 | def _(never: Never, any_: Any, unknown: Unknown, flag: bool):
19-
5 | assert_never(never) # fine
18+
4 | def _():
19+
5 | assert_never(0) # error: [type-assertion-failure]
2020
6 |
21-
7 | assert_never(0) # error: [type-assertion-failure]
21+
7 | def _():
2222
8 | assert_never("") # error: [type-assertion-failure]
23-
9 | assert_never(None) # error: [type-assertion-failure]
24-
10 | assert_never([]) # error: [type-assertion-failure]
25-
11 | assert_never({}) # error: [type-assertion-failure]
26-
12 | assert_never(()) # error: [type-assertion-failure]
27-
13 | assert_never(1 if flag else never) # error: [type-assertion-failure]
28-
14 |
29-
15 | assert_never(any_) # error: [type-assertion-failure]
30-
16 | assert_never(unknown) # error: [type-assertion-failure]
23+
9 |
24+
10 | def _():
25+
11 | assert_never(None) # error: [type-assertion-failure]
26+
12 |
27+
13 | def _():
28+
14 | assert_never([]) # error: [type-assertion-failure]
29+
15 |
30+
16 | def _():
31+
17 | assert_never({}) # error: [type-assertion-failure]
32+
18 |
33+
19 | def _():
34+
20 | assert_never(()) # error: [type-assertion-failure]
35+
21 |
36+
22 | def _(flag: bool, never: Never):
37+
23 | assert_never(1 if flag else never) # error: [type-assertion-failure]
38+
24 |
39+
25 | def _(any_: Any):
40+
26 | assert_never(any_) # error: [type-assertion-failure]
41+
27 |
42+
28 | def _(unknown: Unknown):
43+
29 | assert_never(unknown) # error: [type-assertion-failure]
3144
```
3245

3346
# Diagnostics
3447

3548
```
3649
error[type-assertion-failure]: Argument does not have asserted type `Never`
37-
--> src/mdtest_snippet.py:7:5
50+
--> src/mdtest_snippet.py:5:5
3851
|
39-
5 | assert_never(never) # fine
40-
6 |
41-
7 | assert_never(0) # error: [type-assertion-failure]
52+
4 | def _():
53+
5 | assert_never(0) # error: [type-assertion-failure]
4254
| ^^^^^^^^^^^^^-^
4355
| |
4456
| Inferred type of argument is `Literal[0]`
45-
8 | assert_never("") # error: [type-assertion-failure]
46-
9 | assert_never(None) # error: [type-assertion-failure]
57+
6 |
58+
7 | def _():
4759
|
4860
info: `Never` and `Literal[0]` are not equivalent types
4961
info: rule `type-assertion-failure` is enabled by default
@@ -54,13 +66,13 @@ info: rule `type-assertion-failure` is enabled by default
5466
error[type-assertion-failure]: Argument does not have asserted type `Never`
5567
--> src/mdtest_snippet.py:8:5
5668
|
57-
7 | assert_never(0) # error: [type-assertion-failure]
69+
7 | def _():
5870
8 | assert_never("") # error: [type-assertion-failure]
5971
| ^^^^^^^^^^^^^--^
6072
| |
6173
| Inferred type of argument is `Literal[""]`
62-
9 | assert_never(None) # error: [type-assertion-failure]
63-
10 | assert_never([]) # error: [type-assertion-failure]
74+
9 |
75+
10 | def _():
6476
|
6577
info: `Never` and `Literal[""]` are not equivalent types
6678
info: rule `type-assertion-failure` is enabled by default
@@ -69,16 +81,15 @@ info: rule `type-assertion-failure` is enabled by default
6981

7082
```
7183
error[type-assertion-failure]: Argument does not have asserted type `Never`
72-
--> src/mdtest_snippet.py:9:5
84+
--> src/mdtest_snippet.py:11:5
7385
|
74-
7 | assert_never(0) # error: [type-assertion-failure]
75-
8 | assert_never("") # error: [type-assertion-failure]
76-
9 | assert_never(None) # error: [type-assertion-failure]
86+
10 | def _():
87+
11 | assert_never(None) # error: [type-assertion-failure]
7788
| ^^^^^^^^^^^^^----^
7889
| |
7990
| Inferred type of argument is `None`
80-
10 | assert_never([]) # error: [type-assertion-failure]
81-
11 | assert_never({}) # error: [type-assertion-failure]
91+
12 |
92+
13 | def _():
8293
|
8394
info: `Never` and `None` are not equivalent types
8495
info: rule `type-assertion-failure` is enabled by default
@@ -87,16 +98,15 @@ info: rule `type-assertion-failure` is enabled by default
8798

8899
```
89100
error[type-assertion-failure]: Argument does not have asserted type `Never`
90-
--> src/mdtest_snippet.py:10:5
101+
--> src/mdtest_snippet.py:14:5
91102
|
92-
8 | assert_never("") # error: [type-assertion-failure]
93-
9 | assert_never(None) # error: [type-assertion-failure]
94-
10 | assert_never([]) # error: [type-assertion-failure]
103+
13 | def _():
104+
14 | assert_never([]) # error: [type-assertion-failure]
95105
| ^^^^^^^^^^^^^--^
96106
| |
97107
| Inferred type of argument is `list[Unknown]`
98-
11 | assert_never({}) # error: [type-assertion-failure]
99-
12 | assert_never(()) # error: [type-assertion-failure]
108+
15 |
109+
16 | def _():
100110
|
101111
info: `Never` and `list[Unknown]` are not equivalent types
102112
info: rule `type-assertion-failure` is enabled by default
@@ -105,16 +115,15 @@ info: rule `type-assertion-failure` is enabled by default
105115

106116
```
107117
error[type-assertion-failure]: Argument does not have asserted type `Never`
108-
--> src/mdtest_snippet.py:11:5
118+
--> src/mdtest_snippet.py:17:5
109119
|
110-
9 | assert_never(None) # error: [type-assertion-failure]
111-
10 | assert_never([]) # error: [type-assertion-failure]
112-
11 | assert_never({}) # error: [type-assertion-failure]
120+
16 | def _():
121+
17 | assert_never({}) # error: [type-assertion-failure]
113122
| ^^^^^^^^^^^^^--^
114123
| |
115124
| Inferred type of argument is `dict[Unknown, Unknown]`
116-
12 | assert_never(()) # error: [type-assertion-failure]
117-
13 | assert_never(1 if flag else never) # error: [type-assertion-failure]
125+
18 |
126+
19 | def _():
118127
|
119128
info: `Never` and `dict[Unknown, Unknown]` are not equivalent types
120129
info: rule `type-assertion-failure` is enabled by default
@@ -123,15 +132,15 @@ info: rule `type-assertion-failure` is enabled by default
123132

124133
```
125134
error[type-assertion-failure]: Argument does not have asserted type `Never`
126-
--> src/mdtest_snippet.py:12:5
135+
--> src/mdtest_snippet.py:20:5
127136
|
128-
10 | assert_never([]) # error: [type-assertion-failure]
129-
11 | assert_never({}) # error: [type-assertion-failure]
130-
12 | assert_never(()) # error: [type-assertion-failure]
137+
19 | def _():
138+
20 | assert_never(()) # error: [type-assertion-failure]
131139
| ^^^^^^^^^^^^^--^
132140
| |
133141
| Inferred type of argument is `tuple[()]`
134-
13 | assert_never(1 if flag else never) # error: [type-assertion-failure]
142+
21 |
143+
22 | def _(flag: bool, never: Never):
135144
|
136145
info: `Never` and `tuple[()]` are not equivalent types
137146
info: rule `type-assertion-failure` is enabled by default
@@ -140,16 +149,15 @@ info: rule `type-assertion-failure` is enabled by default
140149

141150
```
142151
error[type-assertion-failure]: Argument does not have asserted type `Never`
143-
--> src/mdtest_snippet.py:13:5
152+
--> src/mdtest_snippet.py:23:5
144153
|
145-
11 | assert_never({}) # error: [type-assertion-failure]
146-
12 | assert_never(()) # error: [type-assertion-failure]
147-
13 | assert_never(1 if flag else never) # error: [type-assertion-failure]
154+
22 | def _(flag: bool, never: Never):
155+
23 | assert_never(1 if flag else never) # error: [type-assertion-failure]
148156
| ^^^^^^^^^^^^^--------------------^
149157
| |
150158
| Inferred type of argument is `Literal[1]`
151-
14 |
152-
15 | assert_never(any_) # error: [type-assertion-failure]
159+
24 |
160+
25 | def _(any_: Any):
153161
|
154162
info: `Never` and `Literal[1]` are not equivalent types
155163
info: rule `type-assertion-failure` is enabled by default
@@ -158,15 +166,15 @@ info: rule `type-assertion-failure` is enabled by default
158166

159167
```
160168
error[type-assertion-failure]: Argument does not have asserted type `Never`
161-
--> src/mdtest_snippet.py:15:5
169+
--> src/mdtest_snippet.py:26:5
162170
|
163-
13 | assert_never(1 if flag else never) # error: [type-assertion-failure]
164-
14 |
165-
15 | assert_never(any_) # error: [type-assertion-failure]
171+
25 | def _(any_: Any):
172+
26 | assert_never(any_) # error: [type-assertion-failure]
166173
| ^^^^^^^^^^^^^----^
167174
| |
168175
| Inferred type of argument is `Any`
169-
16 | assert_never(unknown) # error: [type-assertion-failure]
176+
27 |
177+
28 | def _(unknown: Unknown):
170178
|
171179
info: `Never` and `Any` are not equivalent types
172180
info: rule `type-assertion-failure` is enabled by default
@@ -175,10 +183,10 @@ info: rule `type-assertion-failure` is enabled by default
175183

176184
```
177185
error[type-assertion-failure]: Argument does not have asserted type `Never`
178-
--> src/mdtest_snippet.py:16:5
186+
--> src/mdtest_snippet.py:29:5
179187
|
180-
15 | assert_never(any_) # error: [type-assertion-failure]
181-
16 | assert_never(unknown) # error: [type-assertion-failure]
188+
28 | def _(unknown: Unknown):
189+
29 | assert_never(unknown) # error: [type-assertion-failure]
182190
| ^^^^^^^^^^^^^-------^
183191
| |
184192
| Inferred type of argument is `Unknown`

crates/ty_python_semantic/resources/mdtest/terminal_statements.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,65 @@ def f():
570570
reveal_type(x) # revealed: Literal[1]
571571
```
572572

573+
## Calls to functions returning `Never` / `NoReturn`
574+
575+
### No implicit return
576+
577+
If we see a call to a function returning `Never`, we should be able to understand that the function
578+
cannot implicitly return `None`. In the below examples, verify that there are no errors emitted for
579+
invalid return type.
580+
581+
```py
582+
from typing import NoReturn
583+
import sys
584+
585+
def f() -> NoReturn:
586+
sys.exit(1)
587+
```
588+
589+
Let's try cases where the function annotated with `NoReturn` is some sub-expression.
590+
591+
```py
592+
from typing import NoReturn
593+
import sys
594+
595+
def _() -> NoReturn:
596+
3 + sys.exit(1)
597+
598+
def _() -> NoReturn:
599+
3 if sys.exit(1) else 4
600+
```
601+
602+
### Type narrowing
603+
604+
```py
605+
from typing import NoReturn
606+
import sys
607+
608+
def g(x: int | None):
609+
if x is None:
610+
sys.exit(1)
611+
612+
# TODO: should be just int, not int | None
613+
reveal_type(x) # revealed: int | None
614+
```
615+
616+
### Bindings after call
617+
618+
These should be understood to be unreachable.
619+
620+
```py
621+
import sys
622+
623+
def _():
624+
x = 3
625+
626+
sys.exit(1)
627+
628+
x = 4
629+
reveal_type(x) # revealed: Never
630+
```
631+
573632
## Nested functions
574633

575634
Free references inside of a function body refer to variables defined in the containing scope.

crates/ty_python_semantic/src/semantic_index/builder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,6 +2207,18 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
22072207
}
22082208
walk_expr(self, expr);
22092209
}
2210+
ast::Expr::Call(ast::ExprCall { func, .. }) if !self.source_type.is_stub() => {
2211+
let expression = self.add_standalone_expression(func);
2212+
2213+
let predicate = Predicate {
2214+
node: PredicateNode::ReturnsNever(expression),
2215+
is_positive: false,
2216+
};
2217+
2218+
walk_expr(self, expr);
2219+
2220+
self.record_reachability_constraint(predicate);
2221+
}
22102222
_ => {
22112223
walk_expr(self, expr);
22122224
}

crates/ty_python_semantic/src/semantic_index/predicate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ impl Predicate<'_> {
6161
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
6262
pub(crate) enum PredicateNode<'db> {
6363
Expression(Expression<'db>),
64+
ReturnsNever(Expression<'db>),
6465
Pattern(PatternPredicate<'db>),
6566
StarImportPlaceholder(StarImportPlaceholderPredicate<'db>),
6667
}

0 commit comments

Comments
 (0)