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 }
25
26#[derive(Clone, Copy, PartialEq)]
27pub enum FieldType {
28 Tail,
30 Borrowed,
32 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 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 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 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 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 pub self_referencing: bool,
143 pub covariant: Option<bool>,
147}
148
149#[derive(Clone)]
150pub enum ArgType {
151 Plain(TokenStream),
153 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 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 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 #[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 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 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 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}