Skip to content

Commit b9d3aa1

Browse files
Move inlining to it's own phase
1 parent 6180ef0 commit b9d3aa1

5 files changed

Lines changed: 153 additions & 127 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
mod generator;
1212
mod naming;
1313
mod parser;
14+
mod preprocessor;
1415
mod syntax;
1516
mod writer;
1617

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

Lines changed: 4 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
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;
109
use crate::parser::CaseConversion;
10+
use crate::preprocessor::self_inlining::try_inline_self_invokables;
1111
use crate::{
1212
parser::{
1313
externcxxqt::ParsedExternCxxQt, inherit::ParsedInheritedMethod, method::ParsedMethod,
@@ -19,7 +19,6 @@ use crate::{
1919
},
2020
};
2121
use quote::format_ident;
22-
use std::ops::DerefMut;
2322
use syn::{
2423
spanned::Spanned, Error, ForeignItem, Ident, Item, ItemEnum, ItemForeignMod, ItemImpl,
2524
ItemMacro, Meta, Result,
@@ -68,38 +67,6 @@ impl ParsedCxxQtData {
6867
}
6968
}
7069

71-
/// Inline any `Self` types in the methods signatures with the Ident of a qobject passed in
72-
///
73-
/// If there are unresolved methods in the list, but inline is false, it will error,
74-
/// as the self inlining is only available if there is exactly one `QObject` in the block,
75-
/// and this indicates that no inlining can be done, but some `Self` types were present.
76-
pub fn try_inline_self_types(
77-
inline: bool,
78-
type_to_inline: &Option<Ident>,
79-
invokables: &mut [impl DerefMut<Target = MethodFields>],
80-
) -> Result<()> {
81-
for method in invokables.iter_mut() {
82-
if method.self_unresolved {
83-
if inline {
84-
if let Some(inline_type) = type_to_inline.clone() {
85-
method.qobject_ident = inline_type;
86-
} else {
87-
return Err(Error::new(
88-
method.method.span(),
89-
"Expected a type to inline, no `qobject` typename was passed!",
90-
));
91-
}
92-
} else {
93-
return Err(Error::new(
94-
method.method.span(),
95-
"`Self` type can only be inferred if the extern block contains only one `qobject`.",
96-
));
97-
}
98-
}
99-
}
100-
Ok(())
101-
}
102-
10370
/// Determine if the given [syn::Item] is a CXX-Qt related item
10471
/// If it is then add the [syn::Item] into qobjects BTreeMap
10572
/// Otherwise return the [syn::Item] to pass through to CXX
@@ -253,9 +220,9 @@ impl ParsedCxxQtData {
253220
.last()
254221
.map(|obj| format_ident!("{}", obj.declaration.ident_left));
255222

256-
Self::try_inline_self_types(inline_self, &inline_ident, &mut methods)?;
257-
Self::try_inline_self_types(inline_self, &inline_ident, &mut signals)?;
258-
Self::try_inline_self_types(inline_self, &inline_ident, &mut inherited)?;
223+
try_inline_self_invokables(inline_self, &inline_ident, &mut methods)?;
224+
try_inline_self_invokables(inline_self, &inline_ident, &mut signals)?;
225+
try_inline_self_invokables(inline_self, &inline_ident, &mut inherited)?;
259226

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6-
use crate::parser::cxxqtdata::ParsedCxxQtData;
6+
use crate::preprocessor::self_inlining::try_inline_self_invokables;
77
use crate::{
88
parser::{
99
externqobject::ParsedExternQObject, require_attributes, signals::ParsedSignal,
@@ -113,7 +113,7 @@ impl ParsedExternCxxQt {
113113
.last()
114114
.map(|obj| format_ident!("{}", obj.declaration.ident));
115115

116-
ParsedCxxQtData::try_inline_self_types(inline_self, &inline_ident, &mut signals)?;
116+
try_inline_self_invokables(inline_self, &inline_ident, &mut signals)?;
117117

118118
extern_cxx_block.qobjects.extend(qobjects);
119119
extern_cxx_block.signals.extend(signals);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2+
// SPDX-FileContributor: Ben Ford <ben.ford@kdab.com>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
pub mod self_inlining;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2+
// SPDX-FileContributor: Ben Ford <ben.ford@kdab.com>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
use crate::parser::method::MethodFields;
7+
use proc_macro2::Ident;
8+
use std::ops::DerefMut;
9+
use syn::spanned::Spanned;
10+
use syn::Error;
11+
12+
/// Inline any `Self` types in the methods signatures with the Ident of a qobject passed in
13+
///
14+
/// If there are unresolved methods in the list, but inline is false, it will error,
15+
/// as the self inlining is only available if there is exactly one `QObject` in the block,
16+
/// and this indicates that no inlining can be done, but some `Self` types were present.
17+
pub fn try_inline_self_invokables(
18+
inline: bool,
19+
type_to_inline: &Option<Ident>,
20+
invokables: &mut [impl DerefMut<Target = MethodFields>],
21+
) -> syn::Result<()> {
22+
for method in invokables.iter_mut() {
23+
if method.self_unresolved {
24+
if inline {
25+
if let Some(inline_type) = type_to_inline.clone() {
26+
method.qobject_ident = inline_type;
27+
} else {
28+
return Err(Error::new(
29+
method.method.span(),
30+
"Expected a type to inline, no `qobject` typename was passed!",
31+
));
32+
}
33+
} else {
34+
return Err(Error::new(
35+
method.method.span(),
36+
"`Self` type can only be inferred if the extern block contains only one `qobject`.",
37+
));
38+
}
39+
}
40+
}
41+
Ok(())
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use super::*;
47+
use crate::parser::cxxqtdata::ParsedCxxQtData;
48+
use crate::parser::method::ParsedMethod;
49+
use crate::tests::assert_parse_errors;
50+
use quote::format_ident;
51+
use syn::{parse_quote, Item};
52+
53+
#[test]
54+
fn test_self_inlining_ref() {
55+
let mut parsed_cxxqtdata = ParsedCxxQtData::new(format_ident!("ffi"), None);
56+
let extern_rust_qt: Item = parse_quote! {
57+
unsafe extern "RustQt" {
58+
#[qobject]
59+
type MyObject = super::T;
60+
61+
fn my_method(&self);
62+
63+
#[inherit]
64+
fn my_inherited_method(&self);
65+
}
66+
};
67+
68+
parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap();
69+
}
70+
71+
#[test]
72+
fn test_self_inlining_pin() {
73+
let mut parsed_cxxqtdata = ParsedCxxQtData::new(format_ident!("ffi"), None);
74+
let extern_rust_qt: Item = parse_quote! {
75+
unsafe extern "RustQt" {
76+
#[qobject]
77+
type MyObject = super::T;
78+
79+
#[qsignal]
80+
fn my_signal(self: Pin<&mut Self>);
81+
}
82+
};
83+
84+
let extern_cpp_qt: Item = parse_quote! {
85+
unsafe extern "C++Qt" {
86+
#[qobject]
87+
type MyObject;
88+
89+
#[qsignal]
90+
fn my_signal(self: Pin<&mut Self>);
91+
}
92+
};
93+
94+
parsed_cxxqtdata.parse_cxx_qt_item(extern_rust_qt).unwrap();
95+
parsed_cxxqtdata.parse_cxx_qt_item(extern_cpp_qt).unwrap();
96+
}
97+
98+
#[test]
99+
fn test_self_inlining_methods_invalid() {
100+
assert_parse_errors! {
101+
|item| ParsedCxxQtData::new(format_ident!("ffi"), None).parse_cxx_qt_item(item) =>
102+
// No QObject in block
103+
{
104+
extern "RustQt" {
105+
fn my_method(&self);
106+
}
107+
}
108+
109+
{
110+
extern "RustQt" {
111+
fn my_method(self: Pin<&mut Self>);
112+
}
113+
}
114+
// More than 1 QObjects in block
115+
{
116+
extern "RustQt" {
117+
#[qobject]
118+
type MyObject = super::T;
119+
120+
#[qobject]
121+
type MyOtherObject = super::S;
122+
123+
fn my_method(&self);
124+
}
125+
}
126+
}
127+
}
128+
129+
#[test]
130+
fn test_invalid_inline_call() {
131+
let method_sig = parse_quote! {
132+
fn test(&self);
133+
};
134+
let mut methods = vec![ParsedMethod::mock_qinvokable(&method_sig)];
135+
136+
// If inlining is set to take place, an Ident is required to inline, here it is `None`
137+
let data = try_inline_self_invokables(true, &None, &mut methods);
138+
assert!(data.is_err());
139+
}
140+
}

0 commit comments

Comments
 (0)