Skip to content

Commit 296d67a

Browse files
authored
Special-case value-expression inference of special form subscriptions (#16877)
## Summary Currently for something like `X = typing.Tuple[str, str]`, we infer the value of `X` as `object`. That's because `Tuple` (like many of the symbols in the typing module) is annotated as a `_SpecialForm` instance in typeshed's stubs: https://github.com/astral-sh/ruff/blob/23382f5f8c7b4e356368cdeb1049b8c1910baff3/crates/red_knot_vendored/vendor/typeshed/stdlib/typing.pyi#L215 and we don't understand implicit type aliases yet, and the stub for `_SpecialForm.__getitem__` says it always returns `object`: https://github.com/astral-sh/ruff/blob/23382f5f8c7b4e356368cdeb1049b8c1910baff3/crates/red_knot_vendored/vendor/typeshed/stdlib/typing.pyi#L198-L200 We have existing false positives in our test suite due to this: https://github.com/astral-sh/ruff/blob/23382f5f8c7b4e356368cdeb1049b8c1910baff3/crates/red_knot_python_semantic/resources/mdtest/annotations/annotated.md?plain=1#L76-L78 and it's causing _many_ new false positives in #16872, which tries to make our annotation-expression parsing stricter in some ways. This PR therefore adds some small special casing for `KnownInstanceType` variants that fallback to `_SpecialForm`, so that these false positives can be avoided. ## Test Plan Existing mdtest altered. Cc. @MatthewMckee4
1 parent 42cbce5 commit 296d67a

File tree

3 files changed

+10
-3
lines changed

3 files changed

+10
-3
lines changed

crates/red_knot_python_semantic/resources/mdtest/annotations/annotated.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,10 @@ Inheriting from `Annotated[T, ...]` is equivalent to inheriting from `T` itself.
7373
```py
7474
from typing_extensions import Annotated
7575

76-
# TODO: False positive
77-
# error: [invalid-base]
7876
class C(Annotated[int, "foo"]): ...
7977

8078
# TODO: Should be `tuple[Literal[C], Literal[int], Literal[object]]`
81-
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Unknown, Literal[object]]
79+
reveal_type(C.__mro__) # revealed: tuple[Literal[C], @Todo(Inference of subscript on special form), Literal[object]]
8280
```
8381

8482
### Not parameterized

crates/red_knot_python_semantic/src/types/class.rs

+4
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@ impl<'db> KnownClass {
851851
matches!(self, Self::Bool)
852852
}
853853

854+
pub(crate) const fn is_special_form(self) -> bool {
855+
matches!(self, Self::SpecialForm)
856+
}
857+
854858
/// Determine whether instances of this class are always truthy, always falsy,
855859
/// or have an ambiguous truthiness.
856860
pub(crate) const fn bool(self) -> Truthiness {

crates/red_knot_python_semantic/src/types/infer.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5631,6 +5631,11 @@ impl<'db> TypeInferenceBuilder<'db> {
56315631
(Type::KnownInstance(KnownInstanceType::Protocol), _) => {
56325632
Type::Dynamic(DynamicType::TodoProtocol)
56335633
}
5634+
(Type::KnownInstance(known_instance), _)
5635+
if known_instance.class().is_special_form() =>
5636+
{
5637+
todo_type!("Inference of subscript on special form")
5638+
}
56345639
(value_ty, slice_ty) => {
56355640
// If the class defines `__getitem__`, return its return type.
56365641
//

0 commit comments

Comments
 (0)