Skip to content

Commit a87fcb1

Browse files
Add shorthand for &self in RustQt blocks where the type can be inferred (#1249)
* Add shorthand support for &self and Pin<&mut Self> in RustQt blocks where the type can be inferred - If exactly one QObject is present in a block with a method, it will infer `Self` to represent that qobject - Updates one of the examples to test this out * Update book and docs * Use Self type in examples * Add support for Self inlining in extern C++Qt blocks * Update more examples and fix bug with inlining ident * Move inlining to it's own phase * Move parser to store some things together based on extern block - QObjects, and invokables are now 2D Vecs, kept in their original block - Adds some helper methods for iterating like they are still flat * Move inlining to a separate phase which processes a Parser - Add function call to cxx-qt-build and cxx-qt-macro - Inlining now happens after parsing and before structuring * Separate parsing RustQt blocks, like we do with C++Qt - Refactor parsing steps to be more readable - Refactor tests to be better separated
1 parent 1bffb6a commit a87fcb1

File tree

37 files changed

+728
-453
lines changed

37 files changed

+728
-453
lines changed

book/src/getting-started/2-our-first-cxx-qt-module.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ These functions then need to be implemented **outside** the bridge using `impl q
143143
{{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_invokable_impl}}
144144
```
145145

146+
### Inlining the self receiver
147+
148+
If an `extern "RustQt"` block contains exactly one `QObject`, the self type of methods can be inferred.
149+
For instance, in a block with multiple or no `QObject`s, a function like `foo(&self)` or `foo(self: Pin<&mut Self>)`
150+
would not compile, but will compile with the `Self` type set to that blocks `QObject`.
151+
This is how CXX [handles it](https://cxx.rs/extern-rust.html) (see the Methods heading).
152+
146153
This setup is a bit unusual, as the type `qobject::MyObject` is actually defined in C++.
147154
However, it is still possible to add member functions to it in Rust and then expose them back to C++.
148155
This is the usual workflow for `QObject`s in CXX-Qt.

crates/cxx-qt-build/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ use std::{
4343
};
4444

4545
use cxx_qt_gen::{
46-
parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks, GeneratedOpt,
47-
GeneratedRustBlocks, Parser,
46+
parse_qt_file, self_inlining::qualify_self_types, write_cpp, write_rust, CppFragment,
47+
CxxQtItem, GeneratedCppBlocks, GeneratedOpt, GeneratedRustBlocks, Parser,
4848
};
4949

5050
// TODO: we need to eventually support having multiple modules defined in a single file. This
@@ -132,7 +132,10 @@ impl GeneratedCpp {
132132
}
133133
found_bridge = true;
134134

135-
let parser = Parser::from(*m.clone())
135+
let mut parser = Parser::from(*m.clone())
136+
.map_err(GeneratedError::from)
137+
.map_err(to_diagnostic)?;
138+
qualify_self_types(&mut parser)
136139
.map_err(GeneratedError::from)
137140
.map_err(to_diagnostic)?;
138141
let generated_cpp = GeneratedCppBlocks::from(&parser, &cxx_qt_opt)

crates/cxx-qt-gen/src/generator/rust/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl GeneratedRustBlocks {
103103
// Generate a type declaration for `QObject` if necessary
104104
fn add_qobject_import(cxx_qt_data: &ParsedCxxQtData) -> Option<GeneratedRustFragment> {
105105
let includes = cxx_qt_data
106-
.qobjects
106+
.qobjects()
107107
.iter()
108108
.any(|obj| obj.has_qobject_macro && obj.base_class.is_none());
109109
if includes

crates/cxx-qt-gen/src/generator/structuring/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl<'a> Structures<'a> {
7979
/// Returns an error, if any references could not be resolved.
8080
pub fn new(cxxqtdata: &'a ParsedCxxQtData) -> Result<Self> {
8181
let mut qobjects: Vec<_> = cxxqtdata
82-
.qobjects
83-
.iter()
82+
.qobjects()
83+
.into_iter()
8484
.map(StructuredQObject::from_qobject)
8585
.collect();
8686

@@ -92,19 +92,19 @@ impl<'a> Structures<'a> {
9292
}
9393

9494
// Associate each method parsed with its appropriate qobject
95-
for method in &cxxqtdata.methods {
95+
for method in cxxqtdata.methods() {
9696
let qobject = find_qobject(&mut qobjects, &method.qobject_ident)?;
9797
qobject.methods.push(method);
9898
}
9999

100100
// Associate each inherited method parsed with its appropriate qobject
101-
for inherited_method in &cxxqtdata.inherited_methods {
101+
for inherited_method in cxxqtdata.inherited_methods() {
102102
let qobject = find_qobject(&mut qobjects, &inherited_method.qobject_ident)?;
103103
qobject.inherited_methods.push(inherited_method);
104104
}
105105

106106
// Associate each signal parsed with its appropriate qobject
107-
for signal in &cxxqtdata.signals {
107+
for signal in cxxqtdata.signals() {
108108
let qobject = find_qobject(&mut qobjects, &signal.qobject_ident)?;
109109
qobject.signals.push(signal);
110110
}

crates/cxx-qt-gen/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
mod generator;
1212
mod naming;
1313
mod parser;
14+
mod shorthand;
1415
mod syntax;
1516
mod writer;
1617

@@ -20,6 +21,7 @@ pub use generator::{
2021
GeneratedOpt,
2122
};
2223
pub use parser::Parser;
24+
pub use shorthand::self_inlining;
2325
pub use syntax::{parse_qt_file, CxxQtFile, CxxQtItem};
2426
pub use writer::{cpp::write_cpp, rust::write_rust};
2527

@@ -86,6 +88,7 @@ mod tests {
8688
$(assert!($parse_fn(syn::parse_quote! $input).is_err());)*
8789
}
8890
}
91+
use crate::self_inlining::qualify_self_types;
8992
pub(crate) use assert_parse_errors;
9093

9194
/// Helper for formating C++ code
@@ -174,7 +177,8 @@ mod tests {
174177
expected_cpp_header: &str,
175178
expected_cpp_source: &str,
176179
) {
177-
let parser = Parser::from(syn::parse_str(input).unwrap()).unwrap();
180+
let mut parser = Parser::from(syn::parse_str(input).unwrap()).unwrap();
181+
qualify_self_types(&mut parser).unwrap();
178182

179183
let mut cfg_evaluator = CfgEvaluatorTest::default();
180184
cfg_evaluator.cfgs.insert("crate", Some("cxx-qt-gen"));

crates/cxx-qt-gen/src/naming/name.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use syn::{spanned::Spanned, Attribute, Error, Ident, Path, Result};
1717
/// Naming in CXX can be rather complex.
1818
/// The following Rules apply:
1919
/// - If only a cxx_name **or** a rust_name is given, the identifier of the type/function will be
20-
/// used for part that wasn't specified explicitly.
20+
/// used for part that wasn't specified explicitly.
2121
/// - If **both** attributes are present, the identifier itself is not used!
2222
/// - The `rust_name` is always used to refer to the type within the bridge!.
2323
#[derive(Clone, Debug, PartialEq, Eq)]

crates/cxx-qt-gen/src/naming/type_names.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl TypeNames {
161161
module_ident: &Ident,
162162
) -> Result<()> {
163163
// Find and register the QObjects in the bridge
164-
for qobject in cxx_qt_data.qobjects.iter() {
164+
for qobject in cxx_qt_data.qobjects() {
165165
self.populate_qobject(qobject)?;
166166
}
167167

0 commit comments

Comments
 (0)