From 451c5db7a3d064816b8c2727fd1af6f92c9dd5cb Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Tue, 13 May 2025 12:37:19 -0400 Subject: [PATCH] ty_python_semantic: move some routines to `FunctionType` These are, after all, specific to function types. The methods on `Type` are more like conveniences that return something when the type *happens* to be a function. But defining them on `FunctionType` itself makes it easy to call them when you have a `FunctionType` instead of a `Type`. --- crates/ty_python_semantic/src/types.rs | 107 ++++++++++++++++++------- 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 1ffe70873a..7853e35d2d 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -5372,16 +5372,7 @@ impl<'db> Type<'db> { /// a diagnostic. fn return_type_span(&self, db: &'db dyn Db) -> Option<(Span, Span)> { match *self { - Type::FunctionLiteral(function) => { - let function_scope = function.body_scope(db); - let span = Span::from(function_scope.file(db)); - let node = function_scope.node(db); - let func_def = node.as_function()?; - let return_type_range = func_def.returns.as_ref()?.range(); - let name_span = span.clone().with_range(func_def.name.range); - let return_type_span = span.with_range(return_type_range); - Some((name_span, return_type_span)) - } + Type::FunctionLiteral(function) => function.return_type_span(db), Type::BoundMethod(bound_method) => { Type::FunctionLiteral(bound_method.function(db)).return_type_span(db) } @@ -5418,24 +5409,7 @@ impl<'db> Type<'db> { parameter_index: Option, ) -> Option<(Span, Span)> { match *self { - Type::FunctionLiteral(function) => { - let function_scope = function.body_scope(db); - let span = Span::from(function_scope.file(db)); - let node = function_scope.node(db); - let func_def = node.as_function()?; - let range = parameter_index - .and_then(|parameter_index| { - func_def - .parameters - .iter() - .nth(parameter_index) - .map(|param| param.range()) - }) - .unwrap_or(func_def.parameters.range); - let name_span = span.clone().with_range(func_def.name.range); - let parameter_span = span.with_range(range); - Some((name_span, parameter_span)) - } + Type::FunctionLiteral(function) => function.parameter_span(db, parameter_index), Type::BoundMethod(bound_method) => { Type::FunctionLiteral(bound_method.function(db)).parameter_span(db, parameter_index) } @@ -6919,6 +6893,83 @@ impl<'db> FunctionType<'db> { }) } } + + /// Returns a tuple of two spans. The first is + /// the span for the identifier of the function + /// definition for `self`. The second is + /// the span for the return type in the function + /// definition for `self`. + /// + /// If there are no meaningful spans, then this + /// returns `None`. For example, when this type + /// isn't callable or if the function has no + /// declared return type. + /// + /// # Performance + /// + /// Note that this may introduce cross-module + /// dependencies. This can have an impact on + /// the effectiveness of incremental caching + /// and should therefore be used judiciously. + /// + /// An example of a good use case is to improve + /// a diagnostic. + fn return_type_span(&self, db: &'db dyn Db) -> Option<(Span, Span)> { + let function_scope = self.body_scope(db); + let span = Span::from(function_scope.file(db)); + let node = function_scope.node(db); + let func_def = node.as_function()?; + let return_type_range = func_def.returns.as_ref()?.range(); + let name_span = span.clone().with_range(func_def.name.range); + let return_type_span = span.with_range(return_type_range); + Some((name_span, return_type_span)) + } + + /// Returns a tuple of two spans. The first is + /// the span for the identifier of the function + /// definition for `self`. The second is + /// the span for the parameter in the function + /// definition for `self`. + /// + /// If there are no meaningful spans, then this + /// returns `None`. For example, when this type + /// isn't callable. + /// + /// When `parameter_index` is `None`, then the + /// second span returned covers the entire parameter + /// list. + /// + /// # Performance + /// + /// Note that this may introduce cross-module + /// dependencies. This can have an impact on + /// the effectiveness of incremental caching + /// and should therefore be used judiciously. + /// + /// An example of a good use case is to improve + /// a diagnostic. + fn parameter_span( + &self, + db: &'db dyn Db, + parameter_index: Option, + ) -> Option<(Span, Span)> { + let function_scope = self.body_scope(db); + let span = Span::from(function_scope.file(db)); + let node = function_scope.node(db); + let func_def = node.as_function()?; + let range = parameter_index + .and_then(|parameter_index| { + func_def + .parameters + .iter() + .nth(parameter_index) + .map(|param| param.range()) + }) + .unwrap_or(func_def.parameters.range); + let name_span = span.clone().with_range(func_def.name.range); + let parameter_span = span.with_range(range); + Some((name_span, parameter_span)) + } } fn signature_cycle_recover<'db>(