ouroboros_macro/generate/
with.rs

1use crate::info_structures::{FieldType, Options, StructInfo};
2use proc_macro2::{Span, TokenStream};
3use quote::quote;
4use syn::{Error, Lifetime, WhereClause};
5
6pub fn make_with_all_function(
7    info: &StructInfo,
8    options: Options,
9) -> Result<(TokenStream, TokenStream), Error> {
10    let visibility = if options.do_pub_extras {
11        info.vis.clone()
12    } else {
13        syn::parse_quote! { pub(super) }
14    };
15    let mut fields = Vec::new();
16    let mut field_assignments = Vec::new();
17    // I don't think the reverse is necessary but it does make the expanded code more uniform.
18    for field in info.fields.iter().rev() {
19        let field_name = &field.name;
20        let field_type = &field.typ;
21        if field.field_type == FieldType::Tail {
22            fields.push(quote! { #visibility #field_name: &'outer_borrow #field_type });
23            field_assignments.push(quote! { #field_name: &this.#field_name });
24        } else if field.field_type == FieldType::Borrowed {
25            let ass = quote! { #field_name: unsafe {
26                ::ouroboros::macro_help::change_lifetime(
27                    &*this.#field_name
28                )
29            } };
30            fields.push(quote! { #visibility #field_name: &'this #field_type });
31            field_assignments.push(ass.clone());
32        } else if field.field_type == FieldType::BorrowedMut {
33            // Add nothing because we cannot borrow something that has already been mutably
34            // borrowed.
35        }
36    }
37
38    for (ty, ident) in info.generic_consumers() {
39        fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
40        field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
41    }
42    let new_generic_params = info.borrowed_generic_params();
43    let new_generic_args = info.borrowed_generic_arguments();
44
45    let struct_documentation = format!(
46        concat!(
47            "A struct for holding immutable references to all ",
48            "[tail and immutably borrowed fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ",
49            "[`{0}`]({0})."
50        ),
51        info.ident.to_string()
52    );
53    let ltname = format!("'{}", info.fake_lifetime());
54    let lifetime = Lifetime::new(&ltname, Span::call_site());
55    let generic_where = if let Some(clause) = &info.generics.where_clause {
56        let mut clause = clause.clone();
57        let extra: WhereClause = syn::parse_quote! { where #lifetime: 'this };
58        clause
59            .predicates
60            .push(extra.predicates.first().unwrap().clone());
61        let extra: WhereClause = syn::parse_quote! { where 'this: 'outer_borrow };
62        clause
63            .predicates
64            .push(extra.predicates.first().unwrap().clone());
65        clause
66    } else {
67        syn::parse_quote! { where #lifetime: 'this, 'this: 'outer_borrow }
68    };
69    let struct_defs = quote! {
70        #[doc=#struct_documentation]
71        #visibility struct BorrowedFields #new_generic_params #generic_where { #(#fields),* }
72    };
73    let borrowed_fields_type = quote! { BorrowedFields<#(#new_generic_args),*> };
74    let documentation = concat!(
75        "This method provides immutable references to all ",
76        "[tail and immutably borrowed fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).",
77    );
78    let documentation = if !options.do_no_doc {
79        quote! {
80            #[doc=#documentation]
81        }
82    } else {
83        quote! { #[doc(hidden)] }
84    };
85    let fn_defs = quote! {
86        #documentation
87        #[inline(always)]
88        #visibility fn with <'outer_borrow, ReturnType>(
89            &'outer_borrow self,
90            user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType
91        ) -> ReturnType {
92            let this = unsafe { self.actual_data.assume_init_ref() };
93            user(BorrowedFields {
94                #(#field_assignments),*
95            })
96        }
97    };
98    Ok((struct_defs, fn_defs))
99}