use super::delegate_shape::{delegate_shape, DelegateShape};
use super::index::{DefIndex, MethodOverloadParams};
use super::scope::FileContext;
use crate::graph::{FragLambdaShape, FragRef};
use std::collections::HashMap;
pub(super) fn lambda_arity_admits(
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
idx: usize,
member: Option<&str>,
r: &FragRef,
) -> bool {
let Some(member) = member else { return true };
let arities = r.lambda_arg_arity.as_deref().unwrap_or_default();
let shapes = r.lambda_arg_shape.as_deref().unwrap_or_default();
if arities.iter().all(Option::is_none) && shapes.iter().all(Option::is_none) {
return true;
}
let Some(overloads) = index.member_lists[idx].method_params.get(member) else {
return true;
};
let call = LambdaCall {
arities,
shapes,
type_args_written: r.method_type_arg_count.is_some(),
};
overloads
.iter()
.any(|overload| overload_admits(index, file_contexts, idx, overload, &call))
}
pub(super) fn lambda_overload_admits(
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
idx: usize,
overload: &MethodOverloadParams,
r: &FragRef,
) -> bool {
let call = LambdaCall {
arities: r.lambda_arg_arity.as_deref().unwrap_or_default(),
shapes: r.lambda_arg_shape.as_deref().unwrap_or_default(),
type_args_written: r.method_type_arg_count.is_some(),
};
overload_admits(index, file_contexts, idx, overload, &call)
}
struct LambdaCall<'a> {
arities: &'a [Option<usize>],
shapes: &'a [Option<FragLambdaShape>],
type_args_written: bool,
}
impl LambdaCall<'_> {
fn positions(&self) -> usize {
self.arities.len().max(self.shapes.len())
}
fn arity(&self, i: usize) -> Option<usize> {
self.arities.get(i).copied().flatten()
}
fn shape(&self, i: usize) -> Option<FragLambdaShape> {
self.shapes.get(i).copied().flatten()
}
}
fn overload_admits(
index: &DefIndex,
file_contexts: &HashMap<String, FileContext>,
idx: usize,
overload: &MethodOverloadParams,
call: &LambdaCall,
) -> bool {
let delegates: Vec<Option<DelegateShape>> = (0..call.positions())
.map(|i| {
if call.arity(i).is_none() && call.shape(i).is_none() {
return None;
}
overload
.params
.get(i)
.and_then(|p| delegate_shape(p, index, file_contexts, idx, &overload.file))
})
.collect();
let converts = (0..call.positions()).all(|i| {
let delegate = delegates[i].as_ref();
let arity_ok = call
.arity(i)
.is_none_or(|want| delegate.is_some_and(|d| d.params.len() == want));
let shape_ok = call.shape(i).is_none_or(|shape| {
!shape.value_only || delegate.is_none_or(|d| d.returns_void != Some(true))
});
arity_ok && shape_ok
});
converts && (call.type_args_written || !uninferable(overload, call, &delegates))
}
fn uninferable(
overload: &MethodOverloadParams,
call: &LambdaCall,
delegates: &[Option<DelegateShape>],
) -> bool {
let Some(own) = &overload.type_params else {
return false;
};
let only_in_lambda_inputs = |i: usize, position: i64| {
let implicit = call.shape(i).is_some_and(|s| s.implicitly_typed);
let input = delegates.get(i).and_then(Option::as_ref).and_then(|d| {
let inputs = d.input_args.as_ref()?;
usize::try_from(position)
.ok()
.and_then(|p| inputs.get(p).copied())
});
implicit && input == Some(true)
};
(0..own.count).any(|tp| {
let mut sites = own.sites.iter().enumerate().flat_map(|(i, param)| {
param
.iter()
.filter(move |&&(k, _)| k == tp)
.map(move |&(_, position)| (i, position))
});
let first = sites.next();
first.is_some_and(|(i, position)| only_in_lambda_inputs(i, position))
&& sites.all(|(i, position)| only_in_lambda_inputs(i, position))
})
}