ouroboros_macro/
info_structures.rs

1use crate::utils::{make_generic_arguments, make_generic_consumers, replace_this_with_lifetime};
2use proc_macro2::{Ident, TokenStream};
3use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
4use quote::{format_ident, quote, ToTokens};
5use syn::{
6    punctuated::Punctuated, token::Comma, Attribute, ConstParam, Error, GenericParam, Generics,
7    LifetimeParam, Type, TypeParam, Visibility, spanned::Spanned, 
8};
9
10#[derive(Clone, Copy)]
11pub struct Options {
12    pub do_no_doc: bool,
13    pub do_pub_extras: bool,
14}
15
16impl Options {
17    // pub fn documentation_to_tokens(&self, documentation: &str) -> TokenStream {
18    //     if self.do_no_doc {
19    //         quote! { #[doc(hidden)] }
20    //     } else {
21    //         quote! { #[doc=#documentation] }
22    //     }
23    // }
24}
25
26#[derive(Clone, Copy, PartialEq)]
27pub enum FieldType {
28    /// Not borrowed by other parts of the struct.
29    Tail,
30    /// Immutably borrowed by at least one other field.
31    Borrowed,
32    /// Mutably borrowed by one other field.
33    BorrowedMut,
34}
35
36impl FieldType {
37    pub fn is_tail(self) -> bool {
38        self == Self::Tail
39    }
40}
41
42#[derive(Clone)]
43pub struct BorrowRequest {
44    pub index: usize,
45    pub mutable: bool,
46}
47
48#[derive(Clone)]
49pub enum Derive {
50    Debug,
51    PartialEq,
52    Eq,
53}
54
55#[derive(Copy, Clone)]
56pub enum BuilderType {
57    Sync,
58    Async,
59    AsyncSend,
60}
61
62impl BuilderType {
63    pub fn is_async(&self) -> bool {
64        !matches!(self, BuilderType::Sync)
65    }
66}
67
68#[derive(Clone)]
69pub struct StructInfo {
70    pub derives: Vec<Derive>,
71    pub ident: Ident,
72    pub internal_ident: Ident,
73    pub generics: Generics,
74    pub vis: Visibility,
75    pub fields: Vec<StructFieldInfo>,
76    pub first_lifetime: Ident,
77    pub attributes: Vec<Attribute>,
78}
79
80impl StructInfo {
81    // The lifetime to use in place of 'this for internal implementations,
82    // should never be exposed to the user.
83    pub fn fake_lifetime(&self) -> Ident {
84        self.first_lifetime.clone()
85    }
86
87    pub fn generic_params(&self) -> &Punctuated<GenericParam, Comma> {
88        &self.generics.params
89    }
90
91    /// Same as generic_params but with 'this and 'outer_borrow prepended.
92    pub fn borrowed_generic_params(&self) -> TokenStream {
93        if self.generic_params().is_empty() {
94            quote! { <'outer_borrow, 'this> }
95        } else {
96            let mut new_generic_params = self.generic_params().clone();
97            new_generic_params.insert(0, syn::parse_quote! { 'this });
98            new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow });
99            quote! { <#new_generic_params> }
100        }
101    }
102
103    /// Same as generic_params but without bounds and with '_ prepended twice.
104    pub fn borrowed_generic_params_inferred(&self) -> TokenStream {
105        use GenericParam::*;
106        let params = self.generic_params().iter().map(|p| match p {
107            Type(TypeParam { ident, .. }) | Const(ConstParam { ident, .. }) => {
108                ident.to_token_stream()
109            }
110            Lifetime(LifetimeParam { lifetime, .. }) => lifetime.to_token_stream(),
111        });
112        quote! { <'_, '_, #(#params,)*> }
113    }
114
115    pub fn generic_arguments(&self) -> Vec<TokenStream> {
116        make_generic_arguments(self.generics.params.iter().collect())
117    }
118
119    /// Same as generic_arguments but with 'outer_borrow and 'this prepended.
120    pub fn borrowed_generic_arguments(&self) -> Vec<TokenStream> {
121        let mut args = self.generic_arguments();
122        args.insert(0, quote! { 'this });
123        args.insert(0, quote! { 'outer_borrow });
124        args
125    }
126
127    pub fn generic_consumers(&self) -> impl Iterator<Item = (TokenStream, Ident)> {
128        make_generic_consumers(&self.generics)
129    }
130}
131
132#[derive(Clone)]
133pub struct StructFieldInfo {
134    pub name: Ident,
135    pub typ: Type,
136    pub field_type: FieldType,
137    pub vis: Visibility,
138    pub borrows: Vec<BorrowRequest>,
139    /// If this is true and borrows is empty, the struct will borrow from self in the future but
140    /// does not require a builder to be initialized. It should not be able to be removed from the
141    /// struct with into_heads.
142    pub self_referencing: bool,
143    /// If it is None, the user has not specified whether or not the field is covariant. If this is
144    /// Some(false), we should avoid making borrow_* or borrow_*_mut functions as they will not
145    /// be able to compile.
146    pub covariant: Option<bool>,
147}
148
149#[derive(Clone)]
150pub enum ArgType {
151    /// Used when the initial value of a field can be passed directly into the constructor.
152    Plain(TokenStream),
153    /// Used when a field requires self references and thus requires something that implements
154    /// a builder function trait instead of a simple plain type.
155    TraitBound(TokenStream),
156}
157
158impl StructFieldInfo {
159    pub fn builder_name(&self) -> Ident {
160        format_ident!("{}_builder", self.name)
161    }
162
163    pub fn illegal_ref_name(&self) -> Ident {
164        format_ident!("{}_illegal_static_reference", self.name)
165    }
166
167    pub fn is_borrowed(&self) -> bool {
168        self.field_type != FieldType::Tail
169    }
170
171    pub fn is_mutably_borrowed(&self) -> bool {
172        self.field_type == FieldType::BorrowedMut
173    }
174
175    pub fn boxed(&self) -> TokenStream {
176        let name = &self.name;
177        quote! { ::ouroboros::macro_help::aliasable_boxed(#name) }
178    }
179
180    pub fn stored_type(&self) -> TokenStream {
181        let t = &self.typ;
182        if self.is_borrowed() {
183            quote! { ::ouroboros::macro_help::AliasableBox<#t> }
184        } else {
185            quote! { #t }
186        }
187    }
188
189    /// Returns code which takes a variable with the same name and type as this field and turns it
190    /// into a static reference to its dereffed contents.
191    pub fn make_illegal_static_reference(&self) -> TokenStream {
192        let field_name = &self.name;
193        let ref_name = self.illegal_ref_name();
194        quote! {
195            let #ref_name = unsafe {
196                ::ouroboros::macro_help::change_lifetime(&*#field_name)
197            };
198        }
199    }
200
201    /// Like make_illegal_static_reference, but provides a mutable reference instead.
202    pub fn make_illegal_static_mut_reference(&self) -> TokenStream {
203        let field_name = &self.name;
204        let ref_name = self.illegal_ref_name();
205        quote! {
206            let #ref_name = unsafe {
207                ::ouroboros::macro_help::change_lifetime_mut(&mut *#field_name)
208            };
209        }
210    }
211
212    /// Generates an error requesting that the user explicitly specify whether or not the
213    /// field's type is covariant.
214    #[must_use]
215    pub fn covariance_error(&self) -> Diagnostic {
216        let error = concat!(
217            "Ouroboros cannot automatically determine if this type is covariant.\n\n",
218            "As an example, a Box<&'this ()> is covariant because it can be used as a\n",
219            "Box<&'smaller ()> for any lifetime smaller than 'this. In contrast,\n",
220            "a Fn(&'this ()) is not covariant because it cannot be used as a\n",
221            "Fn(&'smaller ()). In general, values that are safe to use with smaller\n",
222            "lifetimes than they were defined with are covariant, breaking this \n",
223            "guarantee means the value is not covariant.\n\n",
224            "To resolve this error, add #[covariant] or #[not_covariant] to the field.\n",
225        );
226        self.typ.span().error(error)
227    }
228
229    pub fn make_constructor_arg_type_impl(
230        &self,
231        info: &StructInfo,
232        make_builder_return_type: impl FnOnce() -> TokenStream,
233    ) -> Result<ArgType, Error> {
234        let field_type = &self.typ;
235        let fake_lifetime = info.fake_lifetime();
236        if self.borrows.is_empty() {
237            // Even if self_referencing is true, as long as borrows is empty, we don't need to use a
238            // builder to construct it.
239            let field_type =
240                replace_this_with_lifetime(field_type.into_token_stream(), fake_lifetime.clone());
241            Ok(ArgType::Plain(quote! { #field_type }))
242        } else {
243            let mut field_builder_params = Vec::new();
244            for borrow in &self.borrows {
245                if borrow.mutable {
246                    let field = &info.fields[borrow.index];
247                    let field_type = &field.typ;
248                    field_builder_params.push(quote! {
249                        &'this mut #field_type
250                    });
251                } else {
252                    let field = &info.fields[borrow.index];
253                    let field_type = &field.typ;
254                    field_builder_params.push(quote! {
255                        &'this #field_type
256                    });
257                }
258            }
259            let return_type = make_builder_return_type();
260            let bound = quote! { for<'this> ::core::ops::FnOnce(#(#field_builder_params),*) -> #return_type };
261            Ok(ArgType::TraitBound(bound))
262        }
263    }
264
265    /// Returns a trait bound if `for_field` refers to any other fields, and a plain type if not. This
266    /// is the type used in the constructor to initialize the value of `for_field`.
267    pub fn make_constructor_arg_type(
268        &self,
269        info: &StructInfo,
270        builder_type: BuilderType,
271    ) -> Result<ArgType, Error> {
272        let field_type = &self.typ;
273        let return_ty_constructor = || match builder_type {
274            BuilderType::AsyncSend => {
275                quote! {
276                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
277                        dyn ::core::future::Future<Output=#field_type> + ::core::marker::Send + 'this>>
278                }
279            }
280            BuilderType::Async => {
281                quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
282                dyn ::core::future::Future<Output=#field_type> + 'this>> }
283            }
284            BuilderType::Sync => quote! { #field_type },
285        };
286        self.make_constructor_arg_type_impl(info, return_ty_constructor)
287    }
288
289    /// Like make_constructor_arg_type, but used for the try_new constructor.
290    pub fn make_try_constructor_arg_type(
291        &self,
292        info: &StructInfo,
293        builder_type: BuilderType,
294    ) -> Result<ArgType, Error> {
295        let field_type = &self.typ;
296        let return_ty_constructor = || match builder_type {
297            BuilderType::AsyncSend => {
298                quote! {
299                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
300                        dyn ::core::future::Future<Output=::core::result::Result<#field_type, Error_>>
301                            + ::core::marker::Send + 'this>>
302                }
303            }
304            BuilderType::Async => {
305                quote! {
306                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
307                        dyn ::core::future::Future<Output=::core::result::Result<#field_type, Error_>>
308                            + 'this>>
309                }
310            }
311            BuilderType::Sync => quote! { ::core::result::Result<#field_type, Error_> },
312        };
313        self.make_constructor_arg_type_impl(info, return_ty_constructor)
314    }
315}