From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/diplomat/.cargo-checksum.json | 1 + third_party/rust/diplomat/Cargo.toml | 55 ++ third_party/rust/diplomat/LICENSE-APACHE | 201 ++++++ third_party/rust/diplomat/LICENSE-MIT | 27 + third_party/rust/diplomat/src/enum_convert.rs | 85 +++ third_party/rust/diplomat/src/lib.rs | 691 +++++++++++++++++++++ .../snapshots/diplomat__tests__cfgd_struct.snap | 23 + .../snapshots/diplomat__tests__cfged_method-2.snap | 24 + .../snapshots/diplomat__tests__cfged_method.snap | 22 + ...plomat__tests__method_taking_mutable_slice.snap | 20 + ...diplomat__tests__method_taking_mutable_str.snap | 26 + .../diplomat__tests__method_taking_slice.snap | 20 + .../diplomat__tests__method_taking_str.snap | 23 + .../snapshots/diplomat__tests__mod_with_enum.snap | 23 + .../diplomat__tests__mod_with_rust_result.snap | 20 + ...diplomat__tests__mod_with_writeable_result.snap | 25 + .../diplomat__tests__multilevel_borrows.snap | 45 ++ .../snapshots/diplomat__tests__self_params.snap | 22 + .../rust/diplomat/src/transparent_convert.rs | 30 + 19 files changed, 1383 insertions(+) create mode 100644 third_party/rust/diplomat/.cargo-checksum.json create mode 100644 third_party/rust/diplomat/Cargo.toml create mode 100644 third_party/rust/diplomat/LICENSE-APACHE create mode 100644 third_party/rust/diplomat/LICENSE-MIT create mode 100644 third_party/rust/diplomat/src/enum_convert.rs create mode 100644 third_party/rust/diplomat/src/lib.rs create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap create mode 100644 third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap create mode 100644 third_party/rust/diplomat/src/transparent_convert.rs (limited to 'third_party/rust/diplomat') diff --git a/third_party/rust/diplomat/.cargo-checksum.json b/third_party/rust/diplomat/.cargo-checksum.json new file mode 100644 index 0000000000..e68087c073 --- /dev/null +++ b/third_party/rust/diplomat/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"42a489a0e146bba3fe0d2c1e9abc24f8f24164404b88c687a53c575864ac7a7e","LICENSE-APACHE":"639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666","LICENSE-MIT":"3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4","src/enum_convert.rs":"c0068cb8b563043030186cd9a0be6a4eac1a5f1761fe3646a99528e6d3dc5f54","src/lib.rs":"e05eb7285e68c64ee6936868cb3b39bfac041ee722bb4ecd6fb892fdf8bcff06","src/snapshots/diplomat__tests__cfgd_struct.snap":"2874497e83ba3f43541d90869bfa428973c5b23df7fec2826e187d530f1a620b","src/snapshots/diplomat__tests__cfged_method-2.snap":"b2ff2801b5f1e08bf660c63a5b8763cf99326e0b9d13cdcc9ae82532825727b2","src/snapshots/diplomat__tests__cfged_method.snap":"b478d2d14e01209b45032b092bf91a2068f08f704b2395bc3ebd2ada21141077","src/snapshots/diplomat__tests__method_taking_mutable_slice.snap":"eafa335a999e416a044d32106e096ef57b7f41fb2f74b03d4adce13e7179b03d","src/snapshots/diplomat__tests__method_taking_mutable_str.snap":"e4c65337861a78b3c9762545fcdbccc1169bab9183ff750fc467a5367dba9c56","src/snapshots/diplomat__tests__method_taking_slice.snap":"eb2d7d00381ddef71411c170ce27d2490acfe9322bc3de397fe1cedcedeeee7b","src/snapshots/diplomat__tests__method_taking_str.snap":"92aa38d8618f0d52d5bc967a8d67a87f4b9cdc4f8651c624a9cc7b73033dbaa4","src/snapshots/diplomat__tests__mod_with_enum.snap":"fc225e910aa1afe496eb8d4da4342894f7786c53e12725b2f70018cf5230dddc","src/snapshots/diplomat__tests__mod_with_rust_result.snap":"48e30564d2cf0477db7062c58842b264d0cfec1d635e7dbaf12c12a2e9f1ab31","src/snapshots/diplomat__tests__mod_with_writeable_result.snap":"6ddbc34dbf7d362366cddda70c87a2f25d7ba0821ccbab90b5a79748f1064593","src/snapshots/diplomat__tests__multilevel_borrows.snap":"708fb54c2c3b8498aac83c20af891e12212874b94691fd734fd57f500ce54666","src/snapshots/diplomat__tests__self_params.snap":"1f6652799973e4afa751afebd6306e71ea439640cc1cae9848c5757d060bf699","src/transparent_convert.rs":"dde901986a6709a21f359596e85bc4fd009bb645c79b698d5af8e2a603996ac4"},"package":"a31672b3ebc3c7866c3c98726f7a9a5ac8f13962e77d3c8225f6be49a7b8c5f2"} \ No newline at end of file diff --git a/third_party/rust/diplomat/Cargo.toml b/third_party/rust/diplomat/Cargo.toml new file mode 100644 index 0000000000..af5ef28fa6 --- /dev/null +++ b/third_party/rust/diplomat/Cargo.toml @@ -0,0 +1,55 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "diplomat" +version = "0.7.0" +authors = [ + "Shadaj Laddad ", + "Manish Goregaokar ", + "Quinn Okabayashi ", +] +description = "The diplomat FFI generation macro" +documentation = "https://docs.rs/diplomat_core/" +keywords = [ + "ffi", + "codegen", +] +categories = ["development-tools"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-diplomat/diplomat" + +[lib] +path = "src/lib.rs" +proc-macro = true + +[dependencies.diplomat_core] +version = "0.7.0" + +[dependencies.proc-macro2] +version = "1.0.27" + +[dependencies.quote] +version = "1.0" + +[dependencies.syn] +version = "2.0" +features = [ + "full", + "extra-traits", +] + +[dev-dependencies.insta] +version = "1.7.1" + +[dev-dependencies.tempfile] +version = "3.2.0" diff --git a/third_party/rust/diplomat/LICENSE-APACHE b/third_party/rust/diplomat/LICENSE-APACHE new file mode 100644 index 0000000000..05fcffa855 --- /dev/null +++ b/third_party/rust/diplomat/LICENSE-APACHE @@ -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 2022 The Diplomat Developers + +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/third_party/rust/diplomat/LICENSE-MIT b/third_party/rust/diplomat/LICENSE-MIT new file mode 100644 index 0000000000..cf62c31bc7 --- /dev/null +++ b/third_party/rust/diplomat/LICENSE-MIT @@ -0,0 +1,27 @@ +MIT License + +Copyright (c) 2022 The Diplomat Developers + +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/third_party/rust/diplomat/src/enum_convert.rs b/third_party/rust/diplomat/src/enum_convert.rs new file mode 100644 index 0000000000..0244f320e6 --- /dev/null +++ b/third_party/rust/diplomat/src/enum_convert.rs @@ -0,0 +1,85 @@ +use quote::{quote, ToTokens}; +use syn::parse::{Parse, ParseStream}; +use syn::*; + +// An attribute that is a list of idents +pub struct EnumConvertAttribute { + path: Path, + + needs_wildcard: bool, +} + +impl Parse for EnumConvertAttribute { + fn parse(input: ParseStream) -> Result { + let paths = input.parse_terminated(Path::parse, Token![,])?; + if paths.is_empty() { + return Err(input.error("#[diplomat::enum_convert] needs a path argument")); + } + let needs_wildcard = if paths.len() == 2 { + if let Some(ident) = paths[1].get_ident() { + if ident == "needs_wildcard" { + true + } else { + return Err(input.error( + "#[diplomat::enum_convert] only recognizes needs_wildcard keyword", + )); + } + } else { + return Err( + input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword") + ); + } + } else if paths.len() > 1 { + return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments")); + } else { + // no needs_wildcard marker + false + }; + Ok(EnumConvertAttribute { + path: paths[0].clone(), + needs_wildcard, + }) + } +} + +pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream { + let mut from_arms = vec![]; + let mut into_arms = vec![]; + + let this_name = &input.ident; + let other_name = &attr.path; + for variant in &input.variants { + if variant.fields != Fields::Unit { + return Error::new(variant.ident.span(), "variant may not have fields") + .to_compile_error(); + } + + let variant_name = &variant.ident; + from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name)); + into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name)); + } + + if attr.needs_wildcard { + let error = format!( + "Encountered unknown field for {}", + other_name.to_token_stream() + ); + from_arms.push(quote!(_ => unreachable!(#error))) + } + quote! { + impl From<#other_name> for #this_name { + fn from(other: #other_name) -> Self { + match other { + #(#from_arms,)* + } + } + } + impl From<#this_name> for #other_name { + fn from(this: #this_name) -> Self { + match this { + #(#into_arms,)* + } + } + } + } +} diff --git a/third_party/rust/diplomat/src/lib.rs b/third_party/rust/diplomat/src/lib.rs new file mode 100644 index 0000000000..dad44bff62 --- /dev/null +++ b/third_party/rust/diplomat/src/lib.rs @@ -0,0 +1,691 @@ +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use syn::*; + +use diplomat_core::ast; + +mod enum_convert; +mod transparent_convert; + +fn cfgs_to_stream(attrs: &[Attribute]) -> proc_macro2::TokenStream { + attrs + .iter() + .fold(quote!(), |prev, attr| quote!(#prev #attr)) +} + +fn gen_params_at_boundary(param: &ast::Param, expanded_params: &mut Vec) { + match ¶m.ty { + ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => { + let data_type = if let ast::TypeName::PrimitiveSlice(.., prim) = ¶m.ty { + ast::TypeName::Primitive(*prim).to_syn().to_token_stream() + } else { + quote! { u8 } + }; + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new( + parse2({ + if let ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, _) = + ¶m.ty + { + quote! { *mut #data_type } + } else { + quote! { *const #data_type } + } + }) + .unwrap(), + ), + })); + + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new( + parse2(quote! { + usize + }) + .unwrap(), + ), + })); + } + o => { + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(param.name.as_str(), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new(o.to_syn()), + })); + } + } +} + +fn gen_params_invocation(param: &ast::Param, expanded_params: &mut Vec) { + match ¶m.ty { + ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => { + let data_ident = + Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()); + let len_ident = Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()); + + let tokens = if let ast::TypeName::PrimitiveSlice(_, mutability, _) = ¶m.ty { + match mutability { + ast::Mutability::Mutable => quote! { + unsafe { core::slice::from_raw_parts_mut(#data_ident, #len_ident) } + }, + ast::Mutability::Immutable => quote! { + unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) } + }, + } + } else { + // TODO(#57): don't just unwrap? or should we assume that the other side gives us a good value? + quote! { + unsafe { + core::str::from_utf8(core::slice::from_raw_parts(#data_ident, #len_ident)).unwrap() + } + } + }; + expanded_params.push(parse2(tokens).unwrap()); + } + ast::TypeName::Result(_, _, _) => { + let param = ¶m.name; + expanded_params.push(parse2(quote!(#param.into())).unwrap()); + } + _ => { + expanded_params.push(Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Ident::new(param.name.as_str(), Span::call_site()).into(), + })); + } + } +} + +fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item { + let self_ident = Ident::new(strct.name().as_str(), Span::call_site()); + let method_ident = Ident::new(m.name.as_str(), Span::call_site()); + let extern_ident = Ident::new(m.full_path_name.as_str(), Span::call_site()); + + let mut all_params = vec![]; + m.params.iter().for_each(|p| { + gen_params_at_boundary(p, &mut all_params); + }); + + let mut all_params_invocation = vec![]; + m.params.iter().for_each(|p| { + gen_params_invocation(p, &mut all_params_invocation); + }); + + let this_ident = Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new("this", Span::call_site()), + subpat: None, + }); + + if let Some(self_param) = &m.self_param { + all_params.insert( + 0, + FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(this_ident.clone()), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new(self_param.to_typename().to_syn()), + }), + ); + } + + let lifetimes = { + let lifetime_env = &m.lifetime_env; + if lifetime_env.is_empty() { + quote! {} + } else { + quote! { <#lifetime_env> } + } + }; + + let method_invocation = if m.self_param.is_some() { + quote! { #this_ident.#method_ident } + } else { + quote! { #self_ident::#method_ident } + }; + + let (return_tokens, maybe_into) = if let Some(return_type) = &m.return_type { + if let ast::TypeName::Result(ok, err, true) = return_type { + let ok = ok.to_syn(); + let err = err.to_syn(); + ( + quote! { -> diplomat_runtime::DiplomatResult<#ok, #err> }, + quote! { .into() }, + ) + } else { + let return_type_syn = return_type.to_syn(); + (quote! { -> #return_type_syn }, quote! {}) + } + } else { + (quote! {}, quote! {}) + }; + + let writeable_flushes = m + .params + .iter() + .filter(|p| p.is_writeable()) + .map(|p| { + let p = &p.name; + quote! { #p.flush(); } + }) + .collect::>(); + + let cfg = cfgs_to_stream(&m.attrs.cfg); + + if writeable_flushes.is_empty() { + Item::Fn(syn::parse_quote! { + #[no_mangle] + #cfg + extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens { + #method_invocation(#(#all_params_invocation),*) #maybe_into + } + }) + } else { + Item::Fn(syn::parse_quote! { + #[no_mangle] + #cfg + extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens { + let ret = #method_invocation(#(#all_params_invocation),*); + #(#writeable_flushes)* + ret #maybe_into + } + }) + } +} + +struct AttributeInfo { + repr: bool, + opaque: bool, +} + +impl AttributeInfo { + fn extract(attrs: &mut Vec) -> Self { + let mut repr = false; + let mut opaque = false; + attrs.retain(|attr| { + let ident = &attr.path().segments.iter().next().unwrap().ident; + if ident == "repr" { + repr = true; + // don't actually extract repr attrs, just detect them + return true; + } else if ident == "diplomat" { + if attr.path().segments.len() == 2 { + let seg = &attr.path().segments.iter().nth(1).unwrap().ident; + if seg == "opaque" { + opaque = true; + return false; + } else if seg == "rust_link" || seg == "out" || seg == "attr" { + // diplomat-tool reads these, not diplomat::bridge. + // throw them away so rustc doesn't complain about unknown attributes + return false; + } else if seg == "enum_convert" || seg == "transparent_convert" { + // diplomat::bridge doesn't read this, but it's handled separately + // as an attribute + return true; + } else { + panic!("Only #[diplomat::opaque] and #[diplomat::rust_link] are supported") + } + } else { + panic!("#[diplomat::foo] attrs have a single-segment path name") + } + } + true + }); + + Self { repr, opaque } + } +} + +fn gen_bridge(input: ItemMod) -> ItemMod { + let module = ast::Module::from_syn(&input, true); + let (brace, mut new_contents) = input.content.unwrap(); + + new_contents.iter_mut().for_each(|c| match c { + Item::Struct(s) => { + let info = AttributeInfo::extract(&mut s.attrs); + if info.opaque || !info.repr { + let repr = if info.opaque { + // Normal opaque types don't need repr(transparent) because the inner type is + // never referenced. #[diplomat::transparent_convert] handles adding repr(transparent) + // on its own + quote!() + } else { + quote!(#[repr(C)]) + }; + *s = syn::parse_quote! { + #repr + #s + } + } + } + + Item::Enum(e) => { + let info = AttributeInfo::extract(&mut e.attrs); + if info.opaque { + panic!("#[diplomat::opaque] not allowed on enums") + } + for v in &mut e.variants { + let info = AttributeInfo::extract(&mut v.attrs); + if info.opaque { + panic!("#[diplomat::opaque] not allowed on enum variants"); + } + } + *e = syn::parse_quote! { + #[repr(C)] + #e + }; + } + + Item::Impl(i) => { + for item in &mut i.items { + if let syn::ImplItem::Fn(ref mut m) = *item { + let info = AttributeInfo::extract(&mut m.attrs); + if info.opaque { + panic!("#[diplomat::opaque] not allowed on methods") + } + } + } + } + _ => (), + }); + + for custom_type in module.declared_types.values() { + custom_type.methods().iter().for_each(|m| { + new_contents.push(gen_custom_type_method(custom_type, m)); + }); + + let destroy_ident = Ident::new( + format!("{}_destroy", custom_type.name()).as_str(), + Span::call_site(), + ); + + let type_ident = custom_type.name().to_syn(); + + let (lifetime_defs, lifetimes) = if let Some(lifetime_env) = custom_type.lifetimes() { + ( + quote! { <#lifetime_env> }, + lifetime_env.lifetimes_to_tokens(), + ) + } else { + (quote! {}, quote! {}) + }; + + let cfg = cfgs_to_stream(&custom_type.attrs().cfg); + + // for now, body is empty since all we need to do is drop the box + // TODO(#13): change to take a `*mut` and handle DST boxes appropriately + new_contents.push(Item::Fn(syn::parse_quote! { + #[no_mangle] + #cfg + extern "C" fn #destroy_ident#lifetime_defs(this: Box<#type_ident#lifetimes>) {} + })); + } + + ItemMod { + attrs: input.attrs, + vis: input.vis, + mod_token: input.mod_token, + ident: input.ident, + content: Some((brace, new_contents)), + semi: input.semi, + unsafety: None, + } +} + +/// Mark a module to be exposed through Diplomat-generated FFI. +#[proc_macro_attribute] +pub fn bridge( + _attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = gen_bridge(parse_macro_input!(input)); + proc_macro::TokenStream::from(expanded.to_token_stream()) +} + +/// Generate From and Into implementations for a Diplomat enum +/// +/// This is invoked as `#[diplomat::enum_convert(OtherEnumName)]` +/// on a Diplomat enum. It will assume the other enum has exactly the same variants +/// and generate From and Into implementations using those. In case that enum is `#[non_exhaustive]`, +/// you may use `#[diplomat::enum_convert(OtherEnumName, needs_wildcard)]` to generate a panicky wildcard +/// branch. It is up to the library author to ensure the enums are kept in sync. You may use the `#[non_exhaustive_omitted_patterns]` +/// lint to enforce this. +#[proc_macro_attribute] +pub fn enum_convert( + attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + // proc macros handle compile errors by using special error tokens. + // In case of an error, we don't want the original code to go away too + // (otherwise that will cause more errors) so we hold on to it and we tack it in + // with no modifications below + let input_cached: proc_macro2::TokenStream = input.clone().into(); + let expanded = + enum_convert::gen_enum_convert(parse_macro_input!(attr), parse_macro_input!(input)); + + let full = quote! { + #expanded + #input_cached + }; + proc_macro::TokenStream::from(full.to_token_stream()) +} + +/// Generate conversions from inner types for opaque Diplomat types with a single field +/// +/// This is invoked as `#[diplomat::transparent_convert]` +/// on an opaque Diplomat type. It will add `#[repr(transparent)]` and implement `pub(crate) fn transparent_convert()` +/// which allows constructing an `&Self` from a reference to the inner field. +#[proc_macro_attribute] +pub fn transparent_convert( + _attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + // proc macros handle compile errors by using special error tokens. + // In case of an error, we don't want the original code to go away too + // (otherwise that will cause more errors) so we hold on to it and we tack it in + // with no modifications below + let input_cached: proc_macro2::TokenStream = input.clone().into(); + let expanded = transparent_convert::gen_transparent_convert(parse_macro_input!(input)); + + let full = quote! { + #expanded + #input_cached + }; + proc_macro::TokenStream::from(full.to_token_stream()) +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::io::{Read, Write}; + use std::process::Command; + + use quote::ToTokens; + use syn::parse_quote; + use tempfile::tempdir; + + use super::gen_bridge; + + fn rustfmt_code(code: &str) -> String { + let dir = tempdir().unwrap(); + let file_path = dir.path().join("temp.rs"); + let mut file = File::create(file_path.clone()).unwrap(); + + writeln!(file, "{code}").unwrap(); + drop(file); + + Command::new("rustfmt") + .arg(file_path.to_str().unwrap()) + .spawn() + .unwrap() + .wait() + .unwrap(); + + let mut file = File::open(file_path).unwrap(); + let mut data = String::new(); + file.read_to_string(&mut data).unwrap(); + drop(file); + dir.close().unwrap(); + data + } + + #[test] + fn method_taking_str() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn from_str(s: &str) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn method_taking_slice() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn from_slice(s: &[f64]) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn method_taking_mutable_slice() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn fill_slice(s: &mut [f64]) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_enum() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + enum Abc { + A, + B = 123, + } + + impl Abc { + pub fn do_something(&self) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_writeable_result() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_rust_result() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn bar(&self) -> Result<(), ()> { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn multilevel_borrows() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + struct Foo<'a>(&'a str); + + #[diplomat::opaque] + struct Bar<'b, 'a: 'b>(&'b Foo<'a>); + + struct Baz<'x, 'y> { + foo: &'y Foo<'x>, + } + + impl<'a> Foo<'a> { + pub fn new(x: &'a str) -> Box> { + unimplemented!() + } + + pub fn get_bar<'b>(&'b self) -> Box> { + unimplemented!() + } + + pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> { + Bax { foo: self } + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn self_params() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + struct RefList<'a> { + data: &'a i32, + next: Option>, + } + + impl<'b> RefList<'b> { + pub fn extend(&mut self, other: &Self) -> Self { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn cfged_method() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + #[cfg(feature = "bar")] + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn cfgd_struct() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + #[cfg(feature = "foo")] + struct Foo {} + #[cfg(feature = "foo")] + impl Foo { + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } +} diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap new file mode 100644 index 0000000000..4328309170 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] #[cfg(feature = \"foo\")] struct Foo {}\n #[cfg(feature = \"foo\")] impl Foo\n { pub fn bar(s : u8) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[cfg(feature = "foo")] + struct Foo {} + #[cfg(feature = "foo")] + impl Foo { + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap new file mode 100644 index 0000000000..b026704b8d --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap @@ -0,0 +1,24 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} #[cfg(feature = \"bar\")] impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + #[cfg(feature = "bar")] + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + #[cfg(feature = "bar")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap new file mode 100644 index 0000000000..bc4adbb73f --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap @@ -0,0 +1,22 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap new file mode 100644 index 0000000000..01fea65c2e --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn fill_slice(s : & mut [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn fill_slice(s: &mut [f64]) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_fill_slice(s_diplomat_data: *mut f64, s_diplomat_len: usize) { + Foo::fill_slice(unsafe { core::slice::from_raw_parts_mut(s_diplomat_data, s_diplomat_len) }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap new file mode 100644 index 0000000000..bebbcf8455 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap @@ -0,0 +1,26 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn make_uppercase(s : & mut str) { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn make_uppercase(s: &mut str) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_make_uppercase(s_diplomat_data: *mut u8, s_diplomat_len: usize) { + Foo::make_uppercase(unsafe { + core::str::from_utf8_mut(core::slice::from_raw_parts_mut( + s_diplomat_data, + s_diplomat_len, + )) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap new file mode 100644 index 0000000000..94daec2af5 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_slice(s : & [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn from_slice(s: &[f64]) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_from_slice(s_diplomat_data: *const f64, s_diplomat_len: usize) { + Foo::from_slice(unsafe { core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len) }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap new file mode 100644 index 0000000000..7e736e5599 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_str(s : & str) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn from_str(s: &str) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) { + Foo::from_str(unsafe { + core::str::from_utf8(core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len)) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap new file mode 100644 index 0000000000..723e17d786 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n enum Abc { A, B = 123, } impl Abc\n { pub fn do_something(& self) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + enum Abc { + A, + B = 123, + } + impl Abc { + pub fn do_something(&self) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Abc_do_something(this: &Abc) { + this.do_something() + } + #[no_mangle] + extern "C" fn Abc_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap new file mode 100644 index 0000000000..1eeb28808a --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn bar(& self) -> Result < (), () >\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn bar(&self) -> Result<(), ()> { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_bar(this: &Foo) -> diplomat_runtime::DiplomatResult<(), ()> { + this.bar().into() + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap new file mode 100644 index 0000000000..870193052f --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap @@ -0,0 +1,25 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn to_string(& self, to : & mut DiplomatWriteable) ->\n Result < (), () > { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_to_string( + this: &Foo, + to: &mut diplomat_runtime::DiplomatWriteable, + ) -> diplomat_runtime::DiplomatResult<(), ()> { + let ret = this.to_string(to); + to.flush(); + ret.into() + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap new file mode 100644 index 0000000000..f662608096 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap @@ -0,0 +1,45 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct Foo < 'a > (& 'a str) ;\n #[diplomat :: opaque] struct Bar < 'b, 'a : 'b >\n (& 'b Foo < 'a >) ; struct Baz < 'x, 'y >\n { foo : & 'y Foo < 'x >, } impl < 'a > Foo < 'a >\n {\n pub fn new(x : & 'a str) -> Box < Foo < 'a >>\n { unimplemented! () } pub fn get_bar < 'b > (& 'b self) ->\n Box < Bar < 'b, 'a >> { unimplemented! () } pub fn get_baz <\n 'b > (& 'b self) -> Baz < 'b, 'a > { Bax { foo : self } }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + struct Foo<'a>(&'a str); + struct Bar<'b, 'a: 'b>(&'b Foo<'a>); + #[repr(C)] + struct Baz<'x, 'y> { + foo: &'y Foo<'x>, + } + impl<'a> Foo<'a> { + pub fn new(x: &'a str) -> Box> { + unimplemented!() + } + pub fn get_bar<'b>(&'b self) -> Box> { + unimplemented!() + } + pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> { + Bax { foo: self } + } + } + #[no_mangle] + extern "C" fn Bar_destroy<'b, 'a: 'b>(this: Box>) {} + #[no_mangle] + extern "C" fn Baz_destroy<'x: 'y, 'y>(this: Box>) {} + #[no_mangle] + extern "C" fn Foo_new<'a>(x_diplomat_data: *const u8, x_diplomat_len: usize) -> Box> { + Foo::new(unsafe { + core::str::from_utf8(core::slice::from_raw_parts(x_diplomat_data, x_diplomat_len)) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_get_bar<'a: 'b, 'b>(this: &'b Foo<'a>) -> Box> { + this.get_bar() + } + #[no_mangle] + extern "C" fn Foo_get_baz<'a: 'b, 'b>(this: &'b Foo<'a>) -> Baz<'b, 'a> { + this.get_baz() + } + #[no_mangle] + extern "C" fn Foo_destroy<'a>(this: Box>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap new file mode 100644 index 0000000000..6b931197ab --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap @@ -0,0 +1,22 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct RefList < 'a >\n { data : & 'a i32, next : Option < Box < Self >>, } impl <\n 'b > RefList < 'b >\n {\n pub fn extend(& mut self, other : & Self) -> Self\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + struct RefList<'a> { + data: &'a i32, + next: Option>, + } + impl<'b> RefList<'b> { + pub fn extend(&mut self, other: &Self) -> Self { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn RefList_extend<'b>(this: &mut RefList<'b>, other: &RefList<'b>) -> RefList<'b> { + this.extend(other) + } + #[no_mangle] + extern "C" fn RefList_destroy<'a>(this: Box>) {} +} + diff --git a/third_party/rust/diplomat/src/transparent_convert.rs b/third_party/rust/diplomat/src/transparent_convert.rs new file mode 100644 index 0000000000..9ac8bf64e7 --- /dev/null +++ b/third_party/rust/diplomat/src/transparent_convert.rs @@ -0,0 +1,30 @@ +use quote::quote; +use syn::*; + +pub fn gen_transparent_convert(s: ItemStruct) -> proc_macro2::TokenStream { + let mut fields = s.fields.iter(); + let field1 = if let Some(field1) = fields.next() { + &field1.ty + } else { + panic!("#[diplomat::transparent_convert] only allowed on structs with a single field") + }; + + if fields.next().is_some() { + panic!("#[diplomat::transparent_convert] only allowed on structs with a single field") + } + let struct_name = &s.ident; + let (impl_generics, ty_generics, _) = s.generics.split_for_impl(); + let mut impl_generics: Generics = parse_quote!(#impl_generics); + let custom_lifetime: GenericParam = parse_quote!('transparent_convert_outer); + impl_generics.params.push(custom_lifetime); + quote! { + impl #impl_generics #struct_name #ty_generics { + // can potentially add transparent_convert_owned, _mut later + pub(crate) fn transparent_convert(from: &'transparent_convert_outer #field1) -> &'transparent_convert_outer Self { + unsafe { + &*(from as *const #field1 as *const Self) + } + } + } + } +} -- cgit v1.2.3