diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/ouroboros/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/ouroboros/Cargo.toml | 27 | ||||
-rw-r--r-- | third_party/rust/ouroboros/src/lib.rs | 249 | ||||
-rw-r--r-- | third_party/rust/ouroboros_macro/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/ouroboros_macro/Cargo.toml | 36 | ||||
-rw-r--r-- | third_party/rust/ouroboros_macro/src/lib.rs | 1425 |
6 files changed, 1739 insertions, 0 deletions
diff --git a/third_party/rust/ouroboros/.cargo-checksum.json b/third_party/rust/ouroboros/.cargo-checksum.json new file mode 100644 index 0000000000..764dcc22be --- /dev/null +++ b/third_party/rust/ouroboros/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"5e757a99e461764a5fadbc8dd3d55570c1d41c6ffeee2b8bdae58db95fcf44c4","src/lib.rs":"60fad6e825c78e83c577ed4134132c46d7d3e613ea764089e9dce57396e9dfa0"},"package":"b0b666c900ea45a357bc7915a4dc8fab7e295f2e756e843aa3c0788d94aa3a29"}
\ No newline at end of file diff --git a/third_party/rust/ouroboros/Cargo.toml b/third_party/rust/ouroboros/Cargo.toml new file mode 100644 index 0000000000..f622812d9e --- /dev/null +++ b/third_party/rust/ouroboros/Cargo.toml @@ -0,0 +1,27 @@ +# 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 believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "ouroboros" +version = "0.7.0" +authors = ["Joshua Maros <joshua-maros@github.com>"] +description = "Easy, safe self-referential struct generation." +documentation = "https://docs.rs/ouroboros" +readme = "../README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/joshua-maros/ouroboros" +[dependencies.ouroboros_macro] +version = "0.7.0" + +[dependencies.stable_deref_trait] +version = "1.2" diff --git a/third_party/rust/ouroboros/src/lib.rs b/third_party/rust/ouroboros/src/lib.rs new file mode 100644 index 0000000000..12f053a4fe --- /dev/null +++ b/third_party/rust/ouroboros/src/lib.rs @@ -0,0 +1,249 @@ +//! A crate for creating safe self-referencing structs. +//! +//! See the documentation of [`ouroboros_examples`](https://docs.rs/ouroboros_examples) for +//! sample documentation of structs which have had the macro applied to them. + +#![allow(clippy::needless_doctest_main)] + +/// This macro is used to turn a regular struct into a self-referencing one. An example: +/// ```rust +/// use ouroboros::self_referencing; +/// +/// #[self_referencing] +/// struct MyStruct { +/// int_data: Box<i32>, +/// float_data: Box<f32>, +/// #[borrows(int_data)] +/// int_reference: &'this i32, +/// #[borrows(mut float_data)] +/// float_reference: &'this mut f32, +/// } +/// +/// fn main() { +/// let mut my_value = MyStructBuilder { +/// int_data: Box::new(42), +/// float_data: Box::new(3.14), +/// int_reference_builder: |int_data: &i32| int_data, +/// float_reference_builder: |float_data: &mut f32| float_data, +/// }.build(); +/// +/// // Prints 42 +/// println!("{:?}", my_value.borrow_int_data()); +/// // Prints 3.14 +/// println!("{:?}", my_value.borrow_float_reference()); +/// // Sets the value of float_data to 84.0 +/// my_value.with_mut(|fields| { +/// **fields.float_reference = (**fields.int_reference as f32) * 2.0; +/// }); +/// +/// // We can hold on to this reference... +/// let int_ref = *my_value.borrow_int_reference(); +/// println!("{:?}", *int_ref); +/// // As long as the struct is still alive. +/// drop(my_value); +/// // This will cause an error! +/// // println!("{:?}", *int_ref); +/// } +/// ``` +/// To explain the features and limitations of this crate, some definitions are necessary: +/// # Definitions +/// - **immutably borrowed field**: a field which is immutably borrowed by at least one other field. +/// - **mutably borrowed field**: a field which is mutably borrowed by exactly one other field. +/// - **self-referencing field**: a field which borrows at least one other field. +/// - **head field**: a field which does not borrow any other fields, I.E. not self-referencing. +/// This does not include fields with empty borrows annotations (`#[borrows()]`.) +/// - **tail field**: a field which is not borrowed by any other fields. +/// +/// # Usage +/// To make a self-referencing struct, you must write a struct definition and place +/// `#[self_referencing]` on top. For every field that borrows other fields, you must place +/// `#[borrows()]` on top and place inside the parenthesis a list of fields that it borrows. Mut can +/// be prefixed to indicate that a mutable borrow is required. For example, +/// `#[borrows(a, b, mut c)]` indicates that the first two fields need to be borrowed immutably and +/// the third needs to be borrowed mutably. You can also use `#[borrows()]` without any arguments to +/// indicate a field that will eventually borrow from the struct, but does not borrow anything when +/// first created. For example, you could use this on a field like `error: Option<&'this str>`. +/// +/// # You must comply with these limitations +/// - Fields must be declared before the first time they are borrowed. +/// - Normal borrowing rules apply, E.G. a field cannot be borrowed mutably twice. +/// - Fields that are borrowed must be of a data type that implement +/// [`StableDeref`](https://docs.rs/stable_deref_trait/1.2.0/stable_deref_trait/trait.StableDeref.html). +/// Normally this just means `Box<T>`. +/// - Fields that use the `'this` lifetime must have a corresponding `#[borrows()]` annotation. +/// The error for this needs some work, currently you will get an error saying that `'this` is +/// undefined at the location it was illegally used in. +/// +/// Violating them will result in an error message directly pointing out the violated rule. +/// +/// # Flexibility of this crate +/// The example above uses plain references as the self-referencing part of the struct, but you can +/// use anything that is dependent on lifetimes of objects inside the struct. For example, you could +/// do something like this: +/// ```rust +/// use ouroboros::self_referencing; +/// +/// pub struct ComplexData<'a, 'b> { +/// aref: &'a i32, +/// bref: &'b mut i32, +/// number: i32, +/// } +/// +/// impl<'a, 'b> ComplexData<'a, 'b> { +/// fn new(aref: &'a i32, bref: &'b mut i32, number: i32) -> Self { +/// Self { aref, bref, number } +/// } +/// +/// /// Copies the value aref points to into what bref points to. +/// fn transfer(&mut self) { +/// *self.bref = *self.aref; +/// } +/// +/// /// Prints the value bref points to. +/// fn print_bref(&self) { +/// println!("{}", *self.bref); +/// } +/// } +/// +/// fn main() { +/// #[self_referencing] +/// struct DataStorage { +/// immutable: Box<i32>, +/// mutable: Box<i32>, +/// #[borrows(immutable, mut mutable)] +/// complex_data: ComplexData<'this, 'this>, +/// } +/// +/// let mut data_storage = DataStorageBuilder { +/// immutable: Box::new(10), +/// mutable: Box::new(20), +/// complex_data_builder: |i: &i32, m: &mut i32| ComplexData::new(i, m, 12345), +/// }.build(); +/// data_storage.with_complex_data_mut(|data| { +/// // Copies the value in immutable into mutable. +/// data.transfer(); +/// // Prints 10 +/// data.print_bref(); +/// }); +/// } +/// ``` +/// # Using `chain_hack` +/// Unfortunately, as of September 2020, Rust has a +/// [known limitation in its type checker](https://users.rust-lang.org/t/why-does-this-not-compile-box-t-target-t/49027/7?u=aaaaa) +/// which prevents chained references from working (I.E. structs where field C references field B +/// which references field A.) To counteract this problem, you can use +/// `#[self_referencing(chain_hack)]` to allow creating these kinds of structs at the cost of +/// additional restrictions and possible loss of clarity in some error messages. The main limitation +/// is that all fields that are borrowed must be of type `Box<T>`. A nice error message will be +/// generated if you use a different type. There should be no other limitations, but some +/// configurations may produce strange compiler errors. If you find such a configuration, please +/// open an issue on the [Github repository](https://github.com/joshua-maros/ouroboros/issues). +/// You can view a documented example of a struct which uses `chain_hack` [here](https://docs.rs/ouroboros_examples/latest/ouroboros_examples/struct.ChainHack.html). +/// +/// # What does the macro generate? +/// The `#[self_referencing]` struct will replace your definition with an unsafe self-referencing +/// struct with a safe public interface. Many functions will be generated depending on your original +/// struct definition. Documentation is generated for all items, so building documentation for +/// your project allows accessing detailed information about available functions. Using +/// `#[self_referencing(no_doc)]` will hide the generated items from documentation if it is becoming +/// too cluttered. +/// +/// ### A quick note on visibility +/// The visibility of generated items is dependent on one of two things. If the +/// generated item is related to a specific field of the struct, it uses the visibility of the +/// original field. (The actual field in the struct will be made private since accessing it could cause +/// undefined behavior.) If the generated item is not related to any particular field, it will by +/// default only be visible to the module the struct is declared in. (This includes things like +/// `new()` and `with()`.) You can use `#[self_referencing(pub_extras)]` to make these items have the +/// same visibility as the struct itself. +/// +/// # List of generated items +/// ### `MyStruct::new(fields...) -> MyStruct` +/// A basic constructor. It accepts values for each field in the order you declared them in. For +/// **head fields**, you only need to pass in what value it should have and it will be moved in +/// to the output. For **self-referencing fields**, you must provide a function or closure which creates +/// the value based on the values it borrows. A field using the earlier example of +/// `#[borrow(a, b, mut c)]` would require a function typed as +/// `FnOnce(a: &_, b: &_, c: &mut _) -> _`. Fields which have an empty borrows annotation +/// (`#[borrows()]`) should have their value directly passed in. A field using the earlier example +/// of `Option<&'this str>` would require an input of `None`. Do not pass a function. Do not collect +/// 200 dollars. +/// ### `MyStructBuilder` +/// This is the preferred way to create a new instance of your struct. It is similar to using the +/// `MyStruct { a, b, c, d }` syntax instead of `MyStruct::new(a, b, c, d)`. It contains one field +/// for every argument in the actual constructor. **Head fields** have the same name that you +/// originally defined them with. **self-referencing fields** are suffixed with `_builder` since you need +/// to provide a function instead of a value. Fields with an empty borrows annotation are not +/// initialized using builders. Calling `.build()` on an instance of `MyStructBuilder` +/// will convert it to an instance of `MyStruct`. +/// ### `MyStruct::try_new<E>(fields...) -> Result<MyStruct, E>` +/// Similar to the regular `new()` function, except the functions wich create values for all +/// **self-referencing fields** can return `Result<>`s. If any of those are `Err`s, that error will be +/// returned instead of an instance of `MyStruct`. The preferred way to use this function is through +/// `MyStructTryBuilder` and its `try_build()` function. +/// ### `MyStruct::try_new_or_recover<E>(fields...) -> Result<MyStruct, (E, Heads)>` +/// Similar to the `try_new()` function, except that all the **head fields** are returned along side +/// the original error in case of an error. The preferred way to use this function is through +/// `MyStructTryBuilder` and its `try_build_or_recover()` function. +/// ### `MyStruct::with_FIELD<R>(&self, user: FnOnce(field: &FieldType) -> R) -> R` +/// This function is generated for every **tail and immutably-borrowed field** in your struct. It +/// allows safely accessing +/// a reference to that value. The function generates the reference and passes it to `user`. You +/// can do anything you want with the reference, it is constructed to not outlive the struct. +/// ### `MyStruct::borrow_FIELD(&self) -> &FieldType` +/// This function is generated for every **tail and immutably-borrowed field** in your struct. It +/// is equivalent to calling `my_struct.with_FIELD(|field| field)`. Note that certain types of +/// fields would cause this function to generate a compiler error, so it is ommitted. Generally, if +/// your field uses `'this` as a lifetime parameter, the corresponding `borrow_FIELD` function will +/// not be generated. There is no `borrow_FIELD_mut`, unfortunately, as Rust's +/// borrow checker is currently not capable of ensuring that such a method would be used safely. +/// ### `MyStruct::with_FIELD_mut<R>(&mut self, user: FnOnce(field: &mut FieldType) -> R) -> R` +/// This function is generated for every **tail field** in your struct. It is the mutable version +/// of `with_FIELD`. +/// ### `MyStruct::with<R>(&self, user: FnOnce(fields: AllFields) -> R) -> R` +/// Allows borrowing all **tail and immutably-borrowed fields** at once. Functions similarly to +/// `with_FIELD`. +/// ### `MyStruct::with_mut<R>(&self, user: FnOnce(fields: AllFields) -> R) -> R` +/// Allows mutably borrowing all **tail fields** and immutably borrowing all **immutably-borrowed** +/// fields at once. Functions similarly to `with_FIELD_mut`, except that you can borrow multiple +/// fields as mutable at the same time and also have immutable access to any remaining fields. +/// ### `MyStruct::into_heads(self) -> Heads` +/// Drops all self-referencing fields and returns a struct containing all **head fields**. +pub use ouroboros_macro::self_referencing; + +#[doc(hidden)] +pub mod macro_help { + use stable_deref_trait::StableDeref; + use std::ops::DerefMut; + + /// Converts a reference to an object implementing Deref to a static reference to the data it + /// Derefs to. This is obviously unsafe because the compiler can no longer guarantee that the + /// data outlives the reference. This function is templated to only work for containers that + /// implement StableDeref, E.G. Box and Rc. The intent is that the data that is being pointed + /// to will never move as long as the container itself is not dropped. It is up to the consumer + /// to get rid of the reference before the container is dropped. The + 'static ensures that + /// whatever we are referring to will remain valid indefinitely, that there are no limitations + /// on how long the pointer itself can live. + /// + /// # Safety + /// + /// The caller must ensure that the returned reference is not used after the originally passed + /// reference would become invalid. + pub unsafe fn stable_deref_and_strip_lifetime<'a, T: StableDeref + 'static>( + data: &'a T, + ) -> &'static T::Target { + &*((&**data) as *const _) + } + + /// Like stable_deref_and_strip_lifetime, but for mutable references. + /// + /// # Safety + /// + /// The caller must ensure that the returned reference is not used after the originally passed + /// reference would become invalid. + pub unsafe fn stable_deref_and_strip_lifetime_mut<'a, T: StableDeref + DerefMut + 'static>( + data: &'a mut T, + ) -> &'static mut T::Target { + &mut *((&mut **data) as *mut _) + } +} diff --git a/third_party/rust/ouroboros_macro/.cargo-checksum.json b/third_party/rust/ouroboros_macro/.cargo-checksum.json new file mode 100644 index 0000000000..835fe4d32f --- /dev/null +++ b/third_party/rust/ouroboros_macro/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"0fe2091620d2a21b136a30c206cd2e4cd6440d1ebc0380c2d904efe4daf8c63f","src/lib.rs":"7dd4e3cc1e646541384b0dec167e264d73c35e6264a0d4d5de60e63adf13c3e4"},"package":"dfc618f205c0adc415923c74369d45f5f298462fc473711dfc33056a23f23619"}
\ No newline at end of file diff --git a/third_party/rust/ouroboros_macro/Cargo.toml b/third_party/rust/ouroboros_macro/Cargo.toml new file mode 100644 index 0000000000..2eb18f7d60 --- /dev/null +++ b/third_party/rust/ouroboros_macro/Cargo.toml @@ -0,0 +1,36 @@ +# 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 believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "ouroboros_macro" +version = "0.7.0" +authors = ["Joshua Maros <joshua-maros@github.com>"] +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" + +[dependencies.proc-macro2] +version = "1.0" + +[dependencies.quote] +version = "1.0" + +[dependencies.syn] +version = "1.0" +features = ["full"] diff --git a/third_party/rust/ouroboros_macro/src/lib.rs b/third_party/rust/ouroboros_macro/src/lib.rs new file mode 100644 index 0000000000..228c4fa1d0 --- /dev/null +++ b/third_party/rust/ouroboros_macro/src/lib.rs @@ -0,0 +1,1425 @@ +use inflector::Inflector; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{Group, Span, TokenTree}; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + Attribute, Error, Fields, GenericParam, Generics, Ident, ItemStruct, PathArguments, Type, + Visibility, +}; + +#[derive(Clone, Copy, PartialEq)] +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 { + fn is_tail(self) -> bool { + self == Self::Tail + } +} + +struct BorrowRequest { + index: usize, + mutable: bool, +} + +struct StructFieldInfo { + name: Ident, + typ: Type, + field_type: FieldType, + vis: Visibility, + borrows: Vec<BorrowRequest>, + /// 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. + self_referencing: bool, + /// If this is true, we should avoid making borrow_* or borrow_*_mut functions as they will not + /// be able to compile. + uses_this_in_template: bool, +} + +impl StructFieldInfo { + fn builder_name(&self) -> Ident { + format_ident!("{}_builder", self.name) + } + + fn illegal_ref_name(&self) -> Ident { + format_ident!("{}_illegal_static_reference", self.name) + } + + // 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. For example, suppose a field + // `test: Box<i32>`. This method would generate code that looks like: + // ```rust + // // Variable name taken from self.illegal_ref_name() + // let test_illegal_static_reference = unsafe { + // ::ouroboros::macro_help::stable_deref_and_strip_lifetime( + // &((*result.as_ptr()).field) + // ) + // }; + // ``` + fn make_illegal_static_reference(&self) -> TokenStream2 { + let field_name = &self.name; + let ref_name = self.illegal_ref_name(); + quote! { + let #ref_name = unsafe { + ::ouroboros::macro_help::stable_deref_and_strip_lifetime( + &((*result.as_ptr()).#field_name) + ) + }; + } + } + + /// Like make_illegal_static_reference, but provides a mutable reference instead. + fn make_illegal_static_mut_reference(&self) -> TokenStream2 { + let field_name = &self.name; + let ref_name = self.illegal_ref_name(); + quote! { + let #ref_name = unsafe { + ::ouroboros::macro_help::stable_deref_and_strip_lifetime_mut( + &mut ((*result.as_mut_ptr()).#field_name) + ) + }; + } + } +} + +enum ArgType { + /// Used when the initial value of a field can be passed directly into the constructor. + Plain(TokenStream2), + /// Used when a field requires self references and thus requires something that implements + /// a builder function trait instead of a simple plain type. + TraitBound(TokenStream2), +} + +fn deref_type(field_type: &Type, do_chain_hack: bool) -> Result<TokenStream2, Error> { + if do_chain_hack { + if let Type::Path(tpath) = field_type { + if let Some(segment) = tpath.path.segments.last() { + if segment.ident == "Box" || segment.ident == "Arc" || segment.ident == "Rc" { + if let PathArguments::AngleBracketed(args) = &segment.arguments { + if let Some(arg) = args.args.first() { + return Ok(quote! { #arg }); + } + } + } + } + } + Err(Error::new_spanned( + &field_type, + concat!( + "Borrowed fields must be of type Box<T>, Arc<T>, or Rc<T> when chain_hack is ", + "used. Either change the field to one of the listed types or remove chain_hack." + ), + )) + } else { + Ok(quote! { <#field_type as ::core::ops::Deref>::Target }) + } +} + +fn make_constructor_arg_type_impl( + for_field: &StructFieldInfo, + other_fields: &[StructFieldInfo], + make_builder_return_type: impl FnOnce() -> TokenStream2, + do_chain_hack: bool, +) -> Result<ArgType, Error> { + let field_type = &for_field.typ; + if for_field.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_static(field_type.into_token_stream()); + Ok(ArgType::Plain(quote! { #field_type })) + } else { + let mut field_builder_params = Vec::new(); + for borrow in &for_field.borrows { + if borrow.mutable { + let field = &other_fields[borrow.index]; + let field_type = &field.typ; + let content_type = deref_type(field_type, do_chain_hack)?; + field_builder_params.push(quote! { + &'this mut #content_type + }); + } else { + let field = &other_fields[borrow.index]; + let field_type = &field.typ; + let content_type = deref_type(field_type, do_chain_hack)?; + field_builder_params.push(quote! { + &'this #content_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`. +fn make_constructor_arg_type( + for_field: &StructFieldInfo, + other_fields: &[StructFieldInfo], + do_chain_hack: bool, +) -> Result<ArgType, Error> { + let field_type = &for_field.typ; + make_constructor_arg_type_impl( + for_field, + other_fields, + || quote! { #field_type }, + do_chain_hack, + ) +} + +/// Like make_constructor_arg_type, but used for the try_new constructor. +fn make_try_constructor_arg_type( + for_field: &StructFieldInfo, + other_fields: &[StructFieldInfo], + do_chain_hack: bool, +) -> Result<ArgType, Error> { + let field_type = &for_field.typ; + make_constructor_arg_type_impl( + for_field, + other_fields, + || quote! { ::core::result::Result<#field_type, Error_> }, + do_chain_hack, + ) +} + +/// Makes phantom data definitions so that we don't get unused template parameter errors. +fn make_template_consumers(generics: &Generics) -> impl Iterator<Item = (TokenStream2, Ident)> { + 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!(), + }) +} + +fn replace_this_with_static(input: TokenStream2) -> TokenStream2 { + input + .into_iter() + .map(|token| match &token { + TokenTree::Ident(ident) => { + if ident == "this" { + TokenTree::Ident(format_ident!("static")) + } else { + token + } + } + TokenTree::Group(group) => TokenTree::Group(Group::new( + group.delimiter(), + replace_this_with_static(group.stream()), + )), + _ => token, + }) + .collect() +} + +fn handle_borrows_attr( + field_info: &mut [StructFieldInfo], + attr: &Attribute, + borrows: &mut Vec<BorrowRequest>, +) -> 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(()) +} + +/// Returns true if the specified type uses 'this in a template parameter (and not just for a +/// reference), meaning we cannot automatically convert from our internal representation down to +/// &'this in borrow_* functions. +fn type_uses_this_in_template(ty: &syn::Type) -> bool { + use syn::Type::*; + match ty { + Array(arr) => type_uses_this_in_template(&*arr.elem), + BareFn(f) => { + for arg in f.inputs.iter() { + if type_uses_this_in_template(&arg.ty) { + return true; + } + } + if let syn::ReturnType::Type(_, ty) = &f.output { + type_uses_this_in_template(ty) + } else { + false + } + } + Group(ty) => type_uses_this_in_template(&ty.elem), + ImplTrait(..) => false, // Unusable in struct definition. + Infer(..) => false, // Unusable in struct definition. + Macro(..) => true, // Assume true since we don't know. + Never(..) => false, + Paren(ty) => type_uses_this_in_template(&ty.elem), + Path(path) => { + if let Some(qself) = &path.qself { + if type_uses_this_in_template(&qself.ty) { + return 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 type_uses_this_in_template(ty) { + return true; + } + } else if let syn::GenericArgument::Lifetime(lt) = arg { + if lt.ident.to_string() == "this" { + return true; + } + } + } + } else if let syn::PathArguments::Parenthesized(args) = &args { + for arg in args.inputs.iter() { + if type_uses_this_in_template(arg) { + return true; + } + } + if let syn::ReturnType::Type(_, ty) = &args.output { + if type_uses_this_in_template(ty) { + return true; + } + } + } + } + false + } + Ptr(ptr) => type_uses_this_in_template(&ptr.elem), + // Ignore the actual lifetime of the reference because Rust can automatically convert those. + Reference(rf) => type_uses_this_in_template(&rf.elem), + Slice(sl) => type_uses_this_in_template(&sl.elem), + // I don't think this is reachable but panic just in case. + TraitObject(..) => unimplemented!(), + Tuple(tup) => { + for ty in tup.elems.iter() { + if type_uses_this_in_template(ty) { + return true; + } + } + false + } + // As of writing this, syn parses all the types we could need. + Verbatim(..) => unimplemented!(), + _ => unimplemented!(), + } +} + +/// 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. +fn create_actual_struct( + visibility: &Visibility, + original_struct_def: &ItemStruct, +) -> Result<(TokenStream2, Vec<StructFieldInfo>), Error> { + let mut actual_struct_def = original_struct_def.clone(); + actual_struct_def.vis = visibility.clone(); + let mut field_info = Vec::new(); + match &mut actual_struct_def.fields { + Fields::Named(fields) => { + for field in &mut fields.named { + let mut borrows = Vec::new(); + let mut self_referencing = false; + 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" { + self_referencing = true; + handle_borrows_attr(&mut field_info[..], attr, &mut borrows)?; + field.attrs.remove(index); + break; + } + } + field.attrs.push(syn::parse_quote! { #[doc(hidden)] }); + // 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()); + field.vis = syn::Visibility::Inherited; + field_info.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, + uses_this_in_template: type_uses_this_in_template(&field.ty), + }); + } + } + 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 field_info.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 &field_info { + 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}." + ), + field_info[0].name + ), + )); + } + // 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. + match &mut actual_struct_def.fields { + Fields::Named(fields) => { + let reversed = fields.named.iter().rev().cloned().collect(); + fields.named = reversed; + } + Fields::Unnamed(_fields) => unreachable!("Error handled earlier."), + Fields::Unit => unreachable!("Error handled earlier."), + } + // Finally, replace the fake 'this lifetime with 'static. + let actual_struct_def = replace_this_with_static(quote! { #actual_struct_def }); + + Ok((actual_struct_def, field_info)) +} + +// Takes the generics parameters from the original struct and turns them into arguments. +fn make_generic_arguments(generic_params: &Generics) -> Vec<TokenStream2> { + let mut arguments = Vec::new(); + for generic in generic_params.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 +} + +fn create_builder_and_constructor( + struct_visibility: &Visibility, + struct_name: &Ident, + builder_struct_name: &Ident, + generic_params: &Generics, + generic_args: &[TokenStream2], + field_info: &[StructFieldInfo], + do_chain_hack: bool, + do_no_doc: bool, + do_pub_extras: bool, +) -> Result<(TokenStream2, TokenStream2), Error> { + let visibility = if do_pub_extras { + struct_visibility.clone() + } else { + syn::parse_quote! { pub(super) } + }; + 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. " + ), + struct_name.to_string() + ); + let mut doc_table = "".to_owned(); + let mut code: Vec<TokenStream2> = Vec::new(); + let mut params: Vec<TokenStream2> = Vec::new(); + let mut builder_struct_generic_producers: Vec<_> = generic_params + .params + .iter() + .map(|param| quote! { #param }) + .collect(); + let mut builder_struct_generic_consumers = Vec::from(generic_args); + let mut builder_struct_fields = Vec::new(); + let mut builder_struct_field_names = Vec::new(); + + code.push(quote! { let mut result = ::core::mem::MaybeUninit::<Self>::uninit(); }); + + for field in field_info { + let field_name = &field.name; + + let arg_type = make_constructor_arg_type(&field, &field_info[..], do_chain_hack)?; + 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 }); + // 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 = &field_info[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()); + code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); }); + let generic_type_name = + format_ident!("{}Builder_", field_name.to_string().to_class_case()); + + 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 }); + } + let field_type = &field.typ; + let field_type = replace_this_with_static(quote! { #field_type }); + code.push(quote! { unsafe { + ((&mut (*result.as_mut_ptr()).#field_name) as *mut #field_type).write(#field_name); + }}); + + 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 !do_no_doc { + let documentation = documentation + &doc_table; + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + + let builder_documentation = if !do_no_doc { + let builder_documentation = builder_documentation + &doc_table; + quote! { + #[doc=#builder_documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + + let constructor_def = quote! { + #documentation + #visibility fn new(#(#params),*) -> Self { + #(#code)* + unsafe { result.assume_init() } + } + }; + let generic_where = &generic_params.where_clause; + 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 fn build(self) -> #struct_name <#(#generic_args),*> { + #struct_name::new( + #(self.#builder_struct_field_names),* + ) + } + } + }; + Ok((builder_def, constructor_def)) +} + +fn create_try_builder_and_constructor( + struct_visibility: &Visibility, + struct_name: &Ident, + builder_struct_name: &Ident, + generic_params: &Generics, + generic_args: &[TokenStream2], + field_info: &[StructFieldInfo], + do_chain_hack: bool, + do_no_doc: bool, + do_pub_extras: bool, +) -> Result<(TokenStream2, TokenStream2), Error> { + let visibility = if do_pub_extras { + struct_visibility.clone() + } else { + syn::parse_quote! { pub(super) } + }; + let mut head_recover_code = Vec::new(); + for field in field_info { + if !field.self_referencing { + let field_name = &field.name; + head_recover_code.push(quote! { #field_name }); + } + } + for (_ty, ident) in make_template_consumers(generic_params) { + head_recover_code.push(quote! { #ident: ::core::marker::PhantomData }); + } + let mut current_head_index = 0; + + 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. " + ), + struct_name.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. " + ), + struct_name.to_string() + ); + let mut doc_table = "".to_owned(); + let mut or_recover_code: Vec<TokenStream2> = Vec::new(); + let mut params: Vec<TokenStream2> = Vec::new(); + let mut builder_struct_generic_producers: Vec<_> = generic_params + .params + .iter() + .map(|param| quote! { #param }) + .collect(); + let mut builder_struct_generic_consumers = Vec::from(generic_args); + let mut builder_struct_fields = Vec::new(); + let mut builder_struct_field_names = Vec::new(); + + or_recover_code.push(quote! { let mut result = ::core::mem::MaybeUninit::<Self>::uninit(); }); + + for field in field_info { + let field_name = &field.name; + + let arg_type = make_try_constructor_arg_type(&field, &field_info[..], do_chain_hack)?; + 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 { + head_recover_code[current_head_index] = quote! { + #field_name: unsafe { ::core::ptr::read(&(*result.as_ptr()).#field_name as *const _) } + }; + 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 = &field_info[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()); + or_recover_code.push(quote! { + let #field_name = match #builder_name (#(#builder_args),*) { + ::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_", field_name.to_string().to_class_case()); + + 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 }); + } + let field_type = &field.typ; + let field_type = replace_this_with_static(quote! { #field_type }); + let line = quote! { unsafe { + ((&mut (*result.as_mut_ptr()).#field_name) as *mut #field_type).write(#field_name); + }}; + or_recover_code.push(line); + + 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 !do_no_doc { + let documentation = documentation + &doc_table; + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + let or_recover_documentation = if !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 !do_no_doc { + let builder_documentation = builder_documentation + &doc_table; + quote! { + #[doc=#builder_documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + let constructor_def = quote! { + #documentation + #visibility fn try_new<Error_>(#(#params),*) -> ::core::result::Result<Self, Error_> { + Self::try_new_or_recover(#(#builder_struct_field_names),*).map_err(|(error, _heads)| error) + } + #or_recover_documentation + #visibility fn try_new_or_recover<Error_>(#(#params),*) -> ::core::result::Result<Self, (Error_, Heads<#(#generic_args),*>)> { + #(#or_recover_code)* + ::core::result::Result::Ok(unsafe { result.assume_init() }) + } + }; + builder_struct_generic_producers.push(quote! { Error_ }); + builder_struct_generic_consumers.push(quote! { Error_ }); + let generic_where = &generic_params.where_clause; + 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 fn try_build(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> { + #struct_name::try_new( + #(self.#builder_struct_field_names),* + ) + } + #[doc=#build_or_recover_fn_documentation] + #visibility fn try_build_or_recover(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> { + #struct_name::try_new_or_recover( + #(self.#builder_struct_field_names),* + ) + } + } + }; + Ok((builder_def, constructor_def)) +} + +fn make_with_functions( + field_info: &[StructFieldInfo], + do_no_doc: bool, +) -> Result<Vec<TokenStream2>, Error> { + let mut users = Vec::new(); + for field in field_info { + 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 !do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + users.push(quote! { + #documentation + #visibility fn #user_name <'outer_borrow, ReturnType>( + &'outer_borrow self, + user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, + ) -> ReturnType { + user(&self. #field_name) + } + }); + // If 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 !do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + users.push(quote! { + #documentation + #visibility fn #user_name <'outer_borrow, ReturnType>( + &'outer_borrow mut self, + user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow mut #field_type) -> ReturnType, + ) -> ReturnType { + user(&mut self. #field_name) + } + }); + if field.uses_this_in_template { + // Skip the borrower function, it will cause compiler errors. + continue; + } + let borrower_name = format_ident!("borrow_{}", &field.name); + users.push(quote! { + #documentation + #visibility fn #borrower_name<'this>( + &'this self, + ) -> &'this #field_type { + &self.#field_name + } + }); + } else if field.field_type == FieldType::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 !do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + users.push(quote! { + #documentation + #visibility fn #user_name <'outer_borrow, ReturnType>( + &'outer_borrow self, + user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType, + ) -> ReturnType { + user(&self.#field_name) + } + }); + if field.uses_this_in_template { + // Skip the other functions, they will cause compiler errors. + continue; + } + let borrower_name = format_ident!("borrow_{}", &field.name); + users.push(quote! { + #documentation + #visibility fn #borrower_name<'this>( + &'this self, + ) -> &'this #field_type { + &self.#field_name + } + }); + } else if field.field_type == FieldType::BorrowedMut { + // Do not generate anything becaue if it is borrowed mutably once, we should not be able + // to get any other kinds of references to it. + } + } + Ok(users) +} + +fn make_with_all_function( + struct_visibility: &syn::Visibility, + struct_name: &Ident, + field_info: &[StructFieldInfo], + generic_params: &Generics, + generic_args: &[TokenStream2], + do_no_doc: bool, + do_pub_extras: bool, +) -> Result<(TokenStream2, TokenStream2), Error> { + let visibility = if do_pub_extras { + struct_visibility.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 field_info.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 { + &self.#field_name + } }; + fields.push(quote! { #visibility #field_name: &'outer_borrow #field_type }); + field_assignments.push(ass.clone()); + mut_fields.push(quote! { #visibility #field_name: &'outer_borrow #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. + } + } + + let new_generic_params = if generic_params.params.is_empty() { + quote! { <'outer_borrow, 'this> } + } else { + for (ty, ident) in make_template_consumers(generic_params) { + 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 mut new_generic_params = generic_params.clone(); + new_generic_params + .params + .insert(0, syn::parse_quote! { 'this }); + new_generic_params + .params + .insert(0, syn::parse_quote! { 'outer_borrow }); + quote! { #new_generic_params } + }; + let new_generic_args = { + let mut args = Vec::from(generic_args); + args.insert(0, quote! { 'this }); + args.insert(0, quote! { 'outer_borrow }); + args + }; + + 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})." + ), + struct_name.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})." + ), + struct_name.to_string() + ); + let generic_where = &generic_params.where_clause; + 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 !do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + let mut_documentation = if !do_no_doc { + quote! { + #[doc=#mut_documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + let fn_defs = quote! { + #documentation + #visibility fn with <'outer_borrow, ReturnType>( + &'outer_borrow self, + user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType + ) -> ReturnType { + user(BorrowedFields { + #(#field_assignments),* + }) + } + #mut_documentation + #visibility fn with_mut <'outer_borrow, ReturnType>( + &'outer_borrow mut self, + user: impl for<'this> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType + ) -> ReturnType { + user(BorrowedMutFields { + #(#mut_field_assignments),* + }) + } + }; + Ok((struct_defs, fn_defs)) +} + +/// Returns the Heads struct and a function to convert the original struct into a Heads instance. +fn make_into_heads( + struct_visibility: &Visibility, + struct_name: &Ident, + field_info: &[StructFieldInfo], + generic_params: &Generics, + generic_args: &[TokenStream2], + do_no_doc: bool, + do_pub_extras: bool, +) -> (TokenStream2, TokenStream2) { + let visibility = if do_pub_extras { + struct_visibility.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 field_info.iter().rev() { + let field_name = &field.name; + if !field.self_referencing { + code.push(quote! { let #field_name = self.#field_name; }); + 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 make_template_consumers(generic_params) { + 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})." + ), + struct_name.to_string() + ); + let generic_where = &generic_params.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 !do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + + let into_heads_fn = quote! { + #documentation + #[allow(clippy::drop_ref)] + #[allow(clippy::drop_copy)] + #visibility fn into_heads(self) -> Heads<#(#generic_args),*> { + #(#code)* + Heads { + #(#field_initializers),* + } + } + }; + (heads_struct_def, into_heads_fn) +} + +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(), + } +} + +fn self_referencing_impl( + original_struct_def: ItemStruct, + do_chain_hack: bool, + do_no_doc: bool, + do_pub_extras: bool, +) -> Result<TokenStream, Error> { + 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 submodule_contents_visiblity = submodule_contents_visiblity(visibility); + + let (actual_struct_def, field_info) = + create_actual_struct(&submodule_contents_visiblity, &original_struct_def)?; + + let generic_params = original_struct_def.generics.clone(); + let generic_args = make_generic_arguments(&generic_params); + + let builder_struct_name = format_ident!("{}Builder", struct_name); + let (builder_def, constructor_def) = create_builder_and_constructor( + &submodule_contents_visiblity, + &struct_name, + &builder_struct_name, + &generic_params, + &generic_args, + &field_info[..], + do_chain_hack, + do_no_doc, + do_pub_extras, + )?; + let try_builder_struct_name = format_ident!("{}TryBuilder", struct_name); + let (try_builder_def, try_constructor_def) = create_try_builder_and_constructor( + &submodule_contents_visiblity, + &struct_name, + &try_builder_struct_name, + &generic_params, + &generic_args, + &field_info[..], + do_chain_hack, + do_no_doc, + do_pub_extras, + )?; + + let users = make_with_functions(&field_info[..], do_no_doc)?; + let (with_all_struct_defs, with_all_fn_defs) = make_with_all_function( + &submodule_contents_visiblity, + struct_name, + &field_info[..], + &generic_params, + &generic_args, + do_no_doc, + do_pub_extras, + )?; + let (heads_struct_def, into_heads_fn) = make_into_heads( + &submodule_contents_visiblity, + struct_name, + &field_info[..], + &generic_params, + &generic_args, + do_no_doc, + do_pub_extras, + ); + + let extra_visibility = if do_pub_extras { + visibility.clone() + } else { + syn::Visibility::Inherited + }; + + let generic_where = &generic_params.where_clause; + Ok(TokenStream::from(quote! { + #[doc="Encapsulates implementation details for a self-referencing struct. This module is only visible when using --document-private-items."] + mod #mod_name { + use super::*; + #actual_struct_def + #builder_def + #try_builder_def + #with_all_struct_defs + #heads_struct_def + impl #generic_params #struct_name <#(#generic_args),*> #generic_where { + #constructor_def + #try_constructor_def + #(#users)* + #with_all_fn_defs + #into_heads_fn + } + } + #visibility use #mod_name :: #struct_name; + #extra_visibility use #mod_name :: #builder_struct_name; + #extra_visibility use #mod_name :: #try_builder_struct_name; + })) +} + +#[proc_macro_attribute] +pub fn self_referencing(attr: TokenStream, item: TokenStream) -> TokenStream { + let mut do_chain_hack = false; + let mut do_no_doc = false; + let mut do_pub_extras = false; + let mut expecting_comma = false; + for token in <TokenStream as std::convert::Into<TokenStream2>>::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()[..] { + "chain_hack" => do_chain_hack = true, + "no_doc" => do_no_doc = true, + "pub_extras" => do_pub_extras = true, + _ => { + return Error::new_spanned( + &ident, + "Unknown identifier, expected 'chain_hack', '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, do_chain_hack, do_no_doc, do_pub_extras) { + Ok(content) => content, + Err(err) => err.to_compile_error().into(), + } +} |