Skip to content

Commit 48a2088

Browse files
committed
refactor
1 parent 875c287 commit 48a2088

File tree

4 files changed

+297
-294
lines changed

4 files changed

+297
-294
lines changed

gix-macros/src/lib.rs

Lines changed: 3 additions & 290 deletions
Original file line numberDiff line numberDiff line change
@@ -1,299 +1,12 @@
1-
use std::collections::HashMap;
2-
1+
//! A crate of
32
use proc_macro::TokenStream;
4-
use quote::quote;
5-
use syn::{punctuated::Punctuated, spanned::Spanned, *};
6-
7-
#[derive(Copy, Clone)]
8-
// All conversions we support. Check references to this type for an idea how to add more.
9-
enum Conversion<'a> {
10-
Into(&'a Type),
11-
AsRef(&'a Type),
12-
AsMut(&'a Type),
13-
}
14-
15-
impl<'a> Conversion<'a> {
16-
fn conversion_expr(&self, i: &Ident) -> Expr {
17-
match *self {
18-
Conversion::Into(_) => parse_quote!(#i.into()),
19-
Conversion::AsRef(_) => parse_quote!(#i.as_ref()),
20-
Conversion::AsMut(_) => parse_quote!(#i.as_mut()),
21-
}
22-
}
23-
}
24-
25-
fn parse_bounded_type(ty: &Type) -> Option<Ident> {
26-
match &ty {
27-
Type::Path(TypePath { qself: None, path }) if path.segments.len() == 1 => Some(path.segments[0].ident.clone()),
28-
_ => None,
29-
}
30-
}
31-
32-
fn parse_bounds(bounds: &Punctuated<TypeParamBound, Token![+]>) -> Option<Conversion> {
33-
if bounds.len() != 1 {
34-
return None;
35-
}
36-
if let TypeParamBound::Trait(ref tb) = bounds.first().unwrap() {
37-
if let Some(seg) = tb.path.segments.iter().last() {
38-
if let PathArguments::AngleBracketed(ref gen_args) = seg.arguments {
39-
if let GenericArgument::Type(ref arg_ty) = gen_args.args.first().unwrap() {
40-
if seg.ident == "Into" {
41-
return Some(Conversion::Into(arg_ty));
42-
} else if seg.ident == "AsRef" {
43-
return Some(Conversion::AsRef(arg_ty));
44-
} else if seg.ident == "AsMut" {
45-
return Some(Conversion::AsMut(arg_ty));
46-
}
47-
}
48-
}
49-
}
50-
}
51-
None
52-
}
53-
54-
// create a map from generic type to Conversion
55-
fn parse_generics(decl: &Signature) -> HashMap<Ident, Conversion<'_>> {
56-
let mut ty_conversions = HashMap::new();
57-
for gp in decl.generics.params.iter() {
58-
if let GenericParam::Type(ref tp) = gp {
59-
if let Some(conversion) = parse_bounds(&tp.bounds) {
60-
ty_conversions.insert(tp.ident.clone(), conversion);
61-
}
62-
}
63-
}
64-
if let Some(ref wc) = decl.generics.where_clause {
65-
for wp in wc.predicates.iter() {
66-
if let WherePredicate::Type(ref pt) = wp {
67-
if let Some(ident) = parse_bounded_type(&pt.bounded_ty) {
68-
if let Some(conversion) = parse_bounds(&pt.bounds) {
69-
ty_conversions.insert(ident, conversion);
70-
}
71-
}
72-
}
73-
}
74-
}
75-
ty_conversions
76-
}
77-
78-
fn convert<'a>(
79-
inputs: &'a Punctuated<FnArg, Token![,]>,
80-
ty_conversions: &HashMap<Ident, Conversion<'a>>,
81-
) -> (Punctuated<FnArg, Token![,]>, Punctuated<Expr, Token![,]>, bool) {
82-
let mut argtypes = Punctuated::new();
83-
let mut argexprs = Punctuated::new();
84-
let mut has_self = false;
85-
inputs.iter().enumerate().for_each(|(i, input)| match input.clone() {
86-
FnArg::Receiver(receiver) => {
87-
has_self = true;
88-
argtypes.push(FnArg::Receiver(receiver));
89-
}
90-
FnArg::Typed(mut pat_type) => {
91-
let pat_ident = match &mut *pat_type.pat {
92-
Pat::Ident(pat_ident) if pat_ident.by_ref.is_none() && pat_ident.subpat.is_none() => pat_ident,
93-
_ => {
94-
pat_type.pat = Box::new(Pat::Ident(PatIdent {
95-
ident: Ident::new(&format!("arg_{i}_gen_by_momo_"), proc_macro2::Span::call_site()),
96-
attrs: Default::default(),
97-
by_ref: None,
98-
mutability: None,
99-
subpat: None,
100-
}));
101-
102-
if let Pat::Ident(pat_ident) = &mut *pat_type.pat {
103-
pat_ident
104-
} else {
105-
panic!()
106-
}
107-
}
108-
};
109-
// Outer function type argument pat does not need mut unless its
110-
// type is `impl AsMut`.
111-
pat_ident.mutability = None;
112-
113-
let ident = &pat_ident.ident;
114-
115-
let to_expr = || parse_quote!(#ident);
116-
117-
match *pat_type.ty {
118-
Type::ImplTrait(TypeImplTrait { ref bounds, .. }) => {
119-
if let Some(conv) = parse_bounds(bounds) {
120-
argexprs.push(conv.conversion_expr(ident));
121-
if let Conversion::AsMut(_) = conv {
122-
pat_ident.mutability = Some(Default::default());
123-
}
124-
} else {
125-
argexprs.push(to_expr());
126-
}
127-
}
128-
Type::Path(..) => {
129-
if let Some(conv) = parse_bounded_type(&pat_type.ty).and_then(|ident| ty_conversions.get(&ident)) {
130-
argexprs.push(conv.conversion_expr(ident));
131-
if let Conversion::AsMut(_) = conv {
132-
pat_ident.mutability = Some(Default::default());
133-
}
134-
} else {
135-
argexprs.push(to_expr());
136-
}
137-
}
138-
_ => {
139-
argexprs.push(to_expr());
140-
}
141-
}
142-
143-
// Now that mutability is decided, push the type into argtypes
144-
argtypes.push(FnArg::Typed(pat_type));
145-
}
146-
});
147-
(argtypes, argexprs, has_self)
148-
}
149-
150-
fn contains_self_type_path(path: &Path) -> bool {
151-
path.segments.iter().any(|segment| {
152-
segment.ident == "Self"
153-
|| match &segment.arguments {
154-
PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) => {
155-
args.iter().any(|generic_arg| match generic_arg {
156-
GenericArgument::Type(ty) => contains_self_type(ty),
157-
GenericArgument::Const(expr) => contains_self_type_expr(expr),
158-
_ => false,
159-
})
160-
}
161-
PathArguments::Parenthesized(ParenthesizedGenericArguments { inputs, output, .. }) => {
162-
inputs.iter().any(contains_self_type)
163-
|| matches!(output, ReturnType::Type(_, ty) if contains_self_type(ty))
164-
}
165-
_ => false,
166-
}
167-
})
168-
}
169-
170-
fn contains_self_type_expr(expr: &Expr) -> bool {
171-
match expr {
172-
Expr::Path(ExprPath { qself: Some(_), .. }) => true,
173-
Expr::Path(ExprPath { path, .. }) => contains_self_type_path(path),
174-
_ => false,
175-
}
176-
}
177-
178-
fn contains_self_type(input: &Type) -> bool {
179-
match input {
180-
Type::Array(TypeArray { elem, len, .. }) => {
181-
// Call `matches!` first so that we can do tail call here
182-
// as an optimization.
183-
contains_self_type_expr(len) || contains_self_type(elem)
184-
}
185-
Type::Group(TypeGroup { elem, .. }) => contains_self_type(elem),
186-
Type::Paren(TypeParen { elem, .. }) => contains_self_type(elem),
187-
Type::Ptr(TypePtr { elem, .. }) => contains_self_type(elem),
188-
Type::Reference(TypeReference { elem, .. }) => contains_self_type(elem),
189-
Type::Slice(TypeSlice { elem, .. }) => contains_self_type(elem),
190-
191-
Type::Tuple(TypeTuple { elems, .. }) => elems.iter().any(contains_self_type),
192-
193-
Type::Path(TypePath { qself: Some(_), .. }) => true,
194-
Type::Path(TypePath { path, .. }) => contains_self_type_path(path),
195-
196-
_ => false,
197-
}
198-
}
199-
200-
fn has_self_type(input: &FnArg) -> bool {
201-
match input {
202-
FnArg::Receiver(_) => true,
203-
FnArg::Typed(PatType { ty, .. }) => contains_self_type(ty),
204-
}
205-
}
2063

2074
/// Generate lightweight monomorphized wrapper around main implementation.
2085
/// May be applied to functions and methods.
2096
#[proc_macro_attribute]
2107
pub fn momo(_attrs: TokenStream, input: TokenStream) -> TokenStream {
2118
//TODO: alternatively parse ImplItem::Method
212-
momo_inner(input.into()).into()
9+
momo::inner(input.into()).into()
21310
}
21411

215-
fn momo_inner(code: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
216-
let fn_item: Item = match syn::parse2(code.clone()) {
217-
Ok(input) => input,
218-
Err(err) => return err.to_compile_error(),
219-
};
220-
221-
if let Item::Fn(item_fn) = fn_item {
222-
let ty_conversions = parse_generics(&item_fn.sig);
223-
let (argtypes, argexprs, has_self) = convert(&item_fn.sig.inputs, &ty_conversions);
224-
225-
let uses_self = has_self
226-
|| item_fn.sig.inputs.iter().any(has_self_type)
227-
|| matches!(&item_fn.sig.output, ReturnType::Type(_, ty) if contains_self_type(ty));
228-
229-
let inner_ident = Ident::new(
230-
// Use long qualifier to avoid name colision.
231-
&format!("_{}_inner_generated_by_gix_macro_momo", item_fn.sig.ident),
232-
proc_macro2::Span::call_site(),
233-
);
234-
235-
let outer_sig = Signature {
236-
inputs: argtypes,
237-
..item_fn.sig.clone()
238-
};
239-
240-
let new_inner_item = Item::Fn(ItemFn {
241-
// Remove doc comment since they will increase compile-time and
242-
// also generates duplicate warning/error messages for the doc,
243-
// especially if it contains doc-tests.
244-
attrs: {
245-
let mut attrs = item_fn.attrs.clone();
246-
attrs.retain(|attr| {
247-
let segments = &attr.path().segments;
248-
!(segments.len() == 1 && segments[0].ident == "doc")
249-
});
250-
attrs
251-
},
252-
vis: Visibility::Inherited,
253-
sig: Signature {
254-
ident: inner_ident.clone(),
255-
..item_fn.sig
256-
},
257-
block: item_fn.block,
258-
});
259-
260-
if uses_self {
261-
// We can use `self` or `Self` inside function defined within
262-
// the impl-fn, so instead declare two separate functions.
263-
//
264-
// Since it's an impl block, it's unlikely to have name conflict,
265-
// though this won't work for impl-trait.
266-
//
267-
// This approach also make sure we can call the right function
268-
// using `Self` qualifier.
269-
let new_item = Item::Fn(ItemFn {
270-
attrs: item_fn.attrs,
271-
vis: item_fn.vis,
272-
sig: outer_sig,
273-
block: if has_self {
274-
parse_quote!({ self.#inner_ident(#argexprs) })
275-
} else {
276-
parse_quote!({ Self::#inner_ident(#argexprs) })
277-
},
278-
});
279-
quote!(#new_item #new_inner_item)
280-
} else {
281-
// Put the new inner function within the function block
282-
// to avoid duplicate function name and support associated
283-
// function that doesn't use `self` or `Self`.
284-
let new_item = Item::Fn(ItemFn {
285-
attrs: item_fn.attrs,
286-
vis: item_fn.vis,
287-
sig: outer_sig,
288-
block: parse_quote!({
289-
#new_inner_item
290-
291-
#inner_ident(#argexprs)
292-
}),
293-
});
294-
quote!(#new_item)
295-
}
296-
} else {
297-
Error::new(fn_item.span(), "expect a function").to_compile_error()
298-
}
299-
}
12+
mod momo;

0 commit comments

Comments
 (0)