basic_oop_macro/
lib.rs

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