ouroboros_macro-0.18.5/.cargo_vcs_info.json0000644000000001550000000000100143420ustar { "git": { "sha1": "a8cd334fbb25e6559ed5343e7826d283d0ac001c" }, "path_in_vcs": "ouroboros_macro" }ouroboros_macro-0.18.5/Cargo.toml0000644000000022660000000000100123450ustar # 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.18.5" authors = ["Josh "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Proc macro for ouroboros crate." documentation = "https://docs.rs/ouroboros_macro" readme = false license = "MIT OR Apache-2.0" repository = "https://github.com/someguynamedjosh/ouroboros" [lib] name = "ouroboros_macro" path = "src/lib.rs" proc-macro = true [dependencies.heck] version = "0.4.1" [dependencies.proc-macro2] version = "1.0" [dependencies.proc-macro2-diagnostics] version = "0.10" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["full"] [features] std = [] ouroboros_macro-0.18.5/Cargo.toml.orig000064400000000000000000000007641046102023000160270ustar 00000000000000[package] name = "ouroboros_macro" version = "0.18.5" authors = ["Josh "] edition = "2018" license = "MIT OR Apache-2.0" description = "Proc macro for ouroboros crate." documentation = "https://docs.rs/ouroboros_macro" repository = "https://github.com/someguynamedjosh/ouroboros" [lib] proc-macro = true [dependencies] heck = "0.4.1" proc-macro2 = "1.0" proc-macro2-diagnostics = "0.10" quote = "1.0" syn = { version = "2.0", features = ["full"] } [features] std = [] ouroboros_macro-0.18.5/LICENSE_APACHE000064400000000000000000000261071046102023000151450ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2021 Josh Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ouroboros_macro-0.18.5/LICENSE_MIT000064400000000000000000000020461046102023000146510ustar 00000000000000MIT License Copyright (c) 2021 Josh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ouroboros_macro-0.18.5/src/covariance_detection.rs000064400000000000000000000122371046102023000204430ustar 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 = tpath.path.segments.last()?; 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 == "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.18.5/src/generate/constructor.rs000064400000000000000000000205741046102023000204550ustar 00000000000000use crate::{ info_structures::{ArgType, BuilderType, 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, builder_type: BuilderType, ) -> 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 = match builder_type { BuilderType::AsyncSend => format_ident!("{}AsyncSendBuilder", info.ident), BuilderType::Async => format_ident!("{}AsyncBuilder", info.ident), BuilderType::Sync => 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 preferable 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, builder_type)?; 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 ); } else if let ArgType::TraitBound(bound_type) = arg_type { // Trait bounds are much trickier. We need a special syntax to accept them in the // constructor, 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 ); 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, if borrow.mutable { "mut " } else { "" }, ); if index < field.borrows.len() - 1 { doc_table += ", "; } } doc_table += &format!(") -> {}: _` | \n", field_name); if builder_type.is_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 = match builder_type { BuilderType::AsyncSend => quote! { async fn new_async_send }, BuilderType::Async => quote! { async fn new_async }, BuilderType::Sync => quote! { fn new }, }; let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect(); let internal_ident = &info.internal_ident; let constructor_def = quote! { #documentation #vis #constructor_fn(#(#params),*) -> #struct_name <#(#generic_args),*> { #(#code)* unsafe { Self { actual_data: ::core::mem::MaybeUninit::new(#internal_ident { #(#field_names),* }) } } } }; let generic_where = &info.generics.where_clause; let builder_fn = if builder_type.is_async() { quote! { async fn build } } else { quote! { fn build } }; let builder_code = match builder_type { BuilderType::AsyncSend => quote! { #struct_name::new_async_send( #(self.#builder_struct_field_names),* ).await }, BuilderType::Async => quote! { #struct_name::new_async( #(self.#builder_struct_field_names),* ).await }, BuilderType::Sync => 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.18.5/src/generate/derives.rs000064400000000000000000000057341046102023000175320ustar 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(); if let GenericParam::Type(t) = &mut new { 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! { ::core::fmt::Debug }; let struct_name = &info.ident; let body = quote! { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::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.18.5/src/generate/drop.rs000064400000000000000000000012321046102023000170220ustar 00000000000000use crate::info_structures::StructInfo; use proc_macro2::TokenStream; use quote::quote; use syn::Error; pub fn create_drop_impl(info: &StructInfo) -> Result { let ident = &info.ident; let generics = &info.generics; let generic_args = info.generic_arguments(); let mut where_clause = quote! {}; if let Some(clause) = &generics.where_clause { where_clause = quote! { #clause }; } Ok(quote! { impl #generics ::core::ops::Drop for #ident<#(#generic_args,)*> #where_clause { fn drop(&mut self) { unsafe { self.actual_data.assume_init_drop() }; } } }) } ouroboros_macro-0.18.5/src/generate/into_heads.rs000064400000000000000000000063731046102023000202060ustar 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(); let internal_struct = &info.internal_ident; // 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 { // Heads are fields that do not borrow anything. code.push(quote! { ::core::mem::drop(this.#field_name); }); } else { code.push(quote! { let #field_name = this.#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 }); } } 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)] #[allow(clippy::drop_non_drop)] #visibility fn into_heads(self) -> Heads<#(#generic_args),*> { let this_ptr = &self as *const _; let this: #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute_copy(&*this_ptr) }; ::core::mem::forget(self); #(#code)* Heads { #(#field_initializers),* } } }; (heads_struct_def, into_heads_fn) } ouroboros_macro-0.18.5/src/generate/mod.rs000064400000000000000000000003211046102023000166330ustar 00000000000000pub mod constructor; pub mod derives; pub mod drop; pub mod into_heads; pub mod struc; pub mod summon_checker; pub mod try_constructor; pub mod type_asserts; pub mod with; pub mod with_each; pub mod with_mut; ouroboros_macro-0.18.5/src/generate/struc.rs000064400000000000000000000052051046102023000172220ustar 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. pub fn create_actual_struct_def(info: &StructInfo) -> Result { let visibility = utils::submodule_contents_visibility(&info.vis); let mut fields = Vec::new(); for (ty, ident) in info.generic_consumers() { fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); } let generic_params = info.generic_params(); let generic_args = info.generic_arguments(); let generic_where = &info.generics.where_clause; let ident = &info.ident; let internal_ident = &info.internal_ident; Ok(quote! { #[repr(transparent)] #visibility struct #ident <#generic_params> #generic_where { actual_data: ::core::mem::MaybeUninit<#internal_ident<#(#generic_args),*>>, } }) } /// Creates a struct with fields like the original struct. Instances of the /// "actual" struct are reinterpreted as instances of the "internal" struct /// whenever data needs to be accessed. (This gets around the problem that /// references passed to functions must be valid through the entire function, /// but references *created* inside a function can be considered invalid /// whenever, even during the duration of the function.) pub fn create_internal_struct_def(info: &StructInfo) -> Result { let ident = &info.internal_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! { 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.18.5/src/generate/summon_checker.rs000064400000000000000000000051601046102023000210640ustar 00000000000000use proc_macro2::TokenStream; use quote::quote; use syn::Error; use crate::info_structures::{ArgType, BuilderType, 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, BuilderType::Sync)?; 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 // constructor, 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.18.5/src/generate/try_constructor.rs000064400000000000000000000322241046102023000213460ustar 00000000000000use crate::{ info_structures::{ArgType, BuilderType, 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, builder_type: BuilderType, ) -> 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 = match builder_type { BuilderType::AsyncSend => format_ident!("{}AsyncSendTryBuilder", info.ident), BuilderType::Async => format_ident!("{}AsyncTryBuilder", info.ident), BuilderType::Sync => 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 making 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 ", "preferable 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 preferable 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, builder_type)?; 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 ); 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 // constructor, 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 ); 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, if borrow.mutable { "mut " } else { "" }, ); if index < field.borrows.len() - 1 { doc_table += ", "; } } doc_table += &format!(") -> Result<{}: _, Error_>` | \n", field_name); let builder_value = if builder_type.is_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 = match builder_type { BuilderType::AsyncSend => quote! { try_new_or_recover_async_send }, BuilderType::Async => quote! { try_new_or_recover_async }, BuilderType::Sync => quote! { try_new_or_recover }, }; let or_recover_constructor_fn = if builder_type.is_async() { quote! { async fn #or_recover_ident } } else { quote! { fn #or_recover_ident } }; let constructor_fn = match builder_type { BuilderType::AsyncSend => quote! { async fn try_new_async_send }, BuilderType::Async => quote! { async fn try_new_async }, BuilderType::Sync => quote! { fn try_new }, }; let constructor_code = if builder_type.is_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 internal_ident = &info.internal_ident; 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(unsafe { Self { actual_data: ::core::mem::MaybeUninit::new(#internal_ident { #(#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 builder_type.is_async() { quote! { async fn try_build } } else { quote! { fn try_build } }; let or_recover_builder_fn = if builder_type.is_async() { quote! { async fn try_build_or_recover } } else { quote! { fn try_build_or_recover } }; let builder_code = match builder_type { BuilderType::AsyncSend => quote! { #struct_name::try_new_async_send( #(self.#builder_struct_field_names),* ).await }, BuilderType::Async => quote! { #struct_name::try_new_async( #(self.#builder_struct_field_names),* ).await }, BuilderType::Sync => quote! { #struct_name::try_new( #(self.#builder_struct_field_names),* ) }, }; let or_recover_builder_code = match builder_type { BuilderType::AsyncSend => quote! { #struct_name::try_new_or_recover_async_send( #(self.#builder_struct_field_names),* ).await }, BuilderType::Async => quote! { #struct_name::try_new_or_recover_async( #(self.#builder_struct_field_names),* ).await }, BuilderType::Sync => 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.18.5/src/generate/type_asserts.rs000064400000000000000000000026751046102023000206170ustar 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.18.5/src/generate/with.rs000064400000000000000000000077011046102023000170400ustar 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(); // 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: &this.#field_name }); } else if field.field_type == FieldType::Borrowed { let ass = quote! { #field_name: unsafe { ::ouroboros::macro_help::change_lifetime( &*this.#field_name ) } }; fields.push(quote! { #visibility #field_name: &'this #field_type }); field_assignments.push(ass.clone()); } 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> }); 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 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()); let extra: WhereClause = syn::parse_quote! { where 'this: 'outer_borrow }; clause .predicates .push(extra.predicates.first().unwrap().clone()); clause } else { syn::parse_quote! { where #lifetime: 'this, 'this: 'outer_borrow } }; let struct_defs = quote! { #[doc=#struct_documentation] #visibility struct BorrowedFields #new_generic_params #generic_where { #(#fields),* } }; let borrowed_fields_type = quote! { BorrowedFields<#(#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 documentation = if !options.do_no_doc { quote! { #[doc=#documentation] } } else { quote! { #[doc(hidden)] } }; let fn_defs = quote! { #documentation #[inline(always)] #visibility fn with <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType ) -> ReturnType { let this = unsafe { self.actual_data.assume_init_ref() }; user(BorrowedFields { #(#field_assignments),* }) } }; Ok((struct_defs, fn_defs)) } ouroboros_macro-0.18.5/src/generate/with_each.rs000064400000000000000000000141231046102023000200140ustar 00000000000000use crate::info_structures::{FieldType, Options, StructInfo}; use proc_macro2::TokenStream; use proc_macro2_diagnostics::Diagnostic; use quote::{format_ident, quote}; use syn::Error; pub enum ProcessingError { Syntax(Error), Covariance(Vec), } pub fn make_with_functions(info: &StructInfo, options: Options) -> (Vec, Vec) { let mut users = Vec::new(); let mut errors = 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 #[inline(always)] #visibility fn #user_name <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, ) -> ReturnType { let field = &unsafe { self.actual_data.assume_init_ref() }.#field_name; user(field) } }); if field.covariant == Some(true) { let borrower_name = format_ident!("borrow_{}", &field.name); users.push(quote! { #documentation #[inline(always)] #visibility fn #borrower_name<'this>( &'this self, ) -> &'this #field_type { &unsafe { self.actual_data.assume_init_ref() }.#field_name } }); } else if field.covariant.is_none() { errors.push(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 #[inline(always)] #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 { let field = &mut unsafe { self.actual_data.assume_init_mut() }.#field_name; user(field) } }); } 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 #[inline(always)] #visibility fn #user_name <'outer_borrow, ReturnType>( &'outer_borrow self, user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, ) -> ReturnType { let field = &unsafe { self.actual_data.assume_init_ref() }.#field_name; user(field) } }); 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() { errors.push(field.covariance_error()); } } let borrower_name = format_ident!("borrow_{}", &field.name); users.push(quote! { #documentation #[inline(always)] #visibility fn #borrower_name<'this>( &'this self, ) -> &'this #field_type { &unsafe { self.actual_data.assume_init_ref() }.#field_name } }); } else if field.field_type == FieldType::BorrowedMut { // Do not generate anything because if it is borrowed mutably once, we should not be able // to get any other kinds of references to it. } } (users, errors) } ouroboros_macro-0.18.5/src/generate/with_mut.rs000064400000000000000000000125041046102023000177220ustar 00000000000000use crate::{ info_structures::{FieldType, Options, StructInfo}, utils::{replace_this_with_lifetime, uses_this_lifetime}, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{Error, Lifetime, WhereClause}; pub fn make_with_all_mut_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 mut_fields = Vec::new(); let mut mut_field_assignments = Vec::new(); let mut lifetime_idents = Vec::new(); // I don't think the reverse is necessary but it does make the expanded code more uniform. for (index, field) in info.fields.iter().rev().enumerate() { let field_name = &field.name; let original_field_type = &field.typ; let lifetime = format_ident!("this{}", index); let field_type = replace_this_with_lifetime(quote! { #original_field_type }, lifetime.clone()); if field.field_type == FieldType::Tail { mut_fields.push(quote! { #visibility #field_name: &'outer_borrow mut #field_type }); mut_field_assignments.push(quote! { #field_name: &mut this.#field_name }); if uses_this_lifetime(quote! { #original_field_type }) { lifetime_idents.push(lifetime.clone()); } } else if field.field_type == FieldType::Borrowed { let ass = quote! { #field_name: unsafe { ::ouroboros::macro_help::change_lifetime( &*this.#field_name ) } }; let lt = Lifetime::new(&format!("'{}", lifetime), Span::call_site()); mut_fields.push(quote! { #visibility #field_name: &#lt #field_type }); mut_field_assignments.push(ass); lifetime_idents.push(lifetime.clone()); } 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() { mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData }); } let mut new_generic_params = info.generic_params().clone(); for lt in &lifetime_idents { let lt = Lifetime::new(&format!("'{}", lt), Span::call_site()); new_generic_params.insert(0, syn::parse_quote! { #lt }); } new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow }); let mut new_generic_args = info.generic_arguments(); let mut lifetimes = Vec::new(); for lt in &lifetime_idents { let lt = Lifetime::new(&format!("'{}", lt), Span::call_site()); lifetimes.push(lt.clone()); new_generic_args.insert(0, quote! { #lt }); } new_generic_args.insert(0, quote! { 'outer_borrow }); 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 fake_lifetime = Lifetime::new(&format!("'{}", info.fake_lifetime()), Span::call_site()); let mut generic_where = if let Some(clause) = &info.generics.where_clause { clause.clone() } else { syn::parse_quote! { where } }; for lt in &lifetime_idents { let lt = Lifetime::new(&format!("'{}", lt), Span::call_site()); let extra: WhereClause = syn::parse_quote! { where #fake_lifetime: #lt }; generic_where .predicates .extend(extra.predicates.into_iter()); } for idents in lifetime_idents.windows(2) { let lt = Lifetime::new(&format!("'{}", idents[1]), Span::call_site()); let outlives = Lifetime::new(&format!("'{}", idents[0]), Span::call_site()); let extra: WhereClause = syn::parse_quote! { where #lt: #outlives }; generic_where .predicates .extend(extra.predicates.into_iter()); } let struct_defs = quote! { #[doc=#mut_struct_documentation] #visibility struct BorrowedMutFields <#new_generic_params> #generic_where { #(#mut_fields),* } }; let borrowed_mut_fields_type = quote! { BorrowedMutFields<#(#new_generic_args),*> }; 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 mut_documentation = if !options.do_no_doc { quote! { #[doc=#mut_documentation] } } else { quote! { #[doc(hidden)] } }; let fn_defs = quote! { #mut_documentation #[inline(always)] #visibility fn with_mut <'outer_borrow, ReturnType>( &'outer_borrow mut self, user: impl for<#(#lifetimes),*> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType ) -> ReturnType { let this = unsafe { self.actual_data.assume_init_mut() }; user(BorrowedMutFields { #(#mut_field_assignments),* }) } }; Ok((struct_defs, fn_defs)) } ouroboros_macro-0.18.5/src/info_structures.rs000064400000000000000000000256351046102023000175370ustar 00000000000000use crate::utils::{make_generic_arguments, make_generic_consumers, replace_this_with_lifetime}; use proc_macro2::{Ident, TokenStream}; use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt}; use quote::{format_ident, quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, ConstParam, Error, GenericParam, Generics, LifetimeParam, Type, TypeParam, Visibility, spanned::Spanned, }; #[derive(Clone, Copy)] pub struct Options { pub do_no_doc: bool, pub do_pub_extras: bool, } impl Options { // pub fn documentation_to_tokens(&self, documentation: &str) -> TokenStream { // if self.do_no_doc { // quote! { #[doc(hidden)] } // } else { // quote! { #[doc=#documentation] } // } // } } #[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(Copy, Clone)] pub enum BuilderType { Sync, Async, AsyncSend, } impl BuilderType { pub fn is_async(&self) -> bool { !matches!(self, BuilderType::Sync) } } #[derive(Clone)] pub struct StructInfo { pub derives: Vec, pub ident: Ident, pub internal_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 { 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(LifetimeParam { lifetime, .. }) => lifetime.to_token_stream(), }); quote! { <'_, '_, #(#params,)*> } } pub fn generic_arguments(&self) -> Vec { make_generic_arguments(self.generics.params.iter().collect()) } /// 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. #[must_use] pub fn covariance_error(&self) -> Diagnostic { let error = concat!( "Ouroboros cannot automatically determine if this type is covariant.\n\n", "As an example, a Box<&'this ()> is covariant because it can be used as a\n", "Box<&'smaller ()> for any lifetime smaller than 'this. In contrast,\n", "a Fn(&'this ()) is not covariant because it cannot be used as a\n", "Fn(&'smaller ()). In general, values that are safe to use with smaller\n", "lifetimes than they were defined with are covariant, breaking this \n", "guarantee means the value is not covariant.\n\n", "To resolve this error, add #[covariant] or #[not_covariant] to the field.\n", ); self.typ.span().error(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, builder_type: BuilderType, ) -> Result { let field_type = &self.typ; let return_ty_constructor = || match builder_type { BuilderType::AsyncSend => { quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box< dyn ::core::future::Future + ::core::marker::Send + 'this>> } } BuilderType::Async => { quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box< dyn ::core::future::Future + 'this>> } } BuilderType::Sync => 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, builder_type: BuilderType, ) -> Result { let field_type = &self.typ; let return_ty_constructor = || match builder_type { BuilderType::AsyncSend => { quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box< dyn ::core::future::Future> + ::core::marker::Send + 'this>> } } BuilderType::Async => { quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box< dyn ::core::future::Future> + 'this>> } } BuilderType::Sync => quote! { ::core::result::Result<#field_type, Error_> }, }; self.make_constructor_arg_type_impl(info, return_ty_constructor) } } ouroboros_macro-0.18.5/src/lib.rs000064400000000000000000000161711046102023000150420ustar 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_internal_struct_def, summon_checker::generate_checker_summoner, try_constructor::create_try_builder_and_constructor, type_asserts::make_type_asserts, with::make_with_all_function, with_each::make_with_functions, }, info_structures::Options, parse::parse_struct, }; use generate::{ drop::create_drop_impl, struc::create_actual_struct_def, with_mut::make_with_all_mut_function, }; use heck::ToSnakeCase; use info_structures::BuilderType; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenTree; 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 internal_struct_def = create_internal_struct_def(&info)?; let drop_impl = create_drop_impl(&info)?; let borrowchk_summoner = generate_checker_summoner(&info)?; let (builder_struct_name, builder_def, constructor_def) = create_builder_and_constructor(&info, options, BuilderType::Sync)?; let (async_builder_struct_name, async_builder_def, async_constructor_def) = create_builder_and_constructor(&info, options, BuilderType::Async)?; let (async_send_builder_struct_name, async_send_builder_def, async_send_constructor_def) = create_builder_and_constructor(&info, options, BuilderType::AsyncSend)?; let (try_builder_struct_name, try_builder_def, try_constructor_def) = create_try_builder_and_constructor(&info, options, BuilderType::Sync)?; let (async_try_builder_struct_name, async_try_builder_def, async_try_constructor_def) = create_try_builder_and_constructor(&info, options, BuilderType::Async)?; let ( async_send_try_builder_struct_name, async_send_try_builder_def, async_send_try_constructor_def, ) = create_try_builder_and_constructor(&info, options, BuilderType::AsyncSend)?; let (with_defs, with_errors) = make_with_functions(&info, options); let with_errors = with_errors .into_iter() .map(|err| err.emit_as_item_tokens()) .collect::>(); let (with_all_struct_def, with_all_fn_def) = make_with_all_function(&info, options)?; let (with_all_mut_struct_def, with_all_mut_fn_def) = make_with_all_mut_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::*; #[doc="The self-referencing struct."] #actual_struct_def #internal_struct_def #drop_impl #borrowchk_summoner #builder_def #async_builder_def #async_send_builder_def #try_builder_def #async_try_builder_def #async_send_try_builder_def #with_all_struct_def #with_all_mut_struct_def #(#with_errors)* #heads_struct_def #impls impl <#generic_params> #struct_name <#(#generic_args),*> #generic_where { #constructor_def #async_constructor_def #async_send_constructor_def #try_constructor_def #async_try_constructor_def #async_send_try_constructor_def #(#with_defs)* #with_all_fn_def #with_all_mut_fn_def #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 :: #async_send_builder_struct_name; #extra_visibility use #mod_name :: #try_builder_struct_name; #extra_visibility use #mod_name :: #async_try_builder_struct_name; #extra_visibility use #mod_name :: #async_send_try_builder_struct_name; })) } #[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.18.5/src/parse.rs000064400000000000000000000240561046102023000154070ustar 00000000000000use proc_macro2::{Span, TokenTree}; use quote::format_ident; use syn::{ spanned::Spanned, Attribute, Error, Fields, GenericParam, ItemStruct, MacroDelimiter, Meta, }; use crate::{ covariance_detection::type_is_covariant_over_this_lifetime, info_structures::{BorrowRequest, Derive, FieldType, StructFieldInfo, StructInfo}, utils::submodule_contents_visibility, }; 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 = match &attr.meta { Meta::List(ml) => ml.tokens.clone(), _ => { return Err(Error::new_spanned( &attr.meta, "Invalid syntax for borrows() macro.", )) } }; 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 = match &attr.meta { Meta::List(ml) => ml, _ => unreachable!(), }; if !matches!(body.delimiter, MacroDelimiter::Paren(_)) { return Err(Error::new( attr.span(), format!( "malformed derive input, derive attributes are of the form `#[derive({})]`", body.tokens ), )); } let mut derives = Vec::new(); for token in body.tokens.clone().into_iter() { if let Some(derive) = parse_derive_token(&token)? { derives.push(derive); } } Ok(derives) } 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_visibility(&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.is_empty() { return Err(Error::new(p.span(), "Unsupported attribute".to_string())); } let name = p[0].ident.to_string(); let good = matches!(&name[..], "clippy" | "allow" | "deny" | "doc"); if good { attributes.push(attr.clone()) } else if name == "derive" { if !derives.is_empty() { return Err(Error::new( attr.span(), "Multiple derive attributes not allowed", )); } else { derives = parse_derive_attribute(attr)?; } } else { return Err(Error::new(p.span(), "Unsupported attribute".to_string())); } } Ok(StructInfo { derives, ident: def.ident.clone(), internal_ident: format_ident!("{}Internal", def.ident), generics: def.generics.clone(), fields, vis, first_lifetime, attributes, }) } ouroboros_macro-0.18.5/src/utils.rs000064400000000000000000000122501046102023000154260ustar 00000000000000use heck::ToSnakeCase; 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), ) } // rustc don't require constants to consume, so we just skip it. GenericParam::Const(ct) => { let ident = ct.ident; ( quote! { () }, format_ident!( "_comsume_template_const_parameter_{}", ident.to_string().to_snake_case() ), ) }, }) } // Takes the generics parameters from the original struct and turns them into arguments. pub fn make_generic_arguments(generics: Vec<&GenericParam>) -> Vec { let mut arguments = Vec::new(); for generic in generics { 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(ct) => { let ident = &ct.ident; arguments.push(quote! { #ident }); }, } } 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_visibility(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 .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() }