use super::bindings::{canonicalise_type_segments_in_scope, CanonScope};
use super::local_symbols::FileScope;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParamInfo {
pub bounds: Vec<Vec<String>>,
pub turbofish_index: Option<usize>,
}
pub(crate) fn matched_generic_param<'a>(
segments: &[String],
leading_colon_set: bool,
generic_params: &'a HashMap<String, ParamInfo>,
) -> Option<&'a ParamInfo> {
if leading_colon_set {
return None;
}
generic_params.get(segments.first()?)
}
pub(crate) fn extract_signature_params(sig: &syn::Signature) -> Vec<(String, &syn::Type)> {
sig.inputs
.iter()
.filter_map(|arg| match arg {
syn::FnArg::Typed(pt) => {
param_name_from_pat(pt.pat.as_ref()).map(|n| (n, pt.ty.as_ref()))
}
_ => None,
})
.collect()
}
pub(crate) fn item_canonical_generics(
generics: &syn::Generics,
file: &FileScope<'_>,
mod_stack: &[String],
) -> HashMap<String, ParamInfo> {
let raw = extract_item_generics(generics);
let mut out = HashMap::new();
for (idx, (name, bounds)) in raw.into_iter().enumerate() {
out.insert(
name,
ParamInfo {
bounds: canonicalise_bounds(&bounds, file, mod_stack),
turbofish_index: Some(idx),
},
);
}
out
}
pub(crate) fn method_canonical_generics(
sig: &syn::Signature,
impl_generics: &[(String, Vec<Vec<String>>)],
file: &FileScope<'_>,
mod_stack: &[String],
) -> HashMap<String, ParamInfo> {
let outer_names: Vec<&str> = impl_generics.iter().map(|(n, _)| n.as_str()).collect();
let method_raw = extract_method_with_outer(sig, &outer_names);
let method_positions: HashMap<String, usize> = sig
.generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(tp) => Some(tp.ident.to_string()),
_ => None,
})
.enumerate()
.map(|(i, n)| (n, i))
.collect();
let mut out = HashMap::new();
for (name, bounds) in impl_generics {
out.insert(
name.clone(),
ParamInfo {
bounds: canonicalise_bounds(bounds, file, mod_stack),
turbofish_index: None,
},
);
}
for (name, bounds) in method_raw {
let canonical = canonicalise_bounds(&bounds, file, mod_stack);
match out.get_mut(&name) {
Some(existing) => existing.bounds.extend(canonical),
None => {
let turbofish_index = method_positions.get(&name).copied();
out.insert(
name,
ParamInfo {
bounds: canonical,
turbofish_index,
},
);
}
}
}
out
}
pub(crate) fn impl_block_generics(generics: &syn::Generics) -> Vec<(String, Vec<Vec<String>>)> {
extract_item_generics(generics)
}
fn extract_method_with_outer(
sig: &syn::Signature,
outer_names: &[&str],
) -> Vec<(String, Vec<Vec<String>>)> {
let mut bounds_by_name = collect_inline_bounds(&sig.generics);
if let Some(where_clause) = sig.generics.where_clause.as_ref() {
merge_where_bounds_extending(&mut bounds_by_name, where_clause, outer_names);
}
bounds_by_name
}
fn extract_item_generics(generics: &syn::Generics) -> Vec<(String, Vec<Vec<String>>)> {
let mut bounds_by_name = collect_inline_bounds(generics);
if let Some(where_clause) = generics.where_clause.as_ref() {
merge_where_bounds(&mut bounds_by_name, where_clause);
}
bounds_by_name
}
fn collect_inline_bounds(generics: &syn::Generics) -> Vec<(String, Vec<Vec<String>>)> {
generics
.params
.iter()
.filter_map(|p| match p {
syn::GenericParam::Type(tp) => {
Some((tp.ident.to_string(), trait_bound_paths(&tp.bounds)))
}
_ => None,
})
.collect()
}
fn merge_where_bounds(
bounds_by_name: &mut [(String, Vec<Vec<String>>)],
where_clause: &syn::WhereClause,
) {
for_each_simple_predicate(where_clause, |name, bounds| {
if let Some(entry) = bounds_by_name.iter_mut().find(|(n, _)| n == &name) {
entry.1.extend(bounds);
}
});
}
fn merge_where_bounds_extending(
bounds_by_name: &mut Vec<(String, Vec<Vec<String>>)>,
where_clause: &syn::WhereClause,
extending_names: &[&str],
) {
for_each_simple_predicate(where_clause, |name, bounds| {
if let Some(entry) = bounds_by_name.iter_mut().find(|(n, _)| n == &name) {
entry.1.extend(bounds);
} else if extending_names.iter().any(|n| *n == name) {
bounds_by_name.push((name, bounds));
}
});
}
fn for_each_simple_predicate<F: FnMut(String, Vec<Vec<String>>)>(
where_clause: &syn::WhereClause,
mut sink: F,
) {
for predicate in &where_clause.predicates {
let syn::WherePredicate::Type(pt) = predicate else {
continue;
};
let Some(name) = single_ident_type(&pt.bounded_ty) else {
continue;
};
sink(name, trait_bound_paths(&pt.bounds));
}
}
fn single_ident_type(ty: &syn::Type) -> Option<String> {
let syn::Type::Path(p) = ty else {
return None;
};
super::type_infer::single_ident_of(p)
}
fn trait_bound_paths(
bounds: &syn::punctuated::Punctuated<syn::TypeParamBound, syn::Token![+]>,
) -> Vec<Vec<String>> {
bounds
.iter()
.filter_map(|b| match b {
syn::TypeParamBound::Trait(tb) => {
if tb.path.leading_colon.is_some() {
return None;
}
Some(
tb.path
.segments
.iter()
.map(|s| s.ident.to_string())
.collect(),
)
}
_ => None,
})
.collect()
}
fn param_name_from_pat(pat: &syn::Pat) -> Option<String> {
match pat {
syn::Pat::Ident(pi) => Some(pi.ident.to_string()),
syn::Pat::TupleStruct(ts) if ts.elems.len() == 1 => {
if let syn::Pat::Ident(pi) = &ts.elems[0] {
return Some(pi.ident.to_string());
}
None
}
_ => None,
}
}
fn canonicalise_bounds(
bounds: &[Vec<String>],
file: &FileScope<'_>,
mod_stack: &[String],
) -> Vec<Vec<String>> {
let scope = CanonScope { file, mod_stack };
bounds
.iter()
.filter_map(|b| canonicalise_type_segments_in_scope(b, &scope))
.filter(|c| c.first().map(String::as_str) == Some("crate"))
.collect()
}