Skip to content

Commit c60e8a0

Browse files
authored
[red-knot] Add support for calling type[…] (#16597)
## Summary This fixes the non-diagnostics part of #15948. ## Test Plan New Markdown tests. Negative diff on the ecosystem checks: ```diff zipp (https://github.com/jaraco/zipp) - error: lint:call-non-callable - --> /tmp/mypy_primer/projects/zipp/zipp/__init__.py:393:16 - | - 392 | def _next(self, at): - 393 | return self.__class__(self.root, at) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Object of type `type[Unknown]` is not callable - 394 | - 395 | def is_dir(self): - | - - Found 9 diagnostics + Found 8 diagnostics arrow (https://github.com/arrow-py/arrow) + | + | + warning: lint:unused-ignore-comment + --> /tmp/mypy_primer/projects/arrow/arrow/arrow.py:576:66 + 574 | values.append(1) + 575 | + 576 | floor = self.__class__(*values, tzinfo=self.tzinfo) # type: ignore[misc] + | -------------------- Unused blanket `type: ignore` directive + 577 | + 578 | if frame_absolute == "week": - error: lint:call-non-callable - --> /tmp/mypy_primer/projects/arrow/arrow/arrow.py:1080:16 - | - 1078 | dt = self._datetime.astimezone(tz) - 1079 | - 1080 | return self.__class__( - | ________________^ - 1081 | | dt.year, - 1082 | | dt.month, - 1083 | | dt.day, - 1084 | | dt.hour, - 1085 | | dt.minute, - 1086 | | dt.second, - 1087 | | dt.microsecond, - 1088 | | dt.tzinfo, - 1089 | | fold=getattr(dt, "fold", 0), - 1090 | | ) - | |_________^ Object of type `type[Unknown]` is not callable - 1091 | - 1092 | # string output and formatting - | black (https://github.com/psf/black) - - | - | - error: lint:call-non-callable - --> /tmp/mypy_primer/projects/black/src/blib2to3/pgen2/grammar.py:135:15 - 133 | Copy the grammar. - 134 | """ - 135 | new = self.__class__() - | ^^^^^^^^^^^^^^^^ Object of type `type[@todo]` is not callable - 136 | for dict_attr in ( - 137 | "symbol2number", - Found 328 diagnostics + Found 327 diagnostics ```
1 parent f19cb86 commit c60e8a0

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Call `type[...]`
2+
3+
## Single class
4+
5+
### Trivial constructor
6+
7+
```py
8+
class C: ...
9+
10+
def _(subclass_of_c: type[C]):
11+
reveal_type(subclass_of_c()) # revealed: C
12+
```
13+
14+
### Non-trivial constructor
15+
16+
```py
17+
class C:
18+
def __init__(self, x: int): ...
19+
20+
def _(subclass_of_c: type[C]):
21+
reveal_type(subclass_of_c(1)) # revealed: C
22+
23+
# TODO: Those should all be errors
24+
reveal_type(subclass_of_c("a")) # revealed: C
25+
reveal_type(subclass_of_c()) # revealed: C
26+
reveal_type(subclass_of_c(1, 2)) # revealed: C
27+
```
28+
29+
## Dynamic base
30+
31+
```py
32+
from typing import Any
33+
from knot_extensions import Unknown
34+
35+
def _(subclass_of_any: type[Any], subclass_of_unknown: type[Unknown]):
36+
reveal_type(subclass_of_any()) # revealed: Any
37+
reveal_type(subclass_of_any("any", "args", 1, 2)) # revealed: Any
38+
reveal_type(subclass_of_unknown()) # revealed: Unknown
39+
reveal_type(subclass_of_unknown("any", "args", 1, 2)) # revealed: Unknown
40+
```
41+
42+
## Unions of classes
43+
44+
```py
45+
class A: ...
46+
class B: ...
47+
48+
def _(subclass_of_ab: type[A | B]):
49+
reveal_type(subclass_of_ab()) # revealed: A | B
50+
```

crates/red_knot_python_semantic/src/types.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,6 +2678,13 @@ impl<'db> Type<'db> {
26782678
)))
26792679
}
26802680

2681+
Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() {
2682+
ClassBase::Dynamic(dynamic_type) => Ok(CallOutcome::Single(
2683+
CallBinding::from_return_type(Type::Dynamic(dynamic_type)),
2684+
)),
2685+
ClassBase::Class(class) => Type::class_literal(class).try_call(db, arguments),
2686+
},
2687+
26812688
instance_ty @ Type::Instance(_) => {
26822689
instance_ty
26832690
.try_call_dunder(db, "__call__", arguments)

0 commit comments

Comments
 (0)