basic_oop_macro/
lib.rs

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