diff --git a/book/src/getting-started/2-our-first-cxx-qt-module.md b/book/src/getting-started/2-our-first-cxx-qt-module.md index 549c5523a..76eb13251 100644 --- a/book/src/getting-started/2-our-first-cxx-qt-module.md +++ b/book/src/getting-started/2-our-first-cxx-qt-module.md @@ -143,6 +143,13 @@ These functions then need to be implemented **outside** the bridge using `impl q {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_invokable_impl}} ``` +### Inlining the self receiver + +If an `extern "RustQt"` block contains exactly one `QObject`, the self type of methods can be inferred. +For instance, in a block with multiple or no `QObject`s, a function like `foo(&self)` or `foo(self: Pin<&mut Self>)` +would not compile, but will compile with the `Self` type set to that blocks `QObject`. +This is how CXX [handles it](https://cxx.rs/extern-rust.html) (see the Methods heading). + This setup is a bit unusual, as the type `qobject::MyObject` is actually defined in C++. However, it is still possible to add member functions to it in Rust and then expose them back to C++. This is the usual workflow for `QObject`s in CXX-Qt. diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 4cc23ce37..0aabb8765 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -43,8 +43,8 @@ use std::{ }; use cxx_qt_gen::{ - parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks, GeneratedOpt, - GeneratedRustBlocks, Parser, + parse_qt_file, qualify_self_types, write_cpp, write_rust, CppFragment, CxxQtItem, + GeneratedCppBlocks, GeneratedOpt, GeneratedRustBlocks, Parser, }; // TODO: we need to eventually support having multiple modules defined in a single file. This @@ -132,7 +132,10 @@ impl GeneratedCpp { } found_bridge = true; - let parser = Parser::from(m.clone()) + let mut parser = Parser::from(m.clone()) + .map_err(GeneratedError::from) + .map_err(to_diagnostic)?; + qualify_self_types(&mut parser) .map_err(GeneratedError::from) .map_err(to_diagnostic)?; let generated_cpp = GeneratedCppBlocks::from(&parser, &cxx_qt_opt) diff --git a/crates/cxx-qt-gen/src/generator/rust/mod.rs b/crates/cxx-qt-gen/src/generator/rust/mod.rs index dae4216bd..45213f0fe 100644 --- a/crates/cxx-qt-gen/src/generator/rust/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/mod.rs @@ -103,8 +103,7 @@ impl GeneratedRustBlocks { // Generate a type declaration for `QObject` if necessary fn add_qobject_import(cxx_qt_data: &ParsedCxxQtData) -> Option { let includes = cxx_qt_data - .qobjects - .iter() + .qobjects() .any(|obj| obj.has_qobject_macro && obj.base_class.is_none()); if includes || cxx_qt_data diff --git a/crates/cxx-qt-gen/src/generator/structuring/mod.rs b/crates/cxx-qt-gen/src/generator/structuring/mod.rs index 18313697a..ed45cdc5b 100644 --- a/crates/cxx-qt-gen/src/generator/structuring/mod.rs +++ b/crates/cxx-qt-gen/src/generator/structuring/mod.rs @@ -79,8 +79,7 @@ impl<'a> Structures<'a> { /// Returns an error, if any references could not be resolved. pub fn new(cxxqtdata: &'a ParsedCxxQtData) -> Result { let mut qobjects: Vec<_> = cxxqtdata - .qobjects - .iter() + .qobjects() .map(StructuredQObject::from_qobject) .collect(); @@ -92,19 +91,19 @@ impl<'a> Structures<'a> { } // Associate each method parsed with its appropriate qobject - for method in &cxxqtdata.methods { + for method in cxxqtdata.methods() { let qobject = find_qobject(&mut qobjects, &method.qobject_ident)?; qobject.methods.push(method); } // Associate each inherited method parsed with its appropriate qobject - for inherited_method in &cxxqtdata.inherited_methods { + for inherited_method in cxxqtdata.inherited_methods() { let qobject = find_qobject(&mut qobjects, &inherited_method.qobject_ident)?; qobject.inherited_methods.push(inherited_method); } // Associate each signal parsed with its appropriate qobject - for signal in &cxxqtdata.signals { + for signal in cxxqtdata.signals() { let qobject = find_qobject(&mut qobjects, &signal.qobject_ident)?; qobject.signals.push(signal); } diff --git a/crates/cxx-qt-gen/src/lib.rs b/crates/cxx-qt-gen/src/lib.rs index 89e83e6b9..6d0631a2f 100644 --- a/crates/cxx-qt-gen/src/lib.rs +++ b/crates/cxx-qt-gen/src/lib.rs @@ -11,6 +11,7 @@ mod generator; mod naming; mod parser; +mod preprocessor; mod syntax; mod writer; @@ -20,6 +21,7 @@ pub use generator::{ GeneratedOpt, }; pub use parser::Parser; +pub use preprocessor::self_inlining::qualify_self_types; pub use syntax::{parse_qt_file, CxxQtFile, CxxQtItem}; pub use writer::{cpp::write_cpp, rust::write_rust}; @@ -174,7 +176,8 @@ mod tests { expected_cpp_header: &str, expected_cpp_source: &str, ) { - let parser = Parser::from(syn::parse_str(input).unwrap()).unwrap(); + let mut parser = Parser::from(syn::parse_str(input).unwrap()).unwrap(); + qualify_self_types(&mut parser).unwrap(); let mut cfg_evaluator = CfgEvaluatorTest::default(); cfg_evaluator.cfgs.insert("crate", Some("cxx-qt-gen")); diff --git a/crates/cxx-qt-gen/src/naming/name.rs b/crates/cxx-qt-gen/src/naming/name.rs index fac11ec27..ef4bbb6a0 100644 --- a/crates/cxx-qt-gen/src/naming/name.rs +++ b/crates/cxx-qt-gen/src/naming/name.rs @@ -17,7 +17,7 @@ use syn::{spanned::Spanned, Attribute, Error, Ident, Path, Result}; /// Naming in CXX can be rather complex. /// The following Rules apply: /// - If only a cxx_name **or** a rust_name is given, the identifier of the type/function will be -/// used for part that wasn't specified explicitly. +/// used for part that wasn't specified explicitly. /// - If **both** attributes are present, the identifier itself is not used! /// - The `rust_name` is always used to refer to the type within the bridge!. #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/cxx-qt-gen/src/naming/type_names.rs b/crates/cxx-qt-gen/src/naming/type_names.rs index d0f885811..5a4630896 100644 --- a/crates/cxx-qt-gen/src/naming/type_names.rs +++ b/crates/cxx-qt-gen/src/naming/type_names.rs @@ -161,7 +161,7 @@ impl TypeNames { module_ident: &Ident, ) -> Result<()> { // Find and register the QObjects in the bridge - for qobject in cxx_qt_data.qobjects.iter() { + for qobject in cxx_qt_data.qobjects() { self.populate_qobject(qobject)?; } diff --git a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs index ebb572f5a..388aab328 100644 --- a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs +++ b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs @@ -23,19 +23,16 @@ use syn::{ }; pub struct ParsedCxxQtData { - /// Map of the QObjects defined in the module that will be used for code generation - // - // We have to use a BTreeMap here, instead of a HashMap, to keep the order of QObjects stable. - // Otherwise, the output order would be different, depending on the environment, which makes it hard to test/debug. - pub qobjects: Vec, + /// List of QObjects defined in the module, separated by block + pub qobjects: Vec>, /// List of QEnums defined in the module, that aren't associated with a QObject pub qenums: Vec, - /// List of methods and Q_INVOKABLES found - pub methods: Vec, - /// List of the Q_SIGNALS found - pub signals: Vec, - /// List of the inherited methods found - pub inherited_methods: Vec, + /// List of methods and Q_INVOKABLES found, separated by block + pub methods: Vec>, + /// List of the Q_SIGNALS found, separated by block + pub signals: Vec>, + /// List of the inherited methods found, separated by block + pub inherited_methods: Vec>, /// List of QNamespace declarations pub qnamespaces: Vec, /// Blocks of extern "C++Qt" @@ -48,6 +45,11 @@ pub struct ParsedCxxQtData { pub module_ident: Ident, } +/// Used to get a flat iterator view into a 2D Vector +fn flat_view(v: &[Vec]) -> impl Iterator { + v.iter().flat_map(|v| v.iter()) +} + impl ParsedCxxQtData { /// Create a ParsedCxxQtData from a given module and namespace pub fn new(module_ident: Ident, namespace: Option) -> Self { @@ -65,6 +67,26 @@ impl ParsedCxxQtData { } } + /// Iterator wrapper for methods + pub fn methods(&self) -> impl Iterator { + flat_view(&self.methods) + } + + /// Iterator wrapper for signals + pub fn signals(&self) -> impl Iterator { + flat_view(&self.signals) + } + + /// Iterator wrapper for inherited methods + pub fn inherited_methods(&self) -> impl Iterator { + flat_view(&self.inherited_methods) + } + + /// Iterator wrapper for QObjects + pub fn qobjects(&self) -> impl Iterator { + flat_view(&self.qobjects) + } + /// Determine if the given [syn::Item] is a CXX-Qt related item /// If it is then add the [syn::Item] into qobjects BTreeMap /// Otherwise return the [syn::Item] to pass through to CXX @@ -139,6 +161,12 @@ impl ParsedCxxQtData { let auto_case = CaseConversion::from_attrs(&attrs)?; + let mut qobjects = vec![]; + + let mut methods = vec![]; + let mut signals = vec![]; + let mut inherited = vec![]; + let namespace = attrs .get("namespace") .map(|attr| expr_to_string(&attr.meta.require_name_value()?.value)) @@ -159,7 +187,7 @@ impl ParsedCxxQtData { return Err(Error::new(foreign_fn.span(), "block must be declared `unsafe extern \"RustQt\"` if it contains any safe-to-call #[inherit] qsignals")); } - self.signals.push(parsed_signal_method); + signals.push(parsed_signal_method); // Test if the function is an inheritance method // @@ -175,7 +203,7 @@ impl ParsedCxxQtData { let parsed_inherited_method = ParsedInheritedMethod::parse(foreign_fn, auto_case)?; - self.inherited_methods.push(parsed_inherited_method); + inherited.push(parsed_inherited_method); // Remaining methods are either C++ methods or invokables } else { let parsed_method = ParsedMethod::parse( @@ -183,7 +211,7 @@ impl ParsedCxxQtData { auto_case, foreign_mod.unsafety.is_some(), )?; - self.methods.push(parsed_method); + methods.push(parsed_method); } } ForeignItem::Verbatim(tokens) => { @@ -199,12 +227,18 @@ impl ParsedCxxQtData { // Note that we assume a compiler error will occur later // if you had two structs with the same name - self.qobjects.push(qobject); + qobjects.push(qobject); } - // Const Macro, Type are unsupported in extern "RustQt" for now + // Const, Macro, Type are unsupported in extern "RustQt" for now _ => return Err(err_unsupported_item(&item)), } } + + self.qobjects.push(qobjects); + self.methods.push(methods); + self.signals.push(signals); + self.inherited_methods.push(inherited); + Ok(()) } @@ -223,8 +257,7 @@ impl ParsedCxxQtData { #[cfg(test)] fn find_object(&self, id: &Ident) -> Option<&ParsedQObject> { - self.qobjects - .iter() + self.qobjects() .find(|obj| obj.name.rust_unqualified() == id) } } @@ -241,8 +274,9 @@ mod tests { /// Creates a ParsedCxxQtData with a QObject definition already found pub fn create_parsed_cxx_qt_data() -> ParsedCxxQtData { let mut cxx_qt_data = ParsedCxxQtData::new(format_ident!("ffi"), None); - cxx_qt_data.qobjects.push(create_parsed_qobject()); - cxx_qt_data.qobjects.push(create_parsed_qobject()); + cxx_qt_data + .qobjects + .push(vec![create_parsed_qobject(), create_parsed_qobject()]); cxx_qt_data } @@ -352,9 +386,9 @@ mod tests { let result = cxx_qt_data.parse_cxx_qt_item(item).unwrap(); assert!(result.is_none()); - assert_eq!(cxx_qt_data.methods.len(), 2); - assert!(cxx_qt_data.methods[0].is_qinvokable); - assert!(!cxx_qt_data.methods[1].is_qinvokable) + assert_eq!(cxx_qt_data.methods().collect::>().len(), 2); + assert!(cxx_qt_data.methods[0][0].is_qinvokable); + assert!(!cxx_qt_data.methods[0][1].is_qinvokable) } #[test] @@ -382,7 +416,7 @@ mod tests { }; cxx_qt_data.parse_cxx_qt_item(item).unwrap(); assert_eq!(cxx_qt_data.methods.len(), 1); - assert_eq!(cxx_qt_data.methods[0].name.cxx_unqualified(), "fooBar"); + assert_eq!(cxx_qt_data.methods[0][0].name.cxx_unqualified(), "fooBar"); } #[test] @@ -397,9 +431,10 @@ mod tests { } }; cxx_qt_data.parse_cxx_qt_item(item).unwrap(); - assert_eq!(cxx_qt_data.methods.len(), 1); - assert_eq!(cxx_qt_data.methods[0].name.cxx_unqualified(), "foo_bar"); - assert_eq!(cxx_qt_data.methods[0].name.rust_unqualified(), "foo_bar"); + let methods = &cxx_qt_data.methods[0]; + assert_eq!(methods.len(), 1); + assert_eq!(methods[0].name.cxx_unqualified(), "foo_bar"); + assert_eq!(methods[0].name.rust_unqualified(), "foo_bar"); } #[test] @@ -414,8 +449,8 @@ mod tests { } }; cxx_qt_data.parse_cxx_qt_item(item).unwrap(); - assert_eq!(cxx_qt_data.methods.len(), 1); - assert_eq!(cxx_qt_data.methods[0].name.cxx_unqualified(), "renamed"); + assert_eq!(cxx_qt_data.methods[0].len(), 1); + assert_eq!(cxx_qt_data.methods[0][0].name.cxx_unqualified(), "renamed"); } #[test] @@ -587,7 +622,7 @@ mod tests { } }; cxxqtdata.parse_cxx_qt_item(block).unwrap(); - let signals = &cxxqtdata.signals; + let signals = &cxxqtdata.signals().collect::>(); assert_eq!(signals.len(), 2); assert!(signals[0].mutable); assert!(signals[1].mutable); @@ -617,7 +652,7 @@ mod tests { }; cxxqtdata.parse_cxx_qt_item(block).unwrap(); - let signals = &cxxqtdata.signals; + let signals = &cxxqtdata.signals().collect::>(); assert_eq!(signals.len(), 1); assert!(signals[0].mutable); assert!(!signals[0].safe); @@ -692,7 +727,7 @@ mod tests { }; parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap(); - assert_eq!(parsed_cxxqtdata.qobjects.len(), 2); + assert_eq!(parsed_cxxqtdata.qobjects().collect::>().len(), 2); assert!(parsed_cxxqtdata .find_object(&format_ident!("MyObject")) @@ -717,7 +752,7 @@ mod tests { }; parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap(); - assert_eq!(parsed_cxxqtdata.qobjects.len(), 2); + assert_eq!(parsed_cxxqtdata.qobjects().collect::>().len(), 2); assert_eq!( parsed_cxxqtdata .find_object(&format_ident!("MyObject")) diff --git a/crates/cxx-qt-gen/src/parser/externcxxqt.rs b/crates/cxx-qt-gen/src/parser/externcxxqt.rs index 2031d165e..1aa7c12f5 100644 --- a/crates/cxx-qt-gen/src/parser/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/parser/externcxxqt.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::preprocessor::self_inlining::try_inline_self_invokables; use crate::{ parser::{ externqobject::ParsedExternQObject, require_attributes, signals::ParsedSignal, @@ -10,6 +11,7 @@ use crate::{ }, syntax::{attribute::attribute_get_path, expr::expr_to_string}, }; +use quote::format_ident; use syn::{spanned::Spanned, Error, ForeignItem, Ident, ItemForeignMod, Result, Token}; /// Representation of an extern "C++Qt" block @@ -54,6 +56,9 @@ impl ParsedExternCxxQt { ..Default::default() }; + let mut qobjects = vec![]; + let mut signals = vec![]; + // Parse any signals, other items are passed through for item in foreign_mod.items.drain(..) { match item { @@ -74,7 +79,7 @@ impl ParsedExternCxxQt { // extern "C++Qt" signals are always inherit = true // as they always exist on an existing QObject signal.inherit = true; - extern_cxx_block.signals.push(signal); + signals.push(signal); } else { extern_cxx_block .passthrough_items @@ -89,7 +94,7 @@ impl ParsedExternCxxQt { let extern_ty = ParsedExternQObject::parse(foreign_ty, module_ident, parent_namespace)?; // Pass through types separately for generation - extern_cxx_block.qobjects.push(extern_ty); + qobjects.push(extern_ty); } else { return Err(Error::new( foreign_ty.span(), @@ -103,6 +108,16 @@ impl ParsedExternCxxQt { } } + let inline_self = qobjects.len() == 1; + let inline_ident = qobjects + .last() + .map(|obj| format_ident!("{}", obj.declaration.ident)); + + try_inline_self_invokables(inline_self, &inline_ident, &mut signals)?; + + extern_cxx_block.qobjects.extend(qobjects); + extern_cxx_block.signals.extend(signals); + Ok(extern_cxx_block) } } diff --git a/crates/cxx-qt-gen/src/parser/inherit.rs b/crates/cxx-qt-gen/src/parser/inherit.rs index a09081710..9ebb5af17 100644 --- a/crates/cxx-qt-gen/src/parser/inherit.rs +++ b/crates/cxx-qt-gen/src/parser/inherit.rs @@ -8,6 +8,7 @@ use crate::parser::{ }; use core::ops::Deref; use quote::format_ident; +use std::ops::DerefMut; use syn::{Attribute, ForeignItemFn, Ident, Result}; /// Describes a method found in an extern "RustQt" with #[inherit] @@ -56,6 +57,12 @@ impl Deref for ParsedInheritedMethod { } } +impl DerefMut for ParsedInheritedMethod { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.method_fields + } +} + #[cfg(test)] mod tests { use super::*; @@ -68,7 +75,6 @@ mod tests { |item| ParsedInheritedMethod::parse(item, CaseConversion::none()) => // Missing self type - { fn test(&self); } { fn test(self: &mut T); } // Pointer types { fn test(self: *const T); } @@ -91,6 +97,14 @@ mod tests { CaseConversion::none() ) .is_ok()); + // T by ref is ok in this shorthand (provided the block has one QObject) + assert!(ParsedInheritedMethod::parse( + parse_quote! { + fn test(&self); + }, + CaseConversion::none() + ) + .is_ok()); // T by Pin assert!(ParsedInheritedMethod::parse( parse_quote! { diff --git a/crates/cxx-qt-gen/src/parser/method.rs b/crates/cxx-qt-gen/src/parser/method.rs index 7c161c4b8..baecdccf7 100644 --- a/crates/cxx-qt-gen/src/parser/method.rs +++ b/crates/cxx-qt-gen/src/parser/method.rs @@ -9,7 +9,9 @@ use crate::{ syntax::{foreignmod, types}, }; use core::ops::Deref; +use quote::format_ident; use std::collections::{BTreeMap, HashSet}; +use std::ops::DerefMut; use syn::{Attribute, ForeignItemFn, Ident, Result}; /// Describes a C++ specifier for the Q_INVOKABLE @@ -146,6 +148,12 @@ impl Deref for ParsedMethod { } } +impl DerefMut for ParsedMethod { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.method_fields + } +} + /// Struct with common fields between Invokable types. /// These types are ParsedSignal, ParsedMethod and ParsedInheritedMethod #[derive(Clone)] @@ -156,6 +164,7 @@ pub struct MethodFields { pub parameters: Vec, pub safe: bool, pub name: Name, + pub self_unresolved: bool, } impl MethodFields { @@ -164,6 +173,8 @@ impl MethodFields { let (qobject_ident, mutability) = types::extract_qobject_ident(&self_receiver.ty)?; let mutable = mutability.is_some(); + let self_unresolved = qobject_ident == format_ident!("Self"); + let parameters = ParsedFunctionParameter::parse_all_ignoring_receiver(&method.sig)?; let safe = method.sig.unsafety.is_none(); let name = @@ -176,6 +187,7 @@ impl MethodFields { parameters, safe, name, + self_unresolved, }) } } diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 3a0821761..ec98e29f7 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -395,7 +395,7 @@ mod tests { assert_eq!(parser.passthrough_module.module_ident, "ffi"); assert_eq!(parser.passthrough_module.vis, Visibility::Inherited); assert_eq!(parser.cxx_qt_data.namespace, Some("cxx_qt".to_owned())); - assert_eq!(parser.cxx_qt_data.qobjects.len(), 1); + assert_eq!(parser.cxx_qt_data.qobjects().collect::>().len(), 1); assert_eq!(parser.type_names.num_types(), 19); assert_eq!( parser @@ -441,7 +441,7 @@ mod tests { assert_eq!(parser.passthrough_module.module_ident, "ffi"); assert_eq!(parser.passthrough_module.vis, Visibility::Inherited); assert_eq!(parser.cxx_qt_data.namespace, None); - assert_eq!(parser.cxx_qt_data.qobjects.len(), 1); + assert_eq!(parser.cxx_qt_data.qobjects().collect::>().len(), 1); } #[test] diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index 3cbf7e754..81e264ebb 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -8,6 +8,7 @@ use crate::{ syntax::path::path_compare_str, }; use core::ops::Deref; +use std::ops::DerefMut; use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility}; #[derive(Clone)] @@ -74,6 +75,12 @@ impl Deref for ParsedSignal { } } +impl DerefMut for ParsedSignal { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.method_fields + } +} + #[cfg(test)] mod tests { use syn::parse_quote; diff --git a/crates/cxx-qt-gen/src/preprocessor/mod.rs b/crates/cxx-qt-gen/src/preprocessor/mod.rs new file mode 100644 index 000000000..ff9cbee80 --- /dev/null +++ b/crates/cxx-qt-gen/src/preprocessor/mod.rs @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Ben Ford +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +pub mod self_inlining; diff --git a/crates/cxx-qt-gen/src/preprocessor/self_inlining.rs b/crates/cxx-qt-gen/src/preprocessor/self_inlining.rs new file mode 100644 index 000000000..4689923e7 --- /dev/null +++ b/crates/cxx-qt-gen/src/preprocessor/self_inlining.rs @@ -0,0 +1,191 @@ +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Ben Ford +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::parser::cxxqtdata::ParsedCxxQtData; +use crate::parser::inherit::ParsedInheritedMethod; +use crate::parser::method::{MethodFields, ParsedMethod}; +use crate::parser::qobject::ParsedQObject; +use crate::parser::signals::ParsedSignal; +use crate::Parser; +use proc_macro2::Ident; +use quote::format_ident; +use std::ops::DerefMut; +use syn::spanned::Spanned; +use syn::{Error, Result}; + +/// Inline any `Self` types in the methods signatures with the Ident of a qobject passed in +/// +/// If there are unresolved methods in the list, but inline is false, it will error, +/// as the self inlining is only available if there is exactly one `QObject` in the block, +/// and this indicates that no inlining can be done, but some `Self` types were present. +pub fn try_inline_self_invokables( + inline: bool, + type_to_inline: &Option, + invokables: &mut [impl DerefMut], +) -> syn::Result<()> { + for method in invokables.iter_mut() { + if method.self_unresolved { + if inline { + if let Some(inline_type) = type_to_inline.clone() { + method.qobject_ident = inline_type; + } else { + return Err(Error::new( + method.method.span(), + "Expected a type to inline, no `qobject` typename was passed!", + )); + } + } else { + return Err(Error::new( + method.method.span(), + "`Self` type can only be inferred if the extern block contains only one `qobject`.", + )); + } + } + } + Ok(()) +} + +/// A collection of items found in an extern block when parsing +type BlockComponents<'a> = ( + &'a mut Vec, + &'a mut Vec, + &'a mut Vec, + &'a mut Vec, +); + +// Separates the parsed data by block and returns tuples of the components +fn separate_blocks(data: &mut ParsedCxxQtData) -> Vec { + data.qobjects + .iter_mut() + .zip(data.methods.iter_mut()) + .zip(data.inherited_methods.iter_mut()) + .zip(data.signals.iter_mut()) + .map(|(((qobject, method), inherited_method), signal)| { + (qobject, method, inherited_method, signal) + }) + .collect() +} + +/// For a given parser, attempt to inline the `Self` type used in any of the blocks with that blocks unique QObject +pub fn qualify_self_types(parser: &mut Parser) -> Result<()> { + for (qobjects, methods, inherited, signals) in separate_blocks(&mut parser.cxx_qt_data) { + let inline_self = qobjects.len() == 1; + let inline_ident = qobjects + .last() + .map(|obj| format_ident!("{}", obj.declaration.ident_left)); + + try_inline_self_invokables(inline_self, &inline_ident, methods)?; + try_inline_self_invokables(inline_self, &inline_ident, signals)?; + try_inline_self_invokables(inline_self, &inline_ident, inherited)?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parser::method::ParsedMethod; + use crate::tests::assert_parse_errors; + use syn::parse_quote; + + #[test] + fn test_self_inlining_ref() { + let module = parse_quote! { + #[cxx_qt::bridge] + mod ffi { + unsafe extern "RustQt" { + #[qobject] + type MyObject = super::T; + + fn my_method(&self); + + #[inherit] + fn my_inherited_method(&self); + } + } + }; + let mut parser = Parser::from(module).unwrap(); + assert!(qualify_self_types(&mut parser).is_ok()); + } + + #[test] + fn test_self_inlining_pin() { + let module = parse_quote! { + #[cxx_qt::bridge] + mod ffi { + unsafe extern "RustQt" { + #[qobject] + type MyObject = super::T; + + #[qsignal] + fn my_signal(self: Pin<&mut Self>); + } + + unsafe extern "C++Qt" { + #[qobject] + type MyOtherObject; + + #[qsignal] + fn my_signal(self: Pin<&mut Self>); + } + } + }; + let mut parser = Parser::from(module).unwrap(); + assert!(qualify_self_types(&mut parser).is_ok()); + } + + #[test] + fn test_self_inlining_methods_invalid() { + assert_parse_errors! { + |item| qualify_self_types(&mut Parser::from(item)?) => + // No QObject in block + { + #[cxx_qt::bridge] + mod ffi { + extern "RustQt" { + fn my_method(&self); + } + } + } + + { + #[cxx_qt::bridge] + mod ffi { + extern "RustQt" { + fn my_method(self: Pin<&mut Self>); + } + } + } + // More than 1 QObjects in block + { + #[cxx_qt::bridge] + mod ffi { + extern "RustQt" { + #[qobject] + type MyObject = super::T; + + #[qobject] + type MyOtherObject = super::S; + + fn my_method(&self); + } + } + } + } + } + + #[test] + fn test_invalid_inline_call() { + let method_sig = parse_quote! { + fn test(&self); + }; + let mut methods = vec![ParsedMethod::mock_qinvokable(&method_sig)]; + + // If inlining is set to take place, an Ident is required to inline, here it is `None` + let data = try_inline_self_invokables(true, &None, &mut methods); + assert!(data.is_err()); + } +} diff --git a/crates/cxx-qt-gen/src/syntax/foreignmod.rs b/crates/cxx-qt-gen/src/syntax/foreignmod.rs index 7b00734bf..be1565fef 100644 --- a/crates/cxx-qt-gen/src/syntax/foreignmod.rs +++ b/crates/cxx-qt-gen/src/syntax/foreignmod.rs @@ -174,14 +174,7 @@ pub fn self_type_from_foreign_fn(signature: &Signature) -> Result { )); } - if receiver.reference.is_some() { - return Err(Error::new( - receiver.span(), - "Reference on self (i.e. `&self`) are not supported! Use `self: &T` instead", - )); - } - - if receiver.colon_token.is_none() { + if receiver.colon_token.is_none() && receiver.reference.is_none() { return Err(Error::new( receiver.span(), "`self` is not supported as receiver! Use `self: T` to indicate a type.", @@ -254,8 +247,6 @@ mod tests { { fn foo(self); } // self with mut { fn foo(mut self: T); } - // self reference - { fn foo(&self); } // self reference with mut { fn foo(&mut self); } // attribute on self type diff --git a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs index cfa3bae78..bb4fa2c2e 100644 --- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs @@ -113,19 +113,17 @@ pub mod ffi { fn errorOccurred(self: Pin<&mut ExternObject>); } - extern "RustQt" { + unsafe extern "RustQt" { #[qobject] #[base = QStringListModel] #[qproperty(i32, property_name, cxx_name = "propertyName")] type MyObject = super::MyObjectRust; - } - unsafe extern "RustQt" { #[qsignal] - fn ready(self: Pin<&mut MyObject>); + fn ready(self: Pin<&mut Self>); #[qinvokable] - fn invokable_name(self: Pin<&mut MyObject>); + fn invokable_name(self: Pin<&mut Self>); } extern "RustQt" { diff --git a/crates/cxx-qt-gen/test_inputs/signals.rs b/crates/cxx-qt-gen/test_inputs/signals.rs index 3aca1f5bd..ec298cca7 100644 --- a/crates/cxx-qt-gen/test_inputs/signals.rs +++ b/crates/cxx-qt-gen/test_inputs/signals.rs @@ -16,7 +16,7 @@ mod ffi { /// When the QTimer timeout occurs #[qsignal] - pub(self) fn timeout(self: Pin<&mut QTimer>); + pub(self) fn timeout(self: Pin<&mut Self>); } unsafe extern "RustQt" { @@ -24,11 +24,11 @@ mod ffi { type MyObject = super::MyObjectRust; #[qsignal] - fn ready(self: Pin<&mut MyObject>); + fn ready(self: Pin<&mut Self>); #[qsignal] fn data_changed( - self: Pin<&mut MyObject>, + self: Pin<&mut Self>, first: i32, second: UniquePtr, third: QPoint, @@ -39,7 +39,7 @@ mod ffi { #[inherit] #[qsignal] fn base_class_new_data( - self: Pin<&mut MyObject>, + self: Pin<&mut Self>, first: i32, second: UniquePtr, third: QPoint, @@ -47,6 +47,6 @@ mod ffi { ); #[qinvokable] - fn invokable(self: Pin<&mut MyObject>); + fn invokable(self: Pin<&mut Self>); } } diff --git a/crates/cxx-qt-macro/src/lib.rs b/crates/cxx-qt-macro/src/lib.rs index fcd9c01f2..9c40102f0 100644 --- a/crates/cxx-qt-macro/src/lib.rs +++ b/crates/cxx-qt-macro/src/lib.rs @@ -15,7 +15,7 @@ use proc_macro::TokenStream; use syn::{parse_macro_input, ItemMod}; -use cxx_qt_gen::{write_rust, GeneratedRustBlocks, Parser}; +use cxx_qt_gen::{qualify_self_types, write_rust, GeneratedRustBlocks, Parser}; #[proc_macro_attribute] pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { @@ -75,6 +75,10 @@ pub fn init_qml_module(args: TokenStream) -> TokenStream { // Take the module and C++ namespace and generate the rust code fn extract_and_generate(module: ItemMod) -> TokenStream { Parser::from(module) + .and_then(|mut parser| { + qualify_self_types(&mut parser)?; + Ok(parser) + }) .and_then(|parser| GeneratedRustBlocks::from(&parser)) .map(|generated_rust| write_rust(&generated_rust, None)) .unwrap_or_else(|err| err.to_compile_error()) diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index bafd88910..101d1d3b0 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -17,6 +17,9 @@ pub mod qobject { type QString = cxx_qt_lib::QString; } + // Enabling threading on the qobject + impl cxx_qt::Threading for EnergyUsage {} + extern "RustQt" { #[qobject] #[qml_element] @@ -24,31 +27,24 @@ pub mod qobject { #[qproperty(u32, sensors)] #[qproperty(f64, total_use)] type EnergyUsage = super::EnergyUsageRust; - } - - // Enabling threading on the qobject - impl cxx_qt::Threading for EnergyUsage {} - extern "RustQt" { /// A new sensor has been detected #[qsignal] #[cxx_name = "sensorAdded"] - fn sensor_added(self: Pin<&mut EnergyUsage>, uuid: QString); + fn sensor_added(self: Pin<&mut Self>, uuid: QString); /// A value on an existing sensor has changed #[qsignal] #[cxx_name = "sensorChanged"] - fn sensor_changed(self: Pin<&mut EnergyUsage>, uuid: QString); + fn sensor_changed(self: Pin<&mut Self>, uuid: QString); /// An existing sensor has been removed #[qsignal] #[cxx_name = "sensorRemoved"] - fn sensor_removed(self: Pin<&mut EnergyUsage>, uuid: QString); - } + fn sensor_removed(self: Pin<&mut Self>, uuid: QString); - extern "RustQt" { /// A Q_INVOKABLE that returns the current power usage for a given uuid #[qinvokable] #[cxx_name = "sensorPower"] - fn sensor_power(self: Pin<&mut EnergyUsage>, uuid: &QString) -> f64; + fn sensor_power(self: Pin<&mut Self>, uuid: &QString) -> f64; } impl cxx_qt::Initialize for EnergyUsage {} diff --git a/examples/qml_basics/src/main.rs b/examples/qml_basics/src/main.rs index 01c86c096..a23f3dd9c 100644 --- a/examples/qml_basics/src/main.rs +++ b/examples/qml_basics/src/main.rs @@ -31,7 +31,7 @@ mod qobject { type Greeter = super::GreeterRust; #[qinvokable] - fn greet(self: &Greeter) -> QString; + fn greet(&self) -> QString; } } diff --git a/examples/qml_features/rust/src/containers.rs b/examples/qml_features/rust/src/containers.rs index cd7135b3e..a63455219 100644 --- a/examples/qml_features/rust/src/containers.rs +++ b/examples/qml_features/rust/src/containers.rs @@ -46,32 +46,32 @@ pub mod qobject { /// Reset all the containers #[qinvokable] - fn reset(self: Pin<&mut RustContainers>); + fn reset(self: Pin<&mut Self>); /// Append the given number to the vector container #[qinvokable] #[cxx_name = "appendVector"] - fn append_vector(self: Pin<&mut RustContainers>, value: i32); + fn append_vector(self: Pin<&mut Self>, value: i32); /// Append the given number to the list container #[qinvokable] #[cxx_name = "appendList"] - fn append_list(self: Pin<&mut RustContainers>, value: i32); + fn append_list(self: Pin<&mut Self>, value: i32); /// Insert the given number into the set container #[qinvokable] #[cxx_name = "insertSet"] - fn insert_set(self: Pin<&mut RustContainers>, value: i32); + fn insert_set(self: Pin<&mut Self>, value: i32); /// Insert the given string and variant to the hash container #[qinvokable] #[cxx_name = "insertHash"] - fn insert_hash(self: Pin<&mut RustContainers>, key: QString, value: QVariant); + fn insert_hash(self: Pin<&mut Self>, key: QString, value: QVariant); /// Insert the given string and variant to the map container #[qinvokable] #[cxx_name = "insertMap"] - fn insert_map(self: Pin<&mut RustContainers>, key: QString, value: QVariant); + fn insert_map(self: Pin<&mut Self>, key: QString, value: QVariant); } } diff --git a/examples/qml_features/rust/src/custom_parent_class.rs b/examples/qml_features/rust/src/custom_parent_class.rs index 3b36884ba..c0237cece 100644 --- a/examples/qml_features/rust/src/custom_parent_class.rs +++ b/examples/qml_features/rust/src/custom_parent_class.rs @@ -43,15 +43,15 @@ pub mod qobject { /// Override QQuickPaintedItem::paint to draw two rectangles in Rust using QPainter #[qinvokable] #[cxx_override] - unsafe fn paint(self: Pin<&mut CustomParentClass>, painter: *mut QPainter); + unsafe fn paint(self: Pin<&mut Self>, painter: *mut QPainter); /// Define that we need to inherit size() from the base class #[inherit] - fn size(self: &CustomParentClass) -> QSizeF; + fn size(self: &Self) -> QSizeF; /// Define that we need to inherit update() from the base class #[inherit] - fn update(self: Pin<&mut CustomParentClass>); + fn update(self: Pin<&mut Self>); } impl cxx_qt::Initialize for CustomParentClass {} diff --git a/examples/qml_features/rust/src/externcxxqt.rs b/examples/qml_features/rust/src/externcxxqt.rs index 4d2a74a46..efc9fd04b 100644 --- a/examples/qml_features/rust/src/externcxxqt.rs +++ b/examples/qml_features/rust/src/externcxxqt.rs @@ -14,17 +14,18 @@ pub mod ffi { #[qobject] type ExternalQObject; + // Since functions are just passed through the inlining isn't yet supported /// Trigger emitting the signal "amount" times fn trigger(self: Pin<&mut ExternalQObject>, amount: u32); /// Signal that is emitted when trigger is fired #[qsignal] - fn triggered(self: Pin<&mut ExternalQObject>); + fn triggered(self: Pin<&mut Self>); /// Private signal that is emitted when trigger is fired #[qsignal] #[rust_name = "triggered_private_signal"] - pub(self) fn triggeredPrivateSignal(self: Pin<&mut ExternalQObject>); + pub(self) fn triggeredPrivateSignal(self: Pin<&mut Self>); } extern "RustQt" { @@ -36,15 +37,12 @@ pub mod ffi { #[qinvokable] #[cxx_name = "connectToExternal"] - unsafe fn connect_to_external( - self: Pin<&mut ExternalCxxQtHelper>, - external: *mut ExternalQObject, - ); + unsafe fn connect_to_external(self: Pin<&mut Self>, external: *mut ExternalQObject); #[qinvokable] #[cxx_name = "triggerOnExternal"] unsafe fn trigger_on_external( - self: Pin<&mut ExternalCxxQtHelper>, + self: Pin<&mut Self>, external: *mut ExternalQObject, amount: u32, ); diff --git a/examples/qml_features/rust/src/multiple_qobjects.rs b/examples/qml_features/rust/src/multiple_qobjects.rs index f1a24ea6b..128063126 100644 --- a/examples/qml_features/rust/src/multiple_qobjects.rs +++ b/examples/qml_features/rust/src/multiple_qobjects.rs @@ -23,53 +23,45 @@ pub mod qobject { #[qproperty(i32, counter)] #[qproperty(QColor, color)] type FirstObject = super::FirstObjectRust; - } - // Enabling threading on the qobject - impl cxx_qt::Threading for FirstObject {} - - extern "RustQt" { /// Accepted Q_SIGNAL #[qsignal] - fn accepted(self: Pin<&mut FirstObject>); + fn accepted(self: Pin<&mut Self>); /// Rejected Q_SIGNAL #[qsignal] - fn rejected(self: Pin<&mut FirstObject>); - } + fn rejected(self: Pin<&mut Self>); - extern "RustQt" { /// A Q_INVOKABLE on the first QObject which increments a counter #[qinvokable] - fn increment(self: Pin<&mut FirstObject>); + fn increment(self: Pin<&mut Self>); } + // Enabling threading on the qobject + impl cxx_qt::Threading for FirstObject {} + extern "RustQt" { #[qobject] #[qml_element] #[qproperty(i32, counter)] #[qproperty(QUrl, url)] type SecondObject = super::SecondObjectRust; - } - // Enabling threading on the qobject - impl cxx_qt::Threading for SecondObject {} - - extern "RustQt" { /// Accepted Q_SIGNAL #[qsignal] - fn accepted(self: Pin<&mut SecondObject>); + fn accepted(self: Pin<&mut Self>); /// Rejected Q_SIGNAL #[qsignal] - fn rejected(self: Pin<&mut SecondObject>); - } + fn rejected(self: Pin<&mut Self>); - extern "RustQt" { /// A Q_INVOKABLE on the second QObject which increments a counter #[qinvokable] - fn increment(self: Pin<&mut SecondObject>); + fn increment(self: Pin<&mut Self>); } + + // Enabling threading on the qobject + impl cxx_qt::Threading for SecondObject {} } use core::pin::Pin; diff --git a/examples/qml_features/rust/src/naming.rs b/examples/qml_features/rust/src/naming.rs index 0f25fe278..440b58547 100644 --- a/examples/qml_features/rust/src/naming.rs +++ b/examples/qml_features/rust/src/naming.rs @@ -15,13 +15,12 @@ pub mod qobject { #[cxx_name = "RenamedObject"] #[namespace = "my_namespace"] type NamedObject = super::NamedObjectRust; - } - extern "RustQt" { #[qinvokable] #[cxx_name = "increment"] #[rust_name = "plus_one"] - fn increment_number(self: Pin<&mut NamedObject>); + fn increment_number(self: Pin<&mut Self>); + } #[auto_cxx_name] diff --git a/examples/qml_features/rust/src/nested_qobjects.rs b/examples/qml_features/rust/src/nested_qobjects.rs index 14f013f83..70e5d79b7 100644 --- a/examples/qml_features/rust/src/nested_qobjects.rs +++ b/examples/qml_features/rust/src/nested_qobjects.rs @@ -44,7 +44,7 @@ pub mod qobject { /// /// Due to a raw pointer this is considered unsafe in CXX #[qsignal] - unsafe fn called(self: Pin<&mut OuterObject>, inner: *mut InnerObject); + unsafe fn called(self: Pin<&mut Self>, inner: *mut InnerObject); } extern "RustQt" { diff --git a/examples/qml_features/rust/src/properties.rs b/examples/qml_features/rust/src/properties.rs index a1ed2330c..54cca0a86 100644 --- a/examples/qml_features/rust/src/properties.rs +++ b/examples/qml_features/rust/src/properties.rs @@ -31,15 +31,15 @@ pub mod qobject { /// Custom on changed signal, used for all the properties #[qsignal] #[cxx_name = "connectedStateChanged"] - fn connected_state_changed(self: Pin<&mut RustProperties>); + fn connected_state_changed(self: Pin<&mut Self>); /// Custom setter for connected_url, which also handles setting the other qproperties #[cxx_name = "setUrl"] - fn set_url(self: Pin<&mut RustProperties>, url: QUrl); + fn set_url(self: Pin<&mut Self>, url: QUrl); /// Resets value of connected_url to empty, as well as calling the other disconnected logic #[cxx_name = "resetUrl"] - fn reset_url(self: Pin<&mut RustProperties>); + fn reset_url(self: Pin<&mut Self>); } impl cxx_qt::Initialize for RustProperties {} diff --git a/examples/qml_features/rust/src/serialisation.rs b/examples/qml_features/rust/src/serialisation.rs index ea14a3f97..939f4bad3 100644 --- a/examples/qml_features/rust/src/serialisation.rs +++ b/examples/qml_features/rust/src/serialisation.rs @@ -28,18 +28,18 @@ pub mod qobject { /// An error signal #[qsignal] - fn error(self: Pin<&mut Serialisation>, message: QString); + fn error(self: Pin<&mut Self>, message: QString); /// Retrieve the JSON form of this QObject #[qinvokable] #[cxx_name = "asJsonStr"] - fn as_json_str(self: Pin<&mut Serialisation>) -> QString; + fn as_json_str(self: Pin<&mut Self>) -> QString; /// From a given JSON string try to load values for the Q_PROPERTYs // ANCHOR: book_grab_values #[qinvokable] #[cxx_name = "fromJsonStr"] - fn from_json_str(self: Pin<&mut Serialisation>, string: &QString); + fn from_json_str(self: Pin<&mut Self>, string: &QString); // ANCHOR_END: book_grab_values } } diff --git a/examples/qml_features/rust/src/singleton.rs b/examples/qml_features/rust/src/singleton.rs index 98fbdbc41..9c02aec0b 100644 --- a/examples/qml_features/rust/src/singleton.rs +++ b/examples/qml_features/rust/src/singleton.rs @@ -18,7 +18,7 @@ pub mod qobject { /// Increment the persistent value Q_PROPERTY of the QML_SINGLETON #[qinvokable] - fn increment(self: Pin<&mut RustSingleton>); + fn increment(self: Pin<&mut Self>); } } diff --git a/examples/qml_features/rust/src/threading.rs b/examples/qml_features/rust/src/threading.rs index 5111a5396..5e590b2db 100644 --- a/examples/qml_features/rust/src/threading.rs +++ b/examples/qml_features/rust/src/threading.rs @@ -27,24 +27,22 @@ pub mod qobject { #[qproperty(QString, title)] #[qproperty(QUrl, url)] type ThreadingWebsite = super::ThreadingWebsiteRust; - } - - // ANCHOR: book_threading_trait - // Enabling threading on the qobject - impl cxx_qt::Threading for ThreadingWebsite {} - // ANCHOR_END: book_threading_trait - extern "RustQt" { /// Swap the URL between kdab.com and github.com #[qinvokable] #[cxx_name = "changeUrl"] - fn change_url(self: Pin<&mut ThreadingWebsite>); + fn change_url(self: Pin<&mut Self>); /// Simulate delay of a network request to retrieve the title of the website #[qinvokable] #[cxx_name = "fetchTitle"] - fn fetch_title(self: Pin<&mut ThreadingWebsite>); + fn fetch_title(self: Pin<&mut Self>); } + + // ANCHOR: book_threading_trait + // Enabling threading on the qobject + impl cxx_qt::Threading for ThreadingWebsite {} + // ANCHOR_END: book_threading_trait } use core::pin::Pin; diff --git a/examples/qml_features/rust/src/types.rs b/examples/qml_features/rust/src/types.rs index 63d8ba9e6..90907c7e7 100644 --- a/examples/qml_features/rust/src/types.rs +++ b/examples/qml_features/rust/src/types.rs @@ -83,12 +83,12 @@ pub mod ffi { /// Load the value from a QVariant #[qinvokable] #[cxx_name = "loadFromVariant"] - fn load_from_variant(self: Pin<&mut Types>, variant: &QVariant); + fn load_from_variant(self: Pin<&mut Self>, variant: &QVariant); /// Toggle the boolean Q_PROPERTY #[qinvokable] #[cxx_name = "toggleBoolean"] - fn toggle_boolean(self: Pin<&mut Types>); + fn toggle_boolean(self: Pin<&mut Self>); } } diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 84ac60084..15878e1e3 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -21,8 +21,8 @@ pub mod qobject { } // ANCHOR_END: book_qstring_import - // ANCHOR: book_rustobj_struct_signature extern "RustQt" { + // ANCHOR: book_rustobj_struct_signature // The QObject definition // We tell CXX-Qt that we want a QObject class with the name MyObject // based on the Rust struct MyObjectRust. @@ -32,21 +32,19 @@ pub mod qobject { #[qproperty(QString, string)] #[namespace = "my_object"] type MyObject = super::MyObjectRust; - } - // ANCHOR_END: book_rustobj_struct_signature + // ANCHOR_END: book_rustobj_struct_signature - // ANCHOR: book_rustobj_invokable_signature - extern "RustQt" { + // ANCHOR: book_rustobj_invokable_signature // Declare the invokable methods we want to expose on the QObject #[qinvokable] #[cxx_name = "incrementNumber"] - fn increment_number(self: Pin<&mut MyObject>); + fn increment_number(self: Pin<&mut Self>); #[qinvokable] #[cxx_name = "sayHi"] - fn say_hi(self: &MyObject, string: &QString, number: i32); + fn say_hi(&self, string: &QString, number: i32); + // ANCHOR_END: book_rustobj_invokable_signature } - // ANCHOR_END: book_rustobj_invokable_signature } // ANCHOR: book_use diff --git a/examples/qml_multi_crates/rust/main/src/main_object.rs b/examples/qml_multi_crates/rust/main/src/main_object.rs index 3fd832125..b4fb5f6a9 100644 --- a/examples/qml_multi_crates/rust/main/src/main_object.rs +++ b/examples/qml_multi_crates/rust/main/src/main_object.rs @@ -17,7 +17,7 @@ pub mod qobject { type MainObject = super::MainObjectRust; #[qinvokable] - fn increment(self: Pin<&mut MainObject>); + fn increment(self: Pin<&mut Self>); } } diff --git a/examples/qml_multi_crates/rust/sub1/src/sub1_object.rs b/examples/qml_multi_crates/rust/sub1/src/sub1_object.rs index 3b949b0db..084e17692 100644 --- a/examples/qml_multi_crates/rust/sub1/src/sub1_object.rs +++ b/examples/qml_multi_crates/rust/sub1/src/sub1_object.rs @@ -17,7 +17,7 @@ pub mod qobject { type Sub1Object = super::Sub1ObjectRust; #[qinvokable] - fn increment(self: Pin<&mut Sub1Object>); + fn increment(self: Pin<&mut Self>); } } diff --git a/examples/qml_multi_crates/rust/sub2/src/sub2_object.rs b/examples/qml_multi_crates/rust/sub2/src/sub2_object.rs index 73ba275ce..9a849f296 100644 --- a/examples/qml_multi_crates/rust/sub2/src/sub2_object.rs +++ b/examples/qml_multi_crates/rust/sub2/src/sub2_object.rs @@ -17,7 +17,7 @@ pub mod qobject { type Sub2Object = super::Sub2ObjectRust; #[qinvokable] - fn increment(self: Pin<&mut Sub2Object>); + fn increment(self: Pin<&mut Self>); } } diff --git a/examples/todo_app/src/todo_list.rs b/examples/todo_app/src/todo_list.rs index e5cfe6953..7ad696c2d 100644 --- a/examples/todo_app/src/todo_list.rs +++ b/examples/todo_app/src/todo_list.rs @@ -41,32 +41,30 @@ mod qobject { #[cxx_override] #[rust_name = "row_count"] - fn rowCount(self: &TodoList, parent: &QModelIndex) -> i32; + fn rowCount(&self, parent: &QModelIndex) -> i32; #[cxx_override] - fn data(self: &TodoList, index: &QModelIndex, role: i32) -> QVariant; + fn data(&self, index: &QModelIndex, role: i32) -> QVariant; #[cxx_override] #[rust_name = "role_names"] - fn roleNames(self: &TodoList) -> QHash_i32_QByteArray; - } + fn roleNames(&self) -> QHash_i32_QByteArray; - unsafe extern "RustQt" { #[qinvokable] #[rust_name = "set_checked"] - fn setChecked(self: Pin<&mut TodoList>, row: i32, checked: bool); + fn setChecked(self: Pin<&mut Self>, row: i32, checked: bool); #[inherit] #[rust_name = "begin_reset_model"] - fn beginResetModel(self: Pin<&mut TodoList>); + fn beginResetModel(self: Pin<&mut Self>); #[inherit] #[rust_name = "end_reset_model"] - fn endResetModel(self: Pin<&mut TodoList>); + fn endResetModel(self: Pin<&mut Self>); #[qinvokable] #[rust_name = "add_todo"] - fn addTodo(self: Pin<&mut TodoList>, todo: &QString); + fn addTodo(self: Pin<&mut Self>, todo: &QString); } }