basic_oop_macro/
lib.rs

1extern crate proc_macro;
2
3use anycase::{to_pascal, to_snake, to_screaming_snake};
4use iter_identify_first_last::IteratorIdentifyFirstLastExt;
5use macro_magic::import_tokens_attr;
6use proc_macro2::{TokenStream, Span};
7use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
8use quote::{quote, TokenStreamExt, ToTokens};
9use syn::{parse_macro_input, parse_quote, ItemStruct, Path, Type, Fields, Meta, Attribute, Visibility, Ident};
10use syn::{Field, FieldMutability, TypePath, Token, PathSegment, PathArguments, LitInt, TypeBareFn, Expr};
11use syn::{BareFnArg, Generics, Signature, Pat, PatType, PatIdent, FnArg, ImplItemFn, Stmt, ExprPath};
12use syn::{Receiver, TypeReference, AttrStyle};
13use syn::punctuated::Punctuated;
14use syn::spanned::Spanned;
15use syn::token::Bracket;
16
17fn parse_base_field_meta(meta: &Meta) -> bool {
18    match meta {
19        Meta::Path(path) if
20               path.leading_colon.is_none()
21            && path.segments.len() == 1
22            && path.segments[0].arguments.is_none()
23        => match path.segments[0].ident.to_string().as_ref() {
24            "non_virt" => false,
25            _ => true
26        },
27        _ => true
28    }
29}
30
31fn parse_base_field_attrs(attrs: &[Attribute]) -> Result<bool, Diagnostic> {
32    let mut is_virtual = true;
33    for attr in attrs {
34        if !parse_base_field_meta(&attr.meta) {
35            if !is_virtual {
36                return Err(
37                     attr.span()
38                    .error("invalid base class")
39                );
40            }
41            is_virtual = false;
42        }
43    }
44    Ok(is_virtual)
45}
46
47struct Base {
48    ty: Path,
49    non_virt_methods: Vec<(Ident, TypeBareFn)>,
50    virt_methods: Vec<(Ident, TypeBareFn)>,
51}
52
53fn parse_base_types(inherited_from: ItemStruct) -> Result<Vec<Base>, Diagnostic> {
54    let Fields::Named(fields) = inherited_from.fields else {
55        return Err(inherited_from.fields.span().error("invalid base class"));
56    };
57    let mut res = Vec::new();
58    let mut base = None;
59    for field in fields.named {
60        if field.ident.as_ref().unwrap().to_string() == "__class__" {
61            if let Some(base) = base.take() {
62                res.push(base);
63            }
64            let Type::Path(type_path) = field.ty else {
65                return Err(field.ty.span().error("invalid base class"));
66            };
67            if type_path.path.segments.len() < 2 {
68                return Err(type_path.span().error("invalid base class"));
69            }
70            base = Some(Base {
71                ty: type_path.path,
72                non_virt_methods: Vec::new(),
73                virt_methods: Vec::new()
74            });
75        } else {
76            let name = field.ident.as_ref().unwrap().clone();
77            let Type::BareFn(type_fn) = field.ty else {
78                return Err(field.ty.span().error("invalid base class"));
79            };
80            let Some(base) = base.as_mut() else {
81                return Err(type_fn.span().error("invalid base class"));
82            };
83            if parse_base_field_attrs(&field.attrs)? {
84                base.virt_methods.push((name, type_fn));
85            } else {
86                base.non_virt_methods.push((name, type_fn));
87            }
88        }
89    }
90    if let Some(base) = base.take() {
91        res.push(base);
92    }
93    Ok(res)
94}
95
96#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
97enum FieldKind {
98    NonVirtMethod,
99    VirtMethod,
100    Override,
101    Data,
102}
103
104fn parse_field_meta(meta: &Meta) -> FieldKind {
105    match meta {
106        Meta::Path(path) if
107               path.leading_colon.is_none()
108            && path.segments.len() == 1
109            && path.segments[0].arguments.is_none()
110        => match path.segments[0].ident.to_string().as_ref() {
111            "non_virt" => FieldKind::NonVirtMethod,
112            "virt" => FieldKind::VirtMethod,
113            "over" => FieldKind::Override,
114            _ => FieldKind::Data,
115        },
116        _ => FieldKind::Data,
117    }
118}
119
120fn parse_field_attrs(attrs: &[Attribute]) -> Result<FieldKind, Diagnostic> {
121    let mut kind = FieldKind::Data;
122    for attr in attrs {
123        match parse_field_meta(&attr.meta) {
124            FieldKind::Data => { },
125            FieldKind::NonVirtMethod => {
126                if kind != FieldKind::Data {
127                    return Err(
128                         attr.span()
129                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
130                    );
131                }
132                kind = FieldKind::NonVirtMethod;
133            },
134            FieldKind::VirtMethod => {
135                if kind != FieldKind::Data {
136                    return Err(
137                         attr.span()
138                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
139                    );
140                }
141                kind = FieldKind::VirtMethod;
142            },
143            FieldKind::Override => {
144                if kind != FieldKind::Data {
145                    return Err(
146                         attr.span()
147                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
148                    );
149                }
150                kind = FieldKind::Override;
151            },
152        }
153    }
154    Ok(kind)
155}
156
157struct Class {
158    attrs: Vec<Attribute>,
159    vis: Visibility,
160    name: Ident,
161    mod_: Path,
162    fields: Vec<Field>,
163    non_virt_methods: Vec<(Ident, TypeBareFn)>,
164    virt_methods: Vec<(Ident, TypeBareFn)>,
165    overrides: Vec<Ident>,
166}
167
168impl Class {
169    fn parse(descr: ItemStruct) -> Result<Class, Diagnostic> {
170        if descr.generics.lt_token.is_some() || descr.generics.where_clause.is_some() {
171            return Err(descr.generics.span().error("basic-oop does not support generics"));
172        }
173        let mut mod_ = None;
174        let mut fields = Vec::new();
175        let mut non_virt_methods = Vec::new();
176        let mut virt_methods = Vec::new();
177        let mut overrides = Vec::new();
178        let Fields::Named(descr_fields) = descr.fields else {
179            return Err(descr.fields.span().error("class should be described as struct with named fields"));
180        };
181        for (is_first, field) in descr_fields.named.iter().identify_first() {
182            if is_first {
183                if field.ident.as_ref().unwrap().to_string() != "__mod__" {
184                    return Err(
185                         field.ident.as_ref().unwrap().span()
186                        .error("first field should be '__mod__' with full path to class")
187                    );
188                }
189                let Type::Path(type_path) = &field.ty else {
190                    return Err(
191                         field.ty.span()
192                        .error("first field should be '__mod__' with full path to class")
193                    );
194                };
195                if type_path.qself.is_some() {
196                    return Err(
197                         type_path.span()
198                        .error("first field should be '__mod__' with full path to class")
199                    );
200                }
201                if type_path.path.leading_colon.is_none() || type_path.path.segments.is_empty() {
202                    return Err(
203                         type_path.span()
204                        .error("first field should be '__mod__' with full path to class starting with leading colon")
205                    );
206                }
207                mod_ = Some(type_path.path.clone());
208            } else {
209                match parse_field_attrs(&field.attrs)? {
210                    FieldKind::Data => fields.push(field.clone()),
211                    FieldKind::NonVirtMethod => {
212                        let name = field.ident.clone().unwrap();
213                        if name.to_string() == "__class__" {
214                            return Err(name.span().error("this name is reserved"));
215                        }
216                        let Type::BareFn(type_fn) = &field.ty else {
217                            return Err(field.ty.span().error("invalid non-virtual method type"));
218                        };
219                        if
220                               type_fn.lifetimes.is_some()
221                            || type_fn.unsafety.is_some()
222                            || type_fn.abi.is_some()
223                            || type_fn.variadic.is_some()
224                            || type_fn.inputs.iter().any(|x| !x.attrs.is_empty())
225                        {
226                            return Err(field.ty.span().error("invalid non-virtual method type"));
227                        }
228                        if let Some(arg) = type_fn.inputs.iter().find(|x| x.name.is_none()) {
229                            return Err(arg.span().error("argument name required"));
230                        }
231                        non_virt_methods.push((name, type_fn.clone()));
232                    },
233                    FieldKind::VirtMethod => {
234                        let name = field.ident.clone().unwrap();
235                        if name.to_string() == "__class__" {
236                            return Err(name.span().error("this name is reserved"));
237                        }
238                        let Type::BareFn(type_fn) = &field.ty else {
239                            return Err(field.ty.span().error("invalid virtual method type"));
240                        };
241                        if
242                               type_fn.lifetimes.is_some()
243                            || type_fn.unsafety.is_some()
244                            || type_fn.abi.is_some()
245                            || type_fn.variadic.is_some()
246                            || type_fn.inputs.iter().any(|x| !x.attrs.is_empty())
247                        {
248                            return Err(field.ty.span().error("invalid virtual method type"));
249                        }
250                        if let Some(arg) = type_fn.inputs.iter().find(|x| x.name.is_none()) {
251                            return Err(arg.span().error("argument name required"));
252                        }
253                        virt_methods.push((name, type_fn.clone()));
254                    },
255                    FieldKind::Override => {
256                        let name = field.ident.clone().unwrap();
257                        let Type::Tuple(type_tuple) = &field.ty else {
258                            return Err(field.ty.span().error("invalid override method type"));
259                        };
260                        if !type_tuple.elems.is_empty() {
261                            return Err(field.ty.span().error("invalid override method type"));
262                        }
263                        overrides.push(name);
264                    },
265                }
266            }
267        }
268        let Some(mod_) = mod_ else {
269            return Err(descr_fields.span().error("at least '__mod__' field should be specified"));
270        };
271        if mod_.leading_colon.is_none() {
272            return Err(
273                 mod_.span()
274                .error("first field should be '__mod__' with full path to class starting with leading colon")
275            );
276        }
277        Ok(Class {
278            attrs: descr.attrs,
279            vis: descr.vis,
280            name: descr.ident,
281            mod_,
282            fields,
283            non_virt_methods,
284            virt_methods,
285            overrides,
286        })
287    }
288}
289
290fn build_inherited_from(
291    base_types: &[Base],
292    class_name: &Ident,
293    class_mod: &Path,
294    non_virt_methods: &[(Ident, TypeBareFn)],
295    virt_methods: &[(Ident, TypeBareFn)]
296) -> ItemStruct {
297    let name = Ident::new(&("inherited_from_".to_string() + &class_name.to_string()), Span::call_site());
298    let mut struct_: ItemStruct = parse_quote! {
299        #[::basic_oop::macro_magic::export_tokens_no_emit]
300        struct #name {
301            __class__: #class_mod::#class_name
302        }
303    };
304    let Fields::Named(fields) = &mut struct_.fields else { panic!() };
305    for (method_name, method_ty) in non_virt_methods {
306        let mut segments = Punctuated::new();
307        segments.push(PathSegment {
308            ident: Ident::new("non_virt", Span::call_site()),
309            arguments: PathArguments::None
310        });
311        fields.named.push(Field {
312            attrs: vec![Attribute {
313                pound_token: <Token![#]>::default(),
314                style: AttrStyle::Outer,
315                bracket_token: Bracket::default(),
316                meta: Meta::Path(Path { leading_colon: None, segments }),
317            }],
318            vis: Visibility::Inherited,
319            mutability: FieldMutability::None,
320            ident: Some(method_name.clone()),
321            colon_token: Some(<Token![:]>::default()),
322            ty: Type::BareFn(method_ty.clone()),
323        });
324    }
325    for (method_name, method_ty) in virt_methods {
326        fields.named.push(Field {
327            attrs: Vec::new(),
328            vis: Visibility::Inherited,
329            mutability: FieldMutability::None,
330            ident: Some(method_name.clone()),
331            colon_token: Some(<Token![:]>::default()),
332            ty: Type::BareFn(method_ty.clone()),
333        });
334    }
335    for base_type in base_types {
336        fields.named.push(Field {
337            attrs: Vec::new(),
338            vis: Visibility::Inherited,
339            mutability: FieldMutability::None,
340            ident: Some(Ident::new("__class__", Span::call_site())),
341            colon_token: Some(<Token![:]>::default()),
342            ty: Type::Path(TypePath {
343                qself: None,
344                path: base_type.ty.clone()
345            }),
346        });
347        for (method_name, method_ty) in &base_type.non_virt_methods {
348            let mut segments = Punctuated::new();
349            segments.push(PathSegment {
350                ident: Ident::new("non_virt", Span::call_site()),
351                arguments: PathArguments::None
352            });
353            fields.named.push(Field {
354                attrs: vec![Attribute {
355                    pound_token: <Token![#]>::default(),
356                    style: AttrStyle::Outer,
357                    bracket_token: Bracket::default(),
358                    meta: Meta::Path(Path { leading_colon: None, segments }),
359                }],
360                vis: Visibility::Inherited,
361                mutability: FieldMutability::None,
362                ident: Some(method_name.clone()),
363                colon_token: Some(<Token![:]>::default()),
364                ty: Type::BareFn(method_ty.clone()),
365            });
366        }
367        for (method_name, method_ty) in &base_type.virt_methods {
368            fields.named.push(Field {
369                attrs: Vec::new(),
370                vis: Visibility::Inherited,
371                mutability: FieldMutability::None,
372                ident: Some(method_name.clone()),
373                colon_token: Some(<Token![:]>::default()),
374                ty: Type::BareFn(method_ty.clone()),
375            });
376        }
377    }
378    struct_
379}
380
381fn sanitize_base_type(mut base_type: Path, mod_: &Path) -> Path {
382    if base_type.segments[0].ident.to_string() == mod_.segments[0].ident.to_string() {
383        // base type in same crate
384        base_type.leading_colon = None;
385        base_type.segments[0].ident = Ident::new("crate", Span::call_site());
386    }
387    base_type
388}
389
390fn patch_path(path: &mut Path, f: impl FnOnce(String) -> String) {
391    let ident = f(path.segments.last().unwrap().ident.to_string());
392    path.segments.last_mut().unwrap().ident = Ident::new(&ident, Span::call_site());
393}
394
395fn build_attrs(attrs: &[Attribute]) -> TokenStream {
396    let mut tokens = TokenStream::new();
397    tokens.append_all(attrs);
398    tokens
399}
400
401fn build_struct(
402    base_types: &[Base],
403    attrs: &[Attribute],
404    vis: &Visibility,
405    name: &Ident,
406    mod_: &Path,
407    fields: &[Field]
408) -> ItemStruct {
409    let base_type = sanitize_base_type(base_types[0].ty.clone(), mod_);
410    let base_field = Ident::new(&to_snake(base_type.segments.last().unwrap().ident.to_string()), Span::call_site());
411    let attrs = build_attrs(attrs);
412    let mut struct_: ItemStruct = parse_quote! {
413        #attrs
414        #vis struct #name {
415            #base_field: #base_type
416        }
417    };
418    let Fields::Named(struct_fields) = &mut struct_.fields else { panic!() };
419    for field in fields {
420        struct_fields.named.push(field.clone());
421    }
422    struct_
423}
424
425fn build_trait(base_types: &[Base], vis: &Visibility, class_name: &Ident, mod_: &Path) -> TokenStream {
426    let base_type = sanitize_base_type(base_types[0].ty.clone(), mod_);
427    let base_field = Ident::new(
428        &to_snake(base_type.segments.last().unwrap().ident.to_string()),
429        Span::call_site()
430    );
431    let mut base_trait = sanitize_base_type(base_types[0].ty.clone(), mod_);
432    patch_path(&mut base_trait, |x| "T".to_string() + &x);
433    let trait_name = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
434    let method_name = Ident::new(&to_snake(class_name.to_string()), Span::call_site());
435    let mut trait_ = quote! {
436        #vis trait #trait_name: #base_trait {
437            fn #method_name(&self) -> &#class_name;
438        }
439
440        impl #trait_name for #class_name {
441            fn #method_name(&self) -> &#class_name { self }
442        }
443
444        impl #base_trait for #class_name {
445            fn #base_field(&self) -> &#base_type { &self.#base_field }
446        }
447    };
448    for base_base_type in base_types.iter().skip(1) {
449        let method_name = Ident::new(
450            &to_snake(base_base_type.ty.segments.last().unwrap().ident.to_string()),
451            Span::call_site()
452        );
453        let mut base_base_trait = sanitize_base_type(base_base_type.ty.clone(), mod_);
454        patch_path(&mut base_base_trait, |x| "T".to_string() + &x);
455        let base_base_type_ty = &base_base_type.ty;
456        trait_.extend(quote! {
457            impl #base_base_trait for #class_name {
458                fn #method_name(&self) -> &#base_base_type_ty {
459                    #base_base_trait::#method_name(&self.#base_field)
460                }
461            }
462        });
463    }
464    let mut traits_list: Punctuated<Path, Token![,]> = Punctuated::new();
465    let mut trait_path = Path {
466        leading_colon: None,
467        segments: Punctuated::new()
468    };
469    trait_path.segments.push(PathSegment {
470        ident: trait_name,
471        arguments: PathArguments::None
472    });
473    traits_list.push(trait_path);
474    for base_type in base_types {
475        let mut base_trait = sanitize_base_type(base_type.ty.clone(), mod_);
476        patch_path(&mut base_trait, |x| "T".to_string() + &x);
477        traits_list.push(base_trait);
478    }
479    trait_.extend(quote! {
480        ::basic_oop::dynamic_cast_impl_supports_interfaces!(#class_name: #traits_list);
481    });
482    trait_
483}
484
485fn build_virt_methods_enum(
486    base_type: &Path,
487    vis: &Visibility,
488    class_name: &Ident,
489    mod_: &Path,
490    virt_methods: &[(Ident, TypeBareFn)]
491) -> TokenStream {
492    let mut base_methods_enum = sanitize_base_type(base_type.clone(), mod_);
493    patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
494    let methods_enum = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
495    let mut values = TokenStream::new();
496    values.append_terminated(
497        virt_methods.iter().enumerate().map(|(i, (method_name, _method_ty))| {
498            let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
499            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
500            quote! {
501                #name = (#base_methods_enum::VirtMethodsCount as usize) + #index
502            }
503        }),
504        <Token![,]>::default()
505    );
506    let count = virt_methods.len();
507    quote! {
508        #[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)]
509        #[repr(usize)]
510        #vis enum #methods_enum {
511            #values
512            VirtMethodsCount = (#base_methods_enum::VirtMethodsCount as usize) + #count
513        }
514    }
515}
516
517fn build_consts_for_vtable(class_name: &Ident, class_mod: &Path, base_types: &[Base]) -> TokenStream {
518    let enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
519    let mut tokens = TokenStream::new();
520    for base_type in base_types {
521        let mut base_methods_enum = sanitize_base_type(base_type.ty.clone(), class_mod);
522        patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
523        let base_const_name = Ident::new(
524            &(
525                to_screaming_snake(
526                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
527                ) + "_VIRT_METHODS_COUNT"
528            ),
529            Span::call_site()
530        );
531        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
532        tokens.extend(quote! {
533            const #base_const_name: usize = #base_methods_enum::VirtMethodsCount as usize;
534            const #complement_const_name: usize = (#enum_name::VirtMethodsCount as usize) - #base_const_name;
535        });
536    }
537    tokens
538}
539
540fn actual_base_method_ty(mut ty: TypeBareFn, base_type: &Path, class_mod: &Path) -> TypeBareFn {
541    let mut base_trait = sanitize_base_type(base_type.clone(), class_mod);
542    patch_path(&mut base_trait, |x| "T".to_string() + &x);
543    let this_arg = BareFnArg {
544        attrs: Vec::new(),
545        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
546        ty: parse_quote! { &::basic_oop::alloc_sync_Arc<dyn #base_trait> },
547    };
548    ty.inputs.insert(0, this_arg);
549    ty
550}
551
552fn actual_method_ty(mut ty: TypeBareFn, class_name: &Ident) -> TypeBareFn {
553    let trait_name = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
554    let this_arg = BareFnArg {
555        attrs: Vec::new(),
556        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
557        ty: parse_quote! { &::basic_oop::alloc_sync_Arc<dyn #trait_name> },
558    };
559    ty.inputs.insert(0, this_arg);
560    ty
561}
562
563fn fn_ty_without_idents(mut ty: TypeBareFn) -> TypeBareFn {
564    for arg in &mut ty.inputs {
565        arg.name = None;
566    }
567    ty
568}
569
570fn bare_fn_arg_to_fn_arg(a: &BareFnArg) -> FnArg {
571    let Some((name, colon_token)) = &a.name else { panic!() }; 
572    FnArg::Typed(PatType {
573        attrs: Vec::new(),
574        pat: Box::new(Pat::Ident(PatIdent {
575            attrs: Vec::new(),
576            by_ref: None,
577            mutability: None,
578            ident: name.clone(),
579            subpat: None
580        })),
581        colon_token: colon_token.clone(),
582        ty: Box::new(a.ty.clone()),
583    })
584}
585
586fn method_signature(ty: &TypeBareFn, name: Ident) -> Signature {
587    let mut s = Signature {
588        constness: None,
589        asyncness: None,
590        unsafety: None,
591        abi: None,
592        fn_token: ty.fn_token.clone(),
593        ident: name,
594        generics: Generics {
595            lt_token: None,
596            params: Punctuated::new(),
597            gt_token: None,
598            where_clause: None,
599        },
600        paren_token: ty.paren_token.clone(),
601        inputs: Punctuated::new(),
602        variadic: None,
603        output: ty.output.clone(),
604    };
605    let mut segments = Punctuated::new();
606    segments.push(PathSegment {
607        ident: Ident::new("Self", Span::call_site()),
608        arguments: PathArguments::None
609    });
610    s.inputs.push(FnArg::Receiver(Receiver {
611        attrs: Vec::new(),
612        reference: Some((<Token![&]>::default(), None)),
613        mutability: None,
614        self_token: <Token![self]>::default(),
615        colon_token: None,
616        ty: Box::new(Type::Reference(TypeReference {
617            and_token: <Token![&]>::default(),
618            lifetime: None,
619            mutability: None,
620            elem: Box::new(Type::Path(TypePath {
621                qself: None,
622                path: Path {
623                    leading_colon: None,
624                    segments
625                }
626            }))
627        }))
628    }));
629    for arg in ty.inputs.iter().skip(1) {
630        s.inputs.push(bare_fn_arg_to_fn_arg(arg));
631    }
632    s
633}
634
635fn build_vtable(
636    base_types: &[Base],
637    class_name: &Ident,
638    class_mod: &Path,
639    vis: &Visibility,
640    virt_methods: &[(Ident, TypeBareFn)],
641    overrides: &[Ident],
642) -> TokenStream {
643    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
644    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
645    let struct_ = quote! {
646        #vis struct #vtable_name(pub [*const (); #methods_enum_name::VirtMethodsCount as usize]);
647    };
648    let mut methods_impl = TokenStream::new();
649    methods_impl.append_separated(virt_methods.iter().map(|(m, _)| {
650        let impl_name = Ident::new(&(m.to_string() + "_impl"), Span::call_site());
651        quote! { #class_name::#impl_name as *const () }
652    }), <Token![,]>::default());
653    let mut base_vtable = sanitize_base_type(base_types[0].ty.clone(), class_mod);
654    patch_path(&mut base_vtable, |x| x + "Vtable");
655    let base_vtable_new: Expr = parse_quote! { #base_vtable::new() };
656    let mut base_vtable_with_overrides = base_vtable_new;
657    for override_name in overrides {
658        let impl_name = Ident::new(&(override_name.to_string() + "_impl"), Span::call_site());
659        base_vtable_with_overrides = parse_quote! {
660            #base_vtable_with_overrides.#override_name(#class_name::#impl_name)
661        };
662    }
663    let base_const_name = Ident::new(
664        &(
665            to_screaming_snake(
666                class_name.to_string() + &base_types[0].ty.segments.last().unwrap().ident.to_string()
667            ) + "_VIRT_METHODS_COUNT"
668        ),
669        Span::call_site()
670    );
671    let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
672    let mut base_methods = TokenStream::new();
673    for base_type in base_types {
674        let mut base_vtable = sanitize_base_type(base_type.ty.clone(), class_mod);
675        patch_path(&mut base_vtable, |x| x + "Vtable");
676        let base_const_name = Ident::new(
677            &(
678                to_screaming_snake(
679                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
680                ) + "_VIRT_METHODS_COUNT"
681            ),
682            Span::call_site()
683        );
684        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
685        for (base_method, base_method_ty) in &base_type.virt_methods {
686            let ty = actual_base_method_ty(base_method_ty.clone(), &base_type.ty, class_mod);
687            let ty_without_idents = fn_ty_without_idents(ty);
688            base_methods.extend(quote! {
689                pub const fn #base_method(
690                    self,
691                    f: #ty_without_idents
692                ) -> Self {
693                    let vtable = unsafe { ::basic_oop::core_mem_transmute::<
694                        [*const (); #methods_enum_name::VirtMethodsCount as usize],
695                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
696                    >(self.0) };
697                    let vtable: ::basic_oop::VtableJoin<
698                        #base_const_name,
699                        #complement_const_name
700                    > = ::basic_oop::VtableJoin {
701                        a: #base_vtable(vtable.a).#base_method(f).0,
702                        b: vtable.b
703                    };
704                    #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
705                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
706                        [*const (); #methods_enum_name::VirtMethodsCount as usize]
707                    >(vtable) })
708                }
709            });
710        }
711    }
712    let mut methods_tokens = TokenStream::new();
713    for (method_index, (method_name, method_ty)) in virt_methods.iter().enumerate() {
714        let ty = actual_method_ty(method_ty.clone(), class_name);
715        let ty_without_idents = fn_ty_without_idents(ty);
716        let mut list: Punctuated<Expr, Token![,]> = Punctuated::new();
717        for i in 0 .. method_index {
718            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
719            list.push(parse_quote! { vtable.b[#index] });
720        }
721        list.push(parse_quote! { f as *const () });
722        for i in method_index + 1 .. virt_methods.len() {
723            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
724            list.push(parse_quote! { vtable.b[#index] });
725        }
726        methods_tokens.extend(quote! {
727            pub const fn #method_name(
728                self,
729                f: #ty_without_idents
730            ) -> Self {
731                let vtable = unsafe { ::basic_oop::core_mem_transmute::<
732                    [*const (); #methods_enum_name::VirtMethodsCount as usize],
733                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
734                >(self.0) };
735                let vtable: ::basic_oop::VtableJoin<
736                    #base_const_name,
737                    #complement_const_name
738                > = ::basic_oop::VtableJoin {
739                    a: vtable.a,
740                    b: [#list]
741                };
742                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
743                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
744                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
745                >(vtable) })
746            }
747        });
748    }
749    quote! {
750        #struct_
751
752        impl #vtable_name {
753            pub const fn new() -> Self {
754                let vtable: ::basic_oop::VtableJoin<
755                    #base_const_name,
756                    #complement_const_name
757                > = ::basic_oop::VtableJoin {
758                    a: #base_vtable_with_overrides.0,
759                    b: [#methods_impl]
760                };
761                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
762                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
763                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
764                >(vtable) })
765            }
766
767            #base_methods
768            #methods_tokens
769        }
770    }
771}
772
773fn build_methods(
774    base_types: &[Base],
775    class_name: &Ident,
776    class_mod: &Path,
777    non_virt_methods: &[(Ident, TypeBareFn)],
778    virt_methods: &[(Ident, TypeBareFn)]
779) -> TokenStream {
780    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
781    let mut methods_tokens = TokenStream::new();
782    for base_type in base_types {
783        let base_type_ty = sanitize_base_type(base_type.ty.clone(), class_mod);
784        let mut base_trait = base_type_ty.clone();
785        patch_path(&mut base_trait, |x| "T".to_string() + &x);
786        let mut base_trait_ext = base_type_ty.clone();
787        patch_path(&mut base_trait_ext, |x| x + "Ext");
788        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
789            let ty = actual_method_ty(method_ty.clone(), class_name);
790            let signature = method_signature(&ty, method_name.clone());
791            let mut item: ImplItemFn = parse_quote! {
792                #signature {
793                    let this: ::basic_oop::alloc_sync_Arc<dyn #base_trait>
794                        = ::basic_oop::dynamic_cast_dyn_cast_arc(self.clone()).unwrap();
795                    #base_trait_ext::#method_name(&this)
796                }
797            };
798            let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
799            for arg in ty.inputs.iter().skip(1) {
800                let mut segments = Punctuated::new();
801                segments.push(PathSegment {
802                    ident: arg.name.clone().unwrap().0,
803                    arguments: PathArguments::None,
804                });
805                call.args.push(Expr::Path(ExprPath {
806                    attrs: Vec::new(),
807                    qself: None,
808                    path: Path { leading_colon: None, segments },
809                }));
810            }
811            item.to_tokens(&mut methods_tokens);
812        }
813    }
814    for (method_name, method_ty) in non_virt_methods {
815        let ty = actual_method_ty(method_ty.clone(), class_name);
816        let signature = method_signature(&ty, method_name.clone());
817        let name = Ident::new(&(method_name.to_string() + "_impl"), Span::call_site());
818        let mut item: ImplItemFn = parse_quote! {
819            #signature {
820                #class_name::#name(self)
821            }
822        };
823        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
824        for arg in ty.inputs.into_iter().skip(1) {
825            let mut segments = Punctuated::new();
826            segments.push(PathSegment {
827                ident: arg.name.unwrap().0,
828                arguments: PathArguments::None,
829            });
830            call.args.push(Expr::Path(ExprPath {
831                attrs: Vec::new(),
832                qself: None,
833                path: Path { leading_colon: None, segments },
834            }));
835        }
836        item.to_tokens(&mut methods_tokens);
837    }
838    for (method_name, method_ty) in virt_methods {
839        let ty = actual_method_ty(method_ty.clone(), class_name);
840        let signature = method_signature(&ty, method_name.clone());
841        let ty_without_idents = fn_ty_without_idents(ty.clone());
842        let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
843        let mut item: ImplItemFn = parse_quote! {
844            #signature {
845                let vtable = ::basic_oop::TObj::obj(self.as_ref()).vtable();
846                let method = unsafe { ::basic_oop::core_mem_transmute::<*const (), #ty_without_idents>(
847                    *vtable.add(#methods_enum_name::#name as usize)
848                ) };
849                method(self)
850            }
851        };
852        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
853        for arg in ty.inputs.into_iter().skip(1) {
854            let mut segments = Punctuated::new();
855            segments.push(PathSegment {
856                ident: arg.name.unwrap().0,
857                arguments: PathArguments::None,
858            });
859            call.args.push(Expr::Path(ExprPath {
860                attrs: Vec::new(),
861                qself: None,
862                path: Path { leading_colon: None, segments },
863            }));
864        }
865        item.to_tokens(&mut methods_tokens);
866    }
867    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
868    let t = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
869    quote! {
870        impl #trait_name for ::basic_oop::alloc_sync_Arc<dyn #t> {
871            #methods_tokens
872        }
873    }
874}
875
876fn build_vtable_const(class_name: &Ident) -> TokenStream {
877    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
878    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
879    let const_name = Ident::new(&to_screaming_snake(vtable_name.to_string()), Span::call_site());
880    quote! {
881        const #const_name: [*const (); #methods_enum_name::VirtMethodsCount as usize] = #vtable_name::new().0;
882    }
883}
884
885fn build_call_trait(
886    base_types: &[Base],
887    vis: &Visibility,
888    class_name: &Ident,
889    non_virt_methods: &[(Ident, TypeBareFn)],
890    virt_methods: &[(Ident, TypeBareFn)]
891) -> TokenStream {
892    let mut methods_tokens = TokenStream::new();
893    for base_type in base_types {
894        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
895            let ty = actual_method_ty(method_ty.clone(), class_name);
896            let signature = method_signature(&ty, method_name.clone());
897            signature.to_tokens(&mut methods_tokens);
898            <Token![;]>::default().to_tokens(&mut methods_tokens);
899        }
900    }
901    for (method_name, method_ty) in non_virt_methods.iter().chain(virt_methods.iter()) {
902        let ty = actual_method_ty(method_ty.clone(), class_name);
903        let signature = method_signature(&ty, method_name.clone());
904        signature.to_tokens(&mut methods_tokens);
905        <Token![;]>::default().to_tokens(&mut methods_tokens);
906    }
907    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
908    quote! {
909        #vis trait #trait_name {
910            #methods_tokens
911        }
912    }
913}
914
915fn build(inherited_from: ItemStruct, class: ItemStruct) -> Result<TokenStream, Diagnostic> {
916    let base_types = parse_base_types(inherited_from)?;
917    let class = Class::parse(class)?;
918    let new_inherited_from = build_inherited_from(
919        &base_types, &class.name, &class.mod_, &class.non_virt_methods, &class.virt_methods
920    );
921    let struct_ = build_struct(&base_types, &class.attrs, &class.vis, &class.name, &class.mod_, &class.fields);
922    let trait_ = build_trait(&base_types, &class.vis, &class.name, &class.mod_);
923    let methods_enum = build_virt_methods_enum(
924        &base_types[0].ty, &class.vis, &class.name, &class.mod_, &class.virt_methods
925    );
926    let consts_for_vtable = build_consts_for_vtable(&class.name, &class.mod_, &base_types);
927    let vtable = build_vtable(
928        &base_types, &class.name, &class.mod_, &class.vis, &class.virt_methods, &class.overrides
929    );
930    let vtable_const = build_vtable_const(&class.name);
931    let call_trait = build_call_trait(
932        &base_types, &class.vis, &class.name, &class.non_virt_methods, &class.virt_methods
933    );
934    let methods = build_methods(
935        &base_types, &class.name, &class.mod_, &class.non_virt_methods, &class.virt_methods
936    );
937    Ok(quote! {
938        #new_inherited_from
939        #struct_
940        #trait_
941        #methods_enum
942        #consts_for_vtable
943        #vtable
944        #vtable_const
945        #call_trait
946        #methods
947    })
948}
949
950#[import_tokens_attr(::basic_oop::macro_magic)]
951#[proc_macro_attribute]
952pub fn class_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
953    let inherited_from = parse_macro_input!(attr as ItemStruct);
954    let class = parse_macro_input!(input as ItemStruct);
955    match build(inherited_from, class) {
956        Ok(tokens) => tokens.into(),
957        Err(diag) => diag.emit_as_expr_tokens().into(),
958    }
959}