From 7ee22a9abc6eae1e8db0d3711dcef40d9f6499c7 Mon Sep 17 00:00:00 2001 From: Andrew Hayzen Date: Thu, 24 Aug 2023 15:32:05 +0100 Subject: [PATCH] cxx-qt-gen: support private signals on the extern "C++Qt" Related to #661 --- crates/cxx-qt-gen/src/generator/cpp/signal.rs | 5 + .../src/generator/naming/signals.rs | 2 + .../cxx-qt-gen/src/generator/rust/signals.rs | 197 ++++++++++++++++-- crates/cxx-qt-gen/src/parser/signals.rs | 39 +++- 4 files changed, 226 insertions(+), 17 deletions(-) diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index 081ac3a43..a8b2e2ecf 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -209,6 +209,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }]; let qobject_idents = create_qobjectname(); @@ -272,6 +273,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }]; let qobject_idents = create_qobjectname(); @@ -334,6 +336,7 @@ mod tests { }, safe: true, inherit: true, + private: false, }]; let qobject_idents = create_qobjectname(); @@ -382,6 +385,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let generated = generate_cpp_free_signal(&signal, &ParsedCxxMappings::default()).unwrap(); @@ -436,6 +440,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let mut cxx_mappings = ParsedCxxMappings::default(); diff --git a/crates/cxx-qt-gen/src/generator/naming/signals.rs b/crates/cxx-qt-gen/src/generator/naming/signals.rs index 9ca8d2b59..9ead2bf9e 100644 --- a/crates/cxx-qt-gen/src/generator/naming/signals.rs +++ b/crates/cxx-qt-gen/src/generator/naming/signals.rs @@ -61,6 +61,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let names = QSignalName::from(&qsignal); @@ -90,6 +91,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let names = QSignalName::from(&qsignal); diff --git a/crates/cxx-qt-gen/src/generator/rust/signals.rs b/crates/cxx-qt-gen/src/generator/rust/signals.rs index aed3aac65..815c48219 100644 --- a/crates/cxx-qt-gen/src/generator/rust/signals.rs +++ b/crates/cxx-qt-gen/src/generator/rust/signals.rs @@ -85,23 +85,28 @@ pub fn generate_rust_free_signal( std::mem::swap(&mut unsafe_call, &mut unsafe_block); } + let mut cxx_bridge = vec![]; + + if !signal.private { + cxx_bridge.push(quote! { + #unsafe_block extern "C++" { + #original_method + } + }); + } + + cxx_bridge.push(quote! { + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = #connect_namespace] + #[must_use] + #[rust_name = #free_connect_ident_rust_str] + fn #free_connect_ident_cpp(self_value: #self_type_cxx, func: #unsafe_call fn(#self_type_cxx, #(#parameters_cxx),*), conn_type: CxxQtConnectionType) -> CxxQtQMetaObjectConnection; + } + }); + let fragment = RustFragmentPair { - cxx_bridge: vec![ - quote! { - #unsafe_block extern "C++" { - #original_method - } - }, - quote! { - unsafe extern "C++" { - #[doc(hidden)] - #[namespace = #connect_namespace] - #[must_use] - #[rust_name = #free_connect_ident_rust_str] - fn #free_connect_ident_cpp(self_value: #self_type_cxx, func: #unsafe_call fn(#self_type_cxx, #(#parameters_cxx),*), conn_type: CxxQtConnectionType) -> CxxQtQMetaObjectConnection; - } - }, - ], + cxx_bridge, implementation: vec![ quote! { impl #qualified_impl { @@ -274,6 +279,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let qobject_idents = create_qobjectname(); @@ -353,6 +359,7 @@ mod tests { }, safe: true, inherit: false, + private: false, }; let qobject_idents = create_qobjectname(); @@ -426,6 +433,7 @@ mod tests { }, safe: false, inherit: false, + private: false, }; let qobject_idents = create_qobjectname(); @@ -496,6 +504,7 @@ mod tests { }, safe: true, inherit: true, + private: false, }; let qobject_idents = create_qobjectname(); @@ -550,4 +559,160 @@ mod tests { }, ); } + + #[test] + fn test_generate_rust_signal_free() { + let qsignal = ParsedSignal { + method: parse_quote! { + fn ready(self: Pin<&mut MyObject>); + }, + qobject_ident: format_ident!("MyObject"), + mutable: true, + parameters: vec![], + ident: CombinedIdent { + cpp: format_ident!("ready"), + rust: format_ident!("ready"), + }, + safe: true, + inherit: false, + private: false, + }; + + let generated = generate_rust_free_signal( + &qsignal, + &ParsedCxxMappings::default(), + &format_ident!("ffi"), + ) + .unwrap(); + + assert_eq!(generated.cxx_mod_contents.len(), 2); + assert_eq!(generated.cxx_qt_mod_contents.len(), 2); + + assert_tokens_eq( + &generated.cxx_mod_contents[0], + quote! { + unsafe extern "C++" { + fn ready(self: Pin<&mut MyObject>); + } + }, + ); + assert_tokens_eq( + &generated.cxx_mod_contents[1], + quote! { + unsafe extern "C++" { + #[doc (hidden)] + #[namespace = "rust::cxxqtgen1::externcxxqt"] + #[must_use] + #[rust_name = "MyObject_connect_ready"] + fn MyObject_readyConnect(self_value: Pin<&mut MyObject>, func: fn(Pin<&mut MyObject>, ), conn_type : CxxQtConnectionType) -> CxxQtQMetaObjectConnection; + } + }, + ); + assert_tokens_eq( + &generated.cxx_qt_mod_contents[0], + quote! { + impl MyObject { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + #[must_use] + pub fn on_ready(self: core::pin::Pin<&mut MyObject>, func: fn(core::pin::Pin<&mut MyObject>, )) -> cxx_qt_lib::QMetaObjectConnection + { + ffi::MyObject_connect_ready(self, func, cxx_qt_lib::ConnectionType::AutoConnection) + } + } + }, + ); + assert_tokens_eq( + &generated.cxx_qt_mod_contents[1], + quote! { + impl MyObject { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[must_use] + pub fn connect_ready(self: core::pin::Pin<&mut MyObject>, func: fn(core::pin::Pin<&mut MyObject>, ), conn_type: cxx_qt_lib::ConnectionType) -> cxx_qt_lib::QMetaObjectConnection + { + ffi::MyObject_connect_ready(self, func, conn_type) + } + } + }, + ); + } + + #[test] + fn test_generate_rust_signal_free_private() { + let qsignal = ParsedSignal { + method: parse_quote! { + fn ready(self: Pin<&mut MyObject>); + }, + qobject_ident: format_ident!("MyObject"), + mutable: true, + parameters: vec![], + ident: CombinedIdent { + cpp: format_ident!("ready"), + rust: format_ident!("ready"), + }, + safe: true, + inherit: false, + private: true, + }; + + let generated = generate_rust_free_signal( + &qsignal, + &ParsedCxxMappings::default(), + &format_ident!("ffi"), + ) + .unwrap(); + + assert_eq!(generated.cxx_mod_contents.len(), 1); + assert_eq!(generated.cxx_qt_mod_contents.len(), 2); + + assert_tokens_eq( + &generated.cxx_mod_contents[0], + quote! { + unsafe extern "C++" { + #[doc (hidden)] + #[namespace = "rust::cxxqtgen1::externcxxqt"] + #[must_use] + #[rust_name = "MyObject_connect_ready"] + fn MyObject_readyConnect(self_value: Pin<&mut MyObject>, func: fn(Pin<&mut MyObject>, ), conn_type : CxxQtConnectionType) -> CxxQtQMetaObjectConnection; + } + }, + ); + assert_tokens_eq( + &generated.cxx_qt_mod_contents[0], + quote! { + impl MyObject { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + #[must_use] + pub fn on_ready(self: core::pin::Pin<&mut MyObject>, func: fn(core::pin::Pin<&mut MyObject>, )) -> cxx_qt_lib::QMetaObjectConnection + { + ffi::MyObject_connect_ready(self, func, cxx_qt_lib::ConnectionType::AutoConnection) + } + } + }, + ); + assert_tokens_eq( + &generated.cxx_qt_mod_contents[1], + quote! { + impl MyObject { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[must_use] + pub fn connect_ready(self: core::pin::Pin<&mut MyObject>, func: fn(core::pin::Pin<&mut MyObject>, ), conn_type: cxx_qt_lib::ConnectionType) -> cxx_qt_lib::QMetaObjectConnection + { + ffi::MyObject_connect_ready(self, func, conn_type) + } + } + }, + ); + } } diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index a972cfbe0..0f861d343 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -7,10 +7,11 @@ use crate::parser::parameter::ParsedFunctionParameter; use crate::syntax::attribute::{attribute_find_path, attribute_take_path}; use crate::syntax::expr::expr_to_string; use crate::syntax::foreignmod; +use crate::syntax::path::path_compare_str; use crate::syntax::safety::Safety; use crate::{generator::naming::CombinedIdent, syntax::types}; use quote::format_ident; -use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result}; +use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result, Visibility}; /// Describes an individual Signal pub struct ParsedSignal { @@ -28,6 +29,8 @@ pub struct ParsedSignal { pub ident: CombinedIdent, /// If the signal is defined in the base class pub inherit: bool, + /// Whether the signal is private + pub private: bool, } impl ParsedSignal { @@ -45,6 +48,7 @@ impl ParsedSignal { parameters: vec![], ident, inherit: false, + private: false, } } @@ -86,6 +90,11 @@ impl ParsedSignal { let inherit = attribute_take_path(&mut method.attrs, &["inherit"]).is_some(); let safe = method.sig.unsafety.is_none(); + let private = if let Visibility::Restricted(vis_restricted) = &method.vis { + path_compare_str(&vis_restricted.path, &["self"]) + } else { + false + }; Ok(Self { method, @@ -95,6 +104,7 @@ impl ParsedSignal { ident, safe, inherit, + private, }) } } @@ -126,6 +136,7 @@ mod tests { ); assert!(signal.safe); assert!(!signal.inherit); + assert!(!signal.private); } #[test] @@ -153,6 +164,7 @@ mod tests { ); assert!(signal.safe); assert!(!signal.inherit); + assert!(!signal.private); } #[test] @@ -179,6 +191,7 @@ mod tests { ); assert!(signal.safe); assert!(signal.inherit); + assert!(!signal.private); } #[test] @@ -213,6 +226,29 @@ mod tests { ); assert!(signal.safe); assert!(!signal.inherit); + assert!(!signal.private); + } + + #[test] + fn test_parse_signal_private() { + let method: ForeignItemFn = parse_quote! { + pub(self) fn ready(self: Pin<&mut MyObject>); + }; + let signal = ParsedSignal::parse(method.clone(), Safety::Safe).unwrap(); + assert_eq!(signal.method, method); + assert_eq!(signal.qobject_ident, format_ident!("MyObject")); + assert!(signal.mutable); + assert_eq!(signal.parameters, vec![]); + assert_eq!( + signal.ident, + CombinedIdent { + cpp: format_ident!("ready"), + rust: format_ident!("ready") + } + ); + assert!(signal.safe); + assert!(!signal.inherit); + assert!(signal.private); } #[test] @@ -252,6 +288,7 @@ mod tests { ); assert!(!signal.safe); assert!(!signal.inherit); + assert!(!signal.private); } #[test]