From 961fc98344a87351d4f3805fb75c1109cc33b2bb Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 1 Oct 2024 14:16:00 -0400 Subject: [PATCH] Use `__class_getitem__` for more specific non-subscript errors (#13596) --- .../src/types/infer.rs | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index b8b529865ff40..ed25acfafb8d6 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1327,12 +1327,13 @@ impl<'db> TypeInferenceBuilder<'db> { &mut self, node: AnyNodeRef, non_subscriptable_ty: Type<'db>, + method: &str, ) { self.add_diagnostic( node, "non-subscriptable", format_args!( - "Cannot subscript object of type '{}' with no `__getitem__` method.", + "Cannot subscript object of type '{}' with no `{method}` method.", non_subscriptable_ty.display(self.db) ), ); @@ -2627,10 +2628,16 @@ impl<'db> TypeInferenceBuilder<'db> { .call(self.db, &[slice_ty]) .unwrap_with_diagnostic(self.db, value.as_ref().into(), self); } + + self.non_subscriptable_diagnostic( + (&**value).into(), + value_ty, + "__class_getitem__", + ); + } else { + self.non_subscriptable_diagnostic((&**value).into(), value_ty, "__getitem__"); } - // Otherwise, emit a diagnostic. - self.non_subscriptable_diagnostic((&**value).into(), value_ty); Type::Unknown } } @@ -6791,6 +6798,30 @@ mod tests { Ok(()) } + #[test] + fn subscript_class_getitem_unbound() -> anyhow::Result<()> { + let mut db = setup_db(); + + db.write_dedented( + "/src/a.py", + " + class NotSubscriptable: + pass + + a = NotSubscriptable[0] + ", + )?; + + assert_public_ty(&db, "/src/a.py", "a", "Unknown"); + assert_file_diagnostics( + &db, + "/src/a.py", + &["Cannot subscript object of type 'Literal[NotSubscriptable]' with no `__class_getitem__` method."], + ); + + Ok(()) + } + #[test] fn subscript_not_callable_getitem() -> anyhow::Result<()> { let mut db = setup_db();