Skip to content

Commit a115741

Browse files
committed
[red-knot] Support typing.TYPE_CHECKING
1 parent c1837e4 commit a115741

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Known constants
2+
3+
## `typing.TYPE_CHECKING`
4+
5+
This constant is `True` when in type-checking mode, `False` otherwise. The symbol is defined to be
6+
`False` at runtime. In typeshed, it is annotated as `bool`. This test makes sure that we infer
7+
`Literal[True]` for it anyways.
8+
9+
### Basic
10+
11+
```py
12+
from typing import TYPE_CHECKING
13+
import typing
14+
15+
reveal_type(TYPE_CHECKING) # revealed: Literal[True]
16+
reveal_type(typing.TYPE_CHECKING) # revealed: Literal[True]
17+
```
18+
19+
### Aliased
20+
21+
Make sure that we still infer the correct type if the constant has been given a different name:
22+
23+
```py
24+
from typing import TYPE_CHECKING as TC
25+
26+
reveal_type(TC) # revealed: Literal[True]
27+
```
28+
29+
### Must originate from `typing`
30+
31+
Make sure we only use our special handling for `typing.TYPE_CHECKING` and not for other constants
32+
with the same name:
33+
34+
```py path=constants.py
35+
TYPE_CHECKING: bool = False
36+
```
37+
38+
```py
39+
from constants import TYPE_CHECKING
40+
41+
reveal_type(TYPE_CHECKING) # revealed: bool
42+
```
43+
44+
### `typing_extensions` re-export
45+
46+
This should behave in the same way as `typing.TYPE_CHECKING`:
47+
48+
```py
49+
from typing_extensions import TYPE_CHECKING
50+
51+
reveal_type(TYPE_CHECKING) # revealed: Literal[True]
52+
```

crates/red_knot_python_semantic/src/types.rs

+8
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ fn symbol_by_id<'db>(db: &'db dyn Db, scope: ScopeId<'db>, symbol: ScopedSymbolI
120120

121121
/// Shorthand for `symbol_by_id` that takes a symbol name instead of an ID.
122122
fn symbol<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Symbol<'db> {
123+
// We don't need to check for `typing_extensions` here, because `typing_extensions.TYPE_CHECKING`
124+
// is just a re-export of `typing.TYPE_CHECKING`.
125+
if name == "TYPE_CHECKING"
126+
&& file_to_module(db, scope.file(db)).is_some_and(|module| module.name() == "typing")
127+
{
128+
return Symbol::Type(Type::BooleanLiteral(true), Boundness::Bound);
129+
}
130+
123131
let table = symbol_table(db, scope);
124132
table
125133
.symbol_id_by_name(name)

0 commit comments

Comments
 (0)