Skip to content

Commit 0a627ef

Browse files
authored
[red-knot] Never is callable and iterable. Arbitrary attributes can be accessed. (#16533)
## Summary - `Never` is callable - `Never` is iterable - Arbitrary attributes can be accessed on `Never` Split out from #16416 that is going to be required. ## Test Plan Tests for all properties above.
1 parent a25be46 commit 0a627ef

File tree

4 files changed

+42
-5
lines changed

4 files changed

+42
-5
lines changed

crates/red_knot_python_semantic/resources/mdtest/attributes.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,6 +1209,20 @@ class C:
12091209
reveal_type(C().x) # revealed: Unknown
12101210
```
12111211

1212+
### Accessing attributes on `Never`
1213+
1214+
Arbitrary attributes can be accessed on `Never` without emitting any errors:
1215+
1216+
```py
1217+
from typing_extensions import Never
1218+
1219+
def f(never: Never):
1220+
reveal_type(never.arbitrary_attribute) # revealed: Never
1221+
1222+
# Assigning `Never` to an attribute on `Never` is also allowed:
1223+
never.another_attribute = never
1224+
```
1225+
12121226
### Builtin types attributes
12131227

12141228
This test can probably be removed eventually, but we currently include it because we do not yet
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Never is callable
2+
3+
The type `Never` is callable with an arbitrary set of arguments. The result is always `Never`.
4+
5+
```py
6+
from typing_extensions import Never
7+
8+
def f(never: Never):
9+
reveal_type(never()) # revealed: Never
10+
reveal_type(never(1)) # revealed: Never
11+
reveal_type(never(1, "a", never, x=None)) # revealed: Never
12+
```

crates/red_knot_python_semantic/resources/mdtest/loops/for.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,3 +737,13 @@ def _(flag: bool, flag2: bool):
737737
for y in Iterable2():
738738
reveal_type(y) # revealed: bytes | str | int
739739
```
740+
741+
## Never is iterable
742+
743+
```py
744+
from typing_extensions import Never
745+
746+
def f(never: Never):
747+
for x in never:
748+
reveal_type(x) # revealed: Never
749+
```

crates/red_knot_python_semantic/src/types.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,9 +1366,7 @@ impl<'db> Type<'db> {
13661366
#[must_use]
13671367
fn static_member(&self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
13681368
match self {
1369-
Type::Dynamic(_) => Symbol::bound(self),
1370-
1371-
Type::Never => Symbol::todo("attribute lookup on Never"),
1369+
Type::Dynamic(_) | Type::Never => Symbol::bound(self),
13721370

13731371
Type::FunctionLiteral(_) => KnownClass::FunctionType
13741372
.to_instance(db)
@@ -2267,8 +2265,11 @@ impl<'db> Type<'db> {
22672265
})
22682266
}
22692267

2270-
// Dynamic types are callable, and the return type is the same dynamic type
2271-
Type::Dynamic(_) => Ok(CallOutcome::Single(CallBinding::from_return_type(self))),
2268+
// Dynamic types are callable, and the return type is the same dynamic type. Similarly,
2269+
// `Never` is always callable and returns `Never`.
2270+
Type::Dynamic(_) | Type::Never => {
2271+
Ok(CallOutcome::Single(CallBinding::from_return_type(self)))
2272+
}
22722273

22732274
Type::Union(union) => {
22742275
CallOutcome::try_call_union(db, union, |element| element.try_call(db, arguments))

0 commit comments

Comments
 (0)