Skip to content

Commit a45a0a9

Browse files
committed
red_knot_python_semantic: move parameter span helper method
I wanted to use this method in other places, so I moved it to what appears to be a God-type. I also made it slightly more versatile: callers can ask for the entire parameter list by omitting a specific parameter index.
1 parent 43bd043 commit a45a0a9

File tree

2 files changed

+56
-44
lines changed

2 files changed

+56
-44
lines changed

crates/red_knot_python_semantic/src/types.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4903,6 +4903,60 @@ impl<'db> Type<'db> {
49034903
| Self::AlwaysFalsy => None,
49044904
}
49054905
}
4906+
4907+
/// Returns a tuple of two spans. The first is
4908+
/// the span for the identifier of the function
4909+
/// definition for `self`. The second is
4910+
/// the span for the parameter in the function
4911+
/// definition for `self`.
4912+
///
4913+
/// If there are no meaningful spans, then this
4914+
/// returns `None`. For example, when this type
4915+
/// isn't callable.
4916+
///
4917+
/// When `parameter_index` is `None`, then the
4918+
/// second span returned covers the entire parameter
4919+
/// list.
4920+
///
4921+
/// # Performance
4922+
///
4923+
/// Note that this may introduce cross-module
4924+
/// dependencies. This can have an impact on
4925+
/// the effectiveness of incremental caching
4926+
/// and should therefore be used judiciously.
4927+
///
4928+
/// An example of a good use case is to improve
4929+
/// a diagnostic.
4930+
fn parameter_span(
4931+
&self,
4932+
db: &'db dyn Db,
4933+
parameter_index: Option<usize>,
4934+
) -> Option<(Span, Span)> {
4935+
match *self {
4936+
Type::FunctionLiteral(function) => {
4937+
let function_scope = function.body_scope(db);
4938+
let span = Span::from(function_scope.file(db));
4939+
let node = function_scope.node(db);
4940+
let func_def = node.as_function()?;
4941+
let range = parameter_index
4942+
.and_then(|parameter_index| {
4943+
func_def
4944+
.parameters
4945+
.iter()
4946+
.nth(parameter_index)
4947+
.map(|param| param.range())
4948+
})
4949+
.unwrap_or(func_def.parameters.range);
4950+
let name_span = span.clone().with_range(func_def.name.range);
4951+
let parameter_span = span.with_range(range);
4952+
Some((name_span, parameter_span))
4953+
}
4954+
Type::BoundMethod(bound_method) => {
4955+
Type::FunctionLiteral(bound_method.function(db)).parameter_span(db, parameter_index)
4956+
}
4957+
_ => None,
4958+
}
4959+
}
49064960
}
49074961

49084962
impl<'db> From<&Type<'db>> for Type<'db> {

crates/red_knot_python_semantic/src/types/call/bind.rs

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ use crate::types::{
2323
KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType, TupleType,
2424
UnionType, WrapperDescriptorKind,
2525
};
26-
use ruff_db::diagnostic::{Annotation, Severity, Span, SubDiagnostic};
26+
use ruff_db::diagnostic::{Annotation, Severity, SubDiagnostic};
2727
use ruff_python_ast as ast;
28-
use ruff_text_size::Ranged;
2928

3029
/// Binding information for a possible union of callables. At a call site, the arguments must be
3130
/// compatible with _all_ of the types in the union for the call to be valid.
@@ -1386,47 +1385,6 @@ pub(crate) enum BindingError<'db> {
13861385
}
13871386

13881387
impl<'db> BindingError<'db> {
1389-
/// Returns a tuple of two spans. The first is
1390-
/// the span for the identifier of the function
1391-
/// definition for `callable_ty`. The second is
1392-
/// the span for the parameter in the function
1393-
/// definition for `callable_ty`.
1394-
///
1395-
/// If there are no meaningful spans, then this
1396-
/// returns `None`.
1397-
fn parameter_span_from_index(
1398-
db: &'db dyn Db,
1399-
callable_ty: Type<'db>,
1400-
parameter_index: usize,
1401-
) -> Option<(Span, Span)> {
1402-
match callable_ty {
1403-
Type::FunctionLiteral(function) => {
1404-
let function_scope = function.body_scope(db);
1405-
let span = Span::from(function_scope.file(db));
1406-
let node = function_scope.node(db);
1407-
if let Some(func_def) = node.as_function() {
1408-
let range = func_def
1409-
.parameters
1410-
.iter()
1411-
.nth(parameter_index)
1412-
.map(|param| param.range())
1413-
.unwrap_or(func_def.parameters.range);
1414-
let name_span = span.clone().with_range(func_def.name.range);
1415-
let parameter_span = span.with_range(range);
1416-
Some((name_span, parameter_span))
1417-
} else {
1418-
None
1419-
}
1420-
}
1421-
Type::BoundMethod(bound_method) => Self::parameter_span_from_index(
1422-
db,
1423-
Type::FunctionLiteral(bound_method.function(db)),
1424-
parameter_index,
1425-
),
1426-
_ => None,
1427-
}
1428-
}
1429-
14301388
pub(super) fn report_diagnostic(
14311389
&self,
14321390
context: &InferContext<'db>,
@@ -1454,7 +1412,7 @@ impl<'db> BindingError<'db> {
14541412
"Expected `{expected_ty_display}`, found `{provided_ty_display}`"
14551413
));
14561414
if let Some((name_span, parameter_span)) =
1457-
Self::parameter_span_from_index(context.db(), callable_ty, parameter.index)
1415+
callable_ty.parameter_span(context.db(), Some(parameter.index))
14581416
{
14591417
let mut sub = SubDiagnostic::new(Severity::Info, "Function defined here");
14601418
sub.annotate(Annotation::primary(name_span));

0 commit comments

Comments
 (0)