use {
crate::{
analysis::{
dispatch::DispatchTraitInfo,
get_all_parameters,
},
core::{
config::Config,
constants::attributes::{
ALLOW_NAMED_GENERICS,
DOCUMENT_SIGNATURE,
DOCUMENT_TYPE_PARAMETERS,
DOCUMENT_USE,
},
error_handling::{
CollectErrors,
ErrorCollector,
},
},
documentation::document_signature::generate_signature,
resolution::{
ImplKey,
resolver::{
SelfSubstitutor,
get_concrete_type_name,
get_self_type_info,
merge_generics,
},
},
support::{
attributes::{
AttributeExt,
count_attributes,
find_attribute,
},
documentation_parameters::{
DocumentationParameter,
DocumentationParameters,
},
generate_documentation::format_parameter_doc,
parsing::{
self,
parse_parameter_documentation_pairs,
},
},
},
quote::quote,
syn::{
FnArg,
ImplItem,
Item,
Result,
TraitItem,
Type,
TypeParamBound,
parse_quote,
spanned::Spanned,
visit_mut::VisitMut,
},
};
fn insert_signature_docs(
attrs: &mut Vec<syn::Attribute>,
attr_pos: usize,
sig: &syn::Signature,
config: &Config,
) {
let signature_data = generate_signature(sig, config);
let doc_comment = format!("`{signature_data}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
attrs.insert(attr_pos, header_attr);
}
#[expect(clippy::too_many_arguments, reason = "Documentation generation requires many parameters")]
pub(super) fn process_document_signature(
method: &mut syn::ImplItemFn,
attr_pos: usize,
self_ty: &syn::Type,
self_ty_path: &str,
_trait_name: Option<&str>,
trait_path_str: Option<&str>,
document_use: Option<&str>,
item_impl_generics: &syn::Generics,
config: &Config,
errors: &mut ErrorCollector,
) {
method.attrs.remove(attr_pos);
let mut synthetic_sig = method.sig.clone();
let (base_type_name, impl_generic_params) = get_self_type_info(self_ty, item_impl_generics);
let mut substitutor = SelfSubstitutor::new(
self_ty,
self_ty_path,
trait_path_str,
document_use,
config,
base_type_name.clone(),
impl_generic_params.clone(),
);
substitutor.visit_signature_mut(&mut synthetic_sig);
errors.extend(substitutor.errors);
merge_generics(&mut synthetic_sig, item_impl_generics);
let mut sig_config = config.clone();
if let Some(concrete_type_name) = get_concrete_type_name(self_ty, config) {
sig_config.concrete_types.insert(concrete_type_name.clone());
sig_config.self_type_name = Some(concrete_type_name);
}
insert_signature_docs(&mut method.attrs, attr_pos, &synthetic_sig, &sig_config);
}
fn process_type_parameters_core(
attrs: &mut Vec<syn::Attribute>,
generics: &syn::Generics,
item_label: &str,
attr_pos: usize,
errors: &mut ErrorCollector,
) {
let attr = attrs.remove(attr_pos);
let param_names: Vec<String> = get_all_parameters(generics);
if errors
.collect_our_result(|| {
parsing::parse_has_documentable_items(
param_names.len(),
attr.span(),
DOCUMENT_TYPE_PARAMETERS,
&format!("{item_label} with no type parameters"),
)
})
.is_none()
{
return;
}
if let Some(args) = errors.collect(|| attr.parse_args::<DocumentationParameters>()) {
let entries: Vec<_> = args.entries.into_iter().collect();
if let Some(pairs) = errors.collect_our_result(|| {
parse_parameter_documentation_pairs(param_names, entries, attr.span())
}) {
let mut docs = Vec::new();
docs.push((
String::new(),
r#"### Type Parameters
"#
.to_string(),
));
for (name_from_target, entry) in pairs {
let (name, desc) = match entry {
DocumentationParameter::Override(n, d) => (n.value(), d.value()),
DocumentationParameter::Description(d) => (name_from_target, d.value()),
};
docs.push((name, desc));
}
for (i, (name, desc)) in docs.into_iter().enumerate() {
let doc_comment = format_parameter_doc(&name, &desc);
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
attrs.insert(attr_pos + i, doc_attr);
}
}
} else {
errors.push(syn::Error::new(
attr.span(),
format!("Failed to parse {DOCUMENT_TYPE_PARAMETERS} arguments"),
));
}
}
pub(super) fn process_document_type_parameters(
method: &mut syn::ImplItemFn,
attr_pos: usize,
errors: &mut ErrorCollector,
) {
process_type_parameters_core(
&mut method.attrs,
&method.sig.generics,
&format!("method '{}'", method.sig.ident),
attr_pos,
errors,
);
}
#[expect(clippy::too_many_arguments, reason = "Documentation generation requires many parameters")]
fn process_method_documentation(
method: &mut syn::ImplItemFn,
self_ty: &syn::Type,
self_ty_path: &str,
trait_name: Option<&str>,
trait_path_str: Option<&str>,
impl_document_use: Option<&str>,
item_impl_generics: &syn::Generics,
config: &Config,
errors: &mut ErrorCollector,
) {
method.attrs.retain(|attr| !attr.path().is_ident(ALLOW_NAMED_GENERICS));
let method_document_use = method.attrs.find_value_or_collect(DOCUMENT_USE, errors);
let document_use = method_document_use.or_else(|| impl_document_use.map(String::from));
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_SIGNATURE) {
if count_attributes(&method.attrs, DOCUMENT_SIGNATURE) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_SIGNATURE}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_document_signature(
method,
attr_pos,
self_ty,
self_ty_path,
trait_name,
trait_path_str,
document_use.as_deref(),
item_impl_generics,
config,
errors,
);
}
}
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&method.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_document_type_parameters(method, attr_pos, errors);
}
}
}
fn process_impl_block(
item_impl: &mut syn::ItemImpl,
config: &Config,
errors: &mut ErrorCollector,
) {
let self_ty = &*item_impl.self_ty;
let self_ty_path = quote!(#self_ty).to_string();
let trait_path = item_impl.trait_.as_ref().map(|(_, path, _)| path);
let trait_name = trait_path.and_then(|p| p.segments.last().map(|s| s.ident.to_string()));
let trait_path_str = trait_path.map(|p| quote!(#p).to_string());
if count_attributes(&item_impl.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
item_impl.self_ty.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on impl block for `{self_ty_path}`",
),
));
} else if let Some(attr_pos) = find_attribute(&item_impl.attrs, DOCUMENT_TYPE_PARAMETERS) {
let impl_key = ImplKey::from_paths(&self_ty_path, trait_path_str.as_deref());
if let Some(impl_docs) = config.impl_type_param_docs.get(&impl_key) {
item_impl.attrs.remove(attr_pos);
let mut docs = Vec::new();
docs.push((
String::new(),
r#"### Type Parameters
"#
.to_string(),
));
for (param_name, desc) in impl_docs.iter() {
docs.push((param_name.clone(), desc.clone()));
}
for (i, (name, desc)) in docs.into_iter().enumerate() {
let doc_comment = format_parameter_doc(&name, &desc);
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_impl.attrs.insert(attr_pos + i, doc_attr);
}
} else {
item_impl.attrs.remove(attr_pos);
}
}
let impl_document_use = item_impl.attrs.find_value_or_collect(DOCUMENT_USE, errors);
for impl_item in &mut item_impl.items {
if let ImplItem::Fn(method) = impl_item {
process_method_documentation(
method,
self_ty,
&self_ty_path,
trait_name.as_deref(),
trait_path_str.as_deref(),
impl_document_use.as_deref(),
&item_impl.generics,
config,
errors,
);
}
}
}
fn process_trait_method_documentation(
method: &mut syn::TraitItemFn,
config: &Config,
errors: &mut ErrorCollector,
) {
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_SIGNATURE) {
if count_attributes(&method.attrs, DOCUMENT_SIGNATURE) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_SIGNATURE}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
method.attrs.remove(attr_pos);
insert_signature_docs(&mut method.attrs, attr_pos, &method.sig, config);
}
}
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&method.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_type_parameters_core(
&mut method.attrs,
&method.sig.generics,
&format!("method '{}'", method.sig.ident),
attr_pos,
errors,
);
}
}
}
fn process_trait_block(
item_trait: &mut syn::ItemTrait,
config: &Config,
errors: &mut ErrorCollector,
) {
if let Some(attr_pos) = find_attribute(&item_trait.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&item_trait.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
item_trait.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on trait `{}`",
item_trait.ident
),
));
} else {
process_type_parameters_core(
&mut item_trait.attrs,
&item_trait.generics,
&format!("trait '{}'", item_trait.ident),
attr_pos,
errors,
);
}
}
for item in &mut item_trait.items {
if let TraitItem::Fn(method) = item {
process_trait_method_documentation(method, config, errors);
}
}
}
pub(super) fn generate_documentation(
items: &mut [Item],
config: &Config,
) -> Result<()> {
let mut errors = ErrorCollector::new();
for item in items {
match item {
Item::Impl(item_impl) => process_impl_block(item_impl, config, &mut errors),
Item::Trait(item_trait) => process_trait_block(item_trait, config, &mut errors),
Item::Fn(item_fn) => {
item_fn.attrs.retain(|attr| !attr.path().is_ident(ALLOW_NAMED_GENERICS));
process_fn_dispatch_signature(item_fn, config);
}
_ => {}
}
}
errors.finish()
}
fn process_fn_dispatch_signature(
item_fn: &mut syn::ItemFn,
config: &Config,
) {
let Some(attr_pos) = find_attribute(&item_fn.attrs, DOCUMENT_SIGNATURE) else {
return;
};
if let Some(attr) = item_fn.attrs.get(attr_pos)
&& let Some(manual_sig) = extract_manual_signature(attr)
{
item_fn.attrs.remove(attr_pos);
let doc_comment = format!("`{manual_sig}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_fn.attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
item_fn.attrs.insert(attr_pos, header_attr);
return;
}
let Some(dispatch_info) = find_dispatch_trait_in_sig(&item_fn.sig, config) else {
return;
};
let Some(synthetic_sig) = build_synthetic_signature(&item_fn.sig, &dispatch_info) else {
return;
};
item_fn.attrs.remove(attr_pos);
let sig_data = generate_signature(&synthetic_sig, config);
let doc_comment = format!("`{sig_data}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_fn.attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
item_fn.attrs.insert(attr_pos, header_attr);
}
fn extract_manual_signature(attr: &syn::Attribute) -> Option<String> {
let syn::Meta::List(meta_list) = &attr.meta else {
return None;
};
let lit: syn::LitStr = syn::parse2(meta_list.tokens.clone()).ok()?;
let value = lit.value();
if value.is_empty() { None } else { Some(value) }
}
fn find_dispatch_trait_in_sig(
sig: &syn::Signature,
config: &Config,
) -> Option<DispatchTraitInfo> {
for input in &sig.inputs {
let FnArg::Typed(pat_type) = input else {
continue;
};
let Type::ImplTrait(impl_trait) = &*pat_type.ty else {
continue;
};
for bound in &impl_trait.bounds {
let TypeParamBound::Trait(trait_bound) = bound else {
continue;
};
let Some(segment) = trait_bound.path.segments.last() else {
continue;
};
let name = segment.ident.to_string();
if let Some(info) = config.dispatch_traits.get(&name) {
return Some(info.clone());
}
}
}
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate {
for bound in &pred_type.bounds {
if let TypeParamBound::Trait(trait_bound) = bound {
let Some(segment) = trait_bound.path.segments.last() else {
continue;
};
let name = segment.ident.to_string();
if let Some(info) = config.dispatch_traits.get(&name) {
return Some(info.clone());
}
}
}
}
}
}
None
}
struct FnBrandResolution {
input: syn::Ident,
output: syn::Ident,
}
fn extract_fn_brand_resolutions(
sig: &syn::Signature
) -> std::collections::HashMap<String, FnBrandResolution> {
use crate::analysis::generics::{
trait_bound_name,
where_clause_type_predicates,
};
let mut result = std::collections::HashMap::new();
for (ident, bounds) in where_clause_type_predicates(&sig.generics) {
for bound in bounds {
let TypeParamBound::Trait(trait_bound) = bound else {
continue;
};
if trait_bound_name(trait_bound)
.is_none_or(|n| n != crate::core::constants::markers::INFERABLE_FN_BRAND)
{
continue;
}
let Some(segment) = trait_bound.path.segments.last() else {
continue;
};
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
continue;
};
let type_args: Vec<&syn::Ident> = args
.args
.iter()
.filter_map(|arg| {
if let syn::GenericArgument::Type(Type::Path(tp)) = arg {
tp.path.get_ident()
} else {
None
}
})
.collect();
if let [_, input, output, ..] = type_args.as_slice() {
result.insert(
ident.to_string(),
FnBrandResolution {
input: (*input).clone(),
output: (*output).clone(),
},
);
}
}
}
result
}
fn build_synthetic_signature(
_original_sig: &syn::Signature,
dispatch_info: &DispatchTraitInfo,
) -> Option<syn::Signature> {
let kind_trait_name = dispatch_info.kind_trait_name.as_ref()?;
let brand_param = &dispatch_info.brand_param;
let kind_ident: syn::Ident = syn::parse_str(kind_trait_name).ok()?;
let brand_ident: syn::Ident = syn::parse_str(brand_param).ok()?;
let fn_brand_resolutions = extract_fn_brand_resolutions(_original_sig);
let mut generic_params: Vec<syn::GenericParam> = Vec::new();
generic_params.push(parse_quote!('a));
if let Some(ref constraint_name) = dispatch_info.semantic_constraint {
let constraint_ident: syn::Ident = syn::parse_str(constraint_name).ok()?;
generic_params.push(parse_quote!(#brand_ident: #constraint_ident + #kind_ident));
} else {
generic_params.push(parse_quote!(#brand_ident: #kind_ident));
}
let mut all_element_types: Vec<String> = Vec::new();
let secondary_map: std::collections::HashMap<&str, &str> =
dispatch_info.secondary_constraints.iter().map(|(p, c)| (p.as_str(), c.as_str())).collect();
for param_name in &dispatch_info.type_param_order {
if param_name == brand_param {
continue;
}
if fn_brand_resolutions.contains_key(param_name) {
continue;
}
if let Some(constraint_name) = secondary_map.get(param_name.as_str()) {
let param_ident: syn::Ident = syn::parse_str(param_name).ok()?;
let constraint_ident: syn::Ident = syn::parse_str(constraint_name).ok()?;
generic_params.push(parse_quote!(#param_ident: #constraint_ident + #kind_ident));
continue;
}
if let Ok(param_ident) = syn::parse_str::<syn::Ident>(param_name) {
generic_params.push(parse_quote!(#param_ident: 'a));
all_element_types.push(param_name.clone());
}
}
let mut container_map = build_container_map(_original_sig, dispatch_info);
if !fn_brand_resolutions.is_empty() {
for elements in container_map.values_mut() {
resolve_fn_brand_elements(elements, &fn_brand_resolutions);
}
}
let mut fn_params: Vec<syn::FnArg> = Vec::new();
for input in &_original_sig.inputs {
let FnArg::Typed(pat_type) = input else {
continue;
};
if matches!(&*pat_type.ty, Type::ImplTrait(_))
&& let Some(ref arrow) = dispatch_info.arrow_type
&& let Some(closure_param) =
build_closure_param(arrow, dispatch_info.tuple_closure, &brand_ident, &kind_ident)
{
fn_params.push(closure_param);
continue;
}
if let Type::ImplTrait(impl_trait) = &*pat_type.ty {
let is_dispatch_bound = impl_trait.bounds.iter().any(|b| {
if let TypeParamBound::Trait(t) = b {
t.path
.segments
.last()
.map(|s| {
s.ident
.to_string()
.ends_with(crate::core::constants::markers::DISPATCH_SUFFIX)
})
.unwrap_or(false)
} else {
false
}
});
if is_dispatch_bound && dispatch_info.arrow_type.is_none() {
use crate::analysis::dispatch::ReturnStructure;
let element_types: Option<Vec<Type>> = dispatch_info
.self_type_elements
.clone()
.or_else(|| {
let elems: Vec<Type> = dispatch_info
.type_param_order
.iter()
.filter(|p| {
*p != brand_param
&& p.len() == 1 && !dispatch_info
.secondary_constraints
.iter()
.any(|(sc, _)| sc == *p)
})
.filter_map(|p| syn::parse_str::<Type>(p).ok())
.collect();
if elems.is_empty() { None } else { Some(elems) }
})
.or_else(|| match &dispatch_info.return_structure {
ReturnStructure::Applied(args) => Some(args.clone()),
_ => None,
});
if let Some(mut elements) = element_types {
resolve_fn_brand_elements(&mut elements, &fn_brand_resolutions);
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, &elements)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
}
continue;
}
if dispatch_info.tuple_closure
&& let Type::Tuple(param_tuple) = &*pat_type.ty
&& param_tuple.elems.len() >= 2
&& let Some(ref arrow) = dispatch_info.arrow_type
{
let effective_arrow = if is_tuple_order_reversed(param_tuple, _original_sig) {
let mut reversed = arrow.clone();
reversed.inputs.reverse();
reversed
} else {
arrow.clone()
};
if let Some(closure_param) =
build_closure_param(&effective_arrow, true, &brand_ident, &kind_ident)
{
fn_params.push(closure_param);
continue;
}
}
let ty = &pat_type.ty;
let type_ident_str = match &**ty {
Type::Path(tp) => tp.path.get_ident().map(|id| id.to_string()),
_ => None,
};
if let Some(ref ident_str) = type_ident_str
&& let Some(elements) = container_map.get(ident_str.as_str())
{
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, elements)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
if let Type::Path(type_path) = &*pat_type.ty
&& type_path.qself.is_some()
&& let Some(last_seg) = type_path.path.segments.last()
{
let assoc_name = last_seg.ident.to_string();
if let Some((_, elements)) =
dispatch_info.associated_types.iter().find(|(name, _)| name == &assoc_name)
{
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, elements)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
}
if is_dispatch_container_param(ty, _original_sig) {
use crate::analysis::dispatch::ReturnStructure;
let element_types: Option<Vec<Type>> = dispatch_info
.self_type_elements
.clone()
.or_else(|| {
let elems: Vec<Type> = dispatch_info
.type_param_order
.iter()
.filter(|p| {
*p != brand_param
&& p.len() == 1 && !dispatch_info
.secondary_constraints
.iter()
.any(|(sc, _)| sc == *p)
})
.filter_map(|p| syn::parse_str::<Type>(p).ok())
.collect();
if elems.is_empty() { None } else { Some(elems) }
})
.or_else(|| match &dispatch_info.return_structure {
ReturnStructure::Applied(args) => Some(args.clone()),
ReturnStructure::Nested {
inner_args, ..
} => Some(inner_args.clone()),
ReturnStructure::Tuple(elements) => elements.first().cloned(),
ReturnStructure::NestedTuple {
inner_elements, ..
} => inner_elements.first().cloned(),
ReturnStructure::Plain(_) => None,
});
if let Some(mut elems) = element_types {
resolve_fn_brand_elements(&mut elems, &fn_brand_resolutions);
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, &elems)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
}
fn_params.push(input.clone());
}
let return_type =
build_return_type(&dispatch_info.return_structure, &brand_ident, &kind_ident)?;
let generics = syn::Generics {
lt_token: Some(Default::default()),
params: generic_params.into_iter().collect(),
gt_token: Some(Default::default()),
where_clause: None,
};
Some(syn::Signature {
constness: None,
asyncness: None,
unsafety: None,
abi: None,
fn_token: Default::default(),
ident: syn::parse_str("synthetic").ok()?,
generics,
paren_token: Default::default(),
inputs: fn_params.into_iter().collect(),
variadic: None,
output: syn::ReturnType::Type(Default::default(), Box::new(return_type)),
})
}
fn build_container_map(
sig: &syn::Signature,
dispatch_info: &DispatchTraitInfo,
) -> std::collections::HashMap<String, Vec<Type>> {
if dispatch_info.container_params.is_empty() {
return std::collections::HashMap::new();
}
let fn_type_arg_idents = extract_dispatch_type_arg_idents(sig);
if fn_type_arg_idents.is_empty() {
return std::collections::HashMap::new();
}
let mut result = std::collections::HashMap::new();
for cp in &dispatch_info.container_params {
if let Some(Some(fn_arg_ident)) = fn_type_arg_idents.get(cp.position) {
result.insert(fn_arg_ident.to_string(), cp.element_types.clone());
}
}
result
}
fn extract_dispatch_type_arg_idents(sig: &syn::Signature) -> Vec<Option<syn::Ident>> {
for input in &sig.inputs {
let FnArg::Typed(pat_type) = input else { continue };
let Type::ImplTrait(impl_trait) = &*pat_type.ty else { continue };
for bound in &impl_trait.bounds {
if let Some(args) = extract_dispatch_trait_arg_idents(bound) {
return args;
}
}
}
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate {
for bound in &pred_type.bounds {
if let Some(args) = extract_dispatch_trait_arg_idents(bound) {
return args;
}
}
}
}
}
Vec::new()
}
fn extract_dispatch_trait_arg_idents(bound: &TypeParamBound) -> Option<Vec<Option<syn::Ident>>> {
let TypeParamBound::Trait(trait_bound) = bound else {
return None;
};
let segment = trait_bound.path.segments.last()?;
if !segment.ident.to_string().ends_with(crate::core::constants::markers::DISPATCH_SUFFIX) {
return None;
}
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
return None;
};
Some(
args.args
.iter()
.filter_map(|arg| match arg {
syn::GenericArgument::Type(Type::Path(type_path)) =>
Some(type_path.path.get_ident().cloned()),
syn::GenericArgument::Type(_) => Some(None),
_ => None, })
.collect(),
)
}
fn is_dispatch_container_param(
ty: &syn::Type,
sig: &syn::Signature,
) -> bool {
use crate::analysis::generics::{
collect_trait_bounds_for_param,
trait_bound_name,
};
let Type::Path(type_path) = ty else {
return false;
};
let Some(ident) = type_path.path.get_ident() else {
return false;
};
let ident_str = ident.to_string();
for bound in collect_trait_bounds_for_param(&ident_str, &sig.generics) {
if let Some(name) = trait_bound_name(bound) {
let name_str = name.to_string();
if name_str.starts_with(crate::core::constants::markers::INFERABLE_BRAND_PREFIX)
|| name_str.ends_with(crate::core::constants::markers::DISPATCH_SUFFIX)
{
return true;
}
}
}
false
}
fn is_tuple_order_reversed(
param_tuple: &syn::TypeTuple,
sig: &syn::Signature,
) -> bool {
let Some(where_clause) = &sig.generics.where_clause else {
return false;
};
let param_idents: Vec<&syn::Ident> = param_tuple
.elems
.iter()
.filter_map(|elem| if let Type::Path(tp) = elem { tp.path.get_ident() } else { None })
.collect();
if param_idents.len() < 2 {
return false;
}
for predicate in &where_clause.predicates {
let syn::WherePredicate::Type(pred_type) = predicate else {
continue;
};
let Type::Tuple(where_tuple) = &pred_type.bounded_ty else {
continue;
};
let has_dispatch = pred_type.bounds.iter().any(|b| {
if let TypeParamBound::Trait(tb) = b {
tb.path.segments.last().is_some_and(|s| {
s.ident.to_string().ends_with(crate::core::constants::markers::DISPATCH_SUFFIX)
})
} else {
false
}
});
if !has_dispatch {
continue;
}
let where_idents: Vec<&syn::Ident> = where_tuple
.elems
.iter()
.filter_map(|elem| if let Type::Path(tp) = elem { tp.path.get_ident() } else { None })
.collect();
if param_idents.len() == where_idents.len()
&& param_idents != where_idents
&& param_idents.iter().all(|id| where_idents.contains(id))
{
return true;
}
}
false
}
fn resolve_fn_brand_elements(
elements: &mut [Type],
fn_brand_resolutions: &std::collections::HashMap<String, FnBrandResolution>,
) {
for elem in elements.iter_mut() {
let key = if let Type::Path(tp) = elem {
tp.path.get_ident().map(|id| id.to_string())
} else {
None
};
if let Some(key) = key
&& let Some(resolution) = fn_brand_resolutions.get(key.as_str())
{
let input = &resolution.input;
let output = &resolution.output;
let fn_str = format!("fn({input})->{output}");
if let Ok(fn_ty) = syn::parse_str::<Type>(&fn_str) {
*elem = fn_ty;
}
}
}
}
fn build_applied_type(
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
element_types: &[Type],
) -> Option<syn::Type> {
let mut args = vec![quote!('a)];
for elem in element_types {
args.push(quote!(#elem));
}
let args_tokens = quote!(#(#args),*);
Some(parse_quote!(<#brand_ident as #kind_ident>::Of<#args_tokens>))
}
fn build_closure_param(
arrow: &crate::analysis::dispatch::DispatchArrow,
tuple_closure: bool,
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
) -> Option<syn::FnArg> {
use crate::analysis::dispatch::{
ArrowOutput,
DispatchArrowParam,
};
if tuple_closure {
let mut fn_types: Vec<syn::Type> = Vec::new();
for param in &arrow.inputs {
let sub_arrow = match param {
DispatchArrowParam::SubArrow(arrow) => arrow,
DispatchArrowParam::TypeParam(_)
| DispatchArrowParam::AssociatedType {
..
} => {
continue;
}
};
let mut input_types: Vec<syn::Type> = Vec::new();
for sub_param in &sub_arrow.inputs {
match sub_param {
DispatchArrowParam::TypeParam(ident) => {
input_types.push(parse_quote!(#ident));
}
DispatchArrowParam::AssociatedType {
assoc_name,
} => {
input_types.push(parse_quote!(#brand_ident::#assoc_name));
}
DispatchArrowParam::SubArrow(_) => continue,
}
}
let output_type: syn::Type = match &sub_arrow.output {
ArrowOutput::Plain(ty) => *ty.clone(),
ArrowOutput::BrandApplied(args) =>
build_applied_type(brand_ident, kind_ident, args)?,
ArrowOutput::OtherApplied {
brand,
args,
} => build_applied_type(brand, kind_ident, args)?,
};
fn_types.push(parse_quote!(fn(#(#input_types),*) -> #output_type));
}
if fn_types.is_empty() {
return None;
}
return Some(parse_quote!(fg: (#(#fn_types),*)));
}
let mut input_types: Vec<syn::Type> = Vec::new();
for param in &arrow.inputs {
match param {
DispatchArrowParam::TypeParam(ident) => {
input_types.push(parse_quote!(#ident));
}
DispatchArrowParam::AssociatedType {
assoc_name,
} => {
input_types.push(parse_quote!(#brand_ident::#assoc_name));
}
DispatchArrowParam::SubArrow(_) => {
continue;
}
}
}
let output_type: syn::Type = match &arrow.output {
ArrowOutput::Plain(ty) => *ty.clone(),
ArrowOutput::BrandApplied(args) => build_applied_type(brand_ident, kind_ident, args)?,
ArrowOutput::OtherApplied {
brand,
args,
} => build_applied_type(brand, kind_ident, args)?,
};
Some(parse_quote!(f: impl Fn(#(#input_types),*) -> #output_type + 'a))
}
fn build_return_type(
ret: &crate::analysis::dispatch::ReturnStructure,
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
) -> Option<syn::Type> {
use crate::analysis::dispatch::ReturnStructure;
match ret {
ReturnStructure::Plain(ty) => Some(*ty.clone()),
ReturnStructure::Applied(args) => build_applied_type(brand_ident, kind_ident, args),
ReturnStructure::Nested {
outer_param,
inner_args,
} => {
let inner_type = build_applied_type(brand_ident, kind_ident, inner_args)?;
Some(parse_quote!(<#outer_param as #kind_ident>::Of<'a, #inner_type>))
}
ReturnStructure::Tuple(elements) => {
let elem_types: Vec<syn::Type> = elements
.iter()
.filter_map(|args| build_applied_type(brand_ident, kind_ident, args))
.collect();
Some(parse_quote!((#(#elem_types),*)))
}
ReturnStructure::NestedTuple {
outer_param,
inner_elements,
} => {
let tuple_types: Vec<syn::Type> = inner_elements
.iter()
.filter_map(|args| build_applied_type(brand_ident, kind_ident, args))
.collect();
let tuple_type: syn::Type = parse_quote!((#(#tuple_types),*));
Some(parse_quote!(<#outer_param as #kind_ident>::Of<'a, #tuple_type>))
}
}
}