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 into_method_doc = indoc!("
445        Converts to inner data.
446    ");
447    let base_type = base_types[0].ty.clone();
448    let base_field = Ident::new(
449        &to_snake(base_type.segments.last().unwrap().ident.to_string()),
450        Span::call_site()
451    );
452    let into_base = Ident::new(&("into_".to_string() + &base_field.to_string()), Span::call_site());
453    let mut base_trait = base_types[0].ty.clone();
454    patch_path(&mut base_trait, |x| "Is".to_string() + &x);
455    let trait_name = Ident::new(&("Is".to_string() + &class_name.to_string()), Span::call_site());
456    let method_name = Ident::new(&to_snake(class_name.to_string()), Span::call_site());
457    let into_method_name = Ident::new(&("into_".to_string() + &to_snake(class_name.to_string())), Span::call_site());
458    let mut trait_ = quote! {
459        #[doc=#doc]
460        #vis trait #trait_name: #base_trait {
461            #[doc=#method_doc]
462            fn #method_name(&self) -> &#class_name;
463
464            #[doc=#into_method_doc]
465            fn #into_method_name(self) -> #class_name where Self: Sized;
466        }
467
468        impl #trait_name for #class_name {
469            fn #method_name(&self) -> &#class_name { self }
470
471            fn #into_method_name(self) -> #class_name { self }
472        }
473
474        impl #base_trait for #class_name {
475            fn #base_field(&self) -> &#base_type { &self.#base_field }
476
477            fn #into_base(self) -> #base_type { self.#base_field }
478        }
479    };
480    for base_base_type in base_types.iter().skip(1) {
481        let method_name = Ident::new(
482            &to_snake(base_base_type.ty.segments.last().unwrap().ident.to_string()),
483            Span::call_site()
484        );
485        let into_name = Ident::new(&("into_".to_string() + &method_name.to_string()), Span::call_site());
486        let mut base_base_trait = base_base_type.ty.clone();
487        patch_path(&mut base_base_trait, |x| "Is".to_string() + &x);
488        let base_base_type_ty = &base_base_type.ty;
489        trait_.extend(quote! {
490            impl #base_base_trait for #class_name {
491                fn #method_name(&self) -> &#base_base_type_ty {
492                    #base_base_trait::#method_name(&self.#base_field)
493                }
494
495                fn #into_name(self) -> #base_base_type_ty {
496                    #base_base_trait::#into_name(self.#base_field)
497                }
498            }
499        });
500    }
501    let mut traits_list: Punctuated<Path, Token![,]> = Punctuated::new();
502    let mut trait_path = Path {
503        leading_colon: None,
504        segments: Punctuated::new()
505    };
506    trait_path.segments.push(PathSegment {
507        ident: trait_name,
508        arguments: PathArguments::None
509    });
510    traits_list.push(trait_path);
511    for base_type in base_types {
512        let mut base_trait = base_type.ty.clone();
513        patch_path(&mut base_trait, |x| "Is".to_string() + &x);
514        traits_list.push(base_trait);
515    }
516    trait_.extend(quote! {
517        ::basic_oop::dynamic_cast_impl_supports_interfaces!(#class_name: #traits_list);
518    });
519    trait_
520}
521
522fn build_virt_methods_enum(
523    base_type: &Path,
524    vis: &Visibility,
525    class_name: &Ident,
526    virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)]
527) -> TokenStream {
528    let doc_name = class_name.to_string();
529    let doc = formatdoc!("
530        [`{doc_name}`] virtual methods list.
531
532        Used by the [`class_unsafe`](::basic_oop::class_unsafe) macro, not intended for direct use in code.
533    ");
534    let mut base_methods_enum = base_type.clone();
535    patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
536    let methods_enum = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
537    let mut values = TokenStream::new();
538    values.append_terminated(
539        virt_methods.iter().enumerate().map(|(i, (method_name, _method_ty, _))| {
540            let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
541            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
542            quote! {
543                #name = (#base_methods_enum::VirtMethodsCount as usize) + #index
544            }
545        }),
546        <Token![,]>::default()
547    );
548    let count = virt_methods.len();
549    quote! {
550        #[doc=#doc]
551        #[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)]
552        #[repr(usize)]
553        #vis enum #methods_enum {
554            #values
555            VirtMethodsCount = (#base_methods_enum::VirtMethodsCount as usize) + #count
556        }
557    }
558}
559
560fn build_consts_for_vtable(class_name: &Ident, base_types: &[Base]) -> TokenStream {
561    let enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
562    let mut tokens = TokenStream::new();
563    for base_type in base_types {
564        let mut base_methods_enum = base_type.ty.clone();
565        patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
566        let base_const_name = Ident::new(
567            &(
568                to_screaming_snake(
569                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
570                ) + "_VIRT_METHODS_COUNT"
571            ),
572            Span::call_site()
573        );
574        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
575        tokens.extend(quote! {
576            const #base_const_name: usize = #base_methods_enum::VirtMethodsCount as usize;
577            const #complement_const_name: usize = (#enum_name::VirtMethodsCount as usize) - #base_const_name;
578        });
579    }
580    tokens
581}
582
583fn actual_base_method_ty(mut ty: TypeBareFn, base_type: &Path, 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 mut base_trait = base_type.clone();
590    patch_path(&mut base_trait, |x| "Is".to_string() + &x);
591    let this_arg = BareFnArg {
592        attrs: Vec::new(),
593        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
594        ty: parse_quote! { &#rc<dyn #base_trait> },
595    };
596    ty.inputs.insert(0, this_arg);
597    ty
598}
599
600fn actual_method_ty(mut ty: TypeBareFn, class_name: &Ident, sync: bool) -> TypeBareFn {
601    let rc = if sync {
602        quote! { ::basic_oop::alloc_sync_Arc }
603    } else {
604        quote! { ::basic_oop::alloc_rc_Rc }
605    };
606    let trait_name = Ident::new(&("Is".to_string() + &class_name.to_string()), Span::call_site());
607    let this_arg = BareFnArg {
608        attrs: Vec::new(),
609        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
610        ty: parse_quote! { &#rc<dyn #trait_name> },
611    };
612    ty.inputs.insert(0, this_arg);
613    ty
614}
615
616fn fn_ty_without_idents(mut ty: TypeBareFn) -> TypeBareFn {
617    for arg in &mut ty.inputs {
618        arg.name = None;
619    }
620    ty
621}
622
623fn bare_fn_arg_to_fn_arg(a: &BareFnArg) -> FnArg {
624    let Some((name, colon_token)) = &a.name else { panic!() }; 
625    FnArg::Typed(PatType {
626        attrs: Vec::new(),
627        pat: Box::new(Pat::Ident(PatIdent {
628            attrs: Vec::new(),
629            by_ref: None,
630            mutability: None,
631            ident: name.clone(),
632            subpat: None
633        })),
634        colon_token: *colon_token,
635        ty: Box::new(a.ty.clone()),
636    })
637}
638
639fn method_signature(ty: &TypeBareFn, name: Ident) -> Signature {
640    let generics = if let Some(lifetimes) = &ty.lifetimes {
641        Generics {
642            lt_token: Some(lifetimes.lt_token),
643            params: lifetimes.lifetimes.clone(),
644            gt_token: Some(lifetimes.gt_token),
645            where_clause: None,
646        }
647    } else {
648        Generics {
649            lt_token: None,
650            params: Punctuated::new(),
651            gt_token: None,
652            where_clause: None,
653        }
654    };
655    let mut s = Signature {
656        constness: None,
657        asyncness: None,
658        unsafety: None,
659        abi: None,
660        fn_token: ty.fn_token,
661        ident: name,
662        generics,
663        paren_token: ty.paren_token,
664        inputs: Punctuated::new(),
665        variadic: None,
666        output: ty.output.clone(),
667    };
668    let mut segments = Punctuated::new();
669    segments.push(PathSegment {
670        ident: Ident::new("Self", Span::call_site()),
671        arguments: PathArguments::None
672    });
673    s.inputs.push(FnArg::Receiver(Receiver {
674        attrs: Vec::new(),
675        reference: Some((<Token![&]>::default(), None)),
676        mutability: None,
677        self_token: <Token![self]>::default(),
678        colon_token: None,
679        ty: Box::new(Type::Reference(TypeReference {
680            and_token: <Token![&]>::default(),
681            lifetime: None,
682            mutability: None,
683            elem: Box::new(Type::Path(TypePath {
684                qself: None,
685                path: Path {
686                    leading_colon: None,
687                    segments
688                }
689            }))
690        }))
691    }));
692    for arg in ty.inputs.iter().skip(1) {
693        s.inputs.push(bare_fn_arg_to_fn_arg(arg));
694    }
695    s
696}
697
698fn impl_method_signature(ty: &TypeBareFn, name: Ident) -> Signature {
699    let generics = if let Some(lifetimes) = &ty.lifetimes {
700        Generics {
701            lt_token: Some(lifetimes.lt_token),
702            params: lifetimes.lifetimes.clone(),
703            gt_token: Some(lifetimes.gt_token),
704            where_clause: None,
705        }
706    } else {
707        Generics {
708            lt_token: None,
709            params: Punctuated::new(),
710            gt_token: None,
711            where_clause: None,
712        }
713    };
714    let mut s = Signature {
715        constness: None,
716        asyncness: None,
717        unsafety: None,
718        abi: None,
719        fn_token: ty.fn_token,
720        ident: name,
721        generics,
722        paren_token: ty.paren_token,
723        inputs: Punctuated::new(),
724        variadic: None,
725        output: ty.output.clone(),
726    };
727    for arg in ty.inputs.iter() {
728        s.inputs.push(bare_fn_arg_to_fn_arg(arg));
729    }
730    s
731}
732
733fn build_vtable(
734    base_types: &[Base],
735    class_name: &Ident,
736    sync: bool,
737    vis: &Visibility,
738    virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)],
739    overrides: &[Ident],
740) -> TokenStream {
741    let doc_name = class_name.to_string();
742    let doc = formatdoc!("
743        [`{doc_name}`] virtual methods table.
744
745        Used by the [`class_unsafe`](::basic_oop::class_unsafe) macro, not intended for direct use in code.
746    ");
747    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
748    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
749    let struct_ = quote! {
750        #[doc=#doc]
751        #vis struct #vtable_name(pub [*const (); #methods_enum_name::VirtMethodsCount as usize]);
752    };
753    let mut methods_impl = TokenStream::new();
754    methods_impl.append_separated(virt_methods.iter().map(|(m, ty, _)| {
755        let ty = actual_method_ty(ty.clone(), class_name, sync);
756        let ty_without_idents = fn_ty_without_idents(ty);
757        let impl_name = Ident::new(&(m.to_string() + "_impl"), Span::call_site());
758        quote! { { let f: #ty_without_idents = #class_name::#impl_name; f as *const () } }
759    }), <Token![,]>::default());
760    let mut base_vtable = base_types[0].ty.clone();
761    patch_path(&mut base_vtable, |x| x + "Vtable");
762    let base_vtable_new: Expr = parse_quote! { #base_vtable::new() };
763    let mut base_vtable_with_overrides = base_vtable_new;
764    for override_name in overrides {
765        let impl_name = Ident::new(&(override_name.to_string() + "_impl"), Span::call_site());
766        base_vtable_with_overrides = parse_quote! {
767            #base_vtable_with_overrides.#override_name(#class_name::#impl_name)
768        };
769    }
770    let base_const_name = Ident::new(
771        &(
772            to_screaming_snake(
773                class_name.to_string() + &base_types[0].ty.segments.last().unwrap().ident.to_string()
774            ) + "_VIRT_METHODS_COUNT"
775        ),
776        Span::call_site()
777    );
778    let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
779    let mut base_methods = TokenStream::new();
780    for base_type in base_types {
781        let mut base_vtable = base_type.ty.clone();
782        patch_path(&mut base_vtable, |x| x + "Vtable");
783        let base_const_name = Ident::new(
784            &(
785                to_screaming_snake(
786                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
787                ) + "_VIRT_METHODS_COUNT"
788            ),
789            Span::call_site()
790        );
791        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
792        for (base_method, base_method_ty, _) in &base_type.virt_methods {
793            let ty = actual_base_method_ty(base_method_ty.clone(), &base_type.ty, sync);
794            let ty_without_idents = fn_ty_without_idents(ty);
795            base_methods.extend(quote! {
796                pub const fn #base_method(
797                    self,
798                    f: #ty_without_idents
799                ) -> Self {
800                    let vtable = unsafe { ::basic_oop::core_mem_transmute::<
801                        [*const (); #methods_enum_name::VirtMethodsCount as usize],
802                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
803                    >(self.0) };
804                    let vtable: ::basic_oop::VtableJoin<
805                        #base_const_name,
806                        #complement_const_name
807                    > = ::basic_oop::VtableJoin {
808                        a: #base_vtable(vtable.a).#base_method(f).0,
809                        b: vtable.b
810                    };
811                    #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
812                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
813                        [*const (); #methods_enum_name::VirtMethodsCount as usize]
814                    >(vtable) })
815                }
816            });
817        }
818    }
819    let mut methods_tokens = TokenStream::new();
820    for (method_index, (method_name, method_ty, _)) in virt_methods.iter().enumerate() {
821        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
822        let ty_without_idents = fn_ty_without_idents(ty);
823        let mut list: Punctuated<Expr, Token![,]> = Punctuated::new();
824        for i in 0 .. method_index {
825            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
826            list.push(parse_quote! { vtable.b[#index] });
827        }
828        list.push(parse_quote! { f as *const () });
829        for i in method_index + 1 .. virt_methods.len() {
830            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
831            list.push(parse_quote! { vtable.b[#index] });
832        }
833        methods_tokens.extend(quote! {
834            pub const fn #method_name(
835                self,
836                f: #ty_without_idents
837            ) -> Self {
838                let vtable = unsafe { ::basic_oop::core_mem_transmute::<
839                    [*const (); #methods_enum_name::VirtMethodsCount as usize],
840                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
841                >(self.0) };
842                let vtable: ::basic_oop::VtableJoin<
843                    #base_const_name,
844                    #complement_const_name
845                > = ::basic_oop::VtableJoin {
846                    a: vtable.a,
847                    b: [#list]
848                };
849                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
850                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
851                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
852                >(vtable) })
853            }
854        });
855    }
856    quote! {
857        #struct_
858
859        impl #vtable_name {
860            pub const fn new() -> Self {
861                let vtable: ::basic_oop::VtableJoin<
862                    #base_const_name,
863                    #complement_const_name
864                > = ::basic_oop::VtableJoin {
865                    a: #base_vtable_with_overrides.0,
866                    b: [#methods_impl]
867                };
868                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
869                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
870                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
871                >(vtable) })
872            }
873
874            #base_methods
875            #methods_tokens
876        }
877    }
878}
879
880fn build_methods(
881    base_types: &[Base],
882    class_name: &Ident,
883    sync: bool,
884    non_virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)],
885    virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)]
886) -> TokenStream {
887    if 
888           !base_types.iter().any(|x| x.non_virt_methods.iter().chain(x.virt_methods.iter()).next().is_some())
889        && non_virt_methods.is_empty()
890        && virt_methods.is_empty()
891    {
892        return TokenStream::new();
893    }
894    let (rc, cast) = if sync {
895        (quote! { ::basic_oop::alloc_sync_Arc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_arc })
896    } else {
897        (quote! { ::basic_oop::alloc_rc_Rc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_rc })
898    };
899    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
900    let mut methods_tokens = TokenStream::new();
901    for base_type in base_types {
902        let base_type_ty = base_type.ty.clone();
903        let mut base_trait = base_type_ty.clone();
904        patch_path(&mut base_trait, |x| "Is".to_string() + &x);
905        let mut base_trait_ext = base_type_ty.clone();
906        patch_path(&mut base_trait_ext, |x| x + "Ext");
907        for (method_name, method_ty, _) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
908            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
909            let signature = method_signature(&ty, method_name.clone());
910            let mut item: ImplItemFn = parse_quote! {
911                #signature {
912                    let this: #rc<dyn #base_trait> = #cast(self.clone()).unwrap();
913                    #base_trait_ext::#method_name(&this)
914                }
915            };
916            let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
917            for arg in ty.inputs.iter().skip(1) {
918                let mut segments = Punctuated::new();
919                segments.push(PathSegment {
920                    ident: arg.name.clone().unwrap().0,
921                    arguments: PathArguments::None,
922                });
923                call.args.push(Expr::Path(ExprPath {
924                    attrs: Vec::new(),
925                    qself: None,
926                    path: Path { leading_colon: None, segments },
927                }));
928            }
929            item.to_tokens(&mut methods_tokens);
930        }
931    }
932    for (method_name, method_ty, _) in non_virt_methods {
933        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
934        let signature = method_signature(&ty, method_name.clone());
935        let name = Ident::new(&(method_name.to_string() + "_impl"), Span::call_site());
936        let mut item: ImplItemFn = parse_quote! {
937            #signature {
938                #class_name::#name(self)
939            }
940        };
941        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
942        for arg in ty.inputs.into_iter().skip(1) {
943            let mut segments = Punctuated::new();
944            segments.push(PathSegment {
945                ident: arg.name.unwrap().0,
946                arguments: PathArguments::None,
947            });
948            call.args.push(Expr::Path(ExprPath {
949                attrs: Vec::new(),
950                qself: None,
951                path: Path { leading_colon: None, segments },
952            }));
953        }
954        item.to_tokens(&mut methods_tokens);
955    }
956    for (method_name, method_ty, _) in virt_methods {
957        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
958        let signature = method_signature(&ty, method_name.clone());
959        let ty_without_idents = fn_ty_without_idents(ty.clone());
960        let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
961        let mut item: ImplItemFn = if sync {
962            parse_quote! {
963                #signature {
964                    let vtable = ::basic_oop::obj_sync::IsObjSync::obj_sync(self.as_ref()).vtable();
965                    let method = unsafe { ::basic_oop::core_mem_transmute::<*const (), #ty_without_idents>(
966                        *vtable.add(#methods_enum_name::#name as usize)
967                    ) };
968                    method(self)
969                }
970            }
971        } else {
972            parse_quote! {
973                #signature {
974                    let vtable = ::basic_oop::obj::IsObj::obj(self.as_ref()).vtable();
975                    let method = unsafe { ::basic_oop::core_mem_transmute::<*const (), #ty_without_idents>(
976                        *vtable.add(#methods_enum_name::#name as usize)
977                    ) };
978                    method(self)
979                }
980            }
981        };
982        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
983        for arg in ty.inputs.into_iter().skip(1) {
984            let mut segments = Punctuated::new();
985            segments.push(PathSegment {
986                ident: arg.name.unwrap().0,
987                arguments: PathArguments::None,
988            });
989            call.args.push(Expr::Path(ExprPath {
990                attrs: Vec::new(),
991                qself: None,
992                path: Path { leading_colon: None, segments },
993            }));
994        }
995        item.to_tokens(&mut methods_tokens);
996    }
997    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
998    let t = Ident::new(&("Is".to_string() + &class_name.to_string()), Span::call_site());
999    quote! {
1000        impl #trait_name for #rc<dyn #t> {
1001            #methods_tokens
1002        }
1003    }
1004}
1005
1006fn build_vtable_const(class_name: &Ident) -> TokenStream {
1007    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
1008    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
1009    let const_name = Ident::new(&to_screaming_snake(vtable_name.to_string()), Span::call_site());
1010    quote! {
1011        const #const_name: [*const (); #methods_enum_name::VirtMethodsCount as usize] = #vtable_name::new().0;
1012    }
1013}
1014
1015fn build_call_trait(
1016    base_types: &[Base],
1017    vis: &Visibility,
1018    class_name: &Ident,
1019    sync: bool,
1020    non_virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)],
1021    virt_methods: &[(Ident, TypeBareFn, Vec<Attribute>)]
1022) -> TokenStream {
1023    if 
1024           !base_types.iter().any(|x| x.non_virt_methods.iter().chain(x.virt_methods.iter()).next().is_some())
1025        && non_virt_methods.is_empty()
1026        && virt_methods.is_empty()
1027    {
1028        return TokenStream::new();
1029    }
1030    let doc_name = class_name.to_string();
1031    let rc = if sync { "Arc" } else { "Rc" };
1032    let doc = formatdoc!("
1033        [`{doc_name}`] methods extension trait.
1034
1035        Implemented by the `{rc}<{doc_name}>` type for convenient method calling syntax.
1036    ");
1037    let mut methods_tokens = TokenStream::new();
1038    for base_type in base_types {
1039        for
1040            (method_name, method_ty, method_attrs)
1041        in
1042            base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter())
1043        {
1044            for attr in method_attrs {
1045                attr.to_tokens(&mut methods_tokens);
1046            }
1047            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
1048            let signature = method_signature(&ty, method_name.clone());
1049            signature.to_tokens(&mut methods_tokens);
1050            <Token![;]>::default().to_tokens(&mut methods_tokens);
1051        }
1052    }
1053    for (method_name, method_ty, method_attrs) in non_virt_methods.iter().chain(virt_methods.iter()) {
1054        for attr in method_attrs {
1055            attr.to_tokens(&mut methods_tokens);
1056        }
1057        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
1058        let signature = method_signature(&ty, method_name.clone());
1059        signature.to_tokens(&mut methods_tokens);
1060        <Token![;]>::default().to_tokens(&mut methods_tokens);
1061    }
1062    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
1063    quote! {
1064        #[doc=#doc]
1065        #vis trait #trait_name {
1066            #methods_tokens
1067        }
1068    }
1069}
1070
1071fn build_not_overrided_virt_methods(
1072    base_types: &[Base],
1073    vis: &Visibility,
1074    class_name: &Ident,
1075    sync: bool,
1076    overrides: &[Ident],
1077) -> TokenStream {
1078    if !base_types.iter().any(|x|
1079        x.virt_methods.iter().any(|m| !overrides.iter().any(|x| *x == m.0.to_string().as_str()))
1080    ) {
1081        return TokenStream::new();
1082    }
1083    let mut methods_tokens = TokenStream::new();
1084    let base_type_ty = &base_types[0].ty;
1085    for base_type in base_types {
1086        for (method_name, method_ty, _) in &base_type.virt_methods {
1087            if overrides.iter().any(|x| *x == method_name.to_string().as_str()) { continue; }
1088            let impl_method_name = Ident::new(&(method_name.to_string() + "_impl"), Span::call_site());
1089            let ty = actual_method_ty(method_ty.clone(), &base_type.ty.segments.last().unwrap().ident, sync);
1090            let signature = impl_method_signature(&ty, impl_method_name.clone());
1091            let mut item: ImplItemFn = parse_quote! {
1092                #vis #signature {
1093                    #base_type_ty::#impl_method_name(this)
1094                }
1095            };
1096            let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
1097            for arg in ty.inputs.iter().skip(1) {
1098                let mut segments = Punctuated::new();
1099                segments.push(PathSegment {
1100                    ident: arg.name.clone().unwrap().0,
1101                    arguments: PathArguments::None,
1102                });
1103                call.args.push(Expr::Path(ExprPath {
1104                    attrs: Vec::new(),
1105                    qself: None,
1106                    path: Path { leading_colon: None, segments },
1107                }));
1108            }
1109            item.to_tokens(&mut methods_tokens);
1110        }
1111    }
1112    quote! {
1113        impl #class_name {
1114            #methods_tokens
1115        }
1116    }
1117}
1118
1119fn build(inherits: ItemStruct, class: ItemStruct) -> Result<TokenStream, Diagnostic> {
1120    let Some(sync) = parse_base_sync(&inherits) else {
1121        return Err(inherits.span().error("Invalid base class"));
1122    };
1123    let base_types = parse_base_types(inherits)?;
1124    let class = Class::parse(class)?;
1125    let new_inherits = build_inherits(
1126        &base_types, &class.name, sync, &class.non_virt_methods, &class.virt_methods
1127    );
1128    let struct_ = build_struct(&base_types, &class.attrs, &class.vis, &class.name, &class.fields);
1129    let trait_ = build_trait(&base_types, &class.vis, &class.name, sync);
1130    let methods_enum = build_virt_methods_enum(
1131        &base_types[0].ty, &class.vis, &class.name, &class.virt_methods
1132    );
1133    let consts_for_vtable = build_consts_for_vtable(&class.name, &base_types);
1134    let vtable = build_vtable(
1135        &base_types, &class.name, sync, &class.vis, &class.virt_methods, &class.overrides
1136    );
1137    let vtable_const = build_vtable_const(&class.name);
1138    let call_trait = build_call_trait(
1139        &base_types, &class.vis, &class.name, sync, &class.non_virt_methods, &class.virt_methods
1140    );
1141    let methods = build_methods(
1142        &base_types, &class.name, sync, &class.non_virt_methods, &class.virt_methods
1143    );
1144    let not_overrided_methods = build_not_overrided_virt_methods(
1145        &base_types, &class.vis, &class.name, sync, &class.overrides
1146    );
1147    Ok(quote! {
1148        #new_inherits
1149        #struct_
1150        #trait_
1151        #methods_enum
1152        #consts_for_vtable
1153        #vtable
1154        #vtable_const
1155        #call_trait
1156        #methods
1157        #not_overrided_methods
1158    })
1159}
1160
1161#[import_tokens_attr(::basic_oop::macro_magic)]
1162#[proc_macro_attribute]
1163pub fn class_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1164    let inherits = parse_macro_input!(attr as ItemStruct);
1165    let class = parse_macro_input!(input as ItemStruct);
1166    match build(inherits, class) {
1167        Ok(tokens) => tokens.into(),
1168        Err(diag) => diag.emit_as_expr_tokens().into(),
1169    }
1170}