ouroboros_macro/generate/
with_mut.rs
1use crate::{
2 info_structures::{FieldType, Options, StructInfo},
3 utils::{replace_this_with_lifetime, uses_this_lifetime},
4};
5use proc_macro2::{Span, TokenStream};
6use quote::{format_ident, quote};
7use syn::{Error, Lifetime, WhereClause};
8
9pub fn make_with_all_mut_function(
10 info: &StructInfo,
11 options: Options,
12) -> Result<(TokenStream, TokenStream), Error> {
13 let visibility = if options.do_pub_extras {
14 info.vis.clone()
15 } else {
16 syn::parse_quote! { pub(super) }
17 };
18 let mut mut_fields = Vec::new();
19 let mut mut_field_assignments = Vec::new();
20 let mut lifetime_idents = Vec::new();
21 for (index, field) in info.fields.iter().rev().enumerate() {
23 let field_name = &field.name;
24 let original_field_type = &field.typ;
25 let lifetime = format_ident!("this{}", index);
26 let field_type = replace_this_with_lifetime(quote! { #original_field_type }, lifetime.clone());
27 if field.field_type == FieldType::Tail {
28 mut_fields.push(quote! { #visibility #field_name: &'outer_borrow mut #field_type });
29 mut_field_assignments.push(quote! { #field_name: &mut this.#field_name });
30 if uses_this_lifetime(quote! { #original_field_type }) {
31 lifetime_idents.push(lifetime.clone());
32 }
33 } else if field.field_type == FieldType::Borrowed {
34 let ass = quote! { #field_name: unsafe {
35 ::ouroboros::macro_help::change_lifetime(
36 &*this.#field_name
37 )
38 } };
39 let lt = Lifetime::new(&format!("'{}", lifetime), Span::call_site());
40 mut_fields.push(quote! { #visibility #field_name: &#lt #field_type });
41 mut_field_assignments.push(ass);
42 lifetime_idents.push(lifetime.clone());
43 } else if field.field_type == FieldType::BorrowedMut {
44 }
47 }
48
49 for (ty, ident) in info.generic_consumers() {
50 mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
51 mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
52 }
53
54 let mut new_generic_params = info.generic_params().clone();
55 for lt in &lifetime_idents {
56 let lt = Lifetime::new(&format!("'{}", lt), Span::call_site());
57 new_generic_params.insert(0, syn::parse_quote! { #lt });
58 }
59 new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow });
60 let mut new_generic_args = info.generic_arguments();
61 let mut lifetimes = Vec::new();
62 for lt in &lifetime_idents {
63 let lt = Lifetime::new(&format!("'{}", lt), Span::call_site());
64 lifetimes.push(lt.clone());
65 new_generic_args.insert(0, quote! { #lt });
66 }
67 new_generic_args.insert(0, quote! { 'outer_borrow });
68
69 let mut_struct_documentation = format!(
70 concat!(
71 "A struct for holding mutable references to all ",
72 "[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ",
73 "[`{0}`]({0})."
74 ),
75 info.ident.to_string()
76 );
77 let fake_lifetime = Lifetime::new(&format!("'{}", info.fake_lifetime()), Span::call_site());
78 let mut generic_where = if let Some(clause) = &info.generics.where_clause {
79 clause.clone()
80 } else {
81 syn::parse_quote! { where }
82 };
83 for lt in &lifetime_idents {
84 let lt = Lifetime::new(&format!("'{}", lt), Span::call_site());
85 let extra: WhereClause = syn::parse_quote! { where #fake_lifetime: #lt };
86 generic_where
87 .predicates
88 .extend(extra.predicates.into_iter());
89 }
90 for idents in lifetime_idents.windows(2) {
91 let lt = Lifetime::new(&format!("'{}", idents[1]), Span::call_site());
92 let outlives = Lifetime::new(&format!("'{}", idents[0]), Span::call_site());
93 let extra: WhereClause = syn::parse_quote! { where #lt: #outlives };
94 generic_where
95 .predicates
96 .extend(extra.predicates.into_iter());
97 }
98 let struct_defs = quote! {
99 #[doc=#mut_struct_documentation]
100 #visibility struct BorrowedMutFields <#new_generic_params> #generic_where { #(#mut_fields),* }
101 };
102 let borrowed_mut_fields_type = quote! { BorrowedMutFields<#(#new_generic_args),*> };
103 let mut_documentation = concat!(
104 "This method provides mutable references to all ",
105 "[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).",
106 );
107 let mut_documentation = if !options.do_no_doc {
108 quote! {
109 #[doc=#mut_documentation]
110 }
111 } else {
112 quote! { #[doc(hidden)] }
113 };
114 let fn_defs = quote! {
115 #mut_documentation
116 #[inline(always)]
117 #visibility fn with_mut <'outer_borrow, ReturnType>(
118 &'outer_borrow mut self,
119 user: impl for<#(#lifetimes),*> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType
120 ) -> ReturnType {
121 let this = unsafe { self.actual_data.assume_init_mut() };
122 user(BorrowedMutFields {
123 #(#mut_field_assignments),*
124 })
125 }
126 };
127 Ok((struct_defs, fn_defs))
128}