Skip to content

Commit e457080

Browse files
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
1 parent 64d4ee6 commit e457080

File tree

7 files changed

+179
-28
lines changed

7 files changed

+179
-28
lines changed

crates/cxx-qt-gen/src/parser/cxxqtdata.rs

+134-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use super::qnamespace::ParsedQNamespace;
77
use super::trait_impl::TraitImpl;
88
use crate::naming::cpp::err_unsupported_item;
9+
use crate::parser::method::MethodFields;
910
use crate::parser::CaseConversion;
1011
use crate::{
1112
parser::{
@@ -17,6 +18,8 @@ use crate::{
1718
path::path_compare_str,
1819
},
1920
};
21+
use quote::format_ident;
22+
use std::ops::DerefMut;
2023
use syn::{
2124
spanned::Spanned, Error, ForeignItem, Ident, Item, ItemEnum, ItemForeignMod, ItemImpl,
2225
ItemMacro, Meta, Result,
@@ -65,6 +68,33 @@ impl ParsedCxxQtData {
6568
}
6669
}
6770

71+
fn try_inline_self_types(
72+
inline: bool,
73+
type_to_inline: &Option<Ident>,
74+
invokables: &mut [impl DerefMut<Target = MethodFields>],
75+
) -> Result<()> {
76+
for method in invokables.iter_mut() {
77+
if method.self_unresolved {
78+
if inline {
79+
if let Some(inline_type) = type_to_inline.clone() {
80+
method.qobject_ident = inline_type;
81+
} else {
82+
return Err(Error::new(
83+
method.method.span(),
84+
"Expected a type to inline, no `qobject` typename was passed!",
85+
));
86+
}
87+
} else {
88+
return Err(Error::new(
89+
method.method.span(),
90+
"`Self` type can only be inferred if the extern block contains only one `qobject`.",
91+
));
92+
}
93+
}
94+
}
95+
Ok(())
96+
}
97+
6898
/// Determine if the given [syn::Item] is a CXX-Qt related item
6999
/// If it is then add the [syn::Item] into qobjects BTreeMap
70100
/// Otherwise return the [syn::Item] to pass through to CXX
@@ -139,6 +169,12 @@ impl ParsedCxxQtData {
139169

140170
let auto_case = CaseConversion::from_attrs(&attrs)?;
141171

172+
let mut qobjects = vec![];
173+
174+
let mut methods = vec![];
175+
let mut signals = vec![];
176+
let mut inherited = vec![];
177+
142178
let namespace = attrs
143179
.get("namespace")
144180
.map(|attr| expr_to_string(&attr.meta.require_name_value()?.value))
@@ -159,7 +195,7 @@ impl ParsedCxxQtData {
159195
return Err(Error::new(foreign_fn.span(), "block must be declared `unsafe extern \"RustQt\"` if it contains any safe-to-call #[inherit] qsignals"));
160196
}
161197

162-
self.signals.push(parsed_signal_method);
198+
signals.push(parsed_signal_method);
163199

164200
// Test if the function is an inheritance method
165201
//
@@ -175,15 +211,15 @@ impl ParsedCxxQtData {
175211
let parsed_inherited_method =
176212
ParsedInheritedMethod::parse(foreign_fn, auto_case)?;
177213

178-
self.inherited_methods.push(parsed_inherited_method);
214+
inherited.push(parsed_inherited_method);
179215
// Remaining methods are either C++ methods or invokables
180216
} else {
181217
let parsed_method = ParsedMethod::parse(
182218
foreign_fn,
183219
auto_case,
184220
foreign_mod.unsafety.is_some(),
185221
)?;
186-
self.methods.push(parsed_method);
222+
methods.push(parsed_method);
187223
}
188224
}
189225
ForeignItem::Verbatim(tokens) => {
@@ -199,12 +235,28 @@ impl ParsedCxxQtData {
199235

200236
// Note that we assume a compiler error will occur later
201237
// if you had two structs with the same name
202-
self.qobjects.push(qobject);
238+
qobjects.push(qobject);
203239
}
204-
// Const Macro, Type are unsupported in extern "RustQt" for now
240+
// Const, Macro, Type are unsupported in extern "RustQt" for now
205241
_ => return Err(err_unsupported_item(&item)),
206242
}
207243
}
244+
245+
// If there is exaclty one qobject in the block, it can be inlined as a self type.
246+
let inline_self = qobjects.len() == 1;
247+
let inline_ident = qobjects
248+
.last()
249+
.map(|obj| format_ident!("{}", obj.name.cxx_unqualified()));
250+
251+
Self::try_inline_self_types(inline_self, &inline_ident, &mut methods)?;
252+
Self::try_inline_self_types(inline_self, &inline_ident, &mut signals)?;
253+
Self::try_inline_self_types(inline_self, &inline_ident, &mut inherited)?;
254+
255+
self.qobjects.extend(qobjects);
256+
self.methods.extend(methods);
257+
self.signals.extend(signals);
258+
self.inherited_methods.extend(inherited);
259+
208260
Ok(())
209261
}
210262

