From 1735d17d65a007b6e13eb7faf5672d041c6c2ffd Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 13:19:22 +0100 Subject: [PATCH 1/6] Fix typo in TypeNames::cxx_qualified doc comment --- crates/cxx-qt-gen/src/naming/type_names.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cxx-qt-gen/src/naming/type_names.rs b/crates/cxx-qt-gen/src/naming/type_names.rs index 08679390b..62ac9835d 100644 --- a/crates/cxx-qt-gen/src/naming/type_names.rs +++ b/crates/cxx-qt-gen/src/naming/type_names.rs @@ -179,7 +179,7 @@ impl TypeNames { /// For a given rust ident return the CXX name with its namespace /// - /// Ideally we'd want this type name to always be **fully** qualified, staring with `::`. + /// Ideally we'd want this type name to always be **fully** qualified, starting with `::`. /// Unfortunately, this isn't always possible, as the Qt5 meta object system doesn't register /// types with the fully qualified path :( /// E.g. it will recognize `QString`, but not `::QString` from QML. From e4231f2228cae68c38925483cb3923dc0c87e10d Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 14:45:26 +0100 Subject: [PATCH 2/6] Add formatting when assert_tokens_eq fails This makes the errors a lot more readable. --- crates/cxx-qt-gen/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/cxx-qt-gen/src/lib.rs b/crates/cxx-qt-gen/src/lib.rs index 1ee1754c9..dfef2b1d5 100644 --- a/crates/cxx-qt-gen/src/lib.rs +++ b/crates/cxx-qt-gen/src/lib.rs @@ -44,7 +44,20 @@ mod tests { /// Helper to ensure that a given syn item is the same as the given TokenStream pub fn assert_tokens_eq(item: &T, tokens: TokenStream) { - assert_str_eq!(item.to_token_stream().to_string(), tokens.to_string()); + // For understanding what's going on, it is nicer to use format_rs_source + // So that the TokenStream is actually legible. + // + // We don't want to use format_rs_source for the comparison, because it means calling out + // to rustfmt, which is slow. + // So only pretty-print if the comparison fails. + let left = tokens.to_string(); + let right = item.to_token_stream().to_string(); + if left != right { + assert_str_eq!(format_rs_source(&left), format_rs_source(&right)); + // Fallback, in case assert_str_eq doesn't actually panic after formatting for some + // reason. + panic!("assertion failed: left != right"); + } } /// Helper for formating C++ code From fa383232069f2b84cf18d492db54f1c2c42ac05f Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 15:08:44 +0100 Subject: [PATCH 3/6] TypeNames: Return Result from namespace() If the type isn't known, we shouldn't assume it to be valid. So return an "Unknown type" error instead. --- .../src/generator/cpp/externcxxqt.rs | 10 ++- .../src/generator/cpp/property/mod.rs | 4 +- crates/cxx-qt-gen/src/generator/cpp/signal.rs | 14 +-- .../src/generator/naming/signals.rs | 14 +-- .../src/generator/rust/property/mod.rs | 50 +++++------ .../cxx-qt-gen/src/generator/rust/signals.rs | 86 +++++++++---------- crates/cxx-qt-gen/src/naming/name.rs | 7 +- crates/cxx-qt-gen/src/naming/type_names.rs | 79 +++++++++++++---- crates/cxx-qt-gen/src/parser/mod.rs | 3 + 9 files changed, 163 insertions(+), 104 deletions(-) diff --git a/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs b/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs index 446f087c6..e7c163460 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs @@ -58,14 +58,18 @@ mod tests { type MyObject; #[qsignal] - fn signal1(self: Pin<&mut ObjRust>); + fn signal1(self: Pin<&mut MyObject>); #[qsignal] - fn signal2(self: Pin<&mut ObjRust>); + fn signal2(self: Pin<&mut MyObject>); } }) .unwrap()]; - let generated = generate(&blocks, &TypeNames::default()).unwrap(); + + // Unknown types + assert!(generate(&blocks, &TypeNames::default()).is_err()); + + let generated = generate(&blocks, &TypeNames::mock()).unwrap(); assert_eq!(generated.len(), 2); } diff --git a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs index 62c53d5ac..0a93ee361 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs @@ -82,7 +82,7 @@ mod tests { let qobject_idents = create_qobjectname(); let generated = - generate_cpp_properties(&properties, &qobject_idents, &TypeNames::default()).unwrap(); + generate_cpp_properties(&properties, &qobject_idents, &TypeNames::mock()).unwrap(); // metaobjects assert_eq!(generated.metaobjects.len(), 2); @@ -360,7 +360,7 @@ mod tests { }]; let qobject_idents = create_qobjectname(); - let mut type_names = TypeNames::default(); + let mut type_names = TypeNames::mock(); type_names.insert("A", None, Some("A1"), None); let generated = generate_cpp_properties(&properties, &qobject_idents, &type_names).unwrap(); diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index 03abb57ce..3a846b0c2 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -96,7 +96,7 @@ pub fn generate_cpp_signal( // Prepare the idents let idents = QSignalName::from(signal); - let idents_helper = QSignalHelperName::new(&idents, qobject_ident, type_names); + let idents_helper = QSignalHelperName::new(&idents, qobject_ident, type_names)?; let signal_ident = idents.name.cpp; let free_connect_ident_cpp = idents_helper.connect_name.cpp; @@ -251,7 +251,7 @@ mod tests { let qobject_idents = create_qobjectname(); let generated = - generate_cpp_signals(&signals, &qobject_idents, &TypeNames::default()).unwrap(); + generate_cpp_signals(&signals, &qobject_idents, &TypeNames::mock()).unwrap(); assert_eq!(generated.methods.len(), 1); let header = if let CppFragment::Header(header) = &generated.methods[0] { @@ -349,7 +349,7 @@ mod tests { }]; let qobject_idents = create_qobjectname(); - let mut type_names = TypeNames::default(); + let mut type_names = TypeNames::mock(); type_names.insert("A", None, Some("A1"), None); let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names).unwrap(); @@ -444,9 +444,8 @@ mod tests { private: false, }]; let qobject_idents = create_qobjectname(); - let generated = - generate_cpp_signals(&signals, &qobject_idents, &TypeNames::default()).unwrap(); + generate_cpp_signals(&signals, &qobject_idents, &TypeNames::mock()).unwrap(); assert_eq!(generated.methods.len(), 0); assert_eq!(generated.fragments.len(), 1); @@ -531,8 +530,9 @@ mod tests { private: false, }; - let generated = - generate_cpp_signal(&signal, &signal.qobject_ident, &TypeNames::default()).unwrap(); + let mut type_names = TypeNames::default(); + type_names.insert("ObjRust", None, None, None); + let generated = generate_cpp_signal(&signal, &signal.qobject_ident, &type_names).unwrap(); assert_eq!(generated.methods.len(), 0); diff --git a/crates/cxx-qt-gen/src/generator/naming/signals.rs b/crates/cxx-qt-gen/src/generator/naming/signals.rs index 4b9890dc1..eb459f4d9 100644 --- a/crates/cxx-qt-gen/src/generator/naming/signals.rs +++ b/crates/cxx-qt-gen/src/generator/naming/signals.rs @@ -6,7 +6,7 @@ use crate::parser::signals::ParsedSignal; use crate::{generator::naming::CombinedIdent, naming::TypeNames}; use convert_case::{Case, Casing}; use quote::format_ident; -use syn::Ident; +use syn::{Ident, Result}; /// Names for parts of a Q_SIGNAL pub struct QSignalName { @@ -52,7 +52,11 @@ pub struct QSignalHelperName { } impl QSignalHelperName { - pub fn new(idents: &QSignalName, qobject_ident: &Ident, type_names: &TypeNames) -> Self { + pub fn new( + idents: &QSignalName, + qobject_ident: &Ident, + type_names: &TypeNames, + ) -> Result { let signal_ident = &idents.name.cpp; let handler_alias = format_ident!("{qobject_ident}CxxQtSignalHandler{signal_ident}"); let namespace = { @@ -67,7 +71,7 @@ impl QSignalHelperName { // // See the comment on TypeNames::cxx_qualified for why fully qualifying is // unfortunately not possible. - let qobject_namespace = type_names.namespace(qobject_ident); + let qobject_namespace = type_names.namespace(qobject_ident)?; let namespace: Vec<_> = qobject_namespace .into_iter() .chain(vec!["rust::cxxqtgen1".to_owned()]) @@ -78,7 +82,7 @@ impl QSignalHelperName { // TODO: in the future we might improve the naming of the methods // to avoid collisions (maybe use a separator similar to how CXX uses $?) - Self { + Ok(Self { connect_name: CombinedIdent { cpp: format_ident!("{}_{}", qobject_ident, idents.connect_name.cpp), rust: format_ident!("{}_{}", qobject_ident, idents.connect_name.rust), @@ -90,7 +94,7 @@ impl QSignalHelperName { struct_param: format_ident!("{qobject_ident}CxxQtSignalParams{signal_ident}"), namespace, handler_alias, - } + }) } } diff --git a/crates/cxx-qt-gen/src/generator/rust/property/mod.rs b/crates/cxx-qt-gen/src/generator/rust/property/mod.rs index 88c14315b..f6a6c0773 100644 --- a/crates/cxx-qt-gen/src/generator/rust/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/property/mod.rs @@ -92,7 +92,7 @@ mod tests { let generated = generate_rust_properties( &properties, &qobject_idents, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -116,7 +116,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Getter for the Q_PROPERTY "] #[doc = "trivial_property"] pub fn trivial_property(&self) -> &i32 { @@ -139,7 +139,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Setter for the Q_PROPERTY "] #[doc = "trivial_property"] pub fn set_trivial_property(mut self: core::pin::Pin<&mut Self>, value: i32) { @@ -169,7 +169,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[2], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Getter for the Q_PROPERTY "] #[doc = "opaque_property"] pub fn opaque_property(&self) -> &cxx::UniquePtr { @@ -192,7 +192,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[3], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Setter for the Q_PROPERTY "] #[doc = "opaque_property"] pub fn set_opaque_property(mut self: core::pin::Pin<&mut Self>, value: cxx::UniquePtr) { @@ -222,7 +222,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[4], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Getter for the Q_PROPERTY "] #[doc = "unsafe_property"] pub fn unsafe_property(&self) -> &*mut T { @@ -245,7 +245,7 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[5], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Setter for the Q_PROPERTY "] #[doc = "unsafe_property"] pub fn set_unsafe_property(mut self: core::pin::Pin<&mut Self>, value: *mut T) { @@ -307,11 +307,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[6], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "trivialPropertyChanged"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_trivial_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_trivial_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_trivial_property_changed( self, @@ -325,13 +325,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[7], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "trivialPropertyChanged"] #[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."] - pub fn on_trivial_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_trivial_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_trivial_property_changed( self, @@ -354,7 +354,7 @@ mod tests { parse_quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosuretrivialPropertyChanged { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlertrivialPropertyChanged"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -369,7 +369,7 @@ mod tests { parse_quote! { fn call_MyObject_signal_handler_trivialPropertyChanged( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } @@ -433,11 +433,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[14], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "opaquePropertyChanged"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_opaque_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_opaque_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_opaque_property_changed( self, @@ -451,13 +451,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[15], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "opaquePropertyChanged"] #[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."] - pub fn on_opaque_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_opaque_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_opaque_property_changed( self, @@ -480,7 +480,7 @@ mod tests { parse_quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureopaquePropertyChanged { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandleropaquePropertyChanged"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -495,7 +495,7 @@ mod tests { parse_quote! { fn call_MyObject_signal_handler_opaquePropertyChanged( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } @@ -559,11 +559,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[22], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "unsafePropertyChanged"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_unsafe_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_unsafe_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_unsafe_property_changed( self, @@ -577,13 +577,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[23], parse_quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "unsafePropertyChanged"] #[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."] - pub fn on_unsafe_property_changed, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_unsafe_property_changed, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_unsafe_property_changed( self, @@ -606,7 +606,7 @@ mod tests { parse_quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureunsafePropertyChanged { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerunsafePropertyChanged"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -621,7 +621,7 @@ mod tests { parse_quote! { fn call_MyObject_signal_handler_unsafePropertyChanged( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } diff --git a/crates/cxx-qt-gen/src/generator/rust/signals.rs b/crates/cxx-qt-gen/src/generator/rust/signals.rs index a389e2fed..ea98e3186 100644 --- a/crates/cxx-qt-gen/src/generator/rust/signals.rs +++ b/crates/cxx-qt-gen/src/generator/rust/signals.rs @@ -26,7 +26,7 @@ pub fn generate_rust_signal( module_ident: &Ident, ) -> Result { let idents = QSignalName::from(signal); - let idents_helper = QSignalHelperName::new(&idents, qobject_name, type_names); + let idents_helper = QSignalHelperName::new(&idents, qobject_name, type_names)?; let signal_name_cpp = idents.name.cpp; let signal_name_cpp_str = signal_name_cpp.to_string(); @@ -277,7 +277,7 @@ mod tests { let generated = generate_rust_signals( &vec![qsignal], &qobject_idents, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -327,11 +327,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::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."] - pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -345,13 +345,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::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."] - pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -374,7 +374,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureready { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerready"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -389,7 +389,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_ready( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } @@ -441,7 +441,7 @@ mod tests { let generated = generate_rust_signals( &vec![qsignal], &qobject_idents, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -492,11 +492,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "dataChanged"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_data_changed, i32, cxx::UniquePtr) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_data_changed, i32, cxx::UniquePtr) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_data_changed( self, @@ -510,13 +510,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "dataChanged"] #[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."] - pub fn on_data_changed, i32, cxx::UniquePtr) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_data_changed, i32, cxx::UniquePtr) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_data_changed( self, @@ -539,7 +539,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosuredataChanged { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerdataChanged"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, i32, cxx::UniquePtr); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, i32, cxx::UniquePtr); } }, ); @@ -554,7 +554,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_dataChanged( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, trivial: i32, opaque: cxx::UniquePtr ) { @@ -601,7 +601,7 @@ mod tests { let generated = generate_rust_signals( &vec![qsignal], &qobject_idents, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -651,11 +651,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "unsafeSignal"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_unsafe_signal, *mut T) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_unsafe_signal, *mut T) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_unsafe_signal( self, @@ -669,13 +669,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "unsafeSignal"] #[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."] - pub fn on_unsafe_signal, *mut T) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_unsafe_signal, *mut T) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_unsafe_signal( self, @@ -698,7 +698,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureunsafeSignal { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerunsafeSignal"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, *mut T); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, *mut T); } }, ); @@ -713,7 +713,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_unsafeSignal( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, param: *mut T ) { handler.closure()(self_value, param); @@ -757,7 +757,7 @@ mod tests { let generated = generate_rust_signals( &vec![qsignal], &qobject_idents, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -808,11 +808,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "baseName"] #[doc = ", so that when the signal is emitted the function pointer is executed."] - pub fn connect_existing_signal, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_existing_signal, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_existing_signal( self, @@ -826,13 +826,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::MyObject { #[doc = "Connect the given function pointer to the signal "] #[doc = "baseName"] #[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."] - pub fn on_existing_signal, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_existing_signal, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_existing_signal( self, @@ -855,7 +855,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosurebaseName { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerbaseName"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -870,7 +870,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_baseName( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } @@ -911,7 +911,7 @@ mod tests { let generated = generate_rust_signal( &qsignal, &qsignal.qobject_ident, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -960,11 +960,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::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."] - pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -978,13 +978,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::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."] - pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -1007,7 +1007,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureready { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerready"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -1022,7 +1022,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_ready( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } @@ -1063,7 +1063,7 @@ mod tests { let generated = generate_rust_signal( &qsignal, &qsignal.qobject_ident, - &TypeNames::default(), + &TypeNames::mock(), &format_ident!("ffi"), ) .unwrap(); @@ -1104,11 +1104,11 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[0], quote! { - impl MyObject { + impl qobject::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."] - pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard + pub fn connect_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F, conn_type: cxx_qt::ConnectionType) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -1122,13 +1122,13 @@ mod tests { assert_tokens_eq( &generated.cxx_qt_mod_contents[1], quote! { - impl MyObject { + impl qobject::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."] - pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard + pub fn on_ready, ) + 'static>(self: core::pin::Pin<&mut qobject::MyObject>, mut closure: F) -> cxx_qt::QMetaObjectConnectionGuard { cxx_qt::QMetaObjectConnectionGuard::from(ffi::MyObject_connect_ready( self, @@ -1151,7 +1151,7 @@ mod tests { quote! { impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureready { type Id = cxx::type_id!("::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerready"); - type FnType = dyn FnMut(core::pin::Pin<&mut MyObject>, ); + type FnType = dyn FnMut(core::pin::Pin<&mut qobject::MyObject>, ); } }, ); @@ -1166,7 +1166,7 @@ mod tests { quote! { fn call_MyObject_signal_handler_ready( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, - self_value: core::pin::Pin<&mut MyObject>, + self_value: core::pin::Pin<&mut qobject::MyObject>, ) { handler.closure()(self_value, ); } diff --git a/crates/cxx-qt-gen/src/naming/name.rs b/crates/cxx-qt-gen/src/naming/name.rs index 0e30688d8..a806f4938 100644 --- a/crates/cxx-qt-gen/src/naming/name.rs +++ b/crates/cxx-qt-gen/src/naming/name.rs @@ -64,7 +64,7 @@ impl Name { } // Find if there is a cxx_name mapping (for C++ generation) - let cxx_name = attribute_find_path(attrs, &["cxx_name"]) + let mut cxx_name = attribute_find_path(attrs, &["cxx_name"]) .map(|index| -> Result<_> { expr_to_string(&attrs[index].meta.require_name_value()?.value) }) @@ -72,6 +72,11 @@ impl Name { // Find if there is a rust_name mapping let rust_ident = if let Some(index) = attribute_find_path(attrs, &["rust_name"]) { + // If we have a rust_name, but no cxx_name, the original ident is the cxx_name. + if cxx_name.is_none() { + cxx_name = Some(ident.to_string()); + } + format_ident!( "{}", expr_to_string(&attrs[index].meta.require_name_value()?.value)?, diff --git a/crates/cxx-qt-gen/src/naming/type_names.rs b/crates/cxx-qt-gen/src/naming/type_names.rs index 62ac9835d..02a197d8b 100644 --- a/crates/cxx-qt-gen/src/naming/type_names.rs +++ b/crates/cxx-qt-gen/src/naming/type_names.rs @@ -205,20 +205,25 @@ impl TypeNames { } } + fn unknown_type(&self, ident: &Ident) -> syn::Error { + syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!")) + } + /// For a given rust ident return the CXX name **without** its namespace pub fn cxx_unqualified(&self, ident: &Ident) -> Result { if let Some(name) = self.names.get(ident) { Ok(name.cxx.clone().unwrap_or_else(|| ident.to_string())) } else { - Err(syn::Error::new_spanned(ident, "Unknown type!")) + Err(self.unknown_type(ident)) } } /// For a given rust ident return the namespace if it's not empty - pub fn namespace(&self, ident: &Ident) -> Option { + pub fn namespace(&self, ident: &Ident) -> Result> { self.names .get(ident) - .and_then(|name| name.namespace.clone()) + .ok_or_else(|| self.unknown_type(ident)) + .map(|name| name.namespace.clone()) } /// Return a qualified version of the ident that can by used to refer to the type T outside of a CXX bridge @@ -253,6 +258,18 @@ impl TypeNames { self.names.len() } + #[cfg(test)] + // Only for testing, return a TypeNames struct that contains a qobject::MyObject + pub fn mock() -> Self { + use quote::format_ident; + + let mut this = Self { + names: BTreeMap::new(), + }; + this.insert("MyObject", Some(format_ident!("qobject")), None, None); + this + } + #[cfg(test)] // This function only exists for testing, to allow mocking of the type names pub fn insert( @@ -283,6 +300,16 @@ mod tests { use quote::format_ident; use syn::parse_quote; + #[test] + fn test_unknown_type() { + let types = TypeNames::default(); + assert_eq!(types.num_types(), 0); + + assert!(types.cxx_unqualified(&format_ident!("A")).is_err()); + assert!(types.namespace(&format_ident!("A")).is_err()); + // assert!(types.cxx_qualified(&format_ident!("A")).is_err()); + } + #[test] fn test_attribute_none() { let mut types = TypeNames::default(); @@ -294,7 +321,7 @@ mod tests { assert_eq!(types.num_types(), 1); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A }); assert_eq!(types.cxx_qualified(&ident), "A"); - assert!(types.namespace(&ident).is_none()); + assert!(types.namespace(&ident).unwrap().is_none()); } #[test] @@ -312,7 +339,7 @@ mod tests { assert_eq!(types.num_types(), 1); assert_eq!(types.cxx_qualified(&ident), "B"); - assert!(types.namespace(&ident).is_none()); + assert!(types.namespace(&ident).unwrap().is_none()); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A }); } @@ -330,7 +357,10 @@ mod tests { .is_ok()); assert_eq!(types.num_types(), 1); - assert_eq!(types.namespace(&ident), Some("type_namespace".to_owned())); + assert_eq!( + types.namespace(&ident).unwrap(), + Some("type_namespace".to_owned()) + ); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A }); } @@ -348,11 +378,15 @@ mod tests { .is_ok()); assert_eq!(types.num_types(), 1); - assert!(types.namespace(&ident).is_none()); - assert_eq!( - types.rust_qualified(&format_ident!("B")), - parse_quote! { ffi::B } - ); + // The rust_name must be used as the key to the TypeNames struct, otherwise most methods + // return an error. + assert!(types.cxx_unqualified(&ident).is_err()); + assert!(types.namespace(&ident).is_err()); + + let rust_ident = &format_ident!("B"); + assert_eq!(types.rust_qualified(rust_ident), parse_quote! { ffi::B }); + assert_eq!(types.cxx_unqualified(rust_ident).unwrap(), "A"); + assert!(types.namespace(rust_ident).unwrap().is_none()); } #[test] @@ -364,7 +398,10 @@ mod tests { .is_ok()); assert_eq!(types.cxx_qualified(&ident), "bridge_namespace::A"); - assert_eq!(types.namespace(&ident).unwrap(), "bridge_namespace"); + assert_eq!( + types.namespace(&ident).unwrap().unwrap(), + "bridge_namespace" + ); assert_eq!(types.num_types(), 1); assert_eq!( types.rust_qualified(&format_ident!("A")), @@ -380,7 +417,7 @@ mod tests { .populate(&ident, &[], None, &format_ident!("my_module")) .is_ok()); - assert!(types.namespace(&ident).is_none()); + assert!(types.namespace(&ident).unwrap().is_none()); assert_eq!(types.num_types(), 1); assert_eq!(types.rust_qualified(&ident), parse_quote! { my_module::A }); } @@ -452,15 +489,15 @@ mod tests { ); assert_eq!( - types.namespace(&format_ident!("A")).unwrap(), + types.namespace(&format_ident!("A")).unwrap().unwrap(), "type_namespace" ); assert_eq!( - types.namespace(&format_ident!("C")).unwrap(), + types.namespace(&format_ident!("C")).unwrap().unwrap(), "extern_namespace" ); assert_eq!( - types.namespace(&format_ident!("E")).unwrap(), + types.namespace(&format_ident!("E")).unwrap().unwrap(), "bridge_namespace" ); @@ -493,7 +530,10 @@ mod tests { assert_eq!(type_names.num_types(), 1); assert_eq!(type_names.cxx_unqualified(&ident).unwrap(), "EnumB"); - assert_eq!(type_names.namespace(&ident).unwrap(), "enum_namespace"); + assert_eq!( + type_names.namespace(&ident).unwrap().unwrap(), + "enum_namespace" + ); assert_eq!(type_names.cxx_qualified(&ident), "enum_namespace::EnumB"); assert_eq!( type_names.rust_qualified(&ident), @@ -517,7 +557,10 @@ mod tests { assert_eq!(types.num_types(), 1); assert_eq!(types.cxx_unqualified(&ident).unwrap(), "StructB"); assert_eq!(types.cxx_qualified(&ident), "struct_namespace::StructB"); - assert_eq!(types.namespace(&ident).unwrap(), "struct_namespace"); + assert_eq!( + types.namespace(&ident).unwrap().unwrap(), + "struct_namespace" + ); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::StructA }); } } diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index bdd5f06fc..919d3faa3 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -302,6 +302,7 @@ mod tests { parser .type_names .namespace(&format_ident!("MyObjectA")) + .unwrap() .unwrap(), "bridge_namespace" ); @@ -309,6 +310,7 @@ mod tests { parser .type_names .namespace(&format_ident!("MyObjectB")) + .unwrap() .unwrap(), "type_namespace" ); @@ -316,6 +318,7 @@ mod tests { parser .type_names .namespace(&format_ident!("MyObjectC")) + .unwrap() .unwrap(), "extern_namespace" ); From a877c24cc31b0c82425b5b47f1894a642d783333 Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 16:58:20 +0100 Subject: [PATCH 4/6] TypeNames::cxx_qualified(): Error on onknown type --- .../src/generator/cpp/constructor.rs | 18 ++++-- crates/cxx-qt-gen/src/generator/cpp/method.rs | 6 +- .../src/generator/cpp/property/mod.rs | 7 ++- crates/cxx-qt-gen/src/generator/cpp/qenum.rs | 2 +- crates/cxx-qt-gen/src/generator/cpp/signal.rs | 13 ++-- crates/cxx-qt-gen/src/naming/cpp.rs | 37 ++++++----- crates/cxx-qt-gen/src/naming/name.rs | 8 +++ crates/cxx-qt-gen/src/naming/type_names.rs | 62 ++++++++++--------- crates/cxx-qt-gen/test_inputs/invokables.rs | 5 ++ crates/cxx-qt-gen/test_inputs/signals.rs | 2 + crates/cxx-qt-gen/test_outputs/invokables.rs | 18 ++++-- crates/cxx-qt-gen/test_outputs/signals.rs | 17 ++--- 12 files changed, 119 insertions(+), 76 deletions(-) diff --git a/crates/cxx-qt-gen/src/generator/cpp/constructor.rs b/crates/cxx-qt-gen/src/generator/cpp/constructor.rs index 8b112ecee..556936727 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/constructor.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/constructor.rs @@ -162,6 +162,12 @@ mod tests { use syn::parse_quote; + fn type_names_with_qobject() -> TypeNames { + let mut type_names = TypeNames::mock(); + type_names.insert("QObject", None, None, None); + type_names + } + fn qobject_for_testing() -> GeneratedCppQObject { GeneratedCppQObject { ident: "MyObject".to_string(), @@ -197,7 +203,7 @@ mod tests { &[], "BaseClass".to_owned(), &["member1(1)".to_string(), "member2{ 2 }".to_string()], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); @@ -227,7 +233,7 @@ mod tests { &[], "BaseClass".to_owned(), &[], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); @@ -258,7 +264,7 @@ mod tests { &[], "BaseClass".to_owned(), &[], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); @@ -290,7 +296,7 @@ mod tests { }], "BaseClass".to_owned(), &[], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); @@ -340,7 +346,7 @@ mod tests { }], "BaseClass".to_owned(), &["initializer".to_string()], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); @@ -394,7 +400,7 @@ mod tests { ], "BaseClass".to_owned(), &["initializer".to_string()], - &TypeNames::default(), + &type_names_with_qobject(), ) .unwrap(); diff --git a/crates/cxx-qt-gen/src/generator/cpp/method.rs b/crates/cxx-qt-gen/src/generator/cpp/method.rs index b869d703e..9dbef1294 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/method.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/method.rs @@ -228,8 +228,10 @@ mod tests { ]; let qobject_idents = create_qobjectname(); - let generated = - generate_cpp_methods(&invokables, &qobject_idents, &TypeNames::default()).unwrap(); + let mut type_names = TypeNames::mock(); + type_names.insert("QColor", None, None, None); + + let generated = generate_cpp_methods(&invokables, &qobject_idents, &type_names).unwrap(); // methods assert_eq!(generated.methods.len(), 5); diff --git a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs index 0a93ee361..44856f476 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs @@ -81,8 +81,9 @@ mod tests { ]; let qobject_idents = create_qobjectname(); - let generated = - generate_cpp_properties(&properties, &qobject_idents, &TypeNames::mock()).unwrap(); + let mut type_names = TypeNames::mock(); + type_names.insert("QColor", None, None, None); + let generated = generate_cpp_properties(&properties, &qobject_idents, &type_names).unwrap(); // metaobjects assert_eq!(generated.metaobjects.len(), 2); @@ -356,7 +357,7 @@ mod tests { fn test_generate_cpp_properties_mapped_cxx_name() { let properties = vec![ParsedQProperty { ident: format_ident!("mapped_property"), - ty: parse_quote! { A1 }, + ty: parse_quote! { A }, }]; let qobject_idents = create_qobjectname(); diff --git a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs index 408706f8f..ace055eda 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs @@ -53,7 +53,7 @@ pub fn generate( let mut generated = GeneratedCppQObjectBlocks::default(); for qenum in qenums { - let mut qualified_name = type_names.cxx_qualified(&qenum.ident); + let mut qualified_name = type_names.cxx_qualified(&qenum.ident)?; let enum_name = type_names.cxx_unqualified(&qenum.ident)?; // TODO: this is a workaround for type_names.cxx_qualified not always returning a fully-qualified // identifier. diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index 3a846b0c2..a0ffef007 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -66,7 +66,7 @@ fn parameter_types_and_values( let parameter_named_types = parameter_named_types_with_self.join(", "); // Insert the extra argument into the closure - let self_ty = type_names.cxx_qualified(self_ty); + let self_ty = type_names.cxx_qualified(self_ty)?; parameter_named_types_with_self.insert(0, format!("{self_ty}& self")); parameter_types_with_self.insert(0, format!("{self_ty}&")); parameter_values_with_self.insert(0, "self".to_owned()); @@ -92,7 +92,7 @@ pub fn generate_cpp_signal( .insert("#include ".to_owned()); // Build a namespace that includes any namespace for the T - let qobject_ident_namespaced = type_names.cxx_qualified(qobject_ident); + let qobject_ident_namespaced = type_names.cxx_qualified(qobject_ident)?; // Prepare the idents let idents = QSignalName::from(signal); @@ -250,8 +250,9 @@ mod tests { }]; let qobject_idents = create_qobjectname(); - let generated = - generate_cpp_signals(&signals, &qobject_idents, &TypeNames::mock()).unwrap(); + let mut type_names = TypeNames::mock(); + type_names.insert("QColor", None, None, None); + let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names).unwrap(); assert_eq!(generated.methods.len(), 1); let header = if let CppFragment::Header(header) = &generated.methods[0] { @@ -331,13 +332,13 @@ mod tests { fn test_generate_cpp_signals_mapped_cxx_name() { let signals = vec![ParsedSignal { method: parse_quote! { - fn data_changed(self: Pin<&mut MyObject>, mapped: A1); + fn data_changed(self: Pin<&mut MyObject>, mapped: A); }, qobject_ident: format_ident!("MyObject"), mutable: true, parameters: vec![ParsedFunctionParameter { ident: format_ident!("mapped"), - ty: parse_quote! { A1 }, + ty: parse_quote! { A }, }], ident: CombinedIdent { cpp: format_ident!("dataChanged"), diff --git a/crates/cxx-qt-gen/src/naming/cpp.rs b/crates/cxx-qt-gen/src/naming/cpp.rs index 65f172878..ba9f5c130 100644 --- a/crates/cxx-qt-gen/src/naming/cpp.rs +++ b/crates/cxx-qt-gen/src/naming/cpp.rs @@ -209,7 +209,7 @@ fn path_segment_to_string(segment: &PathSegment, type_names: &TypeNames) -> Resu let ident_string = ident.to_string(); // If we are a Pin then for C++ it becomes just T - let args = match &*ident_string { + let arg = match &*ident_string { "Pin" => { let mut args = path_argument_to_string(&segment.arguments, type_names)?.unwrap_or_else(Vec::new); @@ -231,25 +231,27 @@ fn path_segment_to_string(segment: &PathSegment, type_names: &TypeNames) -> Resu }; // If there are template args check that its a supported template type. - let ident = if args.is_some() { + if let Some(arg) = arg { // A built in template base cannot have a cxx_name or a namespace - if let Some(built_in) = possible_built_in_template_base(&ident_string) { - built_in.to_owned() + if let Some(ident) = possible_built_in_template_base(&ident_string) { + Ok(format!("{ident}<{arg}>")) } else { - return Err(Error::new_spanned( + Err(Error::new_spanned( ident, format!("Unsupported template base: {ident}"), - )); + )) } } else { - type_names.cxx_qualified(&segment.ident) - }; - - Ok(format!( - "{ident}{args}", - ident = ident, - args = args.map_or_else(|| "".to_owned(), |arg| format!("<{arg}>")) - )) + // Currently, type_names only includes the names of types that are declared within the bridge. + // Some types are built-in and available without declaration though. + // Check whether its one of those, as otherwise the call to `cxx_qualified` will result in + // an "unknown type" error. + if let Some(built_in) = possible_built_in(&segment.ident.to_string()) { + Ok(built_in.to_owned()) + } else { + type_names.cxx_qualified(&segment.ident) + } + } } /// Convert any built in types to known C++ equivalents @@ -322,9 +324,14 @@ mod tests { macro_rules! test_syn_types_to_cpp_types { [$($input_type:tt => $output_type:literal),*] => { + let mut type_names = TypeNames::default(); + // Add some types to the list of available types so we can use them in tests. + type_names.insert("T", None, None, None); + type_names.insert("QColor", None, None, None); + type_names.insert("QPoint", None, None, None); $( assert_eq!( - syn_type_to_cpp_type(&parse_quote! $input_type, &TypeNames::default()).unwrap(), + syn_type_to_cpp_type(&parse_quote! $input_type, &type_names).unwrap(), $output_type); )* } diff --git a/crates/cxx-qt-gen/src/naming/name.rs b/crates/cxx-qt-gen/src/naming/name.rs index a806f4938..7d20cc86b 100644 --- a/crates/cxx-qt-gen/src/naming/name.rs +++ b/crates/cxx-qt-gen/src/naming/name.rs @@ -93,4 +93,12 @@ impl Name { module: module.clone(), }) } + + /// Get the unqualified name of the type in C++. + /// This is either: + /// - The cxx_name attribute value, if one is provided + /// - The original ident, if no cxx_name was provided + pub(super) fn cxx_unqualified(&self) -> String { + self.cxx.clone().unwrap_or_else(|| self.rust.to_string()) + } } diff --git a/crates/cxx-qt-gen/src/naming/type_names.rs b/crates/cxx-qt-gen/src/naming/type_names.rs index 02a197d8b..6a35d4058 100644 --- a/crates/cxx-qt-gen/src/naming/type_names.rs +++ b/crates/cxx-qt-gen/src/naming/type_names.rs @@ -177,6 +177,10 @@ impl TypeNames { Ok(()) } + fn unknown_type(&self, ident: &Ident) -> syn::Error { + syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!")) + } + /// For a given rust ident return the CXX name with its namespace /// /// Ideally we'd want this type name to always be **fully** qualified, starting with `::`. @@ -187,35 +191,28 @@ impl TypeNames { /// This needs to be considered in many places (properties, signals, invokables, etc.) /// Therefore, for now we'll use the qualified, but not fully qualified version of `namespace::type`. /// This should work in most cases, but it's not perfect. - pub fn cxx_qualified(&self, ident: &Ident) -> String { + pub fn cxx_qualified(&self, ident: &Ident) -> Result { // Check if there is a cxx_name or namespace to handle - let name = self.names.get(ident); - - if name.is_none() { - return ident.to_string(); - } - let name = name.unwrap(); + let name = self + .names + .get(ident) + .ok_or_else(|| self.unknown_type(ident))?; - let cxx_name = name.cxx.clone().unwrap_or_else(|| name.rust.to_string()); + let cxx_name = name.cxx_unqualified(); if let Some(namespace) = &name.namespace { - format!("{namespace}::{cxx_name}") + Ok(format!("{namespace}::{cxx_name}")) } else { - cxx_name + Ok(cxx_name) } } - fn unknown_type(&self, ident: &Ident) -> syn::Error { - syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!")) - } - /// For a given rust ident return the CXX name **without** its namespace pub fn cxx_unqualified(&self, ident: &Ident) -> Result { - if let Some(name) = self.names.get(ident) { - Ok(name.cxx.clone().unwrap_or_else(|| ident.to_string())) - } else { - Err(self.unknown_type(ident)) - } + self.names + .get(ident) + .ok_or_else(|| self.unknown_type(ident)) + .map(Name::cxx_unqualified) } /// For a given rust ident return the namespace if it's not empty @@ -249,6 +246,7 @@ impl TypeNames { module_ident: &Ident, ) -> Result<()> { let name = Name::from_ident_and_attrs(ident, attrs, parent_namespace, module_ident)?; + // TODO: Check for duplicates self.names.insert(name.rust.clone(), name); Ok(()) } @@ -306,8 +304,8 @@ mod tests { assert_eq!(types.num_types(), 0); assert!(types.cxx_unqualified(&format_ident!("A")).is_err()); + assert!(types.cxx_qualified(&format_ident!("A")).is_err()); assert!(types.namespace(&format_ident!("A")).is_err()); - // assert!(types.cxx_qualified(&format_ident!("A")).is_err()); } #[test] @@ -320,7 +318,7 @@ mod tests { assert_eq!(types.num_types(), 1); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A }); - assert_eq!(types.cxx_qualified(&ident), "A"); + assert_eq!(types.cxx_qualified(&ident).unwrap(), "A"); assert!(types.namespace(&ident).unwrap().is_none()); } @@ -338,7 +336,7 @@ mod tests { .is_ok()); assert_eq!(types.num_types(), 1); - assert_eq!(types.cxx_qualified(&ident), "B"); + assert_eq!(types.cxx_qualified(&ident).unwrap(), "B"); assert!(types.namespace(&ident).unwrap().is_none()); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A }); } @@ -397,7 +395,7 @@ mod tests { .populate(&ident, &[], Some("bridge_namespace"), &format_ident!("ffi")) .is_ok()); - assert_eq!(types.cxx_qualified(&ident), "bridge_namespace::A"); + assert_eq!(types.cxx_qualified(&ident).unwrap(), "bridge_namespace::A"); assert_eq!( types.namespace(&ident).unwrap().unwrap(), "bridge_namespace" @@ -443,7 +441,7 @@ mod tests { let type_names = parse_cxx_item(item); let ident = format_ident!("A"); assert_eq!(type_names.num_types(), 1); - assert_eq!(type_names.cxx_qualified(&ident), "B"); + assert_eq!(type_names.cxx_qualified(&ident).unwrap(), "B"); assert_eq!(type_names.rust_qualified(&ident), parse_quote! { ffi::A }); } @@ -476,15 +474,15 @@ mod tests { assert_eq!(types.num_types(), 3); assert_eq!( - &types.cxx_qualified(&format_ident!("A")), + types.cxx_qualified(&format_ident!("A")).unwrap(), "type_namespace::B" ); assert_eq!( - &types.cxx_qualified(&format_ident!("C")), + types.cxx_qualified(&format_ident!("C")).unwrap(), "extern_namespace::D" ); assert_eq!( - &types.cxx_qualified(&format_ident!("E")), + types.cxx_qualified(&format_ident!("E")).unwrap(), "bridge_namespace::E" ); @@ -534,7 +532,10 @@ mod tests { type_names.namespace(&ident).unwrap().unwrap(), "enum_namespace" ); - assert_eq!(type_names.cxx_qualified(&ident), "enum_namespace::EnumB"); + assert_eq!( + type_names.cxx_qualified(&ident).unwrap(), + "enum_namespace::EnumB" + ); assert_eq!( type_names.rust_qualified(&ident), parse_quote! { ffi::EnumA } @@ -556,7 +557,10 @@ mod tests { assert_eq!(types.num_types(), 1); assert_eq!(types.cxx_unqualified(&ident).unwrap(), "StructB"); - assert_eq!(types.cxx_qualified(&ident), "struct_namespace::StructB"); + assert_eq!( + types.cxx_qualified(&ident).unwrap(), + "struct_namespace::StructB" + ); assert_eq!( types.namespace(&ident).unwrap().unwrap(), "struct_namespace" diff --git a/crates/cxx-qt-gen/test_inputs/invokables.rs b/crates/cxx-qt-gen/test_inputs/invokables.rs index bbbe69475..17f5e1e92 100644 --- a/crates/cxx-qt-gen/test_inputs/invokables.rs +++ b/crates/cxx-qt-gen/test_inputs/invokables.rs @@ -6,8 +6,13 @@ mod ffi { type QColor = cxx_qt_lib::QColor; include!("cxx-qt-lib/qpoint.h"); type QPoint = cxx_qt_lib::QPoint; + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + include!(); type QObject; + + type Opaque; } unsafe extern "RustQt" { diff --git a/crates/cxx-qt-gen/test_inputs/signals.rs b/crates/cxx-qt-gen/test_inputs/signals.rs index e045121dc..04209f490 100644 --- a/crates/cxx-qt-gen/test_inputs/signals.rs +++ b/crates/cxx-qt-gen/test_inputs/signals.rs @@ -4,6 +4,8 @@ mod ffi { unsafe extern "C++" { include!("cxx-qt-lib/qpoint.h"); type QPoint = cxx_qt_lib::QPoint; + + type Opaque; } unsafe extern "C++Qt" { diff --git a/crates/cxx-qt-gen/test_outputs/invokables.rs b/crates/cxx-qt-gen/test_outputs/invokables.rs index f829a95ad..4d43f4ee4 100644 --- a/crates/cxx-qt-gen/test_outputs/invokables.rs +++ b/crates/cxx-qt-gen/test_outputs/invokables.rs @@ -6,8 +6,11 @@ mod ffi { type QColor = cxx_qt_lib::QColor; include!("cxx-qt-lib/qpoint.h"); type QPoint = cxx_qt_lib::QPoint; + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; include ! (< QtCore / QObject >); type QObject; + type Opaque; } unsafe extern "C++" { include ! (< QtCore / QObject >); @@ -267,12 +270,14 @@ impl cxx_qt::Locking for ffi::MyObject {} #[doc(hidden)] pub fn route_arguments_my_object_0<'a>( arg0: i32, - arg1: &'a QString, + arg1: &'a ffi::QString, ) -> ffi::CxxQtConstructorArgumentsMyObject0<'a> { #[allow(unused_variables)] #[allow(clippy::let_unit_value)] let (new_arguments, base_arguments, initialize_arguments) = - >::route_arguments((arg0, arg1)); + >::route_arguments(( + arg0, arg1, + )); ffi::CxxQtConstructorArgumentsMyObject0 { base: ffi::CxxQtConstructorBaseArgumentsMyObject0 { arg0: base_arguments.0, @@ -289,9 +294,10 @@ pub fn route_arguments_my_object_0<'a>( pub fn new_rs_my_object_0<'a>( new_arguments: ffi::CxxQtConstructorNewArgumentsMyObject0<'a>, ) -> std::boxed::Box { - std::boxed::Box::new( - >::new((new_arguments.arg0,)), - ) + std::boxed::Box::new(>::new((new_arguments.arg0,))) } #[doc(hidden)] #[allow(unused_variables)] @@ -300,7 +306,7 @@ pub fn initialize_my_object_0<'a>( qobject: core::pin::Pin<&mut ffi::MyObject>, initialize_arguments: ffi::CxxQtConstructorInitializeArgumentsMyObject0, ) { - >::initialize(qobject, ()); + >::initialize(qobject, ()); } #[doc(hidden)] pub fn route_arguments_my_object_1() -> ffi::CxxQtConstructorArgumentsMyObject1 { diff --git a/crates/cxx-qt-gen/test_outputs/signals.rs b/crates/cxx-qt-gen/test_outputs/signals.rs index 338345a92..113e0712e 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.rs +++ b/crates/cxx-qt-gen/test_outputs/signals.rs @@ -4,6 +4,7 @@ mod ffi { unsafe extern "C++" { include!("cxx-qt-lib/qpoint.h"); type QPoint = cxx_qt_lib::QPoint; + type Opaque; } unsafe extern "C++" { include ! (< QtCore / QObject >); @@ -258,7 +259,7 @@ impl ffi::MyObject { F: FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &ffi::QPoint, ) + 'static, @@ -286,7 +287,7 @@ impl ffi::MyObject { F: FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &ffi::QPoint, ) + 'static, @@ -312,7 +313,7 @@ impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClo type FnType = dyn FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &ffi::QPoint, ); @@ -322,7 +323,7 @@ fn call_MyObject_signal_handler_dataChanged( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, self_value: core::pin::Pin<&mut ffi::MyObject>, first: i32, - second: cxx::UniquePtr, + second: cxx::UniquePtr, third: ffi::QPoint, fourth: &ffi::QPoint, ) { @@ -344,7 +345,7 @@ impl ffi::MyObject { F: FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &'a ffi::QPoint, ) + 'static, @@ -372,7 +373,7 @@ impl ffi::MyObject { F: FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &'a ffi::QPoint, ) + 'static, @@ -397,7 +398,7 @@ impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClo type FnType = dyn FnMut( core::pin::Pin<&mut ffi::MyObject>, i32, - cxx::UniquePtr, + cxx::UniquePtr, ffi::QPoint, &'a ffi::QPoint, ); @@ -407,7 +408,7 @@ fn call_MyObject_signal_handler_newData( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, self_value: core::pin::Pin<&mut ffi::MyObject>, first: i32, - second: cxx::UniquePtr, + second: cxx::UniquePtr, third: ffi::QPoint, fourth: &'a ffi::QPoint, ) { From dfb5675425560ea26a1e422bb9a0ab8669a3228d Mon Sep 17 00:00:00 2001 From: Leon Matthes Date: Wed, 13 Mar 2024 17:08:18 +0100 Subject: [PATCH 5/6] Disallow duplicate type names This would be ambiguous otherwise --- crates/cxx-qt-gen/src/naming/type_names.rs | 46 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/crates/cxx-qt-gen/src/naming/type_names.rs b/crates/cxx-qt-gen/src/naming/type_names.rs index 6a35d4058..f1ab9a5b5 100644 --- a/crates/cxx-qt-gen/src/naming/type_names.rs +++ b/crates/cxx-qt-gen/src/naming/type_names.rs @@ -3,10 +3,10 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::collections::BTreeMap; +use std::collections::{btree_map::Entry, BTreeMap}; use syn::{ - token::Brace, Attribute, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Path, Result, + token::Brace, Attribute, Error, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Path, Result, }; use crate::syntax::{ @@ -177,8 +177,8 @@ impl TypeNames { Ok(()) } - fn unknown_type(&self, ident: &Ident) -> syn::Error { - syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!")) + fn unknown_type(&self, ident: &Ident) -> Error { + Error::new_spanned(ident, format!("Undeclared type: `{ident}`!")) } /// For a given rust ident return the CXX name with its namespace @@ -246,9 +246,19 @@ impl TypeNames { module_ident: &Ident, ) -> Result<()> { let name = Name::from_ident_and_attrs(ident, attrs, parent_namespace, module_ident)?; - // TODO: Check for duplicates - self.names.insert(name.rust.clone(), name); - Ok(()) + + let entry = self.names.entry(name.rust.clone()); + + match entry { + Entry::Occupied(_) => Err(Error::new_spanned( + ident, + format!("The type name `{ident}` is defined multiple times"), + )), + Entry::Vacant(entry) => { + entry.insert(name); + Ok(()) + } + } } #[cfg(test)] @@ -567,4 +577,26 @@ mod tests { ); assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::StructA }); } + + #[test] + fn test_duplicate_types() { + let items = [ + parse_quote! { + extern "C++" { + #[rust_name="B"] + type A; + } + }, + parse_quote! { + extern "Rust" { + type B; + } + }, + ]; + + let mut types = TypeNames::default(); + assert!(types + .populate_from_cxx_items(&items, None, &format_ident!("ffi")) + .is_err()); + } } From 4e8c0ff412a5f65998b79eada1ef73c1080f6a3e Mon Sep 17 00:00:00 2001 From: Laurent Montel Date: Fri, 15 Mar 2024 09:39:27 +0100 Subject: [PATCH 6/6] Add Display for QPolygon/QPolygonF --- crates/cxx-qt-lib/src/gui/qpolygon.rs | 12 ++++++++++++ crates/cxx-qt-lib/src/gui/qpolygonf.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/crates/cxx-qt-lib/src/gui/qpolygon.rs b/crates/cxx-qt-lib/src/gui/qpolygon.rs index 8a6b21d64..1e3442312 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygon.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygon.rs @@ -19,6 +19,8 @@ mod ffi { type QPoint = crate::QPoint; include!("cxx-qt-lib/qrect.h"); type QRect = crate::QRect; + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; include!("cxx-qt-lib/qpolygon.h"); type QPolygon = super::QPolygon; @@ -81,6 +83,10 @@ mod ffi { #[doc(hidden)] #[rust_name = "qpolygon_eq"] fn operatorEq(a: &QPolygon, b: &QPolygon) -> bool; + + #[doc(hidden)] + #[rust_name = "qpolygon_to_qstring"] + fn toQString(value: &QPolygon) -> QString; } } @@ -131,6 +137,12 @@ impl PartialEq for QPolygon { } } +impl std::fmt::Display for QPolygon { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", ffi::qpolygon_to_qstring(self)) + } +} + impl Eq for QPolygon {} // Safety: diff --git a/crates/cxx-qt-lib/src/gui/qpolygonf.rs b/crates/cxx-qt-lib/src/gui/qpolygonf.rs index f67a67fac..4d0b33ced 100644 --- a/crates/cxx-qt-lib/src/gui/qpolygonf.rs +++ b/crates/cxx-qt-lib/src/gui/qpolygonf.rs @@ -20,6 +20,8 @@ mod ffi { type QRectF = crate::QRectF; include!("cxx-qt-lib/qpolygon.h"); type QPolygon = crate::QPolygon; + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; include!("cxx-qt-lib/qpolygonf.h"); type QPolygonF = super::QPolygonF; @@ -79,6 +81,10 @@ mod ffi { #[doc(hidden)] #[rust_name = "qpolygonf_eq"] fn operatorEq(a: &QPolygonF, b: &QPolygonF) -> bool; + + #[doc(hidden)] + #[rust_name = "qpolygonf_to_qstring"] + fn toQString(value: &QPolygonF) -> QString; } } @@ -120,6 +126,12 @@ impl PartialEq for QPolygonF { } } +impl std::fmt::Display for QPolygonF { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", ffi::qpolygonf_to_qstring(self)) + } +} + impl Eq for QPolygonF {} // Safety: