Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ does["not"]["exist"] = 0
reveal_type(does["not"]["exist"]) # revealed: Unknown

non_subscriptable = 1
# error: [non-subscriptable]
non_subscriptable[0] = 0
# error: [non-subscriptable]
reveal_type(non_subscriptable[0]) # revealed: Unknown
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Instance subscript

## Getitem unbound
## `__getitem__` unbound

```py
class NotSubscriptable: ...

a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscriptable` with no `__getitem__` method"
```

## Getitem not callable
## `__getitem__` not callable

```py
class NotSubscriptable:
Expand All @@ -18,7 +18,7 @@ class NotSubscriptable:
a = NotSubscriptable()[0]
```

## Valid getitem
## Valid `__getitem__`

```py
class Identity:
Expand All @@ -28,7 +28,7 @@ class Identity:
reveal_type(Identity()[0]) # revealed: int
```

## Getitem union
## `__getitem__` union

```py
def _(flag: bool):
Expand All @@ -42,3 +42,14 @@ def _(flag: bool):

reveal_type(Identity()[0]) # revealed: int | str
```

## `__setitem__` with no `__getitem__`

```py
class NoGetitem:
def __setitem__(self, index: int, value: int) -> None:
pass

a = NoGetitem()
a[0] = 0
```
34 changes: 20 additions & 14 deletions crates/ty_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1900,13 +1900,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} else if let AnyNodeRef::ExprSubscript(ast::ExprSubscript {
value,
slice,
ctx,
..
}) = node
{
let value_ty = self.infer_expression(value);
let slice_ty = self.infer_expression(slice);
let result_ty =
self.infer_subscript_expression_types(value, value_ty, slice_ty);
let result_ty = self
.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
return (result_ty, is_modifiable);
}
}
Expand Down Expand Up @@ -8291,7 +8292,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ExprContext::Store => {
let value_ty = self.infer_expression(value);
let slice_ty = self.infer_expression(slice);
self.infer_subscript_expression_types(value, value_ty, slice_ty);
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
Type::Never
}
ExprContext::Del => {
Expand All @@ -8301,7 +8302,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ExprContext::Invalid => {
let value_ty = self.infer_expression(value);
let slice_ty = self.infer_expression(slice);
self.infer_subscript_expression_types(value, value_ty, slice_ty);
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
Type::unknown()
}
}
Expand All @@ -8313,7 +8314,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
node_index: _,
value,
slice,
ctx: _,
ctx,
} = subscript;
let value_ty = self.infer_expression(value);
let mut constraint_keys = vec![];
Expand All @@ -8330,7 +8331,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
// (to store the expression type and to report errors).
let slice_ty = self.infer_expression(slice);
self.infer_subscript_expression_types(value, value_ty, slice_ty);
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
return ty;
}
}
Expand Down Expand Up @@ -8364,7 +8365,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}

let slice_ty = self.infer_expression(slice);
let result_ty = self.infer_subscript_expression_types(value, value_ty, slice_ty);
let result_ty = self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
self.narrow_expr_with_applicable_constraints(subscript, result_ty, &constraint_keys)
}

Expand Down Expand Up @@ -8418,6 +8419,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
value_node: &ast::Expr,
value_ty: Type<'db>,
slice_ty: Type<'db>,
expr_context: ExprContext,
) -> Type<'db> {
match (value_ty, slice_ty, slice_ty.slice_literal(self.db())) {
(Type::NominalInstance(instance), _, _)
Expand All @@ -8427,11 +8429,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
value_node,
Type::version_info_tuple(self.db()),
slice_ty,
expr_context,
)
}

(Type::Union(union), _, _) => union.map(self.db(), |element| {
self.infer_subscript_expression_types(value_node, *element, slice_ty)
self.infer_subscript_expression_types(value_node, *element, slice_ty, expr_context)
}),

// TODO: we can map over the intersection and fold the results back into an intersection,
Expand Down Expand Up @@ -8562,6 +8565,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
value_node,
value_ty,
Type::IntLiteral(i64::from(bool)),
expr_context,
),

(Type::SpecialForm(SpecialFormType::Protocol), Type::Tuple(typevars), _) => {
Expand Down Expand Up @@ -8754,12 +8758,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
);
}
} else {
report_non_subscriptable(
&self.context,
value_node.into(),
value_ty,
"__getitem__",
);
if expr_context != ExprContext::Store {
report_non_subscriptable(
&self.context,
value_node.into(),
value_ty,
"__getitem__",
);
}
}

Type::unknown()
Expand Down
Loading