@@ -735,4 +787,81 @@ mod tests {
735787
Some("b")
736788
);
737789
}
790+
791+
#[test]
792+
fn test_self_inlining_ref() {
793+
let mut parsed_cxxqtdata = ParsedCxxQtData::new(format_ident!("ffi"), None);
794+
let extern_rust_qt: Item = parse_quote! {
795+
unsafe extern "RustQt" {
796+
#[qobject]
797+
type MyObject = super::T;
798+
799+
fn my_method(&self);
800+
801+
#[inherit]
802+
fn my_inherited_method(&self);
803+
}
804+
};
805+
806+
parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap();
807+
}
808+
809+
#[test]
810+
fn test_self_inlining_pin() {
811+
let mut parsed_cxxqtdata = ParsedCxxQtData::new(format_ident!("ffi"), None);
812+
let extern_rust_qt: Item = parse_quote! {
813+
unsafe extern "RustQt" {
814+
#[qobject]
815+
type MyObject = super::T;
816+
817+
#[qsignal]
818+
fn my_signal(self: Pin<&mut Self>);
819+
}
820+
};
821+
822+
parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap();
823+
}
824+
825+
#[test]
826+
fn test_self_inlining_methods_invalid() {
827+
assert_parse_errors! {
828+
|item| ParsedCxxQtData::new(format_ident!("ffi"), None).parse_cxx_qt_item(item) =>
829+
// No QObject in block
830+
{
831+
extern "RustQt" {
832+
fn my_method(&self);
833+
}
834+
}
835+
836+
{
837+
extern "RustQt" {
838+
fn my_method(self: Pin<&mut Self>);
839+
}
840+
}
841+
// More than 1 QObjects in block
842+
{
843+
extern "RustQt" {
844+
#[qobject]
845+
type MyObject = super::T;
846+
847+
#[qobject]
848+
type MyOtherObject = super::S;
849+
850+
fn my_method(&self);
851+
}
852+
}
853+
}
854+
}
855+
856+
#[test]
857+
fn test_invalid_inline_call() {
858+
let method_sig = parse_quote! {
859+
fn test(&self);
860+
};
861+
let mut methods = vec![ParsedMethod::mock_qinvokable(&method_sig)];
862+
863+
// If inlining is set to take place, an Ident is required to inline, here it is `None`
864+
let data = ParsedCxxQtData::try_inline_self_types(true, &None, &mut methods);
865+
assert!(data.is_err());
866+
}
738867
}

crates/cxx-qt-gen/src/parser/inherit.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::parser::{
88
};
99
use core::ops::Deref;
1010
use quote::format_ident;
11+
use std::ops::DerefMut;
1112
use syn::{Attribute, ForeignItemFn, Ident, Result};
1213

1314
/// Describes a method found in an extern "RustQt" with #[inherit]
@@ -56,6 +57,12 @@ impl Deref for ParsedInheritedMethod {
5657
}
5758
}
5859

