mod extract;
mod fresh;
mod generics;
mod match_ty;
mod range_refs;
mod render;
mod repeat;
mod repeat_drivers;
#[cfg(test)]
mod repeat_tests;
mod shape;
mod splat;
mod sync;
mod top_level;
mod where_at;
#[cfg(test)]
mod where_at_tests;
pub(crate) use extract::*;
pub(crate) use fresh::*;
pub(crate) use generics::*;
pub(crate) use repeat::*;
pub(crate) use shape::*;
pub(crate) use splat::*;
pub(crate) use sync::*;
pub(crate) use top_level::*;
pub(crate) use where_at::*;
use crate::TraitBounds;
use crate::ast::*;
use crate::util::compile_error_str;
use proc_macro2::{Ident, TokenStream, TokenTree};
use std::collections::HashSet;
pub(crate) fn generate_impl(
ty: Ty, trait_name: &TokenStream, is_unsafe_trait: bool, trait_bounds: &TraitBounds,
trait_param_names: &[Ident],
) -> TokenStream {
if let Ty { kind: TyKind::WithCode(TyWithCode(None, code)), .. } = &ty {
let is_top_marked = matches!(
code.0.clone().into_iter().next(),
Some(TokenTree::Punct(p)) if p.as_char() == '!'
);
return compile_error_str(
if is_top_marked {
"batch-impl: a top-level `{! ...}` block needs an attached type \
(the spec body is prepended to the macro input)"
} else {
"batch-impl: a bare `{...}` block without an attached type \
generates no impl (attach it to a type, e.g. `T { ... }`, or \
use the top-level `{! ...}` macro form)"
},
code.0
.clone()
.into_iter()
.next()
.map_or_else(proc_macro2::Span::call_site, |t| t.span()),
);
}
if let Some(result) = top_level_macro(&ty) {
return match result {
Ok((spec, mac)) => {
if spec.is_empty() {
compile_error_str(
"batch-impl: a top-level `{! ...}` block needs an attached type \
(the spec body is prepended to the macro input)",
proc_macro2::Span::call_site(),
)
} else if mac.is_empty() {
compile_error_str(
"batch-impl: a `{! ...}` top-level block must contain a macro \
call (e.g. `{! my_macro!{...}}`)",
proc_macro2::Span::call_site(),
)
} else {
sweep_fresh_names(rewrite_macro_input(mac, spec))
}
}
Err(e) => e,
};
}
if let Ty { kind: TyKind::Error(e), .. } = ty {
return e.0;
}
let mut parts = extract_impl_parts(ty);
substitute_trait_generics(&mut parts, trait_param_names);
parts.target_type = expand_splat_elems(parts.target_type);
let mut nested_params = vec![];
parts.target_type = hoist_type_params(parts.target_type, &mut nested_params);
parts.impl_generics.extend(nested_params);
{
let names = parts
.impl_generics
.iter()
.map(|(n, _)| crate::codegen::generics::bare_param_name(n))
.collect::<Vec<_>>();
if let Err(e) =
crate::codegen::range_refs::expand_range_decls(&mut parts.impl_generics, &names)
{
return e;
}
}
crate::codegen::generics::merge_dup_params(&mut parts);
let impl_name_streams = parts
.impl_generics
.iter()
.map(|(n, _)| crate::codegen::generics::bare_param_name(n))
.collect::<Vec<TokenStream>>();
let impl_names = impl_name_streams.iter().map(|n| n.to_string()).collect::<HashSet<String>>();
let trait_args =
parts.trait_generic_names.iter().map(|n| n.to_string()).collect::<Vec<String>>();
let mut errs = inherit_trait_bounds(&mut parts, trait_bounds, &trait_args, &impl_names);
if let Err(e) = sync_impl_parts(&mut parts, trait_name) {
return e;
}
let where_resolved = match resolve_where_predicates(&parts.where_clauses, &impl_name_streams) {
Ok(ws) => ws,
Err(es) => {
errs.extend(es);
vec![]
}
};
errs.extend(validate_at_refs(
&parts.target_type,
&parts.trait_generic_names,
&impl_name_streams,
));
if !errs.is_empty() {
return errs.into_iter().collect();
}
let (shape_entries, var_segs) = if parts.impl_templates.is_empty() {
(Vec::new(), Vec::new())
} else {
match crate::codegen::render::collect_shape_mapping(&parts) {
Ok((m, s)) => (m.entries().to_vec(), s),
Err(e) => return compile_error_str(&e.message(), proc_macro2::Span::call_site()),
}
};
if !shape_entries.is_empty() {
parts.where_clauses =
parts.where_clauses.iter().map(|p| apply_mapping(p.clone(), &shape_entries)).collect();
if let Some(b) = &mut parts.body {
match expand_repeat_blocks(b.clone(), &var_segs) {
Ok(expanded) => *b = apply_mapping(expanded, &shape_entries),
Err(e) => return e,
}
}
}
crate::codegen::render::render_impl(
parts,
where_resolved,
trait_name,
is_unsafe_trait,
&shape_entries,
&impl_name_streams,
)
}