refloctopus_derive/
lib.rs

1//! Automatically derive reflection metadata with `#[derive(Reflect)]`
2
3use itertools::Itertools;
4use proc_macro2::TokenStream;
5use quote::{format_ident, quote, ToTokens};
6extern crate alloc;
7
8use syn::{Data, DataEnum, DataStruct, Fields, Ident};
9
10// all the state and methods for doing a derive on a single item
11struct DeriveReflect<'a> {
12    consts: Vec<TokenStream>,
13    nesting: Option<String>,
14    // the parent stack tracks the field path through an item
15    parent_stack: Vec<Ident>,
16    most_recent_discriminant_expr: Option<(usize, TokenStream)>,
17    nightly_const: bool,
18    generics: &'a syn::Generics,
19    seen_types: Vec<&'a syn::Type>,
20}
21
22fn primitive(p: &syn::Lit) -> TokenStream {
23    match p {
24        syn::Lit::Str(s) => quote! {_reflect::RustPrimitive::Str(#s)},
25        syn::Lit::ByteStr(bs) => quote! {_reflect::RustPrimitive::ByteStr(#bs)},
26        syn::Lit::Byte(b) => quote! {_reflect::RustPrimitive::Byte(#b)},
27        syn::Lit::Char(c) => quote! {_reflect::RustPrimitive::Char(#c)},
28        syn::Lit::Int(i) => {
29            // don't format the LitInt directly because it might be eg `4u32` which isn't what we want
30            let i = i
31                .base10_parse::<i128>()
32                .expect("syntax error after parsing?");
33            quote! {_reflect::RustPrimitive::Int(#i) }
34        }
35        syn::Lit::Float(f) => {
36            // similar to above, this might be 0x123f32 which is not what we're after
37            let f = f
38                .base10_parse::<f64>()
39                .expect("syntax error after parsing?");
40            quote! {_reflect::RustPrimitive::Float(#f) }
41        }
42        syn::Lit::Bool(b) => quote! {_reflect::RustPrimitive::Bool(#b)},
43        syn::Lit::Verbatim(v) => panic!("not really sure what literal this is: {:?}", v),
44    }
45}
46
47impl<'a> DeriveReflect<'a> {
48    fn parent(&self) -> Ident {
49        format_ident!(
50            "{}",
51            self.parent_stack.iter().map(|x| x.to_string()).join("_")
52        )
53    }
54
55    fn nested<T>(&mut self, ix: usize, f: impl FnOnce(&mut Self) -> T) -> T {
56        let old_nesting = self.nesting.replace(
57            self.nesting
58                .as_ref()
59                .map_or(ix.to_string(), |n| format!("{}_{}", n, ix)),
60        );
61        let res = f(self);
62        self.nesting = old_nesting;
63        res
64    }
65
66    fn meta(&mut self, ix: usize, m: &syn::Meta) -> TokenStream {
67        match m {
68            syn::Meta::Path(p) => {
69                let p = p.to_token_stream().to_string();
70                quote! { Attr::Name(#p)  }
71            }
72            syn::Meta::List(ml) => {
73                let nesteds = ml
74                    .nested
75                    .iter()
76                    .map(|nested| match nested {
77                        syn::NestedMeta::Meta(m) => self.nested(ix, |me| me.meta(0, m)),
78                        syn::NestedMeta::Lit(l) => l.to_token_stream(),
79                    })
80                    .collect::<Vec<_>>();
81                let path = ml.path.to_token_stream().to_string();
82                let nesteds_ident = format_ident!(
83                    "{}_META_{}_{}",
84                    self.parent(),
85                    ix,
86                    self.nesting.as_ref().unwrap_or(&String::new())
87                );
88                self.consts.push(quote!{const #nesteds_ident : &'static [_reflect::Attr<'static>] = &[#(#nesteds),*]; });
89                let list = self.list_reference(nesteds_ident, nesteds);
90                quote! { _reflect::Attr::List(#path, #list) }
91            }
92            syn::Meta::NameValue(mnv) => {
93                let path = mnv.path.to_token_stream().to_string();
94                let lit = primitive(&mnv.lit);
95                quote! { _reflect::Attr::NameValue(#path, #lit) }
96            }
97        }
98    }
99    fn list_reference(&self, id: Ident, elts: Vec<TokenStream>) -> TokenStream {
100        if self.nightly_const {
101            quote! { alloc::borrow::Cow::Borrowed(#id) }
102        } else {
103            quote! { alloc::borrow::Cow::Owned(vec![#(#elts),*]) }
104        }
105    }
106
107    fn attrs(&mut self, attrs: &[syn::Attribute]) -> Vec<TokenStream> {
108        attrs
109            .iter()
110            .enumerate()
111            .map(|(ix, a)| self.meta(ix, &a.parse_meta().expect("expected meta in attribute")))
112            .collect()
113    }
114
115    fn field(&mut self, ix: usize, f: &'a syn::Field) -> TokenStream {
116        let attrs_name = format_ident!(
117            "{}_{}_ATTRS",
118            self.parent(),
119            f.ident
120                .clone()
121                .map_or_else(|| ix.to_string(), |f| f.to_string())
122        );
123        let ix = syn::Index::from(ix);
124
125        let attrs = self.attrs(&f.attrs);
126        self.consts.push(
127            quote! { const #attrs_name : &'static [_reflect::Attr<'static>] = & [#(#attrs),*] },
128        );
129        let attrs = self.list_reference(attrs_name, attrs);
130
131        let offset = match &f.ident {
132            Some(name) => {
133                quote! { memoffset::offset_of!(Self, #name) }
134            }
135            None => {
136                quote! { memoffset::offset_of!(Self, #ix) }
137            }
138        };
139
140        let field_ty = &f.ty;
141        let shape = self.visit_ty(offset.clone(), field_ty);
142
143        match &f.ident {
144            Some(name) => quote! {
145                _reflect::Field {
146                    offset: #offset,
147                    shape: #shape,
148                    name: stringify!(#name),
149                    attrs: #attrs,
150                }
151            },
152            None => {
153                quote! {
154                    _reflect::TupleField {
155                        offset: #offset,
156                        shape: #shape,
157                        attrs: #attrs,
158                    }
159                }
160            }
161        }
162    }
163
164    /// having traversed through all of the "item" components of a type declaration,
165    /// we find ourselves at the very bottom looking at a field containing a structural rust type.
166    fn visit_ty(&mut self, offset: TokenStream, ty: &'a syn::Type) -> TokenStream {
167        match ty {
168            syn::Type::Array(arr) => {
169                let nested_shape = self.visit_ty(offset, &arr.elem);
170                let len = &arr.len;
171                quote! { _reflect::DataShape::FixedArray(#nested_shape, #len)}
172            }
173            syn::Type::Paren(syn::TypeParen { elem, .. })
174            | syn::Type::Group(syn::TypeGroup { elem, .. }) => self.visit_ty(offset, elem),
175            syn::Type::Infer(_) => panic!("are these even allowed where we're parsing for types?"),
176            syn::Type::Path(path) => {
177                let p = path.to_token_stream();
178                quote! {
179                    _reflect::_builtin_search(#path)
180                                .unwrap_or(_reflect::DataShape::Leaf(core::any::TypeId::of::<#path>()))
181                }
182            }
183            syn::Type::Reference(refer) => {
184                let nested = self.visit_ty(quote! { 0 }, &refer.elem);
185                quote! { _reflect::DataShape::Ref(alloc::borrow::Cow::Owned(#nested)) }
186            }
187            syn::Type::Slice(slice) => {
188                let nested = self.visit_ty(quote! { 0 }, &slice.elem);
189                quote! { _reflect::DataShape::Slice(alloc::borrow::Cow::Owned(#nested)) }
190            }
191            syn::Type::Tuple(tuple) => {
192                let tup_elts = tuple
193                    .elems
194                    .iter()
195                    .enumerate()
196                    .map(|(ix, tp)| {
197                        let ix = syn::Index::from(ix);
198                        self.visit_ty(quote! { memoffset::offset_of_tuple!(#offset, #ix) }, tp)
199                    })
200                    .collect::<Vec<_>>();
201                let tup_elts_name = format_ident!("{}_TUP_ELTS", self.parent());
202                self.consts.push(
203                        quote! { const #tup_elts_name : &'static [_reflect::DataShape<'static>] = &[#(#tup_elts),*]; },
204                    );
205
206                let tup_elts = self.list_reference(tup_elts_name, tup_elts);
207                quote! { _reflect::DataShape::Tuple(#tup_elts) }
208            }
209
210            syn::Type::__Nonexhaustive => unreachable!(),
211            syn::Type::BareFn(_) => panic!("don't reflect over function pointers"),
212            m @ syn::Type::ImplTrait(_)
213            | m @ syn::Type::TraitObject(_)
214            | m @ syn::Type::Verbatim(_)
215            | m @ syn::Type::Macro(_) => {
216                self.seen_types.push(ty);
217                quote! { _reflect::DataShape::Leaf(core::any::TypeId::of::<#m>() ) }
218            }
219            syn::Type::Ptr(_) => panic!("don't reflect over raw pointers"),
220            syn::Type::Never(_) => panic!("don't reflect over never"),
221        }
222    }
223
224    /*
225        computing offsets for enum fields is busted.
226        need to relayout the struct according to the repr/union system,
227        and then get the layouts from that.
228    */
229    fn variant_data(&mut self, fields: &'a syn::Fields) -> TokenStream {
230        let fields_ident = format_ident!("{}_FIELDS", self.parent());
231
232        match fields {
233            Fields::Named(n) => {
234                let field_labels = n
235                    .named
236                    .iter()
237                    .map(|f| f.ident.as_ref().expect("should be named").to_string());
238
239                let fields = n
240                    .named
241                    .iter()
242                    .enumerate()
243                    .map(|(ix, f)| self.field(ix + 1, f))
244                    .collect::<Vec<_>>();
245                let field_labels_ident = format_ident!("{}_LABELS", fields_ident);
246
247                self.consts.push(
248                    quote! { const #fields_ident : &'static [&'static str] = &[#(#fields),*]; },
249                );
250                let fields = self.list_reference(fields_ident, fields);
251                quote! { _reflect::VariantData::Fields {
252                    labels_for_serde: { const #field_labels_ident : &'static [&'static str] = &[#(#field_labels),*]; #field_labels_ident },
253                    fields: #fields
254                } }
255            }
256            Fields::Unnamed(n) => {
257                let fields = n
258                    .unnamed
259                    .iter()
260                    .enumerate()
261                    .map(|(ix, f)| self.field(ix, f))
262                    .collect::<Vec<_>>();
263                self.consts.push(quote!{ const #fields_ident : &'static [_reflect::Field<'static>] = &[#(#fields),*]; } );
264
265                let fields = self.list_reference(fields_ident, fields);
266                quote! { _reflect::VariantData::Tuple(#fields) }
267            }
268            Fields::Unit => {
269                quote! { _reflect::VariantData::Unit }
270            }
271        }
272    }
273
274    fn parented<T>(&mut self, new_parent: Ident, f: impl FnOnce(&mut Self) -> T) -> T {
275        self.parent_stack.push(new_parent);
276        let r = f(self);
277        self.parent_stack.pop();
278        r
279    }
280
281    /// For a variant `v` at index `ix` in its containing enum, generate the `EnumArm`
282    /// reflecting it. `disc_base` is the index and defining expression of the last variant
283    /// in this enum to have explicitly set a discriminant. It is returned, unless this variant
284    /// itself has a discriminant set, in which case _that_ is returned.
285    fn variant(&mut self, ix: usize, v: &'a syn::Variant) -> TokenStream {
286        let attrs = self.parented(format_ident!("V{}", ix), |me| me.attrs(&v.attrs));
287
288        let vdata = self.parented(format_ident!("{}", v.ident), |me| {
289            me.variant_data(&v.fields)
290        });
291        let label = v.ident.to_string();
292        let attrs_name = format_ident!("{}_{}_ATTRS", self.parent(), label);
293
294        self.consts.push(
295            quote! { const #attrs_name : &'static [_reflect::Attr<'static>] = &[#(#attrs),*]; },
296        );
297
298        let attrs = self.list_reference(attrs_name, attrs);
299
300        let (new_disc_base, disc_val) = match &v.discriminant {
301            Some((_eq_token, val)) => (Some((ix, val.to_token_stream())), val.to_token_stream()),
302            None => (
303                self.most_recent_discriminant_expr.clone(),
304                match &self.most_recent_discriminant_expr {
305                    Some((disc_setter_ix, prev_disc)) => {
306                        quote! { #prev_disc + #(#ix - #disc_setter_ix) }
307                    }
308                    None => quote! { #ix },
309                },
310            ),
311        };
312        self.most_recent_discriminant_expr = new_disc_base;
313
314        quote! {
315            _reflect::EnumArm {
316                label: #label,
317                variant_index: #ix,
318                discriminant: #disc_val,
319                attrs: #attrs,
320                variant: #vdata,
321            }
322        }
323    }
324
325    fn data_shape(&mut self, d: &'a syn::DeriveInput) -> TokenStream {
326        match &d.data {
327            Data::Struct(DataStruct { fields, .. }) => {
328                let vdata = self.parented(d.ident.clone(), |me| me.variant_data(fields));
329                quote! { _reflect::DataShape::Struct(#vdata) }
330            }
331            Data::Enum(DataEnum { variants, .. }) => {
332                //let variant = variants.iter().map(|v| v.ident.to_string());
333                let (labels, variants): (Vec<_>, Vec<_>) = variants
334                    .iter()
335                    .enumerate()
336                    .map(|(ix, v)| {
337                        let arm = self.parented(d.ident.clone(), |me| me.variant(ix, v));
338                        (v.ident.to_string(), arm)
339                    })
340                    .unzip();
341                let variants_ident = format_ident!("{}_VARIANTS", d.ident);
342                let field_labels_ident = format_ident!("{}_LABELS", variants_ident);
343                self.consts.push(
344                    quote! { { const #field_labels_ident : &'static [&'static str] = &[#(#labels),*]; #field_labels_ident } },
345                );
346                self.consts.push(
347                    quote! { const #variants_ident : &'static [_reflect::EnumArm<'static>] = &[#(#variants),*]},
348                );
349                let variants = self.list_reference(variants_ident, variants);
350
351                quote! { _reflect::DataShape::Enum{
352                    variant_labels_for_serde: #field_labels_ident,
353                    variants: #variants,
354                } }
355            }
356            Data::Union(_) => panic!("unions can not be reflected into"),
357        }
358    }
359}
360
361#[proc_macro_derive(BrokenReflect)]
362pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
363    let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
364    let mut derive = DeriveReflect {
365        consts: vec![],
366        nesting: None,
367        parent_stack: vec![],
368        most_recent_discriminant_expr: None,
369        nightly_const: false,
370        generics: &ast.generics,
371        seen_types: vec![],
372    };
373
374    let attrs = derive.parented(ast.ident.clone(), |me| me.attrs(&ast.attrs));
375    let shape = derive.data_shape(&ast);
376
377    let leafs = vec![quote! {drop("oh well");}];
378
379    // ideas for improving codegen:
380    // - with a const offset_of, we can implement StaticReflect and not just SelfReflect
381    // - how do we figure out when to register leafs?
382
383    let attr_name = format_ident!("{}_ATTRS", ast.ident);
384    derive
385        .consts
386        .push(quote! { const #attr_name : &'static [_reflect::Attr<'static>] = &[#(#attrs),*]; });
387
388    // TODO: static_assert that every type either implements SelfReflect or implements Serialize/Deserialize
389
390    let generics = &ast.generics;
391
392    let me = &ast.ident;
393
394    let consts = if derive.nightly_const {
395        derive.consts
396    } else {
397        Vec::new()
398    };
399
400    let impl_block = quote! {
401        const _ : () = {
402            extern crate op_reflect as _reflect;
403            extern crate core;
404            extern crate memoffset;
405            extern crate alloc;
406
407            #(#consts)*
408
409            impl #generics _reflect::Reflect for #me #generics where Self: 'static {
410                fn rust_type() -> _reflect::StaticType {
411                    alloc::borrow::Cow::Owned(_reflect::ReflectedType {
412                        id: core::any::TypeId::of::<Self>(),
413                        name: stringify!(#me),
414                        layout: core::alloc::Layout::new::<Self>(),
415                        shape: #shape,
416                        attrs: alloc::borrow::Cow::Owned(vec![#(#attrs),*]),
417                    })
418                }
419                fn register(db: &mut _reflect::Db<'_,'_>) {
420                    db.register_type::<Self>();
421                    #(#leafs);*
422                }
423            }
424        };
425    };
426
427    impl_block.into()
428}