ouroboros_macro-0.14.0/.cargo_vcs_info.json0000644000000001110000000000100143210ustar { "git": { "sha1": "386c37a7eb10b408d28aa487f000b2ddc3ddf876" } }ouroboros_macro-0.14.0/Cargo.toml0000644000000020410000000000100123230ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "ouroboros_macro" version = "0.14.0" authors = ["Joshua Maros "] description = "Proc macro for ouroboros crate." documentation = "https://docs.rs/ouroboros_macro" license = "MIT OR Apache-2.0" repository = "https://github.com/joshua-maros/ouroboros" [lib] proc-macro = true [dependencies.Inflector] version = "0.11" default-features = false [dependencies.proc-macro-error] version = "1.0.4" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" features = ["full"] ouroboros_macro-0.14.0/Cargo.toml.orig000064400000000000000000000010050072674642500160330ustar 00000000000000[package] name = "ouroboros_macro" version = "0.14.0" authors = ["Joshua Maros "] edition = "2018" license = "MIT OR Apache-2.0" description = "Proc macro for ouroboros crate." documentation = "https://docs.rs/ouroboros_macro" repository = "https://github.com/joshua-maros/ouroboros" [lib] proc-macro = true [dependencies] Inflector = { version = "0.11", default-features = false } proc-macro2 = "1.0" proc-macro-error = "1.0.4" quote = "1.0" syn = { version = "1.0", features = ["full"] } ouroboros_macro-0.14.0/src/covariance_detection.rs000064400000000000000000000125040072674642500204570ustar 00000000000000use quote::ToTokens; use syn::{GenericArgument, PathArguments, Type}; use crate::utils::uses_this_lifetime; const STD_CONTAINER_TYPES: &[&str] = &["Box", "Arc", "Rc"]; /// Returns Some((type_name, element_type)) if the provided type appears to be Box, Arc, or Rc from /// the standard library. Returns None if not. pub fn apparent_std_container_type(raw_type: &Type) -> Option<(&'static str, &Type)> { let tpath = if let Type::Path(x) = raw_type { x } else { return None; }; let segment = if let Some(segment) = tpath.path.segments.last() { segment } else { return None; }; let args = if let PathArguments::AngleBracketed(args) = &segment.arguments { args } else { return None; }; if args.args.len() != 1 { return None; } let arg = args.args.first().unwrap(); let eltype = if let GenericArgument::Type(x) = arg { x } else { return None; }; for type_name in STD_CONTAINER_TYPES { if segment.ident == type_name { return Some((type_name, eltype)); } } None } /// Returns Some(true or false) if the type is known to be covariant / not covariant. pub fn type_is_covariant_over_this_lifetime(ty: &syn::Type) -> Option { use syn::Type::*; // If the type never uses the 'this lifetime, we don't have to // worry about it not being covariant. if !uses_this_lifetime(ty.to_token_stream()) { return Some(true); } match ty { Array(arr) => type_is_covariant_over_this_lifetime(&*arr.elem), BareFn(f) => { debug_assert!(uses_this_lifetime(f.to_token_stream())); None } Group(ty) => type_is_covariant_over_this_lifetime(&ty.elem), ImplTrait(..) => None, // Unusable in struct definition. Infer(..) => None, // Unusable in struct definition. Macro(..) => None, // We don't know what the macro will resolve to. Never(..) => None, Paren(ty) => type_is_covariant_over_this_lifetime(&ty.elem), Path(path) => { if let Some(qself) = &path.qself { if !type_is_covariant_over_this_lifetime(&qself.ty)? { return Some(false); } } let mut all_parameters_are_covariant = false; // If the type is Box, Arc, or Rc, we can assume it to be covariant. if apparent_std_container_type(ty).is_some() { all_parameters_are_covariant = true; } for segment in path.path.segments.iter() { let args = &segment.arguments; if let syn::PathArguments::AngleBracketed(args) = &args { for arg in args.args.iter() { if let syn::GenericArgument::Type(ty) = arg { if all_parameters_are_covariant { if !type_is_covariant_over_this_lifetime(ty)? { return Some(false); } } else { if uses_this_lifetime(ty.to_token_stream()) { return None; } } } else if let syn::GenericArgument::Lifetime(lt) = arg { if lt.ident.to_string() == "this" && !all_parameters_are_covariant { return None; } } } } else if let syn::PathArguments::Parenthesized(args) = &args { for arg in args.inputs.iter() { if uses_this_lifetime(arg.to_token_stream()) { return None; } } if let syn::ReturnType::Type(_, ty) = &args.output { if uses_this_lifetime(ty.to_token_stream()) { return None; } } } } Some(true) } Ptr(ptr) => { if ptr.mutability.is_some() { Some(false) } else { type_is_covariant_over_this_lifetime(&ptr.elem) } } // Ignore the actual lifetime of the reference because Rust can automatically convert those. Reference(rf) => { if rf.mutability.is_some() { Some(!uses_this_lifetime(rf.elem.to_token_stream())) } else { type_is_covariant_over_this_lifetime(&rf.elem) } } Slice(sl) => type_is_covariant_over_this_lifetime(&sl.elem), TraitObject(..) => None, Tuple(tup) => { let mut result = Some(true); for ty in tup.elems.iter() { match type_is_covariant_over_this_lifetime(ty) { Some(true) => (), Some(false) => return Some(false), None => result = None, } } result } // As of writing this, syn parses all the types we could need. However, // just to be safe, return that we don't know if it's covariant. Verbatim(..) => None, _ => None, } } ouroboros_macro-0.14.0/src/generate/constructor.rs000064400000000000000000000173770072674642500205030ustar 00000000000000use crate::{ info_structures::{ArgType, FieldType, Options, StructInfo}, utils::to_class_case, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use syn::Error; pub fn create_builder_and_constructor( info: &StructInfo, options: Options, make_async: bool, ) -> Result<(Ident, TokenStream, TokenStream), Error> { let struct_name = info.ident.clone(); let generic_args = info.generic_arguments(); let vis = if options.do_pub_extras { info.vis.clone() } else { syn::parse_quote! { pub(super) } }; let builder_struct_name = if make_async { format_ident!("{}AsyncBuilder", info.ident) } else { format_ident!("{}Builder", info.ident) }; let documentation = format!( concat!( "Constructs a new instance of this self-referential struct. (See also ", "[`{0}::build()`]({0}::build)). Each argument is a field of ", "the new struct. Fields that refer to other fields inside the struct are initialized ", "using functions instead of directly passing their value. The arguments are as ", "follows:\n\n| Argument | Suggested Use |\n| --- | --- |\n", ), builder_struct_name.to_string() ); let builder_documentation = concat!( "A more verbose but stable way to construct self-referencing structs. It is ", "comparable to using `StructName { field1: value1, field2: value2 }` rather than ", "`StructName::new(value1, value2)`. This has the dual benefit of making your code ", "both easier to refactor and more readable. Call [`build()`](Self::build) to ", "construct the actual struct. The fields of this struct should be used as follows:\n\n", "| Field | Suggested Use |\n| --- | --- |\n", ) .to_owned(); let build_fn_documentation = format!( concat!( "Calls [`{0}::new()`]({0}::new) using the provided values. This is preferrable over ", "calling `new()` directly for the reasons listed above. " ), info.ident.to_string() ); let mut doc_table = "".to_owned(); let mut code: Vec = Vec::new(); let mut params: Vec = Vec::new(); let mut builder_struct_generic_producers: Vec<_> = info .generic_params() .iter() .map(|param| quote! { #param }) .collect(); let mut builder_struct_generic_consumers = info.generic_arguments(); let mut builder_struct_fields = Vec::new(); let mut builder_struct_field_names = Vec::new(); // code.push(quote! { let mut result = ::core::mem::MaybeUninit::::uninit(); }); for field in &info.fields { let field_name = &field.name; let arg_type = field.make_constructor_arg_type(&info, make_async)?; if let ArgType::Plain(plain_type) = arg_type { // No fancy builder function, we can just move the value directly into the struct. params.push(quote! { #field_name: #plain_type }); builder_struct_fields.push(quote! { #field_name: #plain_type }); builder_struct_field_names.push(quote! { #field_name }); doc_table += &format!( "| `{}` | Directly pass in the value this field should contain |\n", field_name.to_string() ); } else if let ArgType::TraitBound(bound_type) = arg_type { // Trait bounds are much trickier. We need a special syntax to accept them in the // contructor, and generic parameters need to be added to the builder struct to make // it work. let builder_name = field.builder_name(); params.push(quote! { #builder_name : impl #bound_type }); doc_table += &format!( "| `{}` | Use a function or closure: `(", builder_name.to_string() ); let mut builder_args = Vec::new(); for (index, borrow) in field.borrows.iter().enumerate() { let borrowed_name = &info.fields[borrow.index].name; builder_args.push(format_ident!("{}_illegal_static_reference", borrowed_name)); doc_table += &format!( "{}: &{}_", borrowed_name.to_string(), if borrow.mutable { "mut " } else { "" }, ); if index < field.borrows.len() - 1 { doc_table += ", "; } } doc_table += &format!(") -> {}: _` | \n", field_name.to_string()); if make_async { code.push(quote! { let #field_name = #builder_name (#(#builder_args),*).await; }); } else { code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); }); } let generic_type_name = format_ident!("{}Builder_", to_class_case(field_name.to_string().as_str())); builder_struct_generic_producers.push(quote! { #generic_type_name: #bound_type }); builder_struct_generic_consumers.push(quote! { #generic_type_name }); builder_struct_fields.push(quote! { #builder_name: #generic_type_name }); builder_struct_field_names.push(quote! { #builder_name }); } if field.is_borrowed() { let boxed = field.boxed(); if field.field_type == FieldType::BorrowedMut { code.push(quote! { let mut #field_name = #boxed; }); } else { code.push(quote! { let #field_name = #boxed; }); } }; if field.field_type == FieldType::Borrowed { code.push(field.make_illegal_static_reference()); } else if field.field_type == FieldType::BorrowedMut { code.push(field.make_illegal_static_mut_reference()); } } let documentation = if !options.do_no_doc { let documentation = documentation + &doc_table; quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; let builder_documentation = if !options.do_no_doc { let builder_documentation = builder_documentation + &doc_table; quote! { #[doc=#builder_documentation] } } else { quote! { #[doc(hidden)] } }; let constructor_fn = if make_async { quote! { async fn new_async } } else { quote! { fn new } }; let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect(); let constructor_def = quote! { #documentation #vis #constructor_fn(#(#params),*) -> #struct_name <#(#generic_args),*> { #(#code)* Self { #(#field_names),* } } }; let generic_where = &info.generics.where_clause; let builder_fn = if make_async { quote! { async fn build } } else { quote! { fn build } }; let builder_code = if make_async { quote! { #struct_name::new_async( #(self.#builder_struct_field_names),* ).await } } else { quote! { #struct_name::new( #(self.#builder_struct_field_names),* ) } }; let builder_def = quote! { #builder_documentation #vis struct #builder_struct_name <#(#builder_struct_generic_producers),*> #generic_where { #(#vis #builder_struct_fields),* } impl<#(#builder_struct_generic_producers),*> #builder_struct_name <#(#builder_struct_generic_consumers),*> #generic_where { #[doc=#build_fn_documentation] #vis #builder_fn(self) -> #struct_name <#(#generic_args),*> { #builder_code } } }; Ok((builder_struct_name, builder_def, constructor_def)) } ouroboros_macro-0.14.0/src/generate/derives.rs000064400000000000000000000057510072674642500175500ustar 00000000000000use crate::info_structures::{Derive, StructInfo}; use proc_macro2::TokenStream; use quote::quote; use syn::{Error, GenericParam, TypeParamBound}; fn add_trait_bound(param: &GenericParam, bound: &TypeParamBound) -> GenericParam { let mut new = param.clone(); match &mut new { GenericParam::Type(t) => t.bounds.push(bound.clone()), _ => (), } new } fn impl_trait(info: &StructInfo, trait_name: TypeParamBound, body: TokenStream) -> TokenStream { let generic_params = info.generic_params(); let generic_params = generic_params .into_iter() .map(|i| add_trait_bound(i, &trait_name)) .collect::>(); let generic_args = info.generic_arguments(); let generic_where = &info.generics.where_clause; let struct_name = &info.ident; quote! { impl <#(#generic_params),*> #trait_name for #struct_name <#(#generic_args),*> #generic_where { #body } } } fn impl_debug(info: &StructInfo) -> Result { let fields = info .fields .iter() .filter(|field| !field.is_mutably_borrowed()) .map(|field| { let name = &field.name; quote! { field(stringify!(#name), &safe_self.#name) } }) .collect::>(); let trait_name = syn::parse_quote! { ::std::fmt::Debug }; let struct_name = &info.ident; let body = quote! { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { self.with(|safe_self| { f.debug_struct(stringify!(#struct_name)) #(.#fields)* .finish() }) } }; Ok(impl_trait(info, trait_name, body)) } fn impl_partial_eq(info: &StructInfo) -> Result { let fields = info .fields .iter() .filter(|field| !field.is_mutably_borrowed()) .map(|field| { let name = &field.name; quote! { &*safe_self.#name == &*safe_other.#name } }) .collect::>(); let trait_name = syn::parse_quote! { ::core::cmp::PartialEq }; let body = quote! { fn eq(&self, other: &Self) -> bool { self.with(|safe_self| { other.with(|safe_other| { #(#fields)&&* }) }) } }; Ok(impl_trait(info, trait_name, body)) } fn impl_eq(info: &StructInfo) -> Result { let trait_name = syn::parse_quote! { ::core::cmp::Eq }; let body = quote! {}; Ok(impl_trait(info, trait_name, body)) } pub fn create_derives(info: &StructInfo) -> Result { let mut impls = Vec::new(); for derive in &info.derives { match derive { Derive::Debug => impls.push(impl_debug(info)?), Derive::PartialEq => impls.push(impl_partial_eq(info)?), Derive::Eq => impls.push(impl_eq(info)?), } } Ok(quote! { #(#impls)* }) } ouroboros_macro-0.14.0/src/generate/into_heads.rs000064400000000000000000000057360072674642500202270ustar 00000000000000use proc_macro2::TokenStream; use quote::quote; use crate::info_structures::{Options, StructInfo}; /// Returns the Heads struct and a function to convert the original struct into a Heads instance. pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, TokenStream) { let visibility = if options.do_pub_extras { info.vis.clone() } else { syn::parse_quote! { pub(super) } }; let mut code = Vec::new(); let mut field_initializers = Vec::new(); let mut head_fields = Vec::new(); // Drop everything in the reverse order of what it was declared in. Fields that come later // are only dependent on fields that came before them. for field in info.fields.iter().rev() { let field_name = &field.name; if !field.self_referencing { code.push(quote! { let #field_name = self.#field_name; }); if field.is_borrowed() { field_initializers .push(quote! { #field_name: ::ouroboros::macro_help::unbox(#field_name) }); } else { field_initializers.push(quote! { #field_name }); } let field_type = &field.typ; head_fields.push(quote! { #visibility #field_name: #field_type }); } else { // Heads are fields that do not borrow anything. code.push(quote! { ::core::mem::drop(self.#field_name); }); } } for (ty, ident) in info.generic_consumers() { head_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); field_initializers.push(quote! { #ident: ::core::marker::PhantomData }); } let documentation = format!( concat!( "A struct which contains only the ", "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of [`{0}`]({0})." ), info.ident.to_string() ); let generic_params = info.generic_params(); let generic_where = &info.generics.where_clause; let heads_struct_def = quote! { #[doc=#documentation] #visibility struct Heads <#generic_params> #generic_where { #(#head_fields),* } }; let documentation = concat!( "This function drops all internally referencing fields and returns only the ", "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of this struct." ).to_owned(); let documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; let generic_args = info.generic_arguments(); let into_heads_fn = quote! { #documentation #[allow(clippy::drop_ref)] #[allow(clippy::drop_copy)] #visibility fn into_heads(self) -> Heads<#(#generic_args),*> { #(#code)* Heads { #(#field_initializers),* } } }; (heads_struct_def, into_heads_fn) } ouroboros_macro-0.14.0/src/generate/mod.rs000064400000000000000000000002650072674642500166610ustar 00000000000000pub mod constructor; pub mod derives; pub mod into_heads; pub mod struc; pub mod summon_checker; pub mod try_constructor; pub mod type_asserts; pub mod with_all; pub mod with_each; ouroboros_macro-0.14.0/src/generate/struc.rs000064400000000000000000000032760072674642500172470ustar 00000000000000use crate::{ info_structures::StructInfo, utils::{self, replace_this_with_lifetime}, }; use proc_macro2::TokenStream; use quote::quote; use syn::Error; /// Creates the struct that will actually store the data. This involves properly organizing the /// fields, collecting metadata about them, reversing the order everything is stored in, and /// converting any uses of 'this to 'static. pub fn create_actual_struct_def(info: &StructInfo) -> Result { let vis = utils::submodule_contents_visiblity(&info.vis); let ident = &info.ident; let generics = &info.generics; let field_defs: Vec<_> = info .fields .iter() // Reverse the order of all fields. We ensure that items in the struct are only dependent // on references to items above them. Rust drops items in a struct in forward declaration order. // This would cause parents being dropped before children, necessitating the reversal. .rev() .map(|field| { let name = &field.name; let ty = field.stored_type(); quote! { #[doc(hidden)] #name: #ty } }) .collect(); // Create the new struct definition. let mut where_clause = quote! {}; if let Some(clause) = &generics.where_clause { where_clause = quote! { #clause }; } let def = quote! { #vis struct #ident #generics #where_clause { #(#field_defs),* } }; // Finally, replace the fake 'this lifetime with the one we found. let fake_lifetime = info.fake_lifetime(); let def = replace_this_with_lifetime(quote! { #def }, fake_lifetime.clone()); Ok(def) } ouroboros_macro-0.14.0/src/generate/summon_checker.rs000064400000000000000000000051270072674642500211060ustar 00000000000000use proc_macro2::TokenStream; use quote::quote; use syn::Error; use crate::info_structures::{ArgType, StructInfo}; pub fn generate_checker_summoner(info: &StructInfo) -> Result { let mut code: Vec = Vec::new(); let mut params: Vec = Vec::new(); let mut value_consumers: Vec = Vec::new(); let mut template_consumers: Vec = Vec::new(); for field in &info.fields { let field_name = &field.name; let arg_type = field.make_constructor_arg_type(&info, false)?; if let ArgType::Plain(plain_type) = arg_type { // No fancy builder function, we can just move the value directly into the struct. params.push(quote! { #field_name: #plain_type }); } else if let ArgType::TraitBound(bound_type) = arg_type { // Trait bounds are much trickier. We need a special syntax to accept them in the // contructor, and generic parameters need to be added to the builder struct to make // it work. let builder_name = field.builder_name(); params.push(quote! { #builder_name : impl #bound_type }); let mut builder_args = Vec::new(); for (_, borrow) in field.borrows.iter().enumerate() { let borrowed_name = &info.fields[borrow.index].name; if borrow.mutable { builder_args.push(quote! { &mut #borrowed_name }); } else { builder_args.push(quote! { &#borrowed_name }); } } code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); }); } if field.is_mutably_borrowed() { code.push(quote! { let mut #field_name = #field_name; }); } else { code.push(quote! { let #field_name = #field_name; }); value_consumers.push(quote! { #field_name: &#field_name }); } } for (_ty, ident) in info.generic_consumers() { template_consumers.push(quote! { #ident: ::core::marker::PhantomData }); } let generic_params = info.generic_params(); let where_clause = &info.generics.where_clause; let borrowed_generic_params_inferred = info.borrowed_generic_params_inferred(); Ok(quote! { fn check_if_okay_according_to_checkers<#generic_params>( #(#params,)* ) #where_clause { #(#code;)* BorrowedFields::#borrowed_generic_params_inferred { #(#value_consumers,)* #(#template_consumers,)* }; } }) } ouroboros_macro-0.14.0/src/generate/try_constructor.rs000064400000000000000000000301710072674642500213640ustar 00000000000000use crate::{ info_structures::{ArgType, FieldType, Options, StructInfo}, utils::to_class_case, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use syn::Error; pub fn create_try_builder_and_constructor( info: &StructInfo, options: Options, make_async: bool, ) -> Result<(Ident, TokenStream, TokenStream), Error> { let struct_name = info.ident.clone(); let generic_args = info.generic_arguments(); let visibility = if options.do_pub_extras { info.vis.clone() } else { syn::parse_quote! { pub(super) } }; let mut head_recover_code = Vec::new(); for field in &info.fields { if !field.self_referencing { let field_name = &field.name; head_recover_code.push(quote! { #field_name }); } } for (_ty, ident) in info.generic_consumers() { head_recover_code.push(quote! { #ident: ::core::marker::PhantomData }); } let mut current_head_index = 0; let builder_struct_name = if make_async { format_ident!("{}AsyncTryBuilder", info.ident) } else { format_ident!("{}TryBuilder", info.ident) }; let documentation = format!( concat!( "(See also [`{0}::try_build()`]({0}::try_build).) Like [`new`](Self::new), but ", "builders for [self-referencing fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) ", "can return results. If any of them fail, `Err` is returned. If all of them ", "succeed, `Ok` is returned. The arguments are as follows:\n\n", "| Argument | Suggested Use |\n| --- | --- |\n", ), builder_struct_name.to_string() ); let or_recover_documentation = format!( concat!( "(See also [`{0}::try_build_or_recover()`]({0}::try_build_or_recover).) Like ", "[`try_new`](Self::try_new), but all ", "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) ", "are returned in the case of an error. The arguments are as follows:\n\n", "| Argument | Suggested Use |\n| --- | --- |\n", ), builder_struct_name.to_string() ); let builder_documentation = concat!( "A more verbose but stable way to construct self-referencing structs. It is ", "comparable to using `StructName { field1: value1, field2: value2 }` rather than ", "`StructName::new(value1, value2)`. This has the dual benefit of makin your code ", "both easier to refactor and more readable. Call [`try_build()`](Self::try_build) or ", "[`try_build_or_recover()`](Self::try_build_or_recover) to ", "construct the actual struct. The fields of this struct should be used as follows:\n\n", "| Field | Suggested Use |\n| --- | --- |\n", ) .to_owned(); let build_fn_documentation = format!( concat!( "Calls [`{0}::try_new()`]({0}::try_new) using the provided values. This is ", "preferrable over calling `try_new()` directly for the reasons listed above. " ), info.ident.to_string() ); let build_or_recover_fn_documentation = format!( concat!( "Calls [`{0}::try_new_or_recover()`]({0}::try_new_or_recover) using the provided ", "values. This is preferrable over calling `try_new_or_recover()` directly for the ", "reasons listed above. " ), info.ident.to_string() ); let mut doc_table = "".to_owned(); let mut or_recover_code: Vec = Vec::new(); let mut params: Vec = Vec::new(); let mut builder_struct_generic_producers: Vec<_> = info .generic_params() .iter() .map(|param| quote! { #param }) .collect(); let mut builder_struct_generic_consumers = info.generic_arguments(); let mut builder_struct_fields = Vec::new(); let mut builder_struct_field_names = Vec::new(); for field in &info.fields { let field_name = &field.name; let arg_type = field.make_try_constructor_arg_type(info, make_async)?; if let ArgType::Plain(plain_type) = arg_type { // No fancy builder function, we can just move the value directly into the struct. params.push(quote! { #field_name: #plain_type }); builder_struct_fields.push(quote! { #field_name: #plain_type }); builder_struct_field_names.push(quote! { #field_name }); doc_table += &format!( "| `{}` | Directly pass in the value this field should contain |\n", field_name.to_string() ); if !field.self_referencing { if field.is_borrowed() { head_recover_code[current_head_index] = quote! { #field_name: ::ouroboros::macro_help::unbox(#field_name) }; } else { head_recover_code[current_head_index] = quote! { #field_name }; } current_head_index += 1; } } else if let ArgType::TraitBound(bound_type) = arg_type { // Trait bounds are much trickier. We need a special syntax to accept them in the // contructor, and generic parameters need to be added to the builder struct to make // it work. let builder_name = field.builder_name(); params.push(quote! { #builder_name : impl #bound_type }); // Ok so hear me out basically without this thing here my IDE thinks the rest of the // code is a string and it all turns green. {} doc_table += &format!( "| `{}` | Use a function or closure: `(", builder_name.to_string() ); let mut builder_args = Vec::new(); for (index, borrow) in field.borrows.iter().enumerate() { let borrowed_name = &info.fields[borrow.index].name; builder_args.push(format_ident!("{}_illegal_static_reference", borrowed_name)); doc_table += &format!( "{}: &{}_", borrowed_name.to_string(), if borrow.mutable { "mut " } else { "" }, ); if index < field.borrows.len() - 1 { doc_table += ", "; } } doc_table += &format!(") -> Result<{}: _, Error_>` | \n", field_name.to_string()); let builder_value = if make_async { quote! { #builder_name (#(#builder_args),*).await } } else { quote! { #builder_name (#(#builder_args),*) } }; or_recover_code.push(quote! { let #field_name = match #builder_value { ::core::result::Result::Ok(value) => value, ::core::result::Result::Err(err) => return ::core::result::Result::Err((err, Heads { #(#head_recover_code),* })), }; }); let generic_type_name = format_ident!("{}Builder_", to_class_case(field_name.to_string().as_str())); builder_struct_generic_producers.push(quote! { #generic_type_name: #bound_type }); builder_struct_generic_consumers.push(quote! { #generic_type_name }); builder_struct_fields.push(quote! { #builder_name: #generic_type_name }); builder_struct_field_names.push(quote! { #builder_name }); } if field.is_borrowed() { let boxed = field.boxed(); if field.field_type == FieldType::BorrowedMut { or_recover_code.push(quote! { let mut #field_name = #boxed; }); } else { or_recover_code.push(quote! { let #field_name = #boxed; }); } } if field.field_type == FieldType::Borrowed { or_recover_code.push(field.make_illegal_static_reference()); } else if field.field_type == FieldType::BorrowedMut { or_recover_code.push(field.make_illegal_static_mut_reference()); } } let documentation = if !options.do_no_doc { let documentation = documentation + &doc_table; quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; let or_recover_documentation = if !options.do_no_doc { let or_recover_documentation = or_recover_documentation + &doc_table; quote! { #[doc=#or_recover_documentation] } } else { quote! { #[doc(hidden)] } }; let builder_documentation = if !options.do_no_doc { let builder_documentation = builder_documentation + &doc_table; quote! { #[doc=#builder_documentation] } } else { quote! { #[doc(hidden)] } }; let or_recover_ident = if make_async { quote! { try_new_or_recover_async } } else { quote! { try_new_or_recover } }; let or_recover_constructor_fn = if make_async { quote! { async fn #or_recover_ident } } else { quote! { fn #or_recover_ident } }; let constructor_fn = if make_async { quote! { async fn try_new_async } } else { quote! { fn try_new } }; let constructor_code = if make_async { quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).await.map_err(|(error, _heads)| error) } } else { quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).map_err(|(error, _heads)| error) } }; let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect(); let constructor_def = quote! { #documentation #visibility #constructor_fn(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> { #constructor_code } #or_recover_documentation #visibility #or_recover_constructor_fn(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> { #(#or_recover_code)* ::core::result::Result::Ok(Self { #(#field_names),* }) } }; builder_struct_generic_producers.push(quote! { Error_ }); builder_struct_generic_consumers.push(quote! { Error_ }); let generic_where = &info.generics.where_clause; let builder_fn = if make_async { quote! { async fn try_build } } else { quote! { fn try_build } }; let or_recover_builder_fn = if make_async { quote! { async fn try_build_or_recover } } else { quote! { fn try_build_or_recover } }; let builder_code = if make_async { quote! { #struct_name::try_new_async( #(self.#builder_struct_field_names),* ).await } } else { quote! { #struct_name::try_new( #(self.#builder_struct_field_names),* ) } }; let or_recover_builder_code = if make_async { quote! { #struct_name::try_new_or_recover_async( #(self.#builder_struct_field_names),* ).await } } else { quote! { #struct_name::try_new_or_recover( #(self.#builder_struct_field_names),* ) } }; let builder_def = quote! { #builder_documentation #visibility struct #builder_struct_name <#(#builder_struct_generic_producers),*> #generic_where { #(#visibility #builder_struct_fields),* } impl<#(#builder_struct_generic_producers),*> #builder_struct_name <#(#builder_struct_generic_consumers),*> #generic_where { #[doc=#build_fn_documentation] #visibility #builder_fn(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> { #builder_code } #[doc=#build_or_recover_fn_documentation] #visibility #or_recover_builder_fn(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> { #or_recover_builder_code } } }; Ok((builder_struct_name, builder_def, constructor_def)) } ouroboros_macro-0.14.0/src/generate/type_asserts.rs000064400000000000000000000026750072674642500206360ustar 00000000000000use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::GenericParam; use crate::{ covariance_detection::apparent_std_container_type, info_structures::StructInfo, utils::replace_this_with_lifetime, }; pub fn make_type_asserts(info: &StructInfo) -> TokenStream { let mut checks = Vec::new(); let fake_lifetime = if let Some(GenericParam::Lifetime(param)) = info.generic_params().first() { param.lifetime.ident.clone() } else { format_ident!("static") }; for field in &info.fields { let field_type = &field.typ; if let Some((std_type, _eltype)) = apparent_std_container_type(field_type) { let checker_name = match std_type { "Box" => "is_std_box_type", "Arc" => "is_std_arc_type", "Rc" => "is_std_rc_type", _ => unreachable!(), }; let checker_name = format_ident!("{}", checker_name); let static_field_type = replace_this_with_lifetime(quote! { #field_type }, fake_lifetime.clone()); checks.push(quote! { ::ouroboros::macro_help::CheckIfTypeIsStd::<#static_field_type>::#checker_name(); }); } } let generic_params = info.generic_params(); let generic_where = &info.generics.where_clause; quote! { fn type_asserts <#generic_params>() #generic_where { #(#checks)* } } } ouroboros_macro-0.14.0/src/generate/with_all.rs000064400000000000000000000127260072674642500177120ustar 00000000000000use crate::info_structures::{FieldType, Options, StructInfo}; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{Error, Lifetime, WhereClause}; pub fn make_with_all_function( info: &StructInfo, options: Options, ) -> Result<(TokenStream, TokenStream), Error> { let visibility = if options.do_pub_extras { info.vis.clone() } else { syn::parse_quote! { pub(super) } }; let mut fields = Vec::new(); let mut field_assignments = Vec::new(); let mut mut_fields = Vec::new(); let mut mut_field_assignments = Vec::new(); // I don't think the reverse is necessary but it does make the expanded code more uniform. for field in info.fields.iter().rev() { let field_name = &field.name; let field_type = &field.typ; if field.field_type == FieldType::Tail { fields.push(quote! { #visibility #field_name: &'outer_borrow #field_type }); field_assignments.push(quote! { #field_name: &self.#field_name }); mut_fields.push(quote! { #visibility #field_name: &'outer_borrow mut #field_type }); mut_field_assignments.push(quote! { #field_name: &mut self.#field_name }); } else if field.field_type == FieldType::Borrowed { let ass = quote! { #field_name: unsafe { ::ouroboros::macro_help::change_lifetime( &*self.#field_name ) } }; fields.push(quote! { #visibility #field_name: &'this #field_type }); field_assignments.push(ass.clone()); mut_fields.push(quote! { #visibility #field_name: &'this #field_type }); mut_field_assignments.push(ass); } else if field.field_type == FieldType::BorrowedMut { // Add nothing because we cannot borrow something that has already been mutably // borrowed. } } for (ty, ident) in info.generic_consumers() { fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); field_assignments.push(quote! { #ident: ::core::marker::PhantomData }); mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData }); } let new_generic_params = info.borrowed_generic_params(); let new_generic_args = info.borrowed_generic_arguments(); let struct_documentation = format!( concat!( "A struct for holding immutable references to all ", "[tail and immutably borrowed fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ", "[`{0}`]({0})." ), info.ident.to_string() ); let mut_struct_documentation = format!( concat!( "A struct for holding mutable references to all ", "[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ", "[`{0}`]({0})." ), info.ident.to_string() ); let ltname = format!("'{}", info.fake_lifetime()); let lifetime = Lifetime::new(<name, Span::call_site()); let generic_where = if let Some(clause) = &info.generics.where_clause { let mut clause = clause.clone(); let extra: WhereClause = syn::parse_quote! { where #lifetime: 'this }; clause .predicates .push(extra.predicates.first().unwrap().clone()); clause } else { syn::parse_quote! { where #lifetime: 'this } }; let struct_defs = quote! { #[doc=#struct_documentation] #visibility struct BorrowedFields #new_generic_params #generic_where { #(#fields),* } #[doc=#mut_struct_documentation] #visibility struct BorrowedMutFields #new_generic_params #generic_where { #(#mut_fields),* } }; let borrowed_fields_type = quote! { BorrowedFields<#(#new_generic_args),*> }; let borrowed_mut_fields_type = quote! { BorrowedMutFields<#(#new_generic_args),*> }; let documentation = concat!( "This method provides immutable references to all ", "[tail and immutably borrowed fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).", ); let mut_documentation = concat!( "This method provides mutable references to all ", "[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).", ); let documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; let mut_documentation = if !options.do_no_doc { quote! { #[doc=#mut_documentation] } } else { quote! { #[doc(hidden)] } }; let fn_defs = quote! { #documentation #visibility fn with <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType ) -> ReturnType { user(BorrowedFields { #(#field_assignments),* }) } #mut_documentation #visibility fn with_mut <'outer_borrow, ReturnType>( &'outer_borrow mut self, user: impl for<'this> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType ) -> ReturnType { user(BorrowedMutFields { #(#mut_field_assignments),* }) } }; Ok((struct_defs, fn_defs)) } ouroboros_macro-0.14.0/src/generate/with_each.rs000064400000000000000000000126460072674642500200430ustar 00000000000000use crate::info_structures::{FieldType, Options, StructInfo}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::Error; pub fn make_with_functions(info: &StructInfo, options: Options) -> Result, Error> { let mut users = Vec::new(); for field in &info.fields { let visibility = &field.vis; let field_name = &field.name; let field_type = &field.typ; // If the field is not a tail, we need to serve up the same kind of reference that other // fields in the struct may have borrowed to ensure safety. if field.field_type == FieldType::Tail { let user_name = format_ident!("with_{}", &field.name); let documentation = format!( concat!( "Provides an immutable reference to `{0}`. This method was generated because ", "`{0}` is a [tail field](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions)." ), field.name.to_string() ); let documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; users.push(quote! { #documentation #visibility fn #user_name <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, ) -> ReturnType { user(&self. #field_name) } }); if field.covariant == Some(true) { let borrower_name = format_ident!("borrow_{}", &field.name); users.push(quote! { #documentation #visibility fn #borrower_name<'this>( &'this self, ) -> &'this #field_type { &self.#field_name } }); } else if field.covariant.is_none() { field.covariance_error(); } // If it is not borrowed at all it's safe to allow mutably borrowing it. let user_name = format_ident!("with_{}_mut", &field.name); let documentation = format!( concat!( "Provides a mutable reference to `{0}`. This method was generated because ", "`{0}` is a [tail field](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions). ", "No `borrow_{0}_mut` function was generated because Rust's borrow checker is ", "currently unable to guarantee that such a method would be used safely." ), field.name.to_string() ); let documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; users.push(quote! { #documentation #visibility fn #user_name <'outer_borrow, ReturnType>( &'outer_borrow mut self, user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow mut #field_type) -> ReturnType, ) -> ReturnType { user(&mut self. #field_name) } }); } else if field.field_type == FieldType::Borrowed { let user_name = format_ident!("with_{}", &field.name); let documentation = format!( concat!( "Provides limited immutable access to `{0}`. This method was generated ", "because the contents of `{0}` are immutably borrowed by other fields." ), field.name.to_string() ); let documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; users.push(quote! { #documentation #visibility fn #user_name <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, ) -> ReturnType { user(&*self.#field_name) } }); if field.self_referencing { if field.covariant == Some(false) { // Skip the other functions, they will cause compiler errors. continue; } else if field.covariant.is_none() { field.covariance_error(); } } let borrower_name = format_ident!("borrow_{}", &field.name); users.push(quote! { #documentation #visibility fn #borrower_name<'this>( &'this self, ) -> &'this #field_type { &*self.#field_name } }); } else if field.field_type == FieldType::BorrowedMut { // Do not generate anything becaue if it is borrowed mutably once, we should not be able // to get any other kinds of references to it. } } Ok(users) } ouroboros_macro-0.14.0/src/info_structures.rs000064400000000000000000000226540072674642500175540ustar 00000000000000use crate::utils::{make_generic_arguments, make_generic_consumers, replace_this_with_lifetime}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, ConstParam, Error, GenericParam, Generics, LifetimeDef, Type, TypeParam, Visibility, }; #[derive(Clone, Copy)] pub struct Options { pub do_no_doc: bool, pub do_pub_extras: bool, } #[derive(Clone, Copy, PartialEq)] pub enum FieldType { /// Not borrowed by other parts of the struct. Tail, /// Immutably borrowed by at least one other field. Borrowed, /// Mutably borrowed by one other field. BorrowedMut, } impl FieldType { pub fn is_tail(self) -> bool { self == Self::Tail } } #[derive(Clone)] pub struct BorrowRequest { pub index: usize, pub mutable: bool, } #[derive(Clone)] pub enum Derive { Debug, PartialEq, Eq, } #[derive(Clone)] pub struct StructInfo { pub derives: Vec, pub ident: Ident, pub generics: Generics, pub vis: Visibility, pub fields: Vec, pub first_lifetime: Ident, pub attributes: Vec, } impl StructInfo { // The lifetime to use in place of 'this for internal implementations, // should never be exposed to the user. pub fn fake_lifetime(&self) -> Ident { return self.first_lifetime.clone(); } pub fn generic_params(&self) -> &Punctuated { &self.generics.params } /// Same as generic_params but with 'this and 'outer_borrow prepended. pub fn borrowed_generic_params(&self) -> TokenStream { if self.generic_params().is_empty() { quote! { <'outer_borrow, 'this> } } else { let mut new_generic_params = self.generic_params().clone(); new_generic_params.insert(0, syn::parse_quote! { 'this }); new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow }); quote! { <#new_generic_params> } } } /// Same as generic_params but without bounds and with '_ prepended twice. pub fn borrowed_generic_params_inferred(&self) -> TokenStream { use GenericParam::*; let params = self.generic_params().iter().map(|p| match p { Type(TypeParam { ident, .. }) | Const(ConstParam { ident, .. }) => { ident.to_token_stream() } Lifetime(LifetimeDef { lifetime, .. }) => lifetime.to_token_stream(), }); quote! { <'_, '_, #(#params,)*> } } pub fn generic_arguments(&self) -> Vec { make_generic_arguments(&self.generics) } /// Same as generic_arguments but with 'outer_borrow and 'this prepended. pub fn borrowed_generic_arguments(&self) -> Vec { let mut args = self.generic_arguments(); args.insert(0, quote! { 'this }); args.insert(0, quote! { 'outer_borrow }); args } pub fn generic_consumers(&self) -> impl Iterator { make_generic_consumers(&self.generics) } } #[derive(Clone)] pub struct StructFieldInfo { pub name: Ident, pub typ: Type, pub field_type: FieldType, pub vis: Visibility, pub borrows: Vec, /// If this is true and borrows is empty, the struct will borrow from self in the future but /// does not require a builder to be initialized. It should not be able to be removed from the /// struct with into_heads. pub self_referencing: bool, /// If it is None, the user has not specified whether or not the field is covariant. If this is /// Some(false), we should avoid making borrow_* or borrow_*_mut functions as they will not /// be able to compile. pub covariant: Option, } #[derive(Clone)] pub enum ArgType { /// Used when the initial value of a field can be passed directly into the constructor. Plain(TokenStream), /// Used when a field requires self references and thus requires something that implements /// a builder function trait instead of a simple plain type. TraitBound(TokenStream), } impl StructFieldInfo { pub fn builder_name(&self) -> Ident { format_ident!("{}_builder", self.name) } pub fn illegal_ref_name(&self) -> Ident { format_ident!("{}_illegal_static_reference", self.name) } pub fn is_borrowed(&self) -> bool { self.field_type != FieldType::Tail } pub fn is_mutably_borrowed(&self) -> bool { self.field_type == FieldType::BorrowedMut } pub fn boxed(&self) -> TokenStream { let name = &self.name; quote! { ::ouroboros::macro_help::aliasable_boxed(#name) } } pub fn stored_type(&self) -> TokenStream { let t = &self.typ; if self.is_borrowed() { quote! { ::ouroboros::macro_help::AliasableBox<#t> } } else { quote! { #t } } } /// Returns code which takes a variable with the same name and type as this field and turns it /// into a static reference to its dereffed contents. pub fn make_illegal_static_reference(&self) -> TokenStream { let field_name = &self.name; let ref_name = self.illegal_ref_name(); quote! { let #ref_name = unsafe { ::ouroboros::macro_help::change_lifetime(&*#field_name) }; } } /// Like make_illegal_static_reference, but provides a mutable reference instead. pub fn make_illegal_static_mut_reference(&self) -> TokenStream { let field_name = &self.name; let ref_name = self.illegal_ref_name(); quote! { let #ref_name = unsafe { ::ouroboros::macro_help::change_lifetime_mut(&mut *#field_name) }; } } /// Generates an error requesting that the user explicitly specify whether or not the /// field's type is covariant. pub fn covariance_error(&self) { let error = concat!( "Ouroboros cannot automatically determine if this type is covariant.\n\n", "If it is covariant, it should be legal to convert any instance of that type to an ", "instance of that type where all usages of 'this are replaced with a smaller ", "lifetime. For example, Box<&'this i32> is covariant because it is legal to use it as ", "a Box<&'a i32> where 'this: 'a. In contrast, Fn(&'this i32) cannot be used as ", "Fn(&'a i32).\n\n", "To resolve this error, add #[covariant] or #[not_covariant] to the field.\n", ); proc_macro_error::emit_error!(self.typ, error); } pub fn make_constructor_arg_type_impl( &self, info: &StructInfo, make_builder_return_type: impl FnOnce() -> TokenStream, ) -> Result { let field_type = &self.typ; let fake_lifetime = info.fake_lifetime(); if self.borrows.is_empty() { // Even if self_referencing is true, as long as borrows is empty, we don't need to use a // builder to construct it. let field_type = replace_this_with_lifetime(field_type.into_token_stream(), fake_lifetime.clone()); Ok(ArgType::Plain(quote! { #field_type })) } else { let mut field_builder_params = Vec::new(); for borrow in &self.borrows { if borrow.mutable { let field = &info.fields[borrow.index]; let field_type = &field.typ; field_builder_params.push(quote! { &'this mut #field_type }); } else { let field = &info.fields[borrow.index]; let field_type = &field.typ; field_builder_params.push(quote! { &'this #field_type }); } } let return_type = make_builder_return_type(); let bound = quote! { for<'this> ::core::ops::FnOnce(#(#field_builder_params),*) -> #return_type }; Ok(ArgType::TraitBound(bound)) } } /// Returns a trait bound if `for_field` refers to any other fields, and a plain type if not. This /// is the type used in the constructor to initialize the value of `for_field`. pub fn make_constructor_arg_type( &self, info: &StructInfo, make_async: bool, ) -> Result { let field_type = &self.typ; let return_ty_constructor = || { if make_async { quote! { ::std::pin::Pin<::std::boxed::Box + 'this>> } } else { quote! { #field_type } } }; self.make_constructor_arg_type_impl(info, return_ty_constructor) } /// Like make_constructor_arg_type, but used for the try_new constructor. pub fn make_try_constructor_arg_type( &self, info: &StructInfo, make_async: bool, ) -> Result { let field_type = &self.typ; let return_ty_constructor = || { if make_async { quote! { ::std::pin::Pin<::std::boxed::Box> + 'this>> } } else { quote! { ::core::result::Result<#field_type, Error_> } } }; self.make_constructor_arg_type_impl(info, return_ty_constructor) } } ouroboros_macro-0.14.0/src/lib.rs000064400000000000000000000133500072674642500150550ustar 00000000000000extern crate proc_macro; mod covariance_detection; mod generate; mod info_structures; mod parse; mod utils; use crate::{ generate::{ constructor::create_builder_and_constructor, derives::create_derives, into_heads::make_into_heads, struc::create_actual_struct_def, summon_checker::generate_checker_summoner, try_constructor::create_try_builder_and_constructor, type_asserts::make_type_asserts, with_all::make_with_all_function, with_each::make_with_functions, }, info_structures::Options, parse::parse_struct, }; use inflector::Inflector; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenTree; use proc_macro_error::proc_macro_error; use quote::{format_ident, quote}; use syn::{Error, ItemStruct}; fn self_referencing_impl( original_struct_def: &ItemStruct, options: Options, ) -> Result { let struct_name = &original_struct_def.ident; let mod_name = format_ident!("ouroboros_impl_{}", struct_name.to_string().to_snake_case()); let visibility = &original_struct_def.vis; let info = parse_struct(original_struct_def)?; let actual_struct_def = create_actual_struct_def(&info)?; let borrowchk_summoner = generate_checker_summoner(&info)?; let (builder_struct_name, builder_def, constructor_def) = create_builder_and_constructor(&info, options, false)?; let (async_builder_struct_name, async_builder_def, async_constructor_def) = create_builder_and_constructor(&info, options, true)?; let (try_builder_struct_name, try_builder_def, try_constructor_def) = create_try_builder_and_constructor(&info, options, false)?; let (async_try_builder_struct_name, async_try_builder_def, async_try_constructor_def) = create_try_builder_and_constructor(&info, options, true)?; let with_defs = make_with_functions(&info, options)?; let (with_all_struct_defs, with_all_fn_defs) = make_with_all_function(&info, options)?; let (heads_struct_def, into_heads_fn) = make_into_heads(&info, options); let impls = create_derives(&info)?; // These check that types like Box, Arc, and Rc refer to those types in the std lib and have not // been overridden. let type_asserts_def = make_type_asserts(&info); let extra_visibility = if options.do_pub_extras { visibility.clone() } else { syn::Visibility::Inherited }; let generic_params = info.generic_params(); let generic_args = info.generic_arguments(); let generic_where = &info.generics.where_clause; Ok(TokenStream::from(quote! { #[doc="Encapsulates implementation details for a self-referencing struct. This module is only visible when using --document-private-items."] mod #mod_name { use super::*; #actual_struct_def #borrowchk_summoner #builder_def #async_builder_def #try_builder_def #async_try_builder_def #with_all_struct_defs #heads_struct_def #impls impl <#generic_params> #struct_name <#(#generic_args),*> #generic_where { #constructor_def #async_constructor_def #try_constructor_def #async_try_constructor_def #(#with_defs)* #with_all_fn_defs #into_heads_fn } #type_asserts_def } #visibility use #mod_name :: #struct_name; #extra_visibility use #mod_name :: #builder_struct_name; #extra_visibility use #mod_name :: #async_builder_struct_name; #extra_visibility use #mod_name :: #try_builder_struct_name; #extra_visibility use #mod_name :: #async_try_builder_struct_name; })) } #[proc_macro_error] #[proc_macro_attribute] pub fn self_referencing(attr: TokenStream, item: TokenStream) -> TokenStream { let mut options = Options { do_no_doc: false, do_pub_extras: false, }; let mut expecting_comma = false; for token in >::into(attr).into_iter() { if let TokenTree::Ident(ident) = &token { if expecting_comma { return Error::new(token.span(), "Unexpected identifier, expected comma.") .to_compile_error() .into(); } match &ident.to_string()[..] { "no_doc" => options.do_no_doc = true, "pub_extras" => options.do_pub_extras = true, _ => { return Error::new_spanned( &ident, "Unknown identifier, expected 'no_doc' or 'pub_extras'.", ) .to_compile_error() .into() } } expecting_comma = true; } else if let TokenTree::Punct(punct) = &token { if !expecting_comma { return Error::new(token.span(), "Unexpected punctuation, expected identifier.") .to_compile_error() .into(); } if punct.as_char() != ',' { return Error::new(token.span(), "Unknown punctuation, expected comma.") .to_compile_error() .into(); } expecting_comma = false; } else { return Error::new(token.span(), "Unknown syntax, expected identifier.") .to_compile_error() .into(); } } let original_struct_def: ItemStruct = syn::parse_macro_input!(item); match self_referencing_impl(&original_struct_def, options) { Ok(content) => content, Err(err) => err.to_compile_error().into(), } } ouroboros_macro-0.14.0/src/parse.rs000064400000000000000000000237560072674642500154340ustar 00000000000000use proc_macro2::{Delimiter, Span, TokenTree}; use quote::format_ident; use syn::{spanned::Spanned, Attribute, Error, Fields, GenericParam, ItemStruct}; use crate::{ covariance_detection::type_is_covariant_over_this_lifetime, info_structures::{BorrowRequest, Derive, FieldType, StructFieldInfo, StructInfo}, utils::submodule_contents_visiblity, }; fn handle_borrows_attr( field_info: &mut [StructFieldInfo], attr: &Attribute, borrows: &mut Vec, ) -> Result<(), Error> { let mut borrow_mut = false; let mut waiting_for_comma = false; let tokens = attr.tokens.clone(); let possible_error = Error::new_spanned(&tokens, "Invalid syntax for borrows() macro."); let tokens = if let Some(TokenTree::Group(group)) = tokens.into_iter().next() { group.stream() } else { return Err(possible_error); }; for token in tokens { if let TokenTree::Ident(ident) = token { if waiting_for_comma { return Err(Error::new_spanned(&ident, "Expected comma.")); } let istr = ident.to_string(); if istr == "mut" { if borrow_mut { return Err(Error::new_spanned(&ident, "Unexpected double 'mut'")); } borrow_mut = true; } else { let index = field_info.iter().position(|item| item.name == istr); let index = if let Some(v) = index { v } else { return Err(Error::new_spanned( &ident, concat!( "Unknown identifier, make sure that it is spelled ", "correctly and defined above the location it is borrowed." ), )); }; if borrow_mut { if field_info[index].field_type == FieldType::Borrowed { return Err(Error::new_spanned( &ident, "Cannot borrow mutably, this field was previously borrowed immutably.", )); } if field_info[index].field_type == FieldType::BorrowedMut { return Err(Error::new_spanned(&ident, "Cannot borrow mutably twice.")); } field_info[index].field_type = FieldType::BorrowedMut; } else { if field_info[index].field_type == FieldType::BorrowedMut { return Err(Error::new_spanned( &ident, "Cannot borrow as immutable as it was previously borrowed mutably.", )); } field_info[index].field_type = FieldType::Borrowed; } borrows.push(BorrowRequest { index, mutable: borrow_mut, }); waiting_for_comma = true; borrow_mut = false; } } else if let TokenTree::Punct(punct) = token { if punct.as_char() == ',' { if waiting_for_comma { waiting_for_comma = false; } else { return Err(Error::new_spanned(&punct, "Unexpected extra comma.")); } } else { return Err(Error::new_spanned( &punct, "Unexpected punctuation, expected comma or identifier.", )); } } else { return Err(Error::new_spanned( &token, "Unexpected token, expected comma or identifier.", )); } } Ok(()) } fn parse_derive_token(token: &TokenTree) -> Result, Error> { match token { TokenTree::Ident(ident) => match &ident.to_string()[..] { "Debug" => Ok(Some(Derive::Debug)), "PartialEq" => Ok(Some(Derive::PartialEq)), "Eq" => Ok(Some(Derive::Eq)), _ => Err(Error::new( ident.span(), format!("{} cannot be derived for self-referencing structs", ident), )), }, TokenTree::Punct(..) => Ok(None), _ => Err(Error::new(token.span(), "bad syntax")), } } fn parse_derive_attribute(attr: &Attribute) -> Result, Error> { let body = &attr.tokens; if let Some(TokenTree::Group(body)) = body.clone().into_iter().next() { if body.delimiter() != Delimiter::Parenthesis { panic!("TODO: nice error, bad define syntax") } let mut derives = Vec::new(); for token in body.stream().into_iter() { if let Some(derive) = parse_derive_token(&token)? { derives.push(derive); } } Ok(derives) } else { Err(Error::new(attr.span(), "bad syntax")) } } pub fn parse_struct(def: &ItemStruct) -> Result { let vis = def.vis.clone(); let generics = def.generics.clone(); let mut actual_struct_def = def.clone(); actual_struct_def.vis = vis.clone(); let mut fields = Vec::new(); match &mut actual_struct_def.fields { Fields::Named(def_fields) => { for field in &mut def_fields.named { let mut borrows = Vec::new(); let mut self_referencing = false; let mut covariant = type_is_covariant_over_this_lifetime(&field.ty); let mut remove_attrs = Vec::new(); for (index, attr) in field.attrs.iter().enumerate() { let path = &attr.path; if path.leading_colon.is_some() { continue; } if path.segments.len() != 1 { continue; } if path.segments.first().unwrap().ident == "borrows" { if self_referencing { panic!("TODO: Nice error, used #[borrows()] twice."); } self_referencing = true; handle_borrows_attr(&mut fields[..], attr, &mut borrows)?; remove_attrs.push(index); } if path.segments.first().unwrap().ident == "covariant" { if covariant.is_some() { panic!("TODO: Nice error, covariance specified twice."); } covariant = Some(true); remove_attrs.push(index); } if path.segments.first().unwrap().ident == "not_covariant" { if covariant.is_some() { panic!("TODO: Nice error, covariance specified twice."); } covariant = Some(false); remove_attrs.push(index); } } // We should not be able to access the field outside of the hidden module where // everything is generated. let with_vis = submodule_contents_visiblity(&field.vis.clone()); fields.push(StructFieldInfo { name: field.ident.clone().expect("Named field has no name."), typ: field.ty.clone(), field_type: FieldType::Tail, vis: with_vis, borrows, self_referencing, covariant, }); } } Fields::Unnamed(_fields) => { return Err(Error::new( Span::call_site(), "Tuple structs are not supported yet.", )) } Fields::Unit => { return Err(Error::new( Span::call_site(), "Unit structs cannot be self-referential.", )) } } if fields.len() < 2 { return Err(Error::new( Span::call_site(), "Self-referencing structs must have at least 2 fields.", )); } let mut has_non_tail = false; for field in &fields { if !field.field_type.is_tail() { has_non_tail = true; break; } } if !has_non_tail { return Err(Error::new( Span::call_site(), &format!( concat!( "Self-referencing struct cannot be made entirely of tail fields, try adding ", "#[borrows({0})] to a field defined after {0}." ), fields[0].name ), )); } let first_lifetime = if let Some(GenericParam::Lifetime(param)) = generics.params.first() { param.lifetime.ident.clone() } else { format_ident!("static") }; let mut attributes = Vec::new(); let mut derives = Vec::new(); for attr in &def.attrs { let p = &attr.path.segments; if p.len() == 0 { return Err(Error::new(p.span(), &format!("Unsupported attribute"))); } let name = p[0].ident.to_string(); let good = match &name[..] { "clippy" | "allow" | "deny" | "doc" => true, _ => false, }; if good { attributes.push(attr.clone()) } else if name == "derive" { if derives.len() > 0 { return Err(Error::new( attr.span(), "Multiple derive attributes not allowed", )); } else { derives = parse_derive_attribute(attr)?; } } else { return Err(Error::new(p.span(), &format!("Unsupported attribute"))); } } return Ok(StructInfo { derives, ident: def.ident.clone(), generics: def.generics.clone(), fields, vis, first_lifetime, attributes, }); } ouroboros_macro-0.14.0/src/utils.rs000064400000000000000000000114600072674642500154470ustar 00000000000000use inflector::Inflector; use proc_macro2::{Group, Ident, TokenStream, TokenTree}; use quote::{format_ident, quote}; use syn::{GenericParam, Generics, Visibility}; /// Makes phantom data definitions so that we don't get unused template parameter errors. pub fn make_generic_consumers(generics: &Generics) -> impl Iterator { generics .params .clone() .into_iter() .map(|param| match param { GenericParam::Type(ty) => { let ident = &ty.ident; ( quote! { #ident }, format_ident!( "_consume_template_type_{}", ident.to_string().to_snake_case() ), ) } GenericParam::Lifetime(lt) => { let lifetime = <.lifetime; let ident = &lifetime.ident; ( quote! { &#lifetime () }, format_ident!("_consume_template_lifetime_{}", ident), ) } GenericParam::Const(..) => unimplemented!(), }) } // Takes the generics parameters from the original struct and turns them into arguments. pub fn make_generic_arguments(generics: &Generics) -> Vec { let mut arguments = Vec::new(); for generic in generics.params.clone() { match generic { GenericParam::Type(typ) => { let ident = &typ.ident; arguments.push(quote! { #ident }); } GenericParam::Lifetime(lt) => { let lifetime = <.lifetime; arguments.push(quote! { #lifetime }); } GenericParam::Const(_) => unimplemented!("Const generics are not supported yet."), } } arguments } pub fn uses_this_lifetime(input: TokenStream) -> bool { for token in input.into_iter() { match token { TokenTree::Ident(ident) => { if ident == "this" { return true; } } TokenTree::Group(group) => { if uses_this_lifetime(group.stream()) { return true; } } _ => (), } } false } pub fn replace_this_with_lifetime(input: TokenStream, lifetime: Ident) -> TokenStream { input .into_iter() .map(|token| match &token { TokenTree::Ident(ident) => { if ident == "this" { TokenTree::Ident(lifetime.clone()) } else { token } } TokenTree::Group(group) => TokenTree::Group(Group::new( group.delimiter(), replace_this_with_lifetime(group.stream(), lifetime.clone()), )), _ => token, }) .collect() } pub fn submodule_contents_visiblity(original_visibility: &Visibility) -> Visibility { match original_visibility { // inherited: allow parent of inner submodule to see Visibility::Inherited => syn::parse_quote! { pub(super) }, // restricted: add an extra super if needed Visibility::Restricted(ref restricted) => { let is_first_component_super = restricted .path .segments .first() .map(|segm| segm.ident == "super") .unwrap_or(false); if restricted.path.leading_colon.is_none() && is_first_component_super { let mut new_visibility = restricted.clone(); new_visibility.in_token = Some( restricted .in_token .clone() .unwrap_or_else(|| syn::parse_quote! { in }), ); new_visibility.path.segments = std::iter::once(syn::parse_quote! { super }) .chain(restricted.path.segments.iter().cloned()) .collect(); Visibility::Restricted(new_visibility) } else { original_visibility.clone() } } // others are absolute, can use them as-is _ => original_visibility.clone(), } } /// Functionality inspired by `Inflector`, reimplemented here to avoid the /// `regex` dependency. pub fn to_class_case(s: &str) -> String { s.split('_') .flat_map(|word| { let mut chars = word.chars(); let first = chars.next(); // Unicode allows for a single character to become multiple characters when converting between cases. first .into_iter() .flat_map(|c| c.to_uppercase()) .chain(chars.flat_map(|c| c.to_lowercase())) }) .collect() }