Skip to main content

savefile_derive/
lib.rs

1#![recursion_limit = "128"]
2#![allow(warnings)]
3#![allow(clippy::needless_borrowed_reference)]
4#![allow(clippy::match_like_matches_macro)]
5#![allow(clippy::bool_assert_comparison)]
6#![allow(clippy::bool_comparison)]
7#![allow(clippy::match_ref_pats)] // This one I'd really like to clean up some day
8#![allow(clippy::needless_late_init)]
9#![allow(clippy::len_zero)]
10#![allow(clippy::let_and_return)]
11#![allow(clippy::collapsible_match)]
12#![allow(clippy::single_match)]
13
14//! This crate allows automatic derivation of the Savefile-traits: Serialize, Deserialize, WithSchema, Packed and Introspect .
15//! The documentation for this is found in the Savefile crate documentation.
16
17extern crate proc_macro;
18extern crate proc_macro2;
19#[macro_use]
20extern crate quote;
21extern crate syn;
22#[macro_use]
23
24use crate::savefile_abi::is_well_known;
25use common::{
26    check_is_remove, compile_time_check_reprc, compile_time_size, get_extra_where_clauses, parse_attr_tag,
27    path_to_string, FieldInfo,
28};
29use proc_macro2::TokenStream;
30use proc_macro2::{Span, TokenTree};
31use quote::ToTokens;
32use std::collections::{HashMap, HashSet};
33#[allow(unused_imports)]
34use std::iter::IntoIterator;
35use syn::__private::bool;
36use syn::parse::Parse;
37use syn::spanned::Spanned;
38use syn::token::Paren;
39use syn::Type::Tuple;
40use syn::{
41    Attribute, Data, DeriveInput, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index,
42    ItemTrait, Lifetime, Pat, PathArguments, ReturnType, TraitItem, Type, TypeGenerics, TypeParamBound, TypeTuple,
43    WherePredicate,
44};
45
46pub(crate) fn doc_hidden(x: &Vec<Attribute>) -> TokenStream {
47    for attr in x {
48        if attr.path().is_ident("savefile_doc_hidden") {
49            return quote! {#[doc(hidden)]};
50        }
51    }
52    quote!()
53}
54macro_rules! abort {
55    ($span:expr, $($x:tt)*) => {
56        return Err(syn::Error::new($span, std::format!($($x)*)))
57    }
58}
59macro_rules! abort_call_site {
60    ($($x:tt)*) => {
61        return Err(syn::Error::new($crate::proc_macro2::Span::call_site(), std::format!($($x)*)))
62    }
63}
64fn implement_fields_serialize(
65    field_infos: Vec<FieldInfo>,
66    implicit_self: bool,
67    index: bool,
68) -> syn::Result<(TokenStream, Vec<TokenStream>)> {
69    let mut output = Vec::new();
70
71    let defspan = proc_macro2::Span::call_site();
72    let span = proc_macro2::Span::call_site();
73    let local_serializer = quote_spanned! { defspan => local_serializer};
74
75    let reprc = quote! {
76        _savefile::prelude::Packed
77    };
78
79    let mut deferred_reprc: Option<(usize /*align*/, Vec<TokenStream>)> = None;
80    fn realize_any_deferred(
81        local_serializer: &TokenStream,
82        deferred_reprc: &mut Option<(usize, Vec<TokenStream>)>,
83        output: &mut Vec<TokenStream>,
84    ) {
85        let local_serializer: TokenStream = local_serializer.clone();
86        if let Some((_align, deferred)) = deferred_reprc.take() {
87            assert_eq!(deferred.is_empty(), false);
88            let mut conditions = vec![];
89            for item in deferred.windows(2) {
90                let a = item[0].clone();
91                let b = item[1].clone();
92                if conditions.is_empty() == false {
93                    conditions.push(quote!(&&));
94                }
95                conditions.push(quote!(
96                    std::ptr::addr_of!(#a).add(1) as *const u8 == std::ptr::addr_of!(#b) as *const u8
97                ));
98            }
99            if conditions.is_empty() {
100                conditions.push(quote!(true));
101            }
102            let mut fallbacks = vec![];
103            for item in deferred.iter() {
104                fallbacks.push(quote!(
105                <_ as _savefile::prelude::Serialize>::serialize(&#item, #local_serializer)?;
106                ));
107            }
108            if deferred.len() == 1 {
109                return output.push(quote!( #(#fallbacks)* ));
110            }
111            let mut iter = deferred.into_iter();
112            let deferred_from = iter.next().expect("expected deferred_from");
113            let deferred_to = iter.last().unwrap_or(deferred_from.clone());
114
115            output.push(
116                quote!(
117                    unsafe {
118                        if #(#conditions)* {
119                         #local_serializer.raw_write_region(self,&#deferred_from,&#deferred_to, local_serializer.file_version)?;
120                        } else {
121                            #(#fallbacks)*
122                        }
123                    }
124            ));
125        }
126    }
127
128    let get_obj_id = |field: &FieldInfo| -> TokenStream {
129        let objid = if index {
130            assert!(implicit_self);
131            let id = syn::Index {
132                index: field.index,
133                span,
134            };
135            quote! { self.#id}
136        } else {
137            let id = field.ident.clone().expect("Expected identifier[3]");
138            if implicit_self {
139                quote! { self.#id}
140            } else {
141                quote! { *#id}
142            }
143        };
144        objid
145    };
146
147    let mut first_non_ignored_fieldinfo = None;
148    let mut last_non_ignored_fieldinfo = None;
149    for field in &field_infos {
150        {
151            let verinfo = parse_attr_tag(field.attrs)?;
152
153            if verinfo.ignore {
154                continue;
155            }
156            if first_non_ignored_fieldinfo.is_none() {
157                first_non_ignored_fieldinfo = Some(field);
158            }
159            last_non_ignored_fieldinfo = Some(field);
160
161            let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
162
163            let removed = check_is_remove(field.ty);
164
165            let type_size_align = compile_time_size(field.ty);
166            let compile_time_reprc = compile_time_check_reprc(field.ty) && type_size_align.is_some();
167
168            let obj_id = get_obj_id(field);
169
170            if field_from_version == 0 && field_to_version == std::u32::MAX {
171                if removed.is_removed() {
172                    abort!(
173                        field.ty.span(),
174                        "The Removed type can only be used for removed fields. Use the savefile_versions attribute."
175                    );
176                }
177
178                if compile_time_reprc {
179                    let (_cursize, curalign) = type_size_align.expect("type_size_align");
180                    if let Some((deferred_align, deferred_items)) = &mut deferred_reprc {
181                        if *deferred_align == curalign {
182                            deferred_items.push(obj_id);
183                            continue;
184                        }
185                    } else {
186                        deferred_reprc = Some((curalign, vec![obj_id]));
187                        continue;
188                    }
189                }
190                realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
191
192                output.push(quote!(
193                <_ as _savefile::prelude::Serialize>::serialize(&#obj_id, #local_serializer)?;
194                ));
195            } else {
196                realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
197
198                output.push(quote!(
199                if #local_serializer.file_version >= #field_from_version && #local_serializer.file_version <= #field_to_version {
200                    <_ as _savefile::prelude::Serialize>::serialize(&#obj_id, #local_serializer)?;
201                }));
202            }
203        }
204    }
205    realize_any_deferred(&local_serializer, &mut deferred_reprc, &mut output);
206
207    //let contents = format!("//{:?}",output);
208
209    let total_reprc_opt: TokenStream;
210    if let (Some(first), Some(last)) = (first_non_ignored_fieldinfo, last_non_ignored_fieldinfo) {
211        let first_field = get_obj_id(first);
212        let last_field = get_obj_id(last);
213        total_reprc_opt = quote!( unsafe { #local_serializer.raw_write_region(self,&#first_field, &#last_field, local_serializer.file_version)?; } );
214    } else {
215        total_reprc_opt = quote!();
216    }
217
218    let serialize2 = quote! {
219        let local_serializer = serializer;
220
221        if unsafe { <Self as #reprc>::repr_c_optimization_safe(local_serializer.file_version).is_yes() } {
222            #total_reprc_opt
223        } else {
224            #(#output)*
225        }
226    };
227
228    let fields_names = field_infos
229        .iter()
230        .map(|field| {
231            let fieldname = field.ident.clone();
232            quote! { #fieldname }
233        })
234        .collect();
235    Ok((serialize2, fields_names))
236}
237
238pub(crate) mod common;
239
240mod serialize;
241
242mod deserialize;
243
244mod savefile_abi;
245
246#[proc_macro_attribute]
247pub fn savefile_abi_exportable(
248    attr: proc_macro::TokenStream,
249    input: proc_macro::TokenStream,
250) -> proc_macro::TokenStream {
251    match savefile_abi_exportable_impl(attr, input) {
252        Ok(output) => output,
253        Err(err) => err.to_compile_error().into()
254    }
255}
256
257fn savefile_abi_exportable_impl(
258    attr: proc_macro::TokenStream,
259    input: proc_macro::TokenStream,
260) -> syn::Result<proc_macro::TokenStream> {
261    let parsed: ItemTrait = syn::parse(input.clone()).expect("Expected valid rust-code");
262
263    let mut version: Option<u32> = None;
264    let atstr : String = attr.to_string();
265    for item in atstr.split(',') {
266        let keyvals: Vec<_> = item.split('=').collect();
267        if keyvals.len() != 2 {
268            abort!(
269                item.span(),
270                "savefile_abi_exportable arguments should be of form #[savefile_abi_exportable(version=0)], not '{}'",
271                attr
272            );
273        }
274        let key = keyvals[0].trim();
275        let val = keyvals[1].trim();
276        match key {
277            "version" => {
278                if version.is_some() {
279                    abort!(item.span(), "version specified more than once");
280                }
281                version = Some(match val.parse() {
282                    Ok(v) => v,
283                    Err(_) => abort!(item.span(), "Version must be numeric, but was: {}", val),
284                });
285            }
286            _ => abort!(item.span(), "Unknown savefile_abi_exportable key: '{}'", key),
287        }
288    }
289
290    for attr in &parsed.attrs {
291        let name_segs: Vec<_> = attr.path().segments.iter().map(|x| &x.ident).collect();
292        if name_segs == ["async_trait"] || name_segs == ["async_trait", "async_trait"] {
293            abort!(attr.path().segments.span(), "async_trait-attribute macro detected. The {} macro must go _before_ the #[savefile_abi_exportable(..)] macro!",
294            attr.to_token_stream());
295        }
296    }
297    let version: u32 = version.unwrap_or(0);
298
299    let trait_name_str = parsed.ident.to_string();
300    let trait_name = parsed.ident;
301    let defspan = proc_macro2::Span::mixed_site();
302    let uses = quote_spanned! { defspan =>
303        extern crate savefile;
304        extern crate savefile_abi;
305        extern crate savefile_derive;
306        use savefile::prelude::{Packed, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, get_schema, get_result_schema, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,ReceiverType,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition};
307        use savefile_abi::{parse_return_value_impl,abi_result_receiver,abi_boxed_trait_receiver, FlexBuffer, AbiExportable, TraitObject, PackagedTraitObject, Owning, AbiErrorMsg, RawAbiCallResult, AbiConnection, AbiConnectionMethod, AbiProtocol, abi_entry_light, AbiWaker};
308        use std::collections::HashMap;
309        use std::mem::MaybeUninit;
310        use std::io::Cursor;
311        use std::pin::Pin;
312        use std::marker::Unpin;
313        use std::future::Future;
314        use std::task::{Waker, Poll, Context};
315        use std::sync::Arc;
316        use savefile_derive::savefile_abi_exportable;
317    };
318
319    let mut method_metadata: Vec<TokenStream> = vec![];
320    let mut callee_method_trampoline: Vec<TokenStream> = vec![];
321    let mut caller_method_trampoline = vec![];
322    let mut extra_definitions = HashMap::new();
323
324    if parsed.generics.params.is_empty() == false {
325        abort!(
326            parsed.generics.params.span(),
327            "Savefile does not support generic traits."
328        );
329    }
330    let mut send = false;
331    let mut sync = false;
332    for supertrait in parsed.supertraits.iter() {
333        match supertrait {
334            TypeParamBound::Trait(trait_bound) => {
335                if let Some(lif) = &trait_bound.lifetimes {
336                    abort!(lif.span(), "Savefile does not support lifetimes");
337                }
338                if let Some(seg) = trait_bound.path.segments.last() {
339                    let id = seg.ident.to_string();
340                    match id.as_str() {
341                        "Copy" => abort!(seg.span(), "Savefile does not support Copy bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be copy."),
342                        "Clone" => abort!(seg.span(), "Savefile does not support Clone bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper can't be clone."),
343                        /* these are ok, the wrappers actually do implement these*/
344                        "Sync" => { sync = true;}
345                        "Send" => { send = true;}
346                        "Sized" => {}
347                        "Debug" => {}
348                        _ => abort!(seg.span(), "Savefile does not support bounds for traits. The reason is savefile-abi needs to generate a wrapper, and this wrapper doesn't know how to implement arbitrary bounds."),
349                    }
350                }
351            }
352            TypeParamBound::Lifetime(lif) => {
353                if lif.ident != "static" {
354                    abort!(lif.span(), "Savefile does not support lifetimes");
355                }
356            }
357            TypeParamBound::PreciseCapture(c) => {
358                abort!(c.span(), "Savefile does not support precise captures");
359            }
360            TypeParamBound::Verbatim(v) => {
361                abort!(v.span(), "Savefile does not support verbatim bounds");
362            }
363            x => {
364                abort!(x.span(), "Savefile does not support this syntax");
365            }
366        }
367    }
368
369    if parsed.generics.where_clause.is_some() {
370        abort!(
371            parsed.generics.where_clause.span(),
372            "Savefile does not support where-clauses for traits"
373        );
374    }
375
376    for (method_number, item) in parsed.items.iter().enumerate() {
377        if method_number > u16::MAX.into() {
378            abort!(item.span(), "Savefile only supports 2^16 methods per interface. Sorry.");
379        }
380        let method_number = method_number as u16;
381
382        match item {
383            TraitItem::Const(c) => {
384                abort!(
385                    c.span(),
386                    "savefile_abi_exportable does not support associated consts: {}",
387                    c.ident
388                );
389            }
390            TraitItem::Fn(method) => {
391                let mut is_ok = true;
392                let mut async_trait_life_time = 0;
393                let mut life0_life_time = 0;
394                if let Some(wher) = &method.sig.generics.where_clause {
395                    for w in wher.predicates.iter() {
396                        match w {
397                            WherePredicate::Type(t) => {
398                                match &t.bounded_ty {
399                                    Type::Path(p) => {
400                                        if p.path.segments.len() == 1 {
401                                            if p.path.segments[0].ident != "Self" {
402                                                is_ok = false;
403                                            }
404                                        } else {
405                                            is_ok = false;
406                                        }
407                                    }
408                                    _ => {
409                                        is_ok = false;
410                                        break;
411                                    }
412                                }
413                                if let Some(l) = &t.lifetimes {
414                                    is_ok = false;
415                                }
416                                for bound in &t.bounds {
417                                    match bound {
418                                        TypeParamBound::Trait(t) => {
419                                            if t.path.segments.len() == 1 {
420                                                if t.path.segments[0].ident != "Sync" {
421                                                    is_ok = false;
422                                                }
423                                            } else {
424                                                is_ok = false;
425                                            }
426                                        }
427                                        TypeParamBound::Lifetime(l) => {
428                                            if l.ident != "async_trait" {
429                                                is_ok = false;
430                                            }
431                                        }
432                                        x => {
433                                            abort!(x.span(), "Savefile does not support this syntax",);
434                                        }
435                                    }
436                                }
437                            }
438                            WherePredicate::Lifetime(l) => {
439                                if l.lifetime.ident != "life0" {
440                                    if !is_life(&l.lifetime) {
441                                        is_ok = false;
442                                    }
443                                } else {
444                                    life0_life_time += 1;
445                                }
446                                for bound in &l.bounds {
447                                    if bound.ident != "async_trait" {
448                                        is_ok = false;
449                                    } else {
450                                        async_trait_life_time += 1;
451                                    }
452                                }
453                            }
454                            x => {
455                                abort!(x.span(), "Savefile does not support this syntax");
456                            }
457                        }
458                    }
459                    if !is_ok {
460                        abort!(
461                            method.sig.generics.where_clause.span(),
462                            "Savefile does not support where-clauses for methods"
463                        );
464                    }
465                }
466
467                let method_name = method.sig.ident.clone();
468
469                let mut current_name_index = 0u32;
470                let name_baseplate = format!("Temp{}_{}", trait_name_str, method_name);
471                let mut temp_name_generator = move || {
472                    current_name_index += 1;
473                    format!("{}_{}", name_baseplate, current_name_index)
474                };
475                let mut receiver_is_mut = false;
476                let mut receiver_is_pin = false;
477                let ret_type: Type;
478                let ret_declaration;
479                let no_return;
480
481                match &method.sig.output {
482                    ReturnType::Default => {
483                        ret_type = Tuple(TypeTuple {
484                            paren_token: Paren::default(),
485                            elems: Default::default(),
486                        });
487                        ret_declaration = quote! {};
488                        no_return = true;
489                    }
490                    ReturnType::Type(_, ty) => {
491                        ret_type = (**ty).clone();
492                        match &**ty {
493                            Type::Tuple(tup) if tup.elems.is_empty() => {
494                                ret_declaration = quote! {};
495                                no_return = true;
496                            }
497                            _ => {
498                                ret_declaration = quote! { -> #ret_type };
499                                no_return = false;
500                            }
501                        }
502                    }
503                }
504
505                let Some(self_arg) = method.sig.inputs.iter().next() else {
506                    abort!(
507                        method.span(),
508                        "Method '{}' has no arguments. This is not supported by savefile-abi - it must at least have a self-argument.",
509                        method_name
510                    );
511                };
512                let unsupported = || {
513                    abort!(
514                                        method.sig.span(),
515                                        "Method '{}' has an unsupported 'self'-parameter. Try '&self', '&mut self', or 'self: Pin<&mut Self>'. Not supported: {}",
516                                        method_name, self_arg.to_token_stream()
517                                    );
518                };
519
520                let mut parse_receiver_ty = |typ: &Type| -> syn::Result<()> {
521                    if let Type::Path(path) = typ {
522                        if !is_well_known(&path.path.segments, ["std", "pin", "Pin"]) {
523                            return unsupported();
524                        }
525                        let seg = &path.path.segments.last().unwrap();
526                        let PathArguments::AngleBracketed(args) = &seg.arguments else {
527                            return unsupported();
528                            unreachable!();
529                        };
530                        if args.args.len() != 1 {
531                            return unsupported();
532                        }
533                        let arg = &args.args[0];
534                        let GenericArgument::Type(Type::Reference(typref)) = arg else {
535                            return unsupported();
536                        };
537                        if typref.mutability.is_none() {
538                            abort!(
539                                            method.sig.span(),
540                                            "Method '{}' has an unsupported 'self'-parameter. Non-mutable references in Pin are presently not supported: {}",
541                                            method_name, self_arg.to_token_stream()
542                                        );
543                        }
544                        let Type::Path(typepath) = &*typref.elem else {
545                            return unsupported();
546                        };
547                        if typepath.path.segments.len() != 1 {
548                            return unsupported();
549                        };
550                        if typepath.path.segments[0].ident != "Self" {
551                            return unsupported();
552                        }
553                        receiver_is_mut = true;
554                        receiver_is_pin = true;
555                    } else {
556                        return unsupported();
557                    }
558                    Ok(())
559                };
560                match self_arg {
561                    FnArg::Receiver(recv) => {
562                        if recv.colon_token.is_some() {
563                            parse_receiver_ty(&*recv.ty)?;
564                        } else {
565                            if let Some(reference) = &recv.reference {
566                                if let Some(reference) = &reference.1 {
567                                    if reference.ident != "life0" {
568                                        abort!(
569                                        reference.span(),
570                                        "Method '{}' has a lifetime \"'{}\" for 'self' argument. This is not supported by savefile-abi",
571                                        method_name,
572                                        reference.ident,
573                                    );
574                                    } else {
575                                        life0_life_time += 1;
576                                    }
577                                }
578                                if recv.mutability.is_some() {
579                                    receiver_is_mut = true;
580                                }
581                            } else {
582                                abort!(
583                                self_arg.span(),
584                                "Method '{}' takes 'self' by value. This is not supported by savefile-abi. Use &self",
585                                method_name
586                            );
587                            }
588                        }
589                    }
590                    FnArg::Typed(pat) => match &*pat.pat {
591                        Pat::Ident(ident) if ident.ident == "self" => {
592                            if ident.by_ref.is_some() || ident.mutability.is_some() {
593                                unsupported()?;
594                            }
595                            parse_receiver_ty(&*pat.ty)?;
596                        }
597                        _ => {
598                            abort!(
599                                        pat.pat.span(),
600                                        "Method '{}' must have 'self'-parameter (savefile-abi does not support methods without self)",
601                                        method_name
602                                    );
603                        }
604                    },
605                }
606                let mut args = Vec::with_capacity(method.sig.inputs.len());
607                for arg in method.sig.inputs.iter().skip(1) {
608                    match arg {
609                        FnArg::Typed(typ) => {
610                            match &*typ.pat {
611                                Pat::Ident(name) => {
612                                    args.push((name.ident.clone(), &*typ.ty));
613                                }
614                                _ => abort!(typ.pat.span(), "Method '{}' has a parameter which contains a complex pattern. This is not supported by savefile-abi.", method_name)
615                            }
616                        },
617                        _ => abort!(arg.span(), "Unexpected error: method {} had a self parameter that wasn't the first parameter!", method_name)
618                    }
619                }
620                if method.sig.asyncness.is_some() {
621                    let out = match &method.sig.output {
622                        ReturnType::Default => {
623                            quote! {()}
624                        }
625                        ReturnType::Type(_, t) => t.to_token_stream(),
626                    };
627                    abort!(
628                        method.sig.asyncness.span(),
629                        "savefile-abi does not support async methods. You can try returning a boxed future instead: Pin<Box<Future<Output={}>>>",
630                        out
631                    )
632                }
633                if method.sig.variadic.is_some() {
634                    abort!(
635                        method.sig.variadic.span(),
636                        "savefile-abi does not support variadic methods."
637                    )
638                }
639                if method.sig.unsafety.is_some() {
640                    abort!(
641                        method.sig.unsafety.span(),
642                        "savefile-abi does not presently support unsafe methods."
643                    )
644                }
645                if method.sig.abi.is_some() {
646                    abort!(method.sig.abi.span(), "savefile-abi does not need (or support) 'extern \"C\"' or similar ABI-constructs. Just remove this keyword.")
647                }
648
649                // NOTE!
650                // This is part of the heuristics that detects async_trait macro output.
651                // However, we don't actually support reference arguments to functions
652                // returning futures.
653                fn is_life(id: &syn::Lifetime) -> bool {
654                    let s = id.ident.to_string();
655                    if !s.starts_with("life") {
656                        return false;
657                    }
658                    s.strip_prefix("life").unwrap().parse::<usize>().is_ok()
659                }
660
661                if method.sig.generics.params.is_empty() == false {
662                    for item in method.sig.generics.params.iter() {
663                        match item {
664                            GenericParam::Type(typ) => {
665                                abort!(typ.span(), "savefile-abi does not support generic methods.")
666                            }
667                            GenericParam::Const(typ) => {
668                                abort!(typ.span(), "savefile-abi does not support const-generic methods.")
669                            }
670                            GenericParam::Lifetime(l) => {
671                                if l.lifetime.ident != "life0"
672                                    && l.lifetime.ident != "async_trait"
673                                    && !is_life(&l.lifetime)
674                                {
675                                    abort!(
676                                        method.sig.generics.params.span(),
677                                        "savefile-abi does not support methods with lifetimes."
678                                    );
679                                } else {
680                                    if l.lifetime.ident == "life0" {
681                                        life0_life_time += 1;
682                                    }
683                                    async_trait_life_time += 1;
684                                }
685                            }
686                        }
687                    }
688                }
689
690                let async_trait_macro_detected;
691                if life0_life_time >= 3 && async_trait_life_time >= 3 {
692                    async_trait_macro_detected = true;
693                } else if life0_life_time == 0 && async_trait_life_time == 0 {
694                    async_trait_macro_detected = false;
695                } else {
696                    abort!(
697                        item.span(),
698                        "savefile-abi has heuristics that detects the use of the #[async_trait]-macro. This heuristic produced a partial result. It is possible that an incompatible version of async_trait crate has been used. Diagnostics: {} {}",
699                        life0_life_time, async_trait_life_time
700                    );
701                }
702
703                let method_defs = crate::savefile_abi::generate_method_definitions(
704                    version,
705                    trait_name.clone(),
706                    method_number,
707                    method_name,
708                    ret_declaration,
709                    ret_type,
710                    no_return,
711                    receiver_is_mut,
712                    receiver_is_pin,
713                    args,
714                    &mut temp_name_generator,
715                    &mut extra_definitions,
716                    async_trait_macro_detected,
717                )?;
718                method_metadata.push(method_defs.method_metadata);
719                callee_method_trampoline.push(method_defs.callee_method_trampoline);
720                caller_method_trampoline.push(method_defs.caller_method_trampoline);
721            }
722            TraitItem::Type(t) => {
723                abort!(
724                    t.span(),
725                    "savefile_abi_exportable does not support associated types: {}",
726                    t.ident
727                );
728            }
729            TraitItem::Macro(m) => {
730                abort!(
731                    m.span(),
732                    "savefile_abi_exportable does not support macro items: {:?}",
733                    m
734                );
735            }
736            TraitItem::Verbatim(v) => {
737                abort!(
738                    v.span(),
739                    "Unsupported item in trait definition: {}",
740                    v.to_token_stream()
741                );
742            }
743            x => abort!(
744                x.span(),
745                "Unsupported item in trait definition: {}",
746                x.to_token_stream()
747            ),
748        }
749    }
750
751    let abi_entry_light = Ident::new(&format!("abi_entry_light_{}", trait_name_str), Span::call_site());
752
753    let exports_for_trait = quote! {
754
755        unsafe extern "C" fn #abi_entry_light(flag: AbiProtocol) {
756            unsafe { abi_entry_light::<dyn #trait_name>(flag); }
757        }
758
759        #[automatically_derived]
760        #[doc(hidden)]
761        unsafe impl AbiExportable for dyn #trait_name {
762            const ABI_ENTRY : unsafe extern "C" fn (flag: AbiProtocol)  = #abi_entry_light;
763            fn get_definition( version: u32) -> AbiTraitDefinition {
764                AbiTraitDefinition {
765                    name: #trait_name_str.to_string(),
766                    methods: vec! [ #(#method_metadata,)* ],
767                    sync: #sync,
768                    send: #send
769                }
770            }
771
772            fn get_latest_version() -> u32 {
773                #version
774            }
775
776            fn call(trait_object: TraitObject, method_number: u16, effective_version:u32, compatibility_mask: u64, data: &[u8], abi_result: *mut (), __savefile_internal_receiver: unsafe extern "C" fn(outcome: *const RawAbiCallResult, result_receiver: *mut ()/*Result<T,SaveFileError>>*/)) -> Result<(),SavefileError> {
777
778                let mut cursor = Cursor::new(data);
779
780                let mut deserializer = Deserializer {
781                    file_version: cursor.read_u32::<LittleEndian>()?,
782                    reader: &mut cursor,
783                    ephemeral_state: HashMap::new(),
784                };
785
786                match method_number {
787                    #(#callee_method_trampoline,)*
788                    _ => {
789                        return Err(SavefileError::general("Unknown method number"));
790                    }
791                }
792                Ok(())
793            }
794        }
795
796        #[automatically_derived]
797        #[doc(hidden)]
798        impl #trait_name for AbiConnection<dyn #trait_name> {
799            #(#caller_method_trampoline)*
800        }
801    };
802
803    //let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
804    let input = TokenStream::from(input);
805    let extra_definitions: Vec<_> = extra_definitions.values().map(|(_, x)| x).collect();
806    let expanded = quote! {
807        #[allow(clippy::double_comparisons)]
808        #[allow(clippy::needless_question_mark)]
809        #[allow(unused_variables)]
810        #[allow(clippy::needless_late_init)]
811        #[allow(clippy::not_unsafe_ptr_arg_deref)]
812        #[allow(non_upper_case_globals)]
813        #[allow(clippy::manual_range_contains)]
814        #[allow(non_local_definitions)]
815        const _:() = {
816            #uses
817
818            #(#extra_definitions)*
819
820            #exports_for_trait
821
822        };
823
824        #input
825    };
826
827    // For debugging, uncomment to write expanded procmacro to file
828    // std::fs::write(format!("/home/anders/savefile/savefile-min-build/src/{}.rs",trait_name_str),expanded.to_string()).unwrap();
829
830    Ok(expanded.into())
831}
832
833#[proc_macro]
834pub fn savefile_abi_export(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
835    match savefile_abi_export_impl(item) {
836        Ok(x) => x,
837        Err(err) => err.to_compile_error().into()
838    }
839}
840fn savefile_abi_export_impl(item: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenStream> {
841    let tokens = proc_macro2::TokenStream::from(item);
842
843    let mut tokens_iter = tokens.into_iter();
844
845    let Some(implementing_type) = tokens_iter.next() else {
846        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
847    };
848    let Some(comma) = tokens_iter.next() else {
849        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
850    };
851    if let TokenTree::Punct(p) = comma {
852        if p.as_char() != ',' {
853            abort!(p.span(), "Expected a comma (','). The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements, and these must be separated by a comma.");
854        }
855    } else {
856        abort!(comma.span(), "Expected a comma (','). The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements, and these must be separated by a comma.");
857    }
858    let Some(trait_type) = tokens_iter.next() else {
859        abort!(Span::call_site(), "The macro savefile_abi_export! requires two parameters. The first parameter must be the implementing type, the second is the trait it implements. Expected trait name.");
860    };
861
862    if let Some(extra) = tokens_iter.next() {
863        abort!(extra.span(), "Unexpected token. The macro savefile_abi_export! requires exactly two parameters. The first parameter must be the implementing type, the second is the trait it implements.");
864    }
865
866    let defspan = Span::call_site();
867    let uses = quote_spanned! { defspan =>
868        extern crate savefile_abi;
869        use savefile_abi::{AbiProtocol, AbiExportableImplementation, abi_entry,parse_return_value_impl};
870    };
871
872    let abi_entry = Ident::new(
873        ("abi_entry_".to_string() + &trait_type.to_string()).as_str(),
874        Span::call_site(),
875    );
876
877    let expanded = quote! {
878        #[allow(clippy::needless_question_mark)]
879        #[allow(clippy::double_comparisons)]
880        #[allow(non_local_definitions)]
881        const _:() = {
882            #uses
883            #[automatically_derived]
884            #[doc(hidden)]
885            unsafe impl AbiExportableImplementation for #implementing_type where #implementing_type: Default + #trait_type {
886                const ABI_ENTRY: unsafe extern "C" fn (AbiProtocol) = #abi_entry;
887                type AbiInterface = dyn #trait_type;
888
889                fn new() -> Box<Self::AbiInterface> {
890                    std::boxed::Box::new(#implementing_type::default())
891                }
892            }
893            #[no_mangle]
894            unsafe extern "C" fn #abi_entry(flag: AbiProtocol) where #implementing_type: Default + #trait_type {
895                unsafe { abi_entry::<#implementing_type>(flag); }
896            }
897        };
898    };
899
900    Ok(expanded.into())
901}
902
903#[proc_macro_derive(
904    Savefile,
905    attributes(
906        savefile_unsafe_and_fast,
907        savefile_require_fast,
908        savefile_versions,
909        savefile_versions_as,
910        savefile_introspect_ignore,
911        savefile_introspect_key,
912        savefile_ignore,
913        savefile_default_val,
914        savefile_default_fn,
915        savefile_doc_hidden
916    )
917)]
918pub fn savefile(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
919    match savefile_impl(input) {
920        Ok(x) => x,
921        Err(err) => err.to_compile_error().into(),
922    }
923}
924fn savefile_impl(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenStream> {
925    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [Savefile]");
926
927    let s = serialize::savefile_derive_crate_serialize(input.clone())?;
928
929    let d = deserialize::savefile_derive_crate_deserialize(input.clone())?;
930
931    let w = savefile_derive_crate_withschema(input.clone())?;
932
933    let i = savefile_derive_crate_introspect(input.clone())?;
934
935    let r = derive_reprc_new(input)?;
936
937    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
938
939    let expanded: TokenStream = quote! {
940        #s
941
942        #d
943
944        #i
945
946        #[allow(non_upper_case_globals)]
947        #[allow(clippy::double_comparisons)]
948        #[allow(non_camel_case_types)]
949        #[allow(clippy::manual_range_contains)]
950        const #dummy_const: () = {
951            extern crate savefile as _savefile;
952            use std::mem::MaybeUninit;
953            use savefile::prelude::Packed;
954
955            #w
956            #r
957        };
958    };
959    //std::fs::write("/home/anders/savefile/savefile-min-build/src/expanded.rs", expanded.to_string()).unwrap();
960
961    Ok(expanded.into())
962}
963#[proc_macro_derive(
964    SavefileNoIntrospect,
965    attributes(
966        savefile_unsafe_and_fast,
967        savefile_require_fast,
968        savefile_versions,
969        savefile_versions_as,
970        savefile_ignore,
971        savefile_introspect_ignore,
972        savefile_default_val,
973        savefile_default_fn
974    )
975)]
976pub fn savefile_no_introspect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
977    match savefile_no_introspect_impl(input) {
978        Ok(x) => x,
979        Err(err) => err.to_compile_error().into(),
980    }
981}
982
983fn savefile_no_introspect_impl(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenStream> {
984    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [SavefileNoIntrospect]");
985
986    let s = serialize::savefile_derive_crate_serialize(input.clone())?;
987
988    let d = deserialize::savefile_derive_crate_deserialize(input.clone())?;
989
990    let w = savefile_derive_crate_withschema(input.clone())?;
991
992    let r = derive_reprc_new(input)?;
993
994    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
995
996    let expanded = quote! {
997        #s
998
999        #d
1000
1001        #[allow(non_upper_case_globals)]
1002        #[allow(clippy::double_comparisons)]
1003        #[allow(clippy::manual_range_contains)]
1004        const #dummy_const: () = {
1005            extern crate savefile as _savefile;
1006            use std::mem::MaybeUninit;
1007            use savefile::prelude::Packed;
1008
1009            #w
1010            #r
1011        };
1012    };
1013
1014    Ok(expanded.into())
1015}
1016
1017#[proc_macro_derive(
1018    SavefileIntrospectOnly,
1019    attributes(
1020        savefile_versions,
1021        savefile_versions_as,
1022        savefile_introspect_ignore,
1023        savefile_ignore,
1024        savefile_default_val,
1025        savefile_default_fn
1026    )
1027)]
1028pub fn savefile_introspect_only(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1029    match savefile_introspect_only_impl(input) {
1030        Ok(x) => x,
1031        Err(err) => err.to_compile_error().into()
1032    }
1033}
1034fn savefile_introspect_only_impl(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenStream> {
1035    let input: DeriveInput = syn::parse(input).expect("Expected valid rust code [SavefileIntrospectOnly]");
1036
1037    let i = savefile_derive_crate_introspect(input)?;
1038
1039    let expanded = quote! {
1040        #i
1041    };
1042
1043    Ok(expanded.into())
1044}
1045
1046#[allow(non_snake_case)]
1047fn implement_reprc_hardcoded_false(name: syn::Ident, input: &DeriveInput) -> TokenStream {
1048    let defspan = proc_macro2::Span::call_site();
1049
1050    let generics = &input.generics;
1051    let doc_hidden = doc_hidden(&input.attrs);
1052    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1053    let extra_where = get_extra_where_clauses(input, where_clause, quote! {_savefile::prelude::WithSchema});
1054    let reprc = quote_spanned! {defspan=>
1055        _savefile::prelude::Packed
1056    };
1057    let isreprc = quote_spanned! {defspan=>
1058        _savefile::prelude::IsPacked
1059    };
1060    quote! {
1061
1062        #[automatically_derived]
1063        #doc_hidden
1064        impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where{
1065            #[allow(unused_comparisons,unused_variables, unused_variables)]
1066            unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1067                #isreprc::no()
1068            }
1069        }
1070
1071    }
1072}
1073
1074#[allow(non_snake_case)]
1075fn implement_reprc_struct(
1076    field_infos: Vec<FieldInfo>,
1077    input: &DeriveInput,
1078    name: syn::Ident,
1079    expect_fast: bool,
1080) -> syn::Result<TokenStream> {
1081    let generics = &input.generics;
1082    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1083    let extra_where = get_extra_where_clauses(input, where_clause, quote! {_savefile::prelude::Packed});
1084    let doc_hidden = doc_hidden(&input.attrs);
1085    let span = proc_macro2::Span::call_site();
1086    let defspan = proc_macro2::Span::call_site();
1087    let reprc = quote_spanned! {defspan=>
1088        _savefile::prelude::Packed
1089    };
1090    let isreprc = quote_spanned! {defspan=>
1091        _savefile::prelude::IsPacked
1092    };
1093    let offsetof = quote_spanned! {defspan=>
1094        _savefile::prelude::offset_of
1095    };
1096    let local_file_version = quote_spanned! { defspan => local_file_version};
1097    //let WithSchema = quote_spanned! { defspan => _savefile::prelude::WithSchema};
1098    let mut min_safe_version = 0;
1099    let mut packed_outputs = Vec::new();
1100    let mut reprc_outputs = Vec::new();
1101
1102    for field in field_infos.windows(2) {
1103        let field_name1 = field[0].get_accessor();
1104        let field_name2 = field[1].get_accessor();
1105        let ty = field[0].ty;
1106        packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #field_name1) + std::mem::size_of::<#ty>() == #offsetof!(#name #ty_generics, #field_name2) )));
1107    }
1108    if field_infos.len() > 0 {
1109        if field_infos.len() == 1 {
1110            let ty = field_infos[0].ty;
1111            let field_name = field_infos[0].get_accessor();
1112            packed_outputs.push(quote!(  (#offsetof!( #name #ty_generics, #field_name) == 0 )));
1113            packed_outputs.push(quote!(  (#offsetof!( #name #ty_generics, #field_name) + std::mem::size_of::<#ty>() == std::mem::size_of::<#name #ty_generics>() )));
1114        } else {
1115            let first = field_infos.first().expect("field_infos.first()[2]").get_accessor();
1116            let last_field = field_infos.last().expect("field_infos.last()[2]");
1117            let last = last_field.get_accessor();
1118            let last_ty = &last_field.ty;
1119            packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #first) == 0 )));
1120            packed_outputs.push(quote!( (#offsetof!(#name #ty_generics, #last) + std::mem::size_of::<#last_ty>()  == std::mem::size_of::<#name #ty_generics>() )));
1121        }
1122    }
1123
1124    for field in &field_infos {
1125        let verinfo = parse_attr_tag(field.attrs)?;
1126        if verinfo.ignore {
1127            // If it isn't zero-sized, the checks above will fail anyway.
1128            // If it's zero-sized, we can allow it.
1129            continue;
1130        }
1131        let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
1132
1133        let removed = check_is_remove(field.ty);
1134        let field_type = &field.ty;
1135        if field_from_version == 0 && field_to_version == std::u32::MAX {
1136            if removed.is_removed() {
1137                if expect_fast {
1138                    abort!(field.ty.span(), "The Removed type can only be used for removed fields. Use the savefile_version attribute to mark a field as only existing in previous versions.");
1139                } else {
1140                    return Ok(implement_reprc_hardcoded_false(name, input));
1141                }
1142            }
1143            reprc_outputs
1144                .push(quote_spanned!( span => <#field_type as #reprc>::repr_c_optimization_safe(#local_file_version).is_yes()));
1145        } else {
1146            min_safe_version = min_safe_version.max(verinfo.min_safe_version());
1147
1148            if !removed.is_removed() {
1149                reprc_outputs.push(
1150                    quote_spanned!( span => <#field_type as #reprc>::repr_c_optimization_safe(#local_file_version).is_yes()),
1151                );
1152            }
1153        }
1154    }
1155
1156    let require_packed = if expect_fast {
1157        quote!(
1158            const _: () = {
1159                if !PACKED {
1160                    panic!("Memory layout not optimal - requires padding which disables savefile-optimization");
1161                }
1162            };
1163        )
1164    } else {
1165        quote!()
1166    };
1167
1168    let packed_storage = if generics.params.is_empty() == false {
1169        quote!(let)
1170    } else {
1171        quote!(const)
1172    };
1173
1174    Ok(quote! {
1175
1176        #[automatically_derived]
1177        #doc_hidden
1178        impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where {
1179            #[allow(unused_comparisons,unused_variables, unused_variables)]
1180            unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1181                let local_file_version = file_version;
1182                #packed_storage PACKED : bool = true #( && #packed_outputs)*;
1183                #require_packed
1184                if file_version >= #min_safe_version && PACKED #( && #reprc_outputs)*{
1185                    unsafe { #isreprc::yes() }
1186                } else {
1187                    #isreprc::no()
1188                }
1189            }
1190        }
1191    })
1192}
1193
1194#[derive(Debug)]
1195struct EnumSize {
1196    discriminant_size: u8,
1197    #[allow(unused)] //Keep around, useful for debugging
1198    repr_c: bool,
1199    explicit_size: bool,
1200}
1201
1202fn get_enum_size(attrs: &[syn::Attribute], actual_variants: usize) -> syn::Result<EnumSize> {
1203    let mut size_u8: Option<u8> = None;
1204    let mut repr_c_seen = false;
1205    let mut have_seen_explicit_size = false;
1206
1207    for attr in attrs.iter() {
1208        let path = attr.path();
1209
1210        if path.is_ident("repr") {
1211            if let Err(err) = attr.parse_nested_meta(|meta| {
1212                //for x in &metalist.nested {
1213                if meta.path.segments.len() != 1 {
1214                    abort!(
1215                        meta.path.span(),
1216                        "Unsupported repr(X) attribute on enum: {}",
1217                        meta.path.to_token_stream()
1218                    );
1219                }
1220                match meta.path.segments[0].ident.to_string().as_str() {
1221                    "C" => repr_c_seen = true,
1222                    "u8" => {
1223                        size_u8 = Some(1);
1224                        have_seen_explicit_size = true;
1225                    }
1226                    "i8" => {
1227                        size_u8 = Some(1);
1228                        have_seen_explicit_size = true;
1229                    }
1230                    "u16" => {
1231                        size_u8 = Some(2);
1232                        have_seen_explicit_size = true;
1233                    }
1234                    "i16" => {
1235                        size_u8 = Some(2);
1236                        have_seen_explicit_size = true;
1237                    }
1238                    "u32" => {
1239                        size_u8 = Some(4);
1240                        have_seen_explicit_size = true;
1241                    }
1242                    "i32" => {
1243                        size_u8 = Some(4);
1244                        have_seen_explicit_size = true;
1245                    }
1246                    "u64" | "i64" => {
1247                        abort!(
1248                            meta.path.span(),
1249                            "Savefile does not support enums with more than 2^32 variants."
1250                        )
1251                    }
1252                    _ => abort!(
1253                        meta.path.span(),
1254                        "Unsupported repr(X) attribute on enum: {}",
1255                        meta.path.to_token_stream()
1256                    ),
1257                };
1258                Ok(())
1259            }) {
1260                return Err(err);
1261            };
1262        }
1263    }
1264
1265    if actual_variants >= u32::MAX as usize {
1266        abort_call_site!("The enum had an unreasonable number of variants");
1267    }
1268    let discriminant_size = size_u8.unwrap_or_else(|| {
1269        if actual_variants <= 256 {
1270            1
1271        } else if actual_variants <= 65536 {
1272            2
1273        } else {
1274            4
1275        }
1276    });
1277    Ok(EnumSize {
1278        discriminant_size,
1279        repr_c: repr_c_seen,
1280        explicit_size: have_seen_explicit_size,
1281    })
1282}
1283
1284#[proc_macro_derive(
1285    Packed,
1286    attributes(
1287        savefile_versions,
1288        savefile_versions_as,
1289        savefile_ignore,
1290        savefile_default_val,
1291        savefile_default_fn
1292    )
1293)]
1294pub fn reprc(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1295    syn::Error::new(Span::call_site(), "The #[derive(Packed)] style of unsafe performance opt-in has been removed. The performance gains are now available automatically for any packed struct.").to_compile_error().into()
1296}
1297fn derive_reprc_new(input: DeriveInput) -> syn::Result<TokenStream> {
1298    let name = &input.ident;
1299    let doc_hidden = doc_hidden(&input.attrs);
1300    let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl();
1301
1302    let mut opt_in_fast = false;
1303    for attr in input.attrs.iter() {
1304        if attr.path().is_ident("savefile_unsafe_and_fast") || attr.path().is_ident("savefile_require_fast") {
1305            opt_in_fast = true;
1306        }
1307        /*match attr.parse_meta() {
1308            Ok(ref meta) => match meta {
1309                &syn::Meta::Path(ref x) => {
1310                    let x = path_to_string(x);
1311                    if x == "savefile_unsafe_and_fast" || x == "savefile_require_fast" {
1312                        opt_in_fast = true;
1313                    }
1314                }
1315                _ => {}
1316            },
1317            _ => {}
1318        }*/
1319    }
1320
1321    /*if !opt_in_fast {
1322        return implement_reprc_hardcoded_false(name, input.generics);
1323    }*/
1324
1325    let expanded = match &input.data {
1326        &syn::Data::Enum(ref enum1) => {
1327            let enum_size = get_enum_size(&input.attrs, enum1.variants.len())?;
1328            let any_fields = enum1.variants.iter().any(|v| v.fields.len() > 0);
1329            if !enum_size.explicit_size {
1330                if opt_in_fast {
1331                    if any_fields {
1332                        abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8),C],#[repr(u16,C)] or #[repr(u32,C)], attribute.");
1333                    } else {
1334                        abort_call_site!("The #[savefile_require_fast] requires an explicit #[repr(u8)],#[repr(u16)] or #[repr(u32)], attribute.");
1335                    }
1336                }
1337                return Ok(implement_reprc_hardcoded_false(name.clone(), &input));
1338            }
1339
1340            let mut conditions = vec![];
1341
1342            let mut min_safe_version: u32 = 0;
1343
1344            let mut unique_field_types = HashSet::new();
1345
1346            let fn_impl_generics = if !input.generics.params.is_empty() {
1347                quote! { :: #impl_generics}
1348            } else {
1349                quote! {}
1350            };
1351            for (variant_index, variant) in enum1.variants.iter().enumerate() {
1352                let mut attrs: Vec<_> = vec![];
1353
1354                let mut num_fields = 0usize;
1355                let mut field_types = vec![];
1356                match &variant.fields {
1357                    &syn::Fields::Named(ref fields_named) => {
1358                        for field in fields_named.named.iter() {
1359                            attrs.push(&field.attrs);
1360                            field_types.push(&field.ty);
1361                            num_fields += 1;
1362                        }
1363                    }
1364                    &syn::Fields::Unnamed(ref fields_unnamed) => {
1365                        for field in fields_unnamed.unnamed.iter() {
1366                            attrs.push(&field.attrs);
1367                            field_types.push(&field.ty);
1368                            num_fields += 1;
1369                        }
1370                    }
1371                    &syn::Fields::Unit => {}
1372                }
1373                for i in 0usize..num_fields {
1374                    let verinfo = parse_attr_tag(&attrs[i])?;
1375                    if check_is_remove(&field_types[i]).is_removed() {
1376                        if verinfo.version_to == u32::MAX {
1377                            abort!(field_types[i].span(), "Removed fields must have a max version, provide one using #[savefile_versions=\"..N\"]")
1378                        }
1379                        min_safe_version = min_safe_version.max(verinfo.version_to + 1);
1380                    }
1381                    let typ = field_types[i].to_token_stream();
1382
1383                    let variant_index = proc_macro2::Literal::u32_unsuffixed(variant_index as u32);
1384
1385                    unique_field_types.insert(field_types[i].clone());
1386                    if i == 0 {
1387                        let discriminant_bytes = enum_size.discriminant_size as usize;
1388                        conditions.push( quote!( (#discriminant_bytes == (get_variant_offsets #fn_impl_generics(#variant_index)[#i])) ) );
1389                    }
1390                    if i == num_fields - 1 {
1391                        conditions.push(
1392                            quote!(  (std::mem::size_of::<#name #ty_generics>() == (get_variant_offsets #fn_impl_generics(#variant_index)[#i]) + std::mem::size_of::<#typ>())  )
1393                        );
1394                    } else {
1395                        let n = i + 1;
1396                        let end_offset_condition = quote!(  (get_variant_offsets #fn_impl_generics(#variant_index)[#n] == (get_variant_offsets #fn_impl_generics(#variant_index)[#i]) + std::mem::size_of::<#typ>())  );
1397                        conditions.push(quote!(#end_offset_condition));
1398                    };
1399                }
1400
1401                for attr in attrs {
1402                    let verinfo = parse_attr_tag(attr)?;
1403                    if verinfo.ignore {
1404                        if opt_in_fast {
1405                            abort_call_site!(
1406                                "The #[savefile_require_fast] attribute cannot be used for structures containing ignored fields"
1407                            );
1408                        } else {
1409                            return Ok(implement_reprc_hardcoded_false(name.clone(), &input));
1410                        }
1411                    }
1412                    min_safe_version = min_safe_version.max(verinfo.min_safe_version());
1413                }
1414            }
1415
1416            let defspan = proc_macro2::Span::call_site();
1417            let generics = &input.generics;
1418            let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1419            let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::Packed});
1420            let reprc = quote_spanned! { defspan=>
1421                _savefile::prelude::Packed
1422            };
1423            let isreprc = quote_spanned! {defspan=>
1424                _savefile::prelude::IsPacked
1425            };
1426
1427            if conditions.is_empty() {
1428                conditions.push(quote!(true));
1429            }
1430            let require_packed = if opt_in_fast {
1431                quote!(
1432                    const _: () = {
1433                        if !PACKED {
1434                            panic!("Memory layout not optimal - requires padding which disables savefile-optimization");
1435                        }
1436                    };
1437                )
1438            } else {
1439                quote!()
1440            };
1441            let mut reprc_condition = vec![];
1442            for typ in unique_field_types {
1443                reprc_condition.push(quote!(
1444                    <#typ as Packed>::repr_c_optimization_safe(file_version).is_yes()
1445                ));
1446            }
1447
1448            let packed_decl = if generics.params.is_empty() {
1449                quote! { const }
1450            } else {
1451                quote! { let }
1452            };
1453            let packed_constraints = if any_fields {
1454                quote!(
1455                    #packed_decl PACKED : bool = true #( && #conditions)*;
1456                    #require_packed
1457                    if !PACKED {
1458                        return #isreprc::no();
1459                    }
1460                )
1461            } else {
1462                quote!()
1463            };
1464
1465            return Ok(quote! {
1466
1467                #[automatically_derived]
1468                #doc_hidden
1469                impl #impl_generics #reprc for #name #ty_generics #where_clause #extra_where {
1470                    #[allow(unused_comparisons,unused_variables, unused_variables)]
1471                    unsafe fn repr_c_optimization_safe(file_version:u32) -> #isreprc {
1472                        let local_file_version = file_version;
1473
1474                        #packed_constraints
1475
1476                        if file_version >= #min_safe_version #( && #reprc_condition)* {
1477                            unsafe { #isreprc::yes() }
1478                        } else {
1479                            #isreprc::no()
1480                        }
1481                    }
1482                }
1483            });
1484
1485            //implement_reprc_struct(vec![], input.generics, name, opt_in_fast) //Hacky, consider enum without any fields as a field-less struct
1486        }
1487        &syn::Data::Struct(ref struc) => match &struc.fields {
1488            &syn::Fields::Named(ref namedfields) => {
1489                let field_infos: Vec<FieldInfo> = namedfields
1490                    .named
1491                    .iter()
1492                    .enumerate()
1493                    .map(|(field_index, field)| FieldInfo {
1494                        ident: Some(field.ident.clone().expect("Expected identifier [8]")),
1495                        field_span: field.ident.as_ref().unwrap().span(),
1496                        index: field_index as u32,
1497                        ty: &field.ty,
1498                        attrs: &field.attrs,
1499                    })
1500                    .collect();
1501
1502                implement_reprc_struct(field_infos, &input, name.clone(), opt_in_fast)?
1503            }
1504            &syn::Fields::Unnamed(ref fields_unnamed) => {
1505                let field_infos: Vec<FieldInfo> = fields_unnamed
1506                    .unnamed
1507                    .iter()
1508                    .enumerate()
1509                    .map(|(idx, field)| FieldInfo {
1510                        field_span: field.ty.span(),
1511                        ident: None,
1512                        index: idx as u32,
1513                        ty: &field.ty,
1514                        attrs: &field.attrs,
1515                    })
1516                    .collect();
1517
1518                implement_reprc_struct(field_infos, &input, name.clone(), opt_in_fast)?
1519            }
1520            &syn::Fields::Unit => implement_reprc_struct(Vec::new(), &input, name.clone(), opt_in_fast)?,
1521        },
1522        _ => {
1523            if opt_in_fast {
1524                abort_call_site!("Unsupported data type");
1525            }
1526            return Ok(implement_reprc_hardcoded_false(name.clone(), &input));
1527        }
1528    };
1529
1530    Ok(expanded)
1531}
1532
1533#[allow(non_snake_case)]
1534fn implement_introspect(
1535    field_infos: Vec<FieldInfo>,
1536    need_self: bool,
1537) -> syn::Result<(Vec<TokenStream>, Vec<TokenStream>, Option<TokenStream>)> {
1538    let span = proc_macro2::Span::call_site();
1539    let defspan = proc_macro2::Span::call_site();
1540
1541    //let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1542    //let Introspect = quote_spanned! { defspan => _savefile::prelude::Introspect };
1543    //let fields1=quote_spanned! { defspan => fields1 };
1544    let index1 = quote_spanned! { defspan => index };
1545    let introspect_item = quote_spanned! { defspan=>
1546        _savefile::prelude::introspect_item
1547    };
1548
1549    let mut fields = Vec::new();
1550    let mut fields_names = Vec::new();
1551    let mut introspect_key = None;
1552    let mut index_number = 0usize;
1553    for (idx, field) in field_infos.iter().enumerate() {
1554        let verinfo = parse_attr_tag(field.attrs)?;
1555        if verinfo.introspect_key && introspect_key.is_some() {
1556            abort!(
1557                field.field_span,
1558                "Type had more than one field with savefile_introspect_key - attribute"
1559            );
1560        }
1561        if verinfo.introspect_ignore {
1562            continue;
1563        }
1564        if need_self {
1565            let fieldname;
1566            let fieldname_raw;
1567
1568            let id = field.get_accessor();
1569            fieldname = quote! {&self.#id};
1570            fieldname_raw = quote! {#id};
1571
1572            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(stringify!(#fieldname_raw).to_string(), #fieldname))}));
1573            if verinfo.introspect_key {
1574                let fieldname_raw2 = fieldname_raw.clone();
1575                introspect_key = Some(quote! {self.#fieldname_raw2});
1576            }
1577            fields_names.push(fieldname_raw);
1578        } else if let Some(id) = field.ident.clone() {
1579            let fieldname;
1580            let quoted_fieldname;
1581            let raw_fieldname = id.to_string();
1582            let id2 = id.clone();
1583            fieldname = id;
1584            quoted_fieldname = quote! { #fieldname };
1585            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(#raw_fieldname.to_string(), #quoted_fieldname))}));
1586            fields_names.push(quoted_fieldname);
1587            if verinfo.introspect_key {
1588                introspect_key = Some(quote!(#id2))
1589            }
1590        } else {
1591            let fieldname;
1592            let quoted_fieldname;
1593            let raw_fieldname = idx.to_string();
1594            fieldname = Ident::new(&format!("v{}", idx), span);
1595            let fieldname2 = fieldname.clone();
1596            quoted_fieldname = quote! { #fieldname };
1597            fields.push(quote_spanned!( span => if #index1 == #index_number { return Some(#introspect_item(#raw_fieldname.to_string(), #quoted_fieldname))}));
1598            fields_names.push(quoted_fieldname);
1599            if verinfo.introspect_key {
1600                introspect_key = Some(quote!(#fieldname2))
1601            }
1602        }
1603
1604        index_number += 1;
1605    }
1606
1607    Ok((fields_names, fields, introspect_key))
1608}
1609
1610#[allow(non_snake_case)]
1611fn savefile_derive_crate_introspect(input: DeriveInput) -> syn::Result<TokenStream> {
1612    let name = &input.ident;
1613
1614    let generics = &input.generics;
1615    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1616    let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::Introspect});
1617    let doc_hidden = doc_hidden(&input.attrs);
1618    let span = proc_macro2::Span::call_site();
1619    let defspan = proc_macro2::Span::call_site();
1620    let introspect = quote_spanned! {defspan=>
1621        _savefile::prelude::Introspect
1622    };
1623    let introspect_item_type = quote_spanned! {defspan=>
1624        _savefile::prelude::IntrospectItem
1625    };
1626    let uses = quote_spanned! { defspan =>
1627        extern crate savefile as _savefile;
1628    };
1629
1630    //let SchemaStruct = quote_spanned! { defspan => _savefile::prelude::SchemaStruct };
1631    //let SchemaEnum = quote_spanned! { defspan => _savefile::prelude::SchemaEnum };
1632    //let Schema = quote_spanned! { defspan => _savefile::prelude::Schema };
1633    //let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1634    //let Variant = quote_spanned! { defspan => _savefile::prelude::Variant };
1635
1636    let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
1637
1638    let expanded = match &input.data {
1639        &syn::Data::Enum(ref enum1) => {
1640            let mut variants = Vec::new();
1641            let mut value_variants = Vec::new();
1642            let mut len_variants = Vec::new();
1643            for variant in enum1.variants.iter() {
1644                /*if var_idx >= 256 {
1645                    panic!("Savefile does not support enums with 256 variants or more. Sorry.");
1646                }*/
1647                //let var_idx = var_idx as u8;
1648                let var_ident = variant.ident.clone();
1649                let variant_name = quote! { #var_ident };
1650                let variant_name_spanned = quote_spanned! { span => #variant_name};
1651
1652                let mut field_infos = Vec::new();
1653
1654                let return_value_name_str = format!("{}::{}", name, var_ident);
1655                let return_value_name = quote!(#return_value_name_str);
1656                match &variant.fields {
1657                    &syn::Fields::Named(ref fields_named) => {
1658                        for (idx, f) in fields_named.named.iter().enumerate() {
1659                            field_infos.push(FieldInfo {
1660                                ident: Some(f.ident.clone().expect("Expected identifier[9]")),
1661                                field_span: f.ident.as_ref().unwrap().span(),
1662                                index: idx as u32,
1663                                ty: &f.ty,
1664                                attrs: &f.attrs,
1665                            });
1666                        }
1667                        let (fields_names, fields, introspect_key) = implement_introspect(field_infos, false)?;
1668                        let fields_names1 = fields_names.clone();
1669                        let fields_names2 = fields_names.clone();
1670                        let fields_names3 = fields_names.clone();
1671                        let num_fields = fields_names3.len();
1672                        if let Some(introspect_key) = introspect_key {
1673                            value_variants.push(quote!(#name::#variant_name_spanned{#(#fields_names,)*} => {
1674                                #introspect_key.to_string()
1675                            }
1676                            ));
1677                        } else {
1678                            value_variants.push(quote!( #name::#variant_name_spanned{#(#fields_names2,)*} => {
1679                                #return_value_name.to_string()
1680                            } ));
1681                        }
1682                        variants.push(quote!( #name::#variant_name_spanned{#(#fields_names1,)*} => {
1683                                #(#fields;)*
1684                            } ));
1685                        len_variants.push(quote!( #name::#variant_name_spanned{#(#fields_names3,)*} => {
1686                                #num_fields
1687                            } ));
1688                    }
1689                    &syn::Fields::Unnamed(ref fields_unnamed) => {
1690                        for (idx, f) in fields_unnamed.unnamed.iter().enumerate() {
1691                            field_infos.push(FieldInfo {
1692                                field_span: f.ty.span(),
1693                                ident: None,
1694                                index: idx as u32,
1695                                ty: &f.ty,
1696                                attrs: &f.attrs,
1697                            });
1698                        }
1699                        let (fields_names, fields, introspect_key) = implement_introspect(field_infos, false)?;
1700                        let fields_names1 = fields_names.clone();
1701                        let fields_names2 = fields_names.clone();
1702                        let fields_names3 = fields_names.clone();
1703                        let num_fields = fields_names3.len();
1704
1705                        if let Some(introspect_key) = introspect_key {
1706                            value_variants.push(quote!( #name::#variant_name_spanned(#(#fields_names1,)*) => {
1707                                    #introspect_key.to_string()
1708                            }));
1709                        } else {
1710                            value_variants.push(
1711                                quote!( #name::#variant_name_spanned(#(#fields_names2,)*) => #return_value_name.to_string() )
1712                            );
1713                        }
1714
1715                        variants.push(quote!( #name::#variant_name_spanned(#(#fields_names,)*) => { #(#fields;)* } ));
1716                        len_variants.push(quote!( #name::#variant_name_spanned(#(#fields_names3,)*) => {
1717                                #num_fields
1718                            } ));
1719                    }
1720                    &syn::Fields::Unit => {
1721                        //No fields
1722                        variants.push(quote! {
1723                            #name::#variant_name_spanned => {}
1724                        });
1725                        value_variants.push(quote!( #name::#variant_name_spanned => #return_value_name.to_string() ));
1726                        len_variants.push(quote!( #name::#variant_name_spanned => 0));
1727                    }
1728                }
1729
1730                //variants.push(quote!{})
1731            }
1732            quote! {
1733                #[allow(non_upper_case_globals)]
1734                #[allow(clippy::double_comparisons)]
1735                #[allow(clippy::manual_range_contains)]
1736                const #dummy_const: () = {
1737                    #uses
1738
1739                    #[automatically_derived]
1740                    #doc_hidden
1741                    impl #impl_generics #introspect for #name #ty_generics #where_clause #extra_where {
1742
1743                        #[allow(unused_mut)]
1744                        #[allow(unused_comparisons, unused_variables)]
1745                        fn introspect_value(&self) -> String {
1746                            match self {
1747                                #(#value_variants,)*
1748                            }
1749                        }
1750                        #[allow(unused_mut)]
1751                        #[allow(unused_comparisons, unused_variables)]
1752                        fn introspect_child(&self, index:usize) -> Option<Box<dyn #introspect_item_type+'_>> {
1753                            match self {
1754                                #(#variants,)*
1755                            }
1756                            return None;
1757                        }
1758                        #[allow(unused_mut)]
1759                        #[allow(unused_comparisons, unused_variables)]
1760                        fn introspect_len(&self) -> usize {
1761                            match self {
1762                                #(#len_variants,)*
1763                            }
1764                        }
1765
1766                    }
1767                };
1768            }
1769        }
1770        &syn::Data::Struct(ref struc) => {
1771            let fields;
1772            match &struc.fields {
1773                &syn::Fields::Named(ref namedfields) => {
1774                    let field_infos: Vec<FieldInfo> = namedfields
1775                        .named
1776                        .iter()
1777                        .enumerate()
1778                        .map(|(idx, field)| FieldInfo {
1779                            ident: Some(field.ident.clone().expect("Expected identifier[10]")),
1780                            field_span: field.ident.as_ref().unwrap().span(),
1781                            ty: &field.ty,
1782                            index: idx as u32,
1783                            attrs: &field.attrs,
1784                        })
1785                        .collect();
1786
1787                    fields = implement_introspect(field_infos, true)?;
1788                }
1789                &syn::Fields::Unnamed(ref fields_unnamed) => {
1790                    let field_infos: Vec<FieldInfo> = fields_unnamed
1791                        .unnamed
1792                        .iter()
1793                        .enumerate()
1794                        .map(|(idx, f)| FieldInfo {
1795                            ident: None,
1796                            field_span: f.ty.span(),
1797                            ty: &f.ty,
1798                            index: idx as u32,
1799                            attrs: &f.attrs,
1800                        })
1801                        .collect();
1802
1803                    fields = implement_introspect(field_infos, true)?;
1804                }
1805                &syn::Fields::Unit => {
1806                    fields = (Vec::new(), Vec::new(), None);
1807                }
1808            }
1809            let fields1 = fields.1;
1810            let introspect_key: Option<TokenStream> = fields.2;
1811            let field_count = fields1.len();
1812            let value_name;
1813            if let Some(introspect_key) = introspect_key {
1814                value_name = quote! { #introspect_key.to_string()};
1815            } else {
1816                value_name = quote! { stringify!(#name).to_string() };
1817            }
1818            quote! {
1819                #[allow(non_upper_case_globals)]
1820                #[allow(clippy::double_comparisons)]
1821                #[allow(clippy::manual_range_contains)]
1822                const #dummy_const: () = {
1823                    #uses
1824
1825                    #[automatically_derived]
1826                    #doc_hidden
1827                    impl #impl_generics #introspect for #name #ty_generics #where_clause #extra_where {
1828                        #[allow(unused_comparisons)]
1829                        #[allow(unused_mut, unused_variables)]
1830                        fn introspect_value(&self) -> String {
1831                            #value_name
1832                        }
1833                        #[allow(unused_comparisons)]
1834                        #[allow(unused_mut, unused_variables)]
1835                        fn introspect_child(&self, index: usize) -> Option<Box<dyn #introspect_item_type+'_>> {
1836                            #(#fields1;)*
1837                            return None;
1838                        }
1839                        fn introspect_len(&self) -> usize {
1840                            #field_count
1841                        }
1842                    }
1843                };
1844            }
1845        }
1846        _ => {
1847            abort_call_site!("Unsupported datatype");
1848        }
1849    };
1850
1851    Ok(expanded)
1852}
1853
1854#[allow(non_snake_case)]
1855fn implement_withschema(
1856    structname: &str,
1857    field_infos: Vec<FieldInfo>,
1858    is_enum: FieldOffsetStrategy,
1859    generics: &Generics,
1860    ty_generics: &TypeGenerics,
1861    impl_generics: &ImplGenerics,
1862) -> syn::Result<Vec<TokenStream>> {
1863    let span = proc_macro2::Span::call_site();
1864    let defspan = proc_macro2::Span::call_site();
1865    let local_version = quote_spanned! { defspan => local_version};
1866    let Field = quote_spanned! { defspan => _savefile::prelude::Field };
1867    let WithSchema = quote_spanned! { defspan => _savefile::prelude::WithSchema };
1868    let fields1 = quote_spanned! { defspan => fields1 };
1869
1870    let structname = Ident::new(structname, defspan);
1871    let offset_of = quote_spanned! {defspan=>
1872        _savefile::prelude::offset_of
1873    };
1874
1875    let fn_impl_generics = if !generics.params.is_empty() {
1876        quote! { :: #impl_generics}
1877    } else {
1878        quote! {}
1879    };
1880    let mut fields = Vec::new();
1881    for (idx, field) in field_infos.iter().enumerate() {
1882        let verinfo = parse_attr_tag(field.attrs)?;
1883        if verinfo.ignore {
1884            continue;
1885        }
1886        let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
1887
1888        let offset;
1889        match is_enum {
1890            FieldOffsetStrategy::EnumWithKnownOffsets(variant_index) => {
1891                offset = quote! { Some(get_variant_offsets #fn_impl_generics (#variant_index)[#idx]) };
1892            }
1893            FieldOffsetStrategy::EnumWithUnknownOffsets => {
1894                offset = quote! { None };
1895            }
1896            FieldOffsetStrategy::Struct => {
1897                if let Some(name) = field.ident.clone() {
1898                    offset = quote! { Some(#offset_of!(#structname #ty_generics, #name)) };
1899                } else {
1900                    let idx = Index::from(idx);
1901                    offset = quote! { Some(#offset_of!(#structname #ty_generics, #idx)) }
1902                };
1903            }
1904        }
1905
1906        let name_str = if let Some(name) = field.ident.clone() {
1907            name.to_string()
1908        } else {
1909            idx.to_string()
1910        };
1911        let removed = check_is_remove(field.ty);
1912        let field_type = &field.ty;
1913        if field_from_version == 0 && field_to_version == u32::MAX {
1914            if removed.is_removed() {
1915                abort!(
1916                    field.ty.span(),
1917                    "The Removed type can only be used for removed fields. Use the savefile_version attribute."
1918                );
1919            }
1920            fields.push(quote_spanned!( span => #fields1.push(unsafe{#Field::unsafe_new(#name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset)} )));
1921        } else {
1922            let mut version_mappings = Vec::new();
1923            let offset = if field_to_version != u32::MAX {
1924                quote!(None)
1925            } else {
1926                offset
1927            };
1928            for dt in verinfo.deserialize_types.iter() {
1929                let dt_from = dt.from;
1930                let dt_to = dt.to;
1931                let dt_field_type = syn::Ident::new(&dt.serialized_type, span);
1932                // We don't supply offset in this case, deserialized type doesn't match field type
1933                version_mappings.push(quote!{
1934                    if #local_version >= #dt_from && local_version <= #dt_to {
1935                        #fields1.push(#Field ::new( #name_str.to_string(), std::boxed::Box::new(<#dt_field_type as #WithSchema>::schema(#local_version, context))) );
1936                    }
1937                });
1938            }
1939
1940            fields.push(quote_spanned!( span =>
1941                #(#version_mappings)*
1942
1943                if #local_version >= #field_from_version && #local_version <= #field_to_version {
1944                    #fields1.push(unsafe{#Field ::unsafe_new( #name_str.to_string(), std::boxed::Box::new(<#field_type as #WithSchema>::schema(#local_version, context)), #offset )} );
1945                }
1946                ));
1947        }
1948    }
1949    Ok(fields)
1950}
1951
1952enum FieldOffsetStrategy {
1953    Struct,
1954    EnumWithKnownOffsets(usize /*variant index*/),
1955    EnumWithUnknownOffsets,
1956}
1957
1958fn get_phantomdata_types_from_type(ty: &Type, mut in_phantom: bool, generic_params_to_constraint: &mut HashSet<Ident>) {
1959    match ty {
1960        Type::Array(a) => {
1961            get_phantomdata_types_from_type(&*a.elem, in_phantom, generic_params_to_constraint);
1962        }
1963        Type::BareFn(f) => {
1964            match &f.output {
1965                ReturnType::Default => {}
1966                ReturnType::Type(_, ty) => {
1967                    get_phantomdata_types_from_type(&*ty, in_phantom, generic_params_to_constraint);
1968                }
1969            }
1970            for arg in &f.inputs {
1971                get_phantomdata_types_from_type(&arg.ty, in_phantom, generic_params_to_constraint);
1972            }
1973        }
1974        Type::Paren(ty) => {
1975            get_phantomdata_types_from_type(&*ty.elem, in_phantom, generic_params_to_constraint);
1976        }
1977        Type::Path(pd) => {
1978            if pd.path.segments.len() == 1 {
1979                let id = &pd.path.segments.last().unwrap().ident;
1980                if id == "PhantomData" {
1981                    in_phantom = true;
1982                }
1983                if !in_phantom {
1984                    generic_params_to_constraint.insert(id.clone());
1985                }
1986            }
1987            for param in &pd.path.segments {
1988                match &param.arguments {
1989                    PathArguments::None => {}
1990                    PathArguments::AngleBracketed(a) => {
1991                        for arg in &a.args {
1992                            match arg {
1993                                GenericArgument::Type(t) => {
1994                                    get_phantomdata_types_from_type(&*t, in_phantom, generic_params_to_constraint);
1995                                }
1996                                GenericArgument::AssocType(a) => {
1997                                    get_phantomdata_types_from_type(&a.ty, in_phantom, generic_params_to_constraint);
1998                                }
1999                                _ => {}
2000                            }
2001                        }
2002                    }
2003                    PathArguments::Parenthesized(a) => {
2004                        for arg in &a.inputs {
2005                            get_phantomdata_types_from_type(arg, in_phantom, generic_params_to_constraint);
2006                        }
2007                        match &a.output {
2008                            ReturnType::Default => {}
2009                            ReturnType::Type(_, ty) => {
2010                                get_phantomdata_types_from_type(&*ty, in_phantom, generic_params_to_constraint);
2011                            }
2012                        }
2013                    }
2014                }
2015            }
2016        }
2017        Type::Ptr(_) => {}
2018        Type::Reference(r) => {
2019            get_phantomdata_types_from_type(&*r.elem, in_phantom, generic_params_to_constraint);
2020        }
2021        Type::Slice(s) => {
2022            get_phantomdata_types_from_type(&*s.elem, in_phantom, generic_params_to_constraint);
2023        }
2024        Type::TraitObject(to) => {}
2025        Tuple(typ) => {
2026            for t in &typ.elems {
2027                get_phantomdata_types_from_type(t, in_phantom, generic_params_to_constraint);
2028            }
2029        }
2030        _ => {}
2031    }
2032}
2033fn get_phantomdata_types(data: &syn::Data, in_phantom: bool, generic_params_to_constrain: &mut HashSet<Ident>) {
2034    match data {
2035        Data::Struct(s) => {
2036            for field in s.fields.iter() {
2037                get_phantomdata_types_from_type(&field.ty, in_phantom, generic_params_to_constrain);
2038            }
2039        }
2040        Data::Enum(v) => {
2041            for variant in v.variants.iter() {
2042                for field in variant.fields.iter() {
2043                    get_phantomdata_types_from_type(&field.ty, in_phantom, generic_params_to_constrain);
2044                }
2045            }
2046        }
2047        Data::Union(_) => {}
2048    }
2049}
2050
2051#[allow(non_snake_case)]
2052fn savefile_derive_crate_withschema(input: DeriveInput) -> syn::Result<TokenStream> {
2053    //let mut have_u8 = false;
2054
2055    //let discriminant_size = discriminant_size.expect("Enum discriminant must be u8, u16 or u32. Use for example #[repr(u8)].");
2056
2057    let name = &input.ident;
2058    let doc_hidden = doc_hidden(&input.attrs);
2059
2060    let generics = &input.generics;
2061
2062    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
2063
2064    let extra_where = get_extra_where_clauses(&input, where_clause, quote! {_savefile::prelude::WithSchema});
2065
2066    let span = proc_macro2::Span::call_site();
2067    let defspan = proc_macro2::Span::call_site();
2068    let withschema = quote_spanned! {defspan=>
2069        _savefile::prelude::WithSchema
2070    };
2071
2072    let SchemaStruct = quote_spanned! { defspan => _savefile::prelude::SchemaStruct };
2073    let SchemaEnum = quote_spanned! { defspan => _savefile::prelude::SchemaEnum };
2074    let Schema = quote_spanned! { defspan => _savefile::prelude::Schema };
2075    let Field = quote_spanned! { defspan => _savefile::prelude::Field };
2076    let Variant = quote_spanned! { defspan => _savefile::prelude::Variant };
2077
2078    //let dummy_const = syn::Ident::new("_", proc_macro2::Span::call_site());
2079
2080    let expanded = match &input.data {
2081        &syn::Data::Enum(ref enum1) => {
2082            let max_variant_fields = enum1.variants.iter().map(|x| x.fields.len()).max().unwrap_or(0);
2083
2084            let enum_size = get_enum_size(&input.attrs, enum1.variants.len())?;
2085            let need_determine_offsets = enum_size.explicit_size;
2086
2087            let mut variants = Vec::new();
2088            let mut variant_field_offset_extractors = vec![];
2089            for (var_idx, variant) in enum1.variants.iter().enumerate() {
2090                /*if var_idx >= 256 {
2091                    panic!("Savefile does not support enums with 256 total variants. Sorry.");
2092                }*/
2093                let var_idx = var_idx as u8;
2094                let var_ident = variant.ident.clone();
2095                let variant_name = quote! { #var_ident };
2096                let variant_name_spanned = quote_spanned! { span => stringify!(#variant_name).to_string()};
2097
2098                let verinfo = parse_attr_tag(&variant.attrs)?;
2099                let (field_from_version, field_to_version) = (verinfo.version_from, verinfo.version_to);
2100
2101                if field_to_version != std::u32::MAX {
2102                    abort!(
2103                        variant.span(),
2104                        "Savefile automatic derive does not support removal of enum values."
2105                    );
2106                }
2107
2108                let mut field_infos = Vec::new();
2109
2110                let mut field_offset_extractors = vec![];
2111
2112                let offset_extractor_match_clause;
2113                match &variant.fields {
2114                    &syn::Fields::Named(ref fields_named) => {
2115                        let mut field_pattern = vec![];
2116                        for (idx, f) in fields_named.named.iter().enumerate() {
2117                            let field_name = f
2118                                .ident
2119                                .as_ref()
2120                                .expect("Enum variant with named fields *must* actually have a name")
2121                                .clone();
2122                            field_offset_extractors.push(quote!(unsafe { (#field_name as *const _ as *const u8).offset_from(base_ptr) as usize }));
2123                            field_pattern.push(field_name);
2124                            field_infos.push(FieldInfo {
2125                                ident: Some(f.ident.clone().expect("Expected identifier[1]")),
2126                                field_span: f.ident.as_ref().unwrap().span(),
2127                                ty: &f.ty,
2128                                index: idx as u32,
2129                                attrs: &f.attrs,
2130                            });
2131                        }
2132                        offset_extractor_match_clause = quote! {#name::#var_ident { #(#field_pattern,)* } };
2133                    }
2134                    &syn::Fields::Unnamed(ref fields_unnamed) => {
2135                        let mut field_pattern = vec![];
2136                        for (idx, f) in fields_unnamed.unnamed.iter().enumerate() {
2137                            let field_binding = Ident::new(&format!("x{}", idx), Span::call_site());
2138                            field_pattern.push(field_binding.clone());
2139                            field_offset_extractors.push(quote!(unsafe { (#field_binding as *const _ as *const u8).offset_from(base_ptr) as usize }));
2140                            field_infos.push(FieldInfo {
2141                                ident: None,
2142                                field_span: f.ty.span(),
2143                                index: idx as u32,
2144                                ty: &f.ty,
2145                                attrs: &f.attrs,
2146                            });
2147                        }
2148                        offset_extractor_match_clause = quote! {#name::#var_ident ( #(#field_pattern,)* ) };
2149                    }
2150                    &syn::Fields::Unit => {
2151                        offset_extractor_match_clause = quote! {#name::#var_ident};
2152                        //No fields
2153                    }
2154                }
2155                while field_offset_extractors.len() < max_variant_fields {
2156                    field_offset_extractors.push(quote! {0});
2157                }
2158
2159                variant_field_offset_extractors.push(quote! {
2160                   #offset_extractor_match_clause => {
2161                       [ #(#field_offset_extractors,)* ]
2162                   }
2163                });
2164
2165                let field_offset_strategy = if need_determine_offsets && field_infos.is_empty() == false {
2166                    FieldOffsetStrategy::EnumWithKnownOffsets(var_idx as usize)
2167                } else {
2168                    FieldOffsetStrategy::EnumWithUnknownOffsets
2169                };
2170
2171                let fields = implement_withschema(
2172                    &name.to_string(),
2173                    field_infos,
2174                    field_offset_strategy,
2175                    &generics,
2176                    &ty_generics,
2177                    &impl_generics,
2178                )?;
2179
2180                variants.push(quote! {
2181                (#field_from_version,
2182                 #field_to_version,
2183                 #Variant { name: #variant_name_spanned, discriminant: #var_idx, fields:
2184                    {
2185                        let mut fields1 = Vec::<#Field>::new();
2186                        #(#fields;)*
2187                        fields1
2188                    }}
2189                )});
2190            }
2191
2192            let field_offset_impl;
2193            if need_determine_offsets {
2194                let varbuf_assign;
2195                if enum_size.discriminant_size == 1 {
2196                    varbuf_assign = quote!( varbuf[0] = variant as u8; );
2197                } else if enum_size.discriminant_size == 2 {
2198                    // We only support little endian
2199                    varbuf_assign = quote!(
2200                        varbuf[0] = variant as u8;
2201                        varbuf[1] = (variant>>8) as u8;
2202                    );
2203                } else if enum_size.discriminant_size == 4 {
2204                    // We only support little endian
2205                    varbuf_assign = quote!(
2206                        varbuf[0] = variant as u8;
2207                        varbuf[1] = (variant>>8) as u8;
2208                        varbuf[2] = (variant>>16) as u8;
2209                        varbuf[3] = (variant>>24) as u8;
2210                    );
2211                } else {
2212                    abort_call_site!("Unsupported enum size: {}", enum_size.discriminant_size);
2213                }
2214                let not_const_if_gen = if generics.params.is_empty() {
2215                    quote! {const}
2216                } else {
2217                    quote! {}
2218                };
2219                let conjure_variant;
2220                if generics.params.is_empty() {
2221                    conjure_variant = quote! {
2222                        let mut varbuf = [0u8;std::mem::size_of::<#name #ty_generics>()];
2223                        #varbuf_assign
2224                        let mut value : MaybeUninit<#name #ty_generics> = unsafe { std::mem::transmute(varbuf) };
2225                    }
2226                } else {
2227                    let discr_type;
2228                    match enum_size.discriminant_size {
2229                        1 => discr_type = quote! { u8 },
2230                        2 => discr_type = quote! { u16 },
2231                        4 => discr_type = quote! { u32 },
2232                        _ => unreachable!(),
2233                    }
2234                    conjure_variant = quote! {
2235                        let mut value = MaybeUninit::< #name #ty_generics >::uninit();
2236                        let discr: *mut #discr_type = &mut value as *mut MaybeUninit<#name #ty_generics> as *mut #discr_type;
2237                        unsafe {
2238                            *discr = variant as #discr_type;
2239                        }
2240                    }
2241                }
2242
2243                field_offset_impl = quote! {
2244                    #not_const_if_gen fn get_field_offset_impl #impl_generics (value: &#name #ty_generics) -> [usize;#max_variant_fields] {
2245                        assert!(std::mem::size_of::<#name #ty_generics>()>0);
2246                        let base_ptr = value as *const #name #ty_generics as *const u8;
2247                        match value {
2248                            #(#variant_field_offset_extractors)*
2249                        }
2250                    }
2251                    #not_const_if_gen fn get_variant_offsets #impl_generics(variant: usize) -> [usize;#max_variant_fields] {
2252                        #conjure_variant
2253                        //let base_ptr = &mut value as *mut MaybeUninit<#name> as *mut u8;
2254                        //unsafe { *base_ptr = variant as u8; }
2255                        get_field_offset_impl(unsafe { &*(&value as *const MaybeUninit<#name #ty_generics> as *const #name #ty_generics) } )
2256                    }
2257                };
2258            } else {
2259                field_offset_impl = quote! {};
2260            }
2261
2262            let discriminant_size = enum_size.discriminant_size;
2263            let has_explicit_repr = enum_size.repr_c;
2264
2265            quote! {
2266                #field_offset_impl
2267                #[automatically_derived]
2268                #doc_hidden
2269                impl #impl_generics #withschema for #name #ty_generics #where_clause #extra_where {
2270                    #[allow(unused_mut)]
2271                    #[allow(unused_comparisons, unused_variables)]
2272                    fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema {
2273                        let local_version = version;
2274
2275                        #Schema::Enum (
2276                            unsafe{#SchemaEnum::new_unsafe(
2277                                stringify!(#name).to_string(),
2278                                (vec![#(#variants),*]).into_iter().filter_map(|(fromver,tover,x)|{
2279                                    if local_version >= fromver && local_version <= tover {
2280                                        Some(x)
2281                                    } else {
2282                                        None
2283                                    }
2284                                }).collect(),
2285                                #discriminant_size,
2286                                #has_explicit_repr,
2287                                Some(std::mem::size_of::<#name #ty_generics>()),
2288                                Some(std::mem::align_of::<#name #ty_generics>()),
2289                            )}
2290                        )
2291                    }
2292                }
2293
2294            }
2295        }
2296        &syn::Data::Struct(ref struc) => {
2297            let fields;
2298            match &struc.fields {
2299                &syn::Fields::Named(ref namedfields) => {
2300                    let field_infos: Vec<FieldInfo> = namedfields
2301                        .named
2302                        .iter()
2303                        .enumerate()
2304                        .map(|(idx, field)| FieldInfo {
2305                            ident: Some(field.ident.clone().expect("Expected identifier[2]")),
2306                            field_span: field.ident.span(),
2307                            ty: &field.ty,
2308                            index: idx as u32,
2309                            attrs: &field.attrs,
2310                        })
2311                        .collect();
2312
2313                    fields = implement_withschema(
2314                        &name.to_string(),
2315                        field_infos,
2316                        FieldOffsetStrategy::Struct,
2317                        &generics,
2318                        &ty_generics,
2319                        &impl_generics,
2320                    )?;
2321                }
2322                &syn::Fields::Unnamed(ref fields_unnamed) => {
2323                    let field_infos: Vec<FieldInfo> = fields_unnamed
2324                        .unnamed
2325                        .iter()
2326                        .enumerate()
2327                        .map(|(idx, f)| FieldInfo {
2328                            field_span: f.ty.span(),
2329                            ident: None,
2330                            index: idx as u32,
2331                            ty: &f.ty,
2332                            attrs: &f.attrs,
2333                        })
2334                        .collect();
2335                    fields = implement_withschema(
2336                        &name.to_string(),
2337                        field_infos,
2338                        FieldOffsetStrategy::Struct,
2339                        &generics,
2340                        &ty_generics,
2341                        &impl_generics,
2342                    )?;
2343                }
2344                &syn::Fields::Unit => {
2345                    fields = Vec::new();
2346                }
2347            }
2348            quote! {
2349                #[automatically_derived]
2350                #doc_hidden
2351                impl #impl_generics #withschema for #name #ty_generics #where_clause #extra_where {
2352
2353                    #[allow(unused_comparisons)]
2354                    #[allow(unused_mut, unused_variables)]
2355                    fn schema(version:u32, context: &mut _savefile::prelude::WithSchemaContext) -> #Schema {
2356                        let local_version = version;
2357                        let mut fields1 = Vec::new();
2358                        #(#fields;)* ;
2359                        #Schema::Struct(unsafe{#SchemaStruct::new_unsafe(
2360                            stringify!(#name).to_string(),
2361                            fields1,
2362                            Some(std::mem::size_of::<#name #ty_generics>()),
2363                            Some(std::mem::align_of::<#name #ty_generics>()),
2364                        )})
2365
2366                    }
2367                }
2368            }
2369        }
2370        _ => {
2371            abort_call_site!("Unsupported datatype");
2372        }
2373    };
2374    // For debugging, uncomment to write expanded procmacro to file
2375    //std::fs::write(format!("/home/anders/savefile/savefile-abi-min-lib/src/expanded.rs"),expanded.to_string()).unwrap();
2376
2377    Ok(expanded)
2378}