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