From 40cba5dc8aa79fef488bcae366abf7efefb04faa Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 19 Dec 2024 13:03:41 +0000 Subject: [PATCH] [red-knot] Cleanup various `todo_type!()` messages (#15063) Co-authored-by: Micha Reiser --- .../mdtest/annotations/literal_string.md | 4 +- .../resources/mdtest/with/async.md | 2 +- crates/red_knot_python_semantic/src/types.rs | 51 +++++++---------- .../src/types/infer.rs | 57 +++++++++---------- 4 files changed, 52 insertions(+), 62 deletions(-) diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md index 55380d6db288b..16d5ac20c9def 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md @@ -73,12 +73,12 @@ qux = (foo, bar) reveal_type(qux) # revealed: tuple[Literal["foo"], Literal["bar"]] # TODO: Infer "LiteralString" -reveal_type(foo.join(qux)) # revealed: @Todo(call todo) +reveal_type(foo.join(qux)) # revealed: @Todo(Attribute access on `StringLiteral` types) template: LiteralString = "{}, {}" reveal_type(template) # revealed: Literal["{}, {}"] # TODO: Infer `LiteralString` -reveal_type(template.format(foo, bar)) # revealed: @Todo(call todo) +reveal_type(template.format(foo, bar)) # revealed: @Todo(Attribute access on `StringLiteral` types) ``` ### Assignability diff --git a/crates/red_knot_python_semantic/resources/mdtest/with/async.md b/crates/red_knot_python_semantic/resources/mdtest/with/async.md index 9b2b1439fb660..2a98c50cb2036 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/with/async.md +++ b/crates/red_knot_python_semantic/resources/mdtest/with/async.md @@ -17,5 +17,5 @@ class Manager: async def test(): async with Manager() as f: - reveal_type(f) # revealed: @Todo(async with statement) + reveal_type(f) # revealed: @Todo(async `with` statement) ``` diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 4acce9ded5910..4c39197f12847 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -1458,15 +1458,10 @@ impl<'db> Type<'db> { } match self { - Type::Any => Type::Any.into(), - Type::Never => { - // TODO: attribute lookup on Never type - todo_type!().into() - } - Type::Unknown => Type::Unknown.into(), + Type::Any | Type::Unknown | Type::Todo(_) => self.into(), + Type::Never => todo_type!("attribute lookup on Never").into(), Type::FunctionLiteral(_) => { - // TODO: attribute lookup on function type - todo_type!().into() + todo_type!("Attribute access on `FunctionLiteral` types").into() } Type::ModuleLiteral(module_ref) => { // `__dict__` is a very special member that is never overridden by module globals; @@ -1581,40 +1576,38 @@ impl<'db> Type<'db> { Type::Intersection(_) => { // TODO perform the get_member on each type in the intersection // TODO return the intersection of those results - todo_type!().into() + todo_type!("Attribute access on `Intersection` types").into() } - Type::IntLiteral(_) => { - // TODO raise error - todo_type!().into() + Type::IntLiteral(_) => todo_type!("Attribute access on `IntLiteral` types").into(), + Type::BooleanLiteral(_) => { + todo_type!("Attribute access on `BooleanLiteral` types").into() } - Type::BooleanLiteral(_) => todo_type!().into(), Type::StringLiteral(_) => { // TODO defer to `typing.LiteralString`/`builtins.str` methods // from typeshed's stubs - todo_type!().into() + todo_type!("Attribute access on `StringLiteral` types").into() } Type::LiteralString => { // TODO defer to `typing.LiteralString`/`builtins.str` methods // from typeshed's stubs - todo_type!().into() + todo_type!("Attribute access on `LiteralString` types").into() } Type::BytesLiteral(_) => { // TODO defer to Type::Instance().member - todo_type!().into() + todo_type!("Attribute access on `BytesLiteral` types").into() } Type::SliceLiteral(_) => { // TODO defer to `builtins.slice` methods - todo_type!().into() + todo_type!("Attribute access on `SliceLiteral` types").into() } Type::Tuple(_) => { // TODO: implement tuple methods - todo_type!().into() + todo_type!("Attribute access on heterogeneous tuple types").into() } Type::AlwaysTruthy | Type::AlwaysFalsy => { // TODO return `Callable[[], Literal[True/False]]` for `__bool__` access KnownClass::Object.to_instance(db).member(db, name) } - &todo @ Type::Todo(_) => todo.into(), } } @@ -1819,12 +1812,8 @@ impl<'db> Type<'db> { } } - // `Any` is callable, and its return type is also `Any`. - Type::Any => CallOutcome::callable(Type::Any), - - Type::Todo(_) => CallOutcome::callable(todo_type!("call todo")), - - Type::Unknown => CallOutcome::callable(Type::Unknown), + // Dynamic types are callable, and the return type is the same dynamic type + Type::Any | Type::Todo(_) | Type::Unknown => CallOutcome::callable(self), Type::Union(union) => CallOutcome::union( self, @@ -1834,8 +1823,7 @@ impl<'db> Type<'db> { .map(|elem| elem.call(db, arg_types)), ), - // TODO: intersection types - Type::Intersection(_) => CallOutcome::callable(todo_type!()), + Type::Intersection(_) => CallOutcome::callable(todo_type!("Type::Intersection.call()")), _ => CallOutcome::not_callable(self), } @@ -1936,8 +1924,7 @@ impl<'db> Type<'db> { }) => Type::instance(*class), Type::SubclassOf(_) => Type::Any, Type::Union(union) => union.map(db, |element| element.to_instance(db)), - // TODO: we can probably do better here: --Alex - Type::Intersection(_) => todo_type!(), + Type::Intersection(_) => todo_type!("Type::Intersection.to_instance()"), // TODO: calling `.to_instance()` on any of these should result in a diagnostic, // since they already indicate that the object is an instance of some kind: Type::BooleanLiteral(_) @@ -2170,6 +2157,12 @@ impl<'db> From> for Symbol<'db> { } } +impl<'db> From<&Type<'db>> for Symbol<'db> { + fn from(value: &Type<'db>) -> Self { + Self::from(*value) + } +} + /// Error struct providing information on type(s) that were deemed to be invalid /// in a type expression context, and the type we should therefore fallback to /// for the problematic type expression. diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 95e7eca3f1dfd..0fbecd188d8aa 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1447,7 +1447,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) -> Type<'db> { // TODO: Handle async with statements (they use `aenter` and `aexit`) if is_async { - return todo_type!("async with statement"); + return todo_type!("async `with` statement"); } let context_manager_ty = context_expression_ty.to_meta_type(self.db()); @@ -1680,7 +1680,8 @@ impl<'db> TypeInferenceBuilder<'db> { default, } = node; self.infer_optional_expression(default.as_deref()); - self.add_declaration_with_binding(node.into(), definition, todo_type!(), todo_type!()); + let pep_695_todo = todo_type!("PEP-695 ParamSpec definition types"); + self.add_declaration_with_binding(node.into(), definition, pep_695_todo, pep_695_todo); } fn infer_typevartuple_definition( @@ -1694,7 +1695,8 @@ impl<'db> TypeInferenceBuilder<'db> { default, } = node; self.infer_optional_expression(default.as_deref()); - self.add_declaration_with_binding(node.into(), definition, todo_type!(), todo_type!()); + let pep_695_todo = todo_type!("PEP-695 TypeVarTuple definition types"); + self.add_declaration_with_binding(node.into(), definition, pep_695_todo, pep_695_todo); } fn infer_match_statement(&mut self, match_statement: &ast::StmtMatch) { @@ -1729,7 +1731,11 @@ impl<'db> TypeInferenceBuilder<'db> { // against the subject expression type (which we can query via `infer_expression_types`) // and extract the type at the `index` position if the pattern matches. This will be // similar to the logic in `self.infer_assignment_definition`. - self.add_binding(pattern.into(), definition, todo_type!()); + self.add_binding( + pattern.into(), + definition, + todo_type!("`match` pattern definition types"), + ); } fn infer_match_pattern(&mut self, pattern: &ast::Pattern) { @@ -2483,8 +2489,7 @@ impl<'db> TypeInferenceBuilder<'db> { ast::Expr::YieldFrom(yield_from) => self.infer_yield_from_expression(yield_from), ast::Expr::Await(await_expression) => self.infer_await_expression(await_expression), ast::Expr::IpyEscapeCommand(_) => { - // TODO Implement Ipy escape command support - todo_type!() + todo_type!("Ipy escape command support") } }; @@ -2922,8 +2927,7 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_parameters(parameters); } - // TODO function type - todo_type!() + todo_type!("typing.Callable type") } fn infer_call_expression(&mut self, call_expression: &ast::ExprCall) -> Type<'db> { @@ -2959,11 +2963,8 @@ impl<'db> TypeInferenceBuilder<'db> { fn infer_yield_expression(&mut self, yield_expression: &ast::ExprYield) -> Type<'db> { let ast::ExprYield { range: _, value } = yield_expression; - self.infer_optional_expression(value.as_deref()); - - // TODO awaitable type - todo_type!() + todo_type!("yield expressions") } fn infer_yield_from_expression(&mut self, yield_from: &ast::ExprYieldFrom) -> Type<'db> { @@ -2975,16 +2976,13 @@ impl<'db> TypeInferenceBuilder<'db> { .unwrap_with_diagnostic(&self.context, value.as_ref().into()); // TODO get type from `ReturnType` of generator - todo_type!() + todo_type!("Generic `typing.Generator` type") } fn infer_await_expression(&mut self, await_expression: &ast::ExprAwait) -> Type<'db> { let ast::ExprAwait { range: _, value } = await_expression; - self.infer_expression(value); - - // TODO awaitable type - todo_type!() + todo_type!("generic `typing.Awaitable` type") } /// Look up a name reference that isn't bound in the local scope. @@ -3521,7 +3519,7 @@ impl<'db> TypeInferenceBuilder<'db> { (left, Type::BooleanLiteral(bool_value), op) => { self.infer_binary_expression_type(left, Type::IntLiteral(i64::from(bool_value)), op) } - _ => Some(todo_type!()), // TODO + _ => Some(todo_type!("Support for more binary expressions")), } } @@ -4040,10 +4038,9 @@ impl<'db> TypeInferenceBuilder<'db> { } } } - // TODO: handle more types _ => match op { ast::CmpOp::Is | ast::CmpOp::IsNot => Ok(KnownClass::Bool.to_instance(self.db())), - _ => Ok(todo_type!()), + _ => Ok(todo_type!("Binary comparisons between more types")), }, } } @@ -4795,7 +4792,7 @@ impl<'db> TypeInferenceBuilder<'db> { single_element => { let single_element_ty = self.infer_type_expression(single_element); if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) { - todo_type!() + todo_type!("full tuple[...] support") } else { Type::tuple(self.db(), [single_element_ty]) } @@ -5034,39 +5031,39 @@ impl<'db> TypeInferenceBuilder<'db> { KnownInstanceType::ReadOnly => { self.infer_type_expression(arguments_slice); - todo_type!("Required[] type qualifier") + todo_type!("`ReadOnly[]` type qualifier") } KnownInstanceType::NotRequired => { self.infer_type_expression(arguments_slice); - todo_type!("NotRequired[] type qualifier") + todo_type!("`NotRequired[]` type qualifier") } KnownInstanceType::ClassVar => { self.infer_type_expression(arguments_slice); - todo_type!("ClassVar[] type qualifier") + todo_type!("`ClassVar[]` type qualifier") } KnownInstanceType::Final => { self.infer_type_expression(arguments_slice); - todo_type!("Final[] type qualifier") + todo_type!("`Final[]` type qualifier") } KnownInstanceType::Required => { self.infer_type_expression(arguments_slice); - todo_type!("Required[] type qualifier") + todo_type!("`Required[]` type qualifier") } KnownInstanceType::TypeIs => { self.infer_type_expression(arguments_slice); - todo_type!("TypeIs[] special form") + todo_type!("`TypeIs[]` special form") } KnownInstanceType::TypeGuard => { self.infer_type_expression(arguments_slice); - todo_type!("TypeGuard[] special form") + todo_type!("`TypeGuard[]` special form") } KnownInstanceType::Concatenate => { self.infer_type_expression(arguments_slice); - todo_type!("Concatenate[] special form") + todo_type!("`Concatenate[]` special form") } KnownInstanceType::Unpack => { self.infer_type_expression(arguments_slice); - todo_type!("Unpack[] special form") + todo_type!("`Unpack[]` special form") } KnownInstanceType::NoReturn | KnownInstanceType::Never | KnownInstanceType::Any => { self.context.report_lint(