From 8cdfa445d6629ffef4cb84967ff7017654045bc2 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 2 Jul 2025 18:36:06 -0600 Subject: chore: add vendor directory --- vendor/windows-implement/.cargo-checksum.json | 1 + vendor/windows-implement/Cargo.lock | 47 +++ vendor/windows-implement/Cargo.toml | 70 +++ vendor/windows-implement/license-apache-2.0 | 201 +++++++++ vendor/windows-implement/license-mit | 21 + vendor/windows-implement/src/gen.rs | 587 ++++++++++++++++++++++++++ vendor/windows-implement/src/lib.rs | 383 +++++++++++++++++ vendor/windows-implement/src/tests.rs | 135 ++++++ 8 files changed, 1445 insertions(+) create mode 100644 vendor/windows-implement/.cargo-checksum.json create mode 100644 vendor/windows-implement/Cargo.lock create mode 100644 vendor/windows-implement/Cargo.toml create mode 100644 vendor/windows-implement/license-apache-2.0 create mode 100644 vendor/windows-implement/license-mit create mode 100644 vendor/windows-implement/src/gen.rs create mode 100644 vendor/windows-implement/src/lib.rs create mode 100644 vendor/windows-implement/src/tests.rs (limited to 'vendor/windows-implement') diff --git a/vendor/windows-implement/.cargo-checksum.json b/vendor/windows-implement/.cargo-checksum.json new file mode 100644 index 00000000..f616b8a3 --- /dev/null +++ b/vendor/windows-implement/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"07ffe21585cb9601285232fa32088f8ac8a419e3e2c8a19d5479ca4ab6bb75c7","Cargo.toml":"4dab9cdda3e2a5cf3b4212f42b3ddcba34ecb766ffc0bd3449264289165aa73f","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","src/gen.rs":"e7e4cbeb1f685af8f2c1843d6064008fdc7610ebdda2657cded8beb59ecdf9b7","src/lib.rs":"94f2d415896df707932fe8da9dbba0844319f24cb1951526ce553fde47717e57","src/tests.rs":"ea34a27316c2c7440bdc41d03fa7a3426be344a238588d3728b490a44bfecc2e"},"package":"a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"} \ No newline at end of file diff --git a/vendor/windows-implement/Cargo.lock b/vendor/windows-implement/Cargo.lock new file mode 100644 index 00000000..88fdf819 --- /dev/null +++ b/vendor/windows-implement/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "windows-implement" +version = "0.60.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/vendor/windows-implement/Cargo.toml b/vendor/windows-implement/Cargo.toml new file mode 100644 index 00000000..270b2f6e --- /dev/null +++ b/vendor/windows-implement/Cargo.toml @@ -0,0 +1,70 @@ +# 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 = "2021" +rust-version = "1.74" +name = "windows-implement" +version = "0.60.0" +authors = ["Microsoft"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "The implement macro for the windows crate" +readme = false +license = "MIT OR Apache-2.0" +repository = "https://github.com/microsoft/windows-rs" + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" +targets = [] + +[lib] +name = "windows_implement" +path = "src/lib.rs" +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0" +default-features = false + +[dependencies.quote] +version = "1.0" +default-features = false + +[dependencies.syn] +version = "2.0" +features = [ + "parsing", + "proc-macro", + "printing", + "full", + "clone-impls", +] +default-features = false + +[dev-dependencies] + +[lints.rust] +missing_docs = "warn" +unsafe_op_in_unsafe_fn = "warn" + +[lints.rust.rust_2018_idioms] +level = "warn" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"] diff --git a/vendor/windows-implement/license-apache-2.0 b/vendor/windows-implement/license-apache-2.0 new file mode 100644 index 00000000..b5ed4ece --- /dev/null +++ b/vendor/windows-implement/license-apache-2.0 @@ -0,0 +1,201 @@ + 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 (c) Microsoft Corporation. + + 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/vendor/windows-implement/license-mit b/vendor/windows-implement/license-mit new file mode 100644 index 00000000..9e841e7a --- /dev/null +++ b/vendor/windows-implement/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + 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/vendor/windows-implement/src/gen.rs b/vendor/windows-implement/src/gen.rs new file mode 100644 index 00000000..56253b99 --- /dev/null +++ b/vendor/windows-implement/src/gen.rs @@ -0,0 +1,587 @@ +//! Generates output for the `implement` proc macro. +//! +//! Each function in this module focuses on generating one thing, or one kind of thing. +//! Each takes `ImplementInputs` as its input. `gen_all` calls all of the `gen_*` functions +//! and merges them into the final list of output items. +//! +//! We use `parse_quote` so that we can verify that a given function generates a well-formed AST +//! item, within the narrowest possible scope. This allows us to detect errors more quickly during +//! development. If the input to `parse_quote` cannot be parsed, then the macro will panic and +//! the panic will point to the specific `parse_quote` call, rather than the entire output of the +//! `implement` proc macro being unparsable. This greatly aids in development. + +use super::*; +use quote::{quote, quote_spanned}; +use syn::{parse_quote, parse_quote_spanned}; + +/// Generates code for the `#[implements]` macro. +pub(crate) fn gen_all(inputs: &ImplementInputs) -> Vec { + let mut items: Vec = Vec::with_capacity(64); + + items.push(gen_original_impl(inputs)); + items.push(gen_impl_struct(inputs)); + items.push(gen_impl_deref(inputs)); + items.push(gen_impl_impl(inputs)); + items.push(gen_iunknown_impl(inputs)); + items.push(gen_impl_com_object_inner(inputs)); + items.extend(gen_impl_from(inputs)); + items.extend(gen_impl_com_object_interfaces(inputs)); + + for (i, interface_chain) in inputs.interface_chains.iter().enumerate() { + items.push(gen_impl_as_impl(inputs, interface_chain, i)); + } + + items +} + +/// Generates an `impl` block for the original `Foo` type. +/// +/// This `impl` block will contain `into_outer` and `into_static` (if applicable). +fn gen_original_impl(inputs: &ImplementInputs) -> syn::Item { + let original_ident = &inputs.original_ident; + let generics = &inputs.generics; + let constraints = &inputs.constraints; + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics #original_ident::#generics where #constraints {} + }; + + output.items.push(gen_into_outer(inputs)); + + // Static COM objects have a lot of constraints. They can't be generic (open parameters), + // because that would be meaningless (an open generic type cannot have a known representation). + // + // Right now, we can't generate static COM objects that have base classes because we rely on + // boxing and then unboxing during construction of aggregated types. + if !inputs.is_generic { + output.items.push(gen_into_static(inputs)); + } + + syn::Item::Impl(output) +} + +/// Generates the structure definition for the `Foo_Impl` type. +fn gen_impl_struct(inputs: &ImplementInputs) -> syn::Item { + let impl_ident = &inputs.impl_ident; + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let original_ident = &inputs.original_ident; + let vis = &inputs.original_type.vis; + + let mut impl_fields = quote! { + identity: &'static ::windows_core::IInspectable_Vtbl, + }; + + for interface_chain in inputs.interface_chains.iter() { + let vtbl_ty = interface_chain.implement.to_vtbl_ident(); + let chain_field_ident = &interface_chain.field_ident; + impl_fields.extend(quote! { + #chain_field_ident: &'static #vtbl_ty, + }); + } + + impl_fields.extend(quote! { + this: #original_ident::#generics, + count: ::windows_core::imp::WeakRefCount, + }); + + parse_quote! { + #[repr(C)] + #[allow(non_camel_case_types)] + #vis struct #impl_ident #generics where #constraints { + #impl_fields + } + } +} + +/// Generates the implementation of `core::ops::Deref` for the generated `Foo_Impl` type. +fn gen_impl_deref(inputs: &ImplementInputs) -> syn::Item { + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let original_ident = &inputs.original_type.ident; + let impl_ident = &inputs.impl_ident; + + parse_quote! { + impl #generics ::core::ops::Deref for #impl_ident::#generics where #constraints { + type Target = #original_ident::#generics; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.this + } + } + } +} + +/// Generates an `impl` block for the generated `Foo_Impl` block. +/// +/// This generates: +/// +/// ```rust,ignore +/// const VTABLE_IDENTITY = IInspectable_Vtbl = ...; +/// const VTABLE_INTERFACE1_IFOO: IFoo_Vtbl = ...; +/// const VTABLE_INTERFACE2_IBAR: IBar_Vtbl = ...; +/// ``` +/// +/// These constants are used when constructing vtables. The benefit of using constants instead +/// of directly generating these expressions is that it allows us to overcome limitations in +/// using generics in constant contexts. Right now, Rust has a lot of limitations around using +/// constants in constant contexts. Fortunately, associated constants (constants defined within +/// `impl` blocks) work in stable Rust, even for generic types. +fn gen_impl_impl(inputs: &ImplementInputs) -> syn::Item { + let impl_ident = &inputs.impl_ident; + let generics = &inputs.generics; + let constraints = &inputs.constraints; + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics #impl_ident::#generics where #constraints {} + }; + + // This is here so that IInspectable::GetRuntimeClassName can work properly. + // For a test case for this, see crates/tests/misc/component_client. + let identity_type = if let Some(first) = inputs.interface_chains.first() { + first.implement.to_ident() + } else { + quote! { ::windows_core::IInspectable } + }; + + output.items.push(parse_quote! { + const VTABLE_IDENTITY: ::windows_core::IInspectable_Vtbl = + ::windows_core::IInspectable_Vtbl::new::< + #impl_ident::#generics, + #identity_type, + 0, + >(); + }); + + for (interface_index, interface_chain) in inputs.interface_chains.iter().enumerate() { + let vtbl_ty = interface_chain.implement.to_vtbl_ident(); + let vtable_const_ident = &interface_chain.vtable_const_ident; + + let chain_offset_in_pointers: isize = -1 - interface_index as isize; + output.items.push(parse_quote! { + const #vtable_const_ident: #vtbl_ty = #vtbl_ty::new::< + #impl_ident::#generics, + #chain_offset_in_pointers, + >(); + }); + } + + syn::Item::Impl(output) +} + +/// Generates the `IUnknownImpl` implementation for the `Foo_Impl` type. +fn gen_iunknown_impl(inputs: &ImplementInputs) -> syn::Item { + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + let original_ident = &inputs.original_type.ident; + + let trust_level = proc_macro2::Literal::usize_unsuffixed(inputs.trust_level); + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics ::windows_core::IUnknownImpl for #impl_ident::#generics where #constraints { + type Impl = #original_ident::#generics; + + #[inline(always)] + fn get_impl(&self) -> &Self::Impl { + &self.this + } + + #[inline(always)] + fn get_impl_mut(&mut self) -> &mut Self::Impl { + &mut self.this + } + + #[inline(always)] + fn into_inner(self) -> Self::Impl { + self.this + } + + #[inline(always)] + fn AddRef(&self) -> u32 { + self.count.add_ref() + } + + #[inline(always)] + unsafe fn Release(self_: *mut Self) -> u32 { + let remaining = (*self_).count.release(); + if remaining == 0 { + _ = ::windows_core::imp::Box::from_raw(self_); + } + remaining + } + + #[inline(always)] + fn is_reference_count_one(&self) -> bool { + self.count.is_one() + } + + unsafe fn GetTrustLevel(&self, value: *mut i32) -> ::windows_core::HRESULT { + if value.is_null() { + return ::windows_core::imp::E_POINTER; + } + *value = #trust_level; + ::windows_core::HRESULT(0) + } + + fn to_object(&self) -> ::windows_core::ComObject { + self.count.add_ref(); + unsafe { + ::windows_core::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self) + ) + } + } + } + }; + + let query_interface_fn = gen_query_interface(inputs); + output.items.push(syn::ImplItem::Fn(query_interface_fn)); + + syn::Item::Impl(output) +} + +/// Generates the implementation of `ComObjectInner`. +fn gen_impl_com_object_inner(inputs: &ImplementInputs) -> syn::Item { + let original_ident = &inputs.original_type.ident; + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + + parse_quote! { + impl #generics ::windows_core::ComObjectInner for #original_ident::#generics where #constraints { + type Outer = #impl_ident::#generics; + + // IMPORTANT! This function handles assembling the "boxed" type of a COM object. + // It immediately moves the box into a heap allocation (box) and returns only a ComObject + // reference that points to it. We intentionally _do not_ expose any owned instances of + // Foo_Impl to safe Rust code, because doing so would allow unsound behavior in safe Rust + // code, due to the adjustments of the reference count that Foo_Impl permits. + // + // This is why this function returns ComObject instead of returning #impl_ident. + + fn into_object(self) -> ::windows_core::ComObject { + let boxed = ::windows_core::imp::Box::<#impl_ident::#generics>::new(self.into_outer()); + unsafe { + let ptr = ::windows_core::imp::Box::into_raw(boxed); + ::windows_core::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(ptr) + ) + } + } + } + } +} + +/// Generates the `query_interface` method. +fn gen_query_interface(inputs: &ImplementInputs) -> syn::ImplItemFn { + let queries = inputs.interface_chains.iter().map(|interface_chain| { + let chain_ty = interface_chain.implement.to_vtbl_ident(); + let chain_field = &interface_chain.field_ident; + quote_spanned! { + interface_chain.implement.span => + if #chain_ty::matches(&iid) { + break 'found &self.#chain_field as *const _ as *const ::core::ffi::c_void; + } + } + }); + + // Dynamic casting requires that the object not contain non-static lifetimes. + let enable_dyn_casting = inputs.original_type.generics.lifetimes().count() == 0; + let dynamic_cast_query = if enable_dyn_casting { + quote! { + if iid == ::windows_core::DYNAMIC_CAST_IID { + // DYNAMIC_CAST_IID is special. We _do not_ increase the reference count for this pseudo-interface. + // Also, instead of returning an interface pointer, we simply write the `&dyn Any` directly to the + // 'interface' pointer. Since the size of `&dyn Any` is 2 pointers, not one, the caller must be + // prepared for this. This is not a normal QueryInterface call. + // + // See the `Interface::cast_to_any` method, which is the only caller that should use DYNAMIC_CAST_ID. + (interface as *mut *const dyn core::any::Any).write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any); + return ::windows_core::HRESULT(0); + } + } + } else { + quote!() + }; + + let identity_query = quote! { + if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID + || iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID + || iid == <::windows_core::imp::IAgileObject as ::windows_core::Interface>::IID { + break 'found &self.identity as *const _ as *const ::core::ffi::c_void; + } + }; + + let marshal_query = quote! { + #[cfg(windows)] + if iid == <::windows_core::imp::IMarshal as ::windows_core::Interface>::IID { + return ::windows_core::imp::marshaler(self.to_interface(), interface); + } + }; + + let tear_off_query = quote! { + let tear_off_ptr = self.count.query(&iid, &self.identity as *const _ as *mut _); + if !tear_off_ptr.is_null() { + *interface = tear_off_ptr; + return ::windows_core::HRESULT(0); + } + }; + + parse_quote! { + unsafe fn QueryInterface( + &self, + iid: *const ::windows_core::GUID, + interface: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT { + unsafe { + if iid.is_null() || interface.is_null() { + return ::windows_core::imp::E_POINTER; + } + + let iid = *iid; + + let interface_ptr: *const ::core::ffi::c_void = 'found: { + #identity_query + #(#queries)* + #marshal_query + #dynamic_cast_query + #tear_off_query + + *interface = ::core::ptr::null_mut(); + return ::windows_core::imp::E_NOINTERFACE; + }; + + debug_assert!(!interface_ptr.is_null()); + *interface = interface_ptr as *mut ::core::ffi::c_void; + self.count.add_ref(); + return ::windows_core::HRESULT(0); + } + } + } +} + +/// Generates the `T::into_outer` function. This function is part of how we construct a +/// `ComObject` from a `T`. +fn gen_into_outer(inputs: &ImplementInputs) -> syn::ImplItem { + let generics = &inputs.generics; + let impl_ident = &inputs.impl_ident; + + let mut initializers = quote! { + identity: &#impl_ident::#generics::VTABLE_IDENTITY, + }; + + for interface_chain in inputs.interface_chains.iter() { + let vtbl_field_ident = &interface_chain.field_ident; + let vtable_const_ident = &interface_chain.vtable_const_ident; + + initializers.extend(quote_spanned! { + interface_chain.implement.span => + #vtbl_field_ident: &#impl_ident::#generics::#vtable_const_ident, + }); + } + + // If the type is generic then into_outer() cannot be a const fn. + let maybe_const = if inputs.is_generic { + quote!() + } else { + quote!(const) + }; + + parse_quote! { + // This constructs an "outer" object. This should only be used by the implementation + // of the outer object, never by application code. + // + // The callers of this function (`into_static` and `into_object`) are both responsible + // for maintaining one of our invariants: Application code never has an owned instance + // of the outer (implementation) type. into_static() maintains this invariant by + // returning a wrapped StaticComObject value, which owns its contents but never gives + // application code a way to mutably access its contents. This prevents the refcount + // shearing problem. + // + // TODO: Make it impossible for app code to call this function, by placing it in a + // module and marking this as private to the module. + #[inline(always)] + #maybe_const fn into_outer(self) -> #impl_ident::#generics { + #impl_ident::#generics { + #initializers + count: ::windows_core::imp::WeakRefCount::new(), + this: self, + } + } + } +} + +/// Generates the `T::into_static` function. This function is part of how we construct a +/// `StaticComObject` from a `T`. +fn gen_into_static(inputs: &ImplementInputs) -> syn::ImplItem { + assert!(!inputs.is_generic); + parse_quote! { + /// This converts a partially-constructed COM object (in the sense that it contains + /// application state but does not yet have vtable and reference count constructed) + /// into a `StaticComObject`. This allows the COM object to be stored in static + /// (global) variables. + pub const fn into_static(self) -> ::windows_core::StaticComObject { + ::windows_core::StaticComObject::from_outer(self.into_outer()) + } + } +} + +/// Generates `From`-based conversions. +/// +/// These conversions convert from the user's type `T` to `ComObject` or to an interface +/// implemented by `T`. These conversions are shorthand for calling `ComObject::new(value)`. +/// +/// We can only generate conversions from `T` to the roots of each interface chain. We can't +/// generate `From` conversions from `T` to an interface that is inherited by an interface chain, +/// because this proc macro does not have access to any information about the inheritance chain +/// of interfaces that are referenced. +/// +/// For example: +/// +/// ```rust,ignore +/// #[implement(IFoo3)] +/// struct MyType; +/// ``` +/// +/// If `IFoo3` inherits from `IFoo2`, then this code will _not_ generate a conversion for `IFoo2`. +/// However, user code can still do this: +/// +/// ```rust,ignore +/// let ifoo2 = IFoo3::from(MyType).into(); +/// ``` +/// +/// This works because the `IFoo3` type has an `Into` impl for `IFoo2`. +fn gen_impl_from(inputs: &ImplementInputs) -> Vec { + let mut items = Vec::new(); + + let original_ident = &inputs.original_type.ident; + let generics = &inputs.generics; + let constraints = &inputs.constraints; + + items.push(parse_quote! { + impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IUnknown where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + + items.push(parse_quote! { + impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IInspectable where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + + for interface_chain in inputs.interface_chains.iter() { + let interface_ident = interface_chain.implement.to_ident(); + + items.push(parse_quote_spanned! { + interface_chain.implement.span => + impl #generics ::core::convert::From<#original_ident::#generics> for #interface_ident where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + } + + items +} + +/// Generates the `ComObjectInterface` implementation for each interface chain. +/// +/// Each of these `impl` blocks says "this COM object implements this COM interface". +/// It allows the `ComObject` type to do conversions from the `ComObject` to `IFoo` instances, +/// _without_ doing a `QueryInterface` call. +fn gen_impl_com_object_interfaces(inputs: &ImplementInputs) -> Vec { + let mut items = Vec::new(); + + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + + items.push(parse_quote! { + impl #generics ::windows_core::ComObjectInterface<::windows_core::IUnknown> for #impl_ident::#generics where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IUnknown> { + unsafe { + let interface_ptr = &self.identity; + ::core::mem::transmute(interface_ptr) + } + } + } + }); + + items.push(parse_quote! { + impl #generics ::windows_core::ComObjectInterface<::windows_core::IInspectable> for #impl_ident::#generics where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IInspectable> { + unsafe { + let interface_ptr = &self.identity; + ::core::mem::transmute(interface_ptr) + } + } + } + }); + + for interface_chain in inputs.interface_chains.iter() { + let chain_field = &interface_chain.field_ident; + let interface_ident = interface_chain.implement.to_ident(); + + items.push(parse_quote_spanned! { + interface_chain.implement.span => + #[allow(clippy::needless_lifetimes)] + impl #generics ::windows_core::ComObjectInterface<#interface_ident> for #impl_ident::#generics where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, #interface_ident> { + unsafe { + ::core::mem::transmute(&self.#chain_field) + } + } + } + }); + } + + items +} + +/// Generates the implementation of the `AsImpl` trait for a given interface chain. +fn gen_impl_as_impl( + inputs: &ImplementInputs, + interface_chain: &InterfaceChain, + interface_chain_index: usize, +) -> syn::Item { + let generics = &inputs.generics; + let constraints = &inputs.constraints; + let interface_ident = interface_chain.implement.to_ident(); + let original_ident = &inputs.original_type.ident; + let impl_ident = &inputs.impl_ident; + + parse_quote_spanned! { + interface_chain.implement.span => + impl #generics ::windows_core::AsImpl<#original_ident::#generics> for #interface_ident where #constraints { + // SAFETY: the offset is guaranteed to be in bounds, and the implementation struct + // is guaranteed to live at least as long as `self`. + #[inline(always)] + unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> { + unsafe { + let this = ::windows_core::Interface::as_raw(self); + // Subtract away the vtable offset plus 1, for the `identity` field, to get + // to the impl struct which contains that original implementation type. + let this = (this as *mut *mut ::core::ffi::c_void).sub(1 + #interface_chain_index) as *mut #impl_ident::#generics; + ::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics as *mut #original_ident::#generics) + } + } + } + } +} diff --git a/vendor/windows-implement/src/lib.rs b/vendor/windows-implement/src/lib.rs new file mode 100644 index 00000000..c50509af --- /dev/null +++ b/vendor/windows-implement/src/lib.rs @@ -0,0 +1,383 @@ +//! Implement COM interfaces for Rust types. +//! +//! Take a look at [macro@implement] for an example. +//! +//! Learn more about Rust for Windows here: + +use quote::{quote, ToTokens}; + +mod r#gen; +use r#gen::gen_all; + +#[cfg(test)] +mod tests; + +/// Implements one or more COM interfaces. +/// +/// # Example +/// ```rust,no_run +/// use windows_core::*; +/// +/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")] +/// unsafe trait IValue: IUnknown { +/// fn GetValue(&self, value: *mut i32) -> HRESULT; +/// } +/// +/// #[implement(IValue)] +/// struct Value(i32); +/// +/// impl IValue_Impl for Value_Impl { +/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT { +/// *value = self.0; +/// HRESULT(0) +/// } +/// } +/// +/// let object: IValue = Value(123).into(); +/// // Call interface methods... +/// ``` +#[proc_macro_attribute] +pub fn implement( + attributes: proc_macro::TokenStream, + type_tokens: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + implement_core(attributes.into(), type_tokens.into()).into() +} + +fn implement_core( + attributes: proc_macro2::TokenStream, + item_tokens: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let attributes = syn::parse2::(attributes).unwrap(); + let original_type = syn::parse2::(item_tokens).unwrap(); + + // Do a little thinking and assemble ImplementInputs. We pass ImplementInputs to + // all of our gen_* function. + let inputs = ImplementInputs { + original_ident: original_type.ident.clone(), + interface_chains: convert_implements_to_interface_chains(attributes.implement), + trust_level: attributes.trust_level, + impl_ident: quote::format_ident!("{}_Impl", &original_type.ident), + constraints: { + if let Some(where_clause) = &original_type.generics.where_clause { + where_clause.predicates.to_token_stream() + } else { + quote!() + } + }, + generics: if !original_type.generics.params.is_empty() { + let mut params = quote! {}; + original_type.generics.params.to_tokens(&mut params); + quote! { <#params> } + } else { + quote! { <> } + }, + is_generic: !original_type.generics.params.is_empty(), + original_type, + }; + + let items = gen_all(&inputs); + let mut tokens = inputs.original_type.into_token_stream(); + for item in items { + tokens.extend(item.into_token_stream()); + } + + tokens +} + +/// This provides the inputs to the `gen_*` functions, which generate the proc macro output. +struct ImplementInputs { + /// The user's type that was marked with `#[implement]`. + original_type: syn::ItemStruct, + + /// The identifier for the user's original type definition. + original_ident: syn::Ident, + + /// The list of interface chains that this type implements. + interface_chains: Vec, + + /// The "trust level", which is returned by `IInspectable::GetTrustLevel`. + trust_level: usize, + + /// The identifier of the `Foo_Impl` type. + impl_ident: syn::Ident, + + /// The list of constraints needed for this `Foo_Impl` type. + constraints: proc_macro2::TokenStream, + + /// The list of generic parameters for this `Foo_Impl` type, including `<` and `>`. + /// If there are no generics, this contains `<>`. + generics: proc_macro2::TokenStream, + + /// True if the user type has any generic parameters. + is_generic: bool, +} + +/// Describes one COM interface chain. +struct InterfaceChain { + /// The name of the field for the vtable chain, e.g. `interface4_ifoo`. + field_ident: syn::Ident, + + /// The name of the associated constant item for the vtable chain's initializer, + /// e.g. `INTERFACE4_IFOO_VTABLE`. + vtable_const_ident: syn::Ident, + + implement: ImplementType, +} + +struct ImplementType { + type_name: String, + generics: Vec, + + /// The best span for diagnostics. + span: proc_macro2::Span, +} + +impl ImplementType { + fn to_ident(&self) -> proc_macro2::TokenStream { + let type_name = syn::parse_str::(&self.type_name) + .expect("Invalid token stream"); + let generics = self.generics.iter().map(|g| g.to_ident()); + quote! { #type_name<#(#generics,)*> } + } + fn to_vtbl_ident(&self) -> proc_macro2::TokenStream { + let ident = self.to_ident(); + quote! { + <#ident as ::windows_core::Interface>::Vtable + } + } +} + +#[derive(Default)] +struct ImplementAttributes { + pub implement: Vec, + pub trust_level: usize, +} + +impl syn::parse::Parse for ImplementAttributes { + fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result { + let mut input = Self::default(); + + while !cursor.is_empty() { + input.parse_implement(cursor)?; + } + + Ok(input) + } +} + +impl ImplementAttributes { + fn parse_implement(&mut self, cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result<()> { + let tree = cursor.parse::()?; + self.walk_implement(&tree, &mut String::new())?; + + if !cursor.is_empty() { + cursor.parse::()?; + } + + Ok(()) + } + + fn walk_implement( + &mut self, + tree: &UseTree2, + namespace: &mut String, + ) -> syn::parse::Result<()> { + match tree { + UseTree2::Path(input) => { + if !namespace.is_empty() { + namespace.push_str("::"); + } + + namespace.push_str(&input.ident.to_string()); + self.walk_implement(&input.tree, namespace)?; + } + UseTree2::Name(_) => { + self.implement.push(tree.to_element_type(namespace)?); + } + UseTree2::Group(input) => { + for tree in &input.items { + self.walk_implement(tree, namespace)?; + } + } + UseTree2::TrustLevel(input) => self.trust_level = *input, + } + + Ok(()) + } +} + +enum UseTree2 { + Path(UsePath2), + Name(UseName2), + Group(UseGroup2), + TrustLevel(usize), +} + +impl UseTree2 { + fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result { + match self { + UseTree2::Path(input) => { + if !namespace.is_empty() { + namespace.push_str("::"); + } + + namespace.push_str(&input.ident.to_string()); + input.tree.to_element_type(namespace) + } + UseTree2::Name(input) => { + let mut type_name = input.ident.to_string(); + let span = input.ident.span(); + + if !namespace.is_empty() { + type_name = format!("{namespace}::{type_name}"); + } + + let mut generics = vec![]; + + for g in &input.generics { + generics.push(g.to_element_type(&mut String::new())?); + } + + Ok(ImplementType { + type_name, + generics, + span, + }) + } + UseTree2::Group(input) => Err(syn::parse::Error::new( + input.brace_token.span.join(), + "Syntax not supported", + )), + _ => unimplemented!(), + } + } +} + +struct UsePath2 { + pub ident: syn::Ident, + pub tree: Box, +} + +struct UseName2 { + pub ident: syn::Ident, + pub generics: Vec, +} + +struct UseGroup2 { + pub brace_token: syn::token::Brace, + pub items: syn::punctuated::Punctuated, +} + +impl syn::parse::Parse for UseTree2 { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Ident) { + use syn::ext::IdentExt; + let ident = input.call(syn::Ident::parse_any)?; + if input.peek(syn::Token![::]) { + input.parse::()?; + Ok(UseTree2::Path(UsePath2 { + ident, + tree: Box::new(input.parse()?), + })) + } else if input.peek(syn::Token![=]) { + if ident != "TrustLevel" { + return Err(syn::parse::Error::new( + ident.span(), + "Unrecognized key-value pair", + )); + } + input.parse::()?; + let span = input.span(); + let value = input.call(syn::Ident::parse_any)?; + match value.to_string().as_str() { + "Partial" => Ok(UseTree2::TrustLevel(1)), + "Full" => Ok(UseTree2::TrustLevel(2)), + _ => Err(syn::parse::Error::new( + span, + "`TrustLevel` must be `Partial` or `Full`", + )), + } + } else { + let generics = if input.peek(syn::Token![<]) { + input.parse::()?; + let mut generics = Vec::new(); + loop { + generics.push(input.parse::()?); + + if input.parse::().is_err() { + break; + } + } + input.parse::]>()?; + generics + } else { + Vec::new() + }; + + Ok(UseTree2::Name(UseName2 { ident, generics })) + } + } else if lookahead.peek(syn::token::Brace) { + let content; + let brace_token = syn::braced!(content in input); + let items = content.parse_terminated(UseTree2::parse, syn::Token![,])?; + + Ok(UseTree2::Group(UseGroup2 { brace_token, items })) + } else { + Err(lookahead.error()) + } + } +} + +fn convert_implements_to_interface_chains(implements: Vec) -> Vec { + let mut chains = Vec::with_capacity(implements.len()); + + for (i, implement) in implements.into_iter().enumerate() { + // Create an identifier for this interface chain. + // We only use this for naming fields; it is never visible to the developer. + // This helps with debugging. + // + // We use i + 1 so that it matches the numbering of our interface offsets. Interface 0 + // is the "identity" interface. + + let mut ident_string = format!("interface{}", i + 1); + + let suffix = get_interface_ident_suffix(&implement.type_name); + if !suffix.is_empty() { + ident_string.push('_'); + ident_string.push_str(&suffix); + } + let field_ident = syn::Ident::new(&ident_string, implement.span); + + let mut vtable_const_string = ident_string.clone(); + vtable_const_string.make_ascii_uppercase(); + vtable_const_string.insert_str(0, "VTABLE_"); + let vtable_const_ident = syn::Ident::new(&vtable_const_string, implement.span); + + chains.push(InterfaceChain { + implement, + field_ident, + vtable_const_ident, + }); + } + + chains +} + +fn get_interface_ident_suffix(type_name: &str) -> String { + let mut suffix = String::new(); + for c in type_name.chars() { + let c = c.to_ascii_lowercase(); + + if suffix.len() >= 20 { + break; + } + + if c.is_ascii_alphanumeric() { + suffix.push(c); + } + } + + suffix +} diff --git a/vendor/windows-implement/src/tests.rs b/vendor/windows-implement/src/tests.rs new file mode 100644 index 00000000..3f5c3493 --- /dev/null +++ b/vendor/windows-implement/src/tests.rs @@ -0,0 +1,135 @@ +//! These tests are just a way to quickly run the `#[implement]` macro and see its output. +//! They don't check the output in any way. +//! +//! This exists because of some difficulties of running `cargo expand` against the `#[implement]` +//! macro. It's also just really convenient. You can see the output by using `--nocapture` and +//! you'll probably want to restrict the output to a single thread: +//! +//! ```text +//! cargo test -p windows-implement --lib -- --nocapture --test-threads=1 +//! ``` + +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +use proc_macro2::TokenStream; +use quote::quote; + +fn implement(attributes: TokenStream, item_tokens: TokenStream) -> String { + let out_tokens = crate::implement_core(attributes, item_tokens); + let tokens_string = out_tokens.to_string(); + + let out_string = rustfmt(&tokens_string); + println!("// output of #[implement] :"); + println!(); + println!("{}", out_string); + out_string +} + +fn rustfmt(input: &str) -> String { + let mut rustfmt = Command::new("rustfmt"); + + rustfmt.stdin(Stdio::piped()); + rustfmt.stdout(Stdio::piped()); + rustfmt.stderr(Stdio::inherit()); + + let mut child = match rustfmt.spawn() { + Ok(c) => c, + Err(e) => { + eprintln!("failed to spawn rustfmt: {e:?}"); + return input.to_string(); + } + }; + + let mut stdout = child.stdout.take().unwrap(); + + // spawn thread to read stdout + let stdout_thread = std::thread::spawn(move || { + let mut buf = String::new(); + stdout.read_to_string(&mut buf).unwrap(); + buf + }); + + // write unformatted into stdin + let mut stdin = child.stdin.take().unwrap(); + stdin.write_all(input.as_bytes()).unwrap(); + drop(stdin); + + let stdout_string: String = stdout_thread.join().unwrap(); + + let exit = child.wait().unwrap(); + if !exit.success() { + eprintln!("rustfmt terminated with failure status code"); + return input.to_string(); + } + + stdout_string +} + +#[test] +fn simple_type() { + implement( + quote!(IFoo), + quote! { + struct Foo { + x: u32, + } + }, + ); +} + +#[test] +fn zero_sized_type() { + implement( + quote!(IFoo), + quote! { + struct Foo; + }, + ); +} + +#[test] +fn no_interfaces() { + implement( + quote!(), + quote! { + struct Foo {} + }, + ); +} + +#[test] +fn generic_no_lifetime() { + implement( + quote!(IAsyncOperationWithProgress, IAsyncInfo), + quote! { + struct OperationWithProgress(SyncState>) + where + T: RuntimeType + 'static, + P: RuntimeType + 'static; + + }, + ); +} + +#[test] +fn generic_with_lifetime() { + implement( + quote!(), + quote! { + pub struct Foo<'a> { + pub x: &'a [u8], + } + }, + ); +} + +#[test] +fn tuple_type() { + implement( + quote!(IFoo), + quote! { + struct Foo(pub i32); + }, + ); +} -- cgit v1.2.3