From 90d567594ac6b681018c3c727209ba27b229e08a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 1 Mar 2026 10:44:10 -0500 Subject: [PATCH] [ty] Limit recursion depth when displaying self-referential function types --- .../resources/mdtest/ty_extensions.md | 30 +++++++++++++++++++ .../ty_python_semantic/src/types/display.rs | 9 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md index cd0a4fa342bd6..e20a82ca7b628 100644 --- a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md +++ b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md @@ -439,6 +439,36 @@ def foo(x: "TypeOf[foo]"): reveal_type(x) # revealed: def foo(x: def foo(...)) -> Unknown ``` +## Deeply nested `TypeOf` chains + +Multiple redefinitions of a function with `TypeOf[foo]` as the return type create a chain of +distinct function types. The display of such chains is truncated to prevent extremely long output: + +```py +from ty_extensions import TypeOf + +def foo() -> TypeOf[foo]: # error: [unresolved-reference] + return foo + +def foo() -> TypeOf[foo]: + return foo # error: [invalid-return-type] + +def foo() -> TypeOf[foo]: + return foo # error: [invalid-return-type] + +def foo() -> TypeOf[foo]: + return foo # error: [invalid-return-type] + +def foo() -> TypeOf[foo]: + return foo # error: [invalid-return-type] + +def foo() -> TypeOf[foo]: + return foo # error: [invalid-return-type] + +# Truncated after 4 levels of function type nesting: +reveal_type(foo) # revealed: def foo() -> def foo() -> def foo() -> def foo() -> def foo(...) +``` + ## `CallableTypeOf` The `CallableTypeOf` special form can be used to extract the `Callable` structural type inhabited by diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index f29da948a65c1..9f91105d50cec 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -1468,8 +1468,13 @@ pub(crate) struct DisplayFunctionType<'db> { impl<'db> FmtDetailed<'db> for DisplayFunctionType<'db> { fn fmt_detailed(&self, f: &mut TypeWriter<'_, '_, 'db>) -> fmt::Result { - // Detect self-referential function types to prevent infinite recursion. - if self.settings.visited_function_types.contains(&self.ty) { + // Detect self-referential function types to prevent infinite recursion, + // and limit display depth for chains of different function types + // (e.g. multiple redefinitions with `TypeOf[foo]` return types). + const MAX_FUNCTION_TYPE_DISPLAY_DEPTH: usize = 4; + if self.settings.visited_function_types.contains(&self.ty) + || self.settings.visited_function_types.len() >= MAX_FUNCTION_TYPE_DISPLAY_DEPTH + { f.set_invalid_type_annotation(); f.write_str("def ")?; write!(f, "{}", self.ty.name(self.db))?;