From 23c1e5ff66c23e3872877ab8cf257fe22e8f68f1 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Thu, 2 Mar 2023 22:57:26 +0000 Subject: Revert "Import ouroboros_macro crate" Revert submission 2428041-ouroboros-import Reason for revert: b/271004059 Reverted changes: /q/submissionid:2428041-ouroboros-import Change-Id: I3179f731e00e2f93fcc9114b85d6bb760f9e8f3e --- .cargo_vcs_info.json | 6 - Cargo.toml | 43 ------ Cargo.toml.orig | 22 --- LICENSE | 1 - LICENSE_APACHE | 202 -------------------------- LICENSE_MIT | 22 --- METADATA | 19 --- MODULE_LICENSE_APACHE2 | 0 OWNERS | 1 - src/covariance_detection.rs | 142 ------------------- src/generate/constructor.rs | 202 -------------------------- src/generate/derives.rs | 98 ------------- src/generate/into_heads.rs | 81 ----------- src/generate/mod.rs | 9 -- src/generate/struc.rs | 50 ------- src/generate/summon_checker.rs | 62 -------- src/generate/try_constructor.rs | 301 --------------------------------------- src/generate/type_asserts.rs | 41 ------ src/generate/with_all.rs | 134 ------------------ src/generate/with_each.rs | 132 ----------------- src/info_structures.rs | 304 ---------------------------------------- src/lib.rs | 165 ---------------------- src/parse.rs | 271 ----------------------------------- src/utils.rs | 140 ------------------ 24 files changed, 2448 deletions(-) delete mode 100644 .cargo_vcs_info.json delete mode 100644 Cargo.toml delete mode 100644 Cargo.toml.orig delete mode 120000 LICENSE delete mode 100644 LICENSE_APACHE delete mode 100644 LICENSE_MIT delete mode 100644 METADATA delete mode 100644 MODULE_LICENSE_APACHE2 delete mode 100644 OWNERS delete mode 100644 src/covariance_detection.rs delete mode 100644 src/generate/constructor.rs delete mode 100644 src/generate/derives.rs delete mode 100644 src/generate/into_heads.rs delete mode 100644 src/generate/mod.rs delete mode 100644 src/generate/struc.rs delete mode 100644 src/generate/summon_checker.rs delete mode 100644 src/generate/try_constructor.rs delete mode 100644 src/generate/type_asserts.rs delete mode 100644 src/generate/with_all.rs delete mode 100644 src/generate/with_each.rs delete mode 100644 src/info_structures.rs delete mode 100644 src/lib.rs delete mode 100644 src/parse.rs delete mode 100644 src/utils.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json deleted file mode 100644 index e454f04..0000000 --- a/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "79bac55f29edbb44f7205246ab5c3f706cbc4647" - }, - "path_in_vcs": "ouroboros_macro" -} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 52734ba..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -# 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.15.5" -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"] - -[features] -std = [] diff --git a/Cargo.toml.orig b/Cargo.toml.orig deleted file mode 100644 index 2db8277..0000000 --- a/Cargo.toml.orig +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "ouroboros_macro" -version = "0.15.5" -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"] } - -[features] -std = [] diff --git a/LICENSE b/LICENSE deleted file mode 120000 index 6d46dcc..0000000 --- a/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE_MIT \ No newline at end of file diff --git a/LICENSE_APACHE b/LICENSE_APACHE deleted file mode 100644 index 574cb18..0000000 --- a/LICENSE_APACHE +++ /dev/null @@ -1,202 +0,0 @@ - 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 Joshua Maros - - 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. - diff --git a/LICENSE_MIT b/LICENSE_MIT deleted file mode 100644 index 9ed5b2a..0000000 --- a/LICENSE_MIT +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2021 Joshua Maros - -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. - diff --git a/METADATA b/METADATA deleted file mode 100644 index bdc87ab..0000000 --- a/METADATA +++ /dev/null @@ -1,19 +0,0 @@ -name: "ouroboros_macro" -description: "Proc macro for ouroboros crate." -third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/ouroboros_macro" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/ouroboros_macro/ouroboros_macro-0.15.5.crate" - } - version: "0.15.5" - license_type: NOTICE - last_upgrade_date { - year: 2023 - month: 2 - day: 10 - } -} diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 diff --git a/OWNERS b/OWNERS deleted file mode 100644 index 45dc4dd..0000000 --- a/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include platform/prebuilts/rust:master:/OWNERS diff --git a/src/covariance_detection.rs b/src/covariance_detection.rs deleted file mode 100644 index 2f50983..0000000 --- a/src/covariance_detection.rs +++ /dev/null @@ -1,142 +0,0 @@ -use 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, - } -} diff --git a/src/generate/constructor.rs b/src/generate/constructor.rs deleted file mode 100644 index 6ab47bc..0000000 --- a/src/generate/constructor.rs +++ /dev/null @@ -1,202 +0,0 @@ -use 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 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, 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.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 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 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 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)) -} diff --git a/src/generate/derives.rs b/src/generate/derives.rs deleted file mode 100644 index 0233a3f..0000000 --- a/src/generate/derives.rs +++ /dev/null @@ -1,98 +0,0 @@ -use 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! { ::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)* }) -} diff --git a/src/generate/into_heads.rs b/src/generate/into_heads.rs deleted file mode 100644 index 5784e46..0000000 --- a/src/generate/into_heads.rs +++ /dev/null @@ -1,81 +0,0 @@ -use 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)] - #[allow(clippy::drop_non_drop)] - #visibility fn into_heads(self) -> Heads<#(#generic_args),*> { - #(#code)* - Heads { - #(#field_initializers),* - } - } - }; - (heads_struct_def, into_heads_fn) -} diff --git a/src/generate/mod.rs b/src/generate/mod.rs deleted file mode 100644 index 0b229d1..0000000 --- a/src/generate/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub 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; diff --git a/src/generate/struc.rs b/src/generate/struc.rs deleted file mode 100644 index 9b2ff51..0000000 --- a/src/generate/struc.rs +++ /dev/null @@ -1,50 +0,0 @@ -use 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) -} diff --git a/src/generate/summon_checker.rs b/src/generate/summon_checker.rs deleted file mode 100644 index 743c199..0000000 --- a/src/generate/summon_checker.rs +++ /dev/null @@ -1,62 +0,0 @@ -use 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 - // 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,)* - }; - } - }) -} diff --git a/src/generate/try_constructor.rs b/src/generate/try_constructor.rs deleted file mode 100644 index 4078c56..0000000 --- a/src/generate/try_constructor.rs +++ /dev/null @@ -1,301 +0,0 @@ -use 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 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, 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.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 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 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 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)) -} diff --git a/src/generate/type_asserts.rs b/src/generate/type_asserts.rs deleted file mode 100644 index 3c8c7d1..0000000 --- a/src/generate/type_asserts.rs +++ /dev/null @@ -1,41 +0,0 @@ -use 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)* - } - } -} diff --git a/src/generate/with_all.rs b/src/generate/with_all.rs deleted file mode 100644 index e6a2665..0000000 --- a/src/generate/with_all.rs +++ /dev/null @@ -1,134 +0,0 @@ -use 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 - #[inline(always)] - #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 - #[inline(always)] - #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)) -} diff --git a/src/generate/with_each.rs b/src/generate/with_each.rs deleted file mode 100644 index 8985857..0000000 --- a/src/generate/with_each.rs +++ /dev/null @@ -1,132 +0,0 @@ -use 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 - #[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 { - user(&self. #field_name) - } - }); - 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 { - &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 - #[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 { - 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 - #[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 { - 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 - #[inline(always)] - #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) -} diff --git a/src/info_structures.rs b/src/info_structures.rs deleted file mode 100644 index 05dc734..0000000 --- a/src/info_structures.rs +++ /dev/null @@ -1,304 +0,0 @@ -use 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(Copy, Clone)] -pub enum BuilderType { - Sync, - Async, - AsyncSend, -} - -impl BuilderType { - pub fn is_async(&self) -> bool { - match self { - BuilderType::Sync => false, - _ => true, - } - } -} - -#[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, - 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) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index cd12419..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,165 +0,0 @@ -extern 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 info_structures::BuilderType; -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, 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 = 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::*; - #[doc="The self-referencing struct."] - #actual_struct_def - #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_defs - #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_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 :: #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_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(), - } -} diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index 546aa7c..0000000 --- a/src/parse.rs +++ /dev/null @@ -1,271 +0,0 @@ -use 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, - }); -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 5bbae6c..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,140 +0,0 @@ -use 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() -} -- cgit v1.2.3