60+
impl DerefMut for ParsedInheritedMethod {
61+
fn deref_mut(&mut self) -> &mut Self::Target {
62+
&mut self.method_fields
63+
}
64+
}
65+
5966
#[cfg(test)]
6067
mod tests {
6168
use super::*;
@@ -68,7 +75,6 @@ mod tests {
6875
|item| ParsedInheritedMethod::parse(item, CaseConversion::none()) =>
6976

7077
// Missing self type
71-
{ fn test(&self); }
7278
{ fn test(self: &mut T); }
7379
// Pointer types
7480
{ fn test(self: *const T); }
@@ -91,6 +97,14 @@ mod tests {
9197
CaseConversion::none()
9298
)
9399
.is_ok());
100+
// T by ref is ok in this shorthand (provided the block has one QObject)
101+
assert!(ParsedInheritedMethod::parse(
102+
parse_quote! {
103+
fn test(&self);
104+
},
105+
CaseConversion::none()
106+
)
107+
.is_ok());
94108
// T by Pin
95109
assert!(ParsedInheritedMethod::parse(
96110
parse_quote! {

crates/cxx-qt-gen/src/parser/method.rs

+12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use crate::{
99
syntax::{foreignmod, types},
1010
};
1111
use core::ops::Deref;
12+
use quote::format_ident;
1213
use std::collections::{BTreeMap, HashSet};
14+
use std::ops::DerefMut;
1315
use syn::{Attribute, ForeignItemFn, Ident, Result};
1416

1517
/// Describes a C++ specifier for the Q_INVOKABLE
@@ -146,6 +148,12 @@ impl Deref for ParsedMethod {
146148
}
147149
}
148150

151+
impl DerefMut for ParsedMethod {
152+
fn deref_mut(&mut self) -> &mut Self::Target {
153+
&mut self.method_fields
154+
}
155+
}
156+
149157
/// Struct with common fields between Invokable types.
150158
/// These types are ParsedSignal, ParsedMethod and ParsedInheritedMethod
151159
#[derive(Clone)]
@@ -156,6 +164,7 @@ pub struct MethodFields {
156164
pub parameters: Vec<ParsedFunctionParameter>,
157165
pub safe: bool,
158166
pub name: Name,
167+
pub self_unresolved: bool,
159168
}
160169

161170
impl MethodFields {
@@ -164,6 +173,8 @@ impl MethodFields {
164173
let (qobject_ident, mutability) = types::extract_qobject_ident(&self_receiver.ty)?;
165174
let mutable = mutability.is_some();
166175

176+
let self_unresolved = qobject_ident == format_ident!("Self");
177+
167178
let parameters = ParsedFunctionParameter::parse_all_ignoring_receiver(&method.sig)?;
168179
let safe = method.sig.unsafety.is_none();
169180
let name =
@@ -176,6 +187,7 @@ impl MethodFields {
176187
parameters,
177188
safe,
178189
name,
190+
self_unresolved,
179191
})
180192
}
181193
}

crates/cxx-qt-gen/src/parser/signals.rs

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
syntax::path::path_compare_str,
99
};
1010
use core::ops::Deref;
11+
use std::ops::DerefMut;
1112
use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility};
1213

1314
#[derive(Clone)]
@@ -74,6 +75,12 @@ impl Deref for ParsedSignal {
7475
}
7576
}
7677

78+
impl DerefMut for ParsedSignal {
79+
fn deref_mut(&mut self) -> &mut Self::Target {
80+
&mut self.method_fields
81+
}
82+
}
83+
7784
#[cfg(test)]
7885
mod tests {
7986
use syn::parse_quote;

crates/cxx-qt-gen/src/syntax/foreignmod.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,7 @@ pub fn self_type_from_foreign_fn(signature: &Signature) -> Result<Receiver> {
174174
));
175175
}
176176

177-
if receiver.reference.is_some() {
178-
return Err(Error::new(
179-
receiver.span(),
180-
"Reference on self (i.e. `&self`) are not supported! Use `self: &T` instead",
181-
));
182-
}
183-
184-
if receiver.colon_token.is_none() {
177+
if receiver.colon_token.is_none() && receiver.reference.is_none() {
185178
return Err(Error::new(
186179
receiver.span(),
187180
"`self` is not supported as receiver! Use `self: T` to indicate a type.",
@@ -254,8 +247,6 @@ mod tests {
254247
{ fn foo(self); }
255248
// self with mut
256249
{ fn foo(mut self: T); }
257-
// self reference
258-
{ fn foo(&self); }
259250
// self reference with mut
260251
{ fn foo(&mut self); }
261252
// attribute on self type

crates/cxx-qt-gen/test_inputs/signals.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ mod ffi {
2424
type MyObject = super::MyObjectRust;
2525

2626
#[qsignal]
27-
fn ready(self: Pin<&mut MyObject>);
27+
fn ready(self: Pin<&mut Self>);
2828

2929
#[qsignal]
3030
fn data_changed(
31-
self: Pin<&mut MyObject>,
31+
self: Pin<&mut Self>,
3232
first: i32,
3333
second: UniquePtr<Opaque>,
3434
third: QPoint,
@@ -39,14 +39,14 @@ mod ffi {
3939
#[inherit]
4040
#[qsignal]
4141
fn base_class_new_data(
42-
self: Pin<&mut MyObject>,
42+
self: Pin<&mut Self>,
4343
first: i32,
4444
second: UniquePtr<Opaque>,
4545
third: QPoint,
4646
fourth: &'a QPoint,
4747
);
4848

4949
#[qinvokable]
50-
fn invokable(self: Pin<&mut MyObject>);
50+
fn invokable(self: Pin<&mut Self>);
5151
}
5252
}

0 commit comments

Comments
 (0)