Skip to content

Commit 4ed6351

Browse files
Move ParsedMethods into structuring phase
* Start refactor of methods and signals int o structuring phase. ParsedCxxQtData now has method and signal fields, and StructuredQObject has a methods hashmap * Assemble HashMap of methods from structure phase. StructuredQObject now should have a correct HashMap of it's methods, as built in Structure * Add signals HashMap to StructuredQObject. * Refactor Structuring phase Added a name check function to StructuredQObject for searching for matches with method definitions Switched methods and signals to use a Vec instead of HashMap in StructuredQObject Rewrite test in Structuring due to using Vec over HashMap Related to #1004
1 parent fd77bcb commit 4ed6351

File tree

8 files changed

+179
-39
lines changed

8 files changed

+179
-39
lines changed

crates/cxx-qt-gen/src/generator/cpp/qobject.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ impl GeneratedCppQObject {
154154
&qobject_idents,
155155
type_names,
156156
)?);
157+
157158
generated.blocks.append(&mut inherit::generate(
158159
&qobject.inherited_methods,
159160
&qobject.base_class,
@@ -175,7 +176,7 @@ impl GeneratedCppQObject {
175176
let (initializer, mut blocks) = threading::generate(&qobject_idents)?;
176177
generated.blocks.append(&mut blocks);
177178
class_initializers.push(initializer);
178-
// If this type has locking enabled then add generation
179+
// If this type has locking enabled then add generation
179180
} else if qobject.locking {
180181
let (initializer, mut blocks) = locking::generate()?;
181182
generated.blocks.append(&mut blocks);

crates/cxx-qt-gen/src/generator/cpp/signal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ mod tests {
521521
let mut type_names = TypeNames::default();
522522
type_names.mock_insert("ObjRust", None, None, None);
523523
let qobject_name = type_names.lookup(&signal.qobject_ident).unwrap();
524-
let generated = generate_cpp_signal(&signal, &qobject_name, &type_names).unwrap();
524+
let generated = generate_cpp_signal(&signal, qobject_name, &type_names).unwrap();
525525

526526
assert_eq!(generated.methods.len(), 0);
527527

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

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use quote::quote;
2323
use syn::{Ident, Result};
2424

2525
impl GeneratedRustFragment {
26+
// Might need to be refactored to use a StructuredQObject instead (confirm with Leon)
2627
pub fn from_qobject(
2728
qobject: &ParsedQObject,
2829
type_names: &TypeNames,

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

+118-17
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99
/// cxx-qt-gen, especially to simplify parsing.
1010
/// This module is responsible for structuring the parsed data into a form that is easier to work
1111
/// with when generating C++ code.
12-
/// This mostly means grouping QObjects with their QEnums, QSignals, etc..
12+
/// This mostly means grouping QObjects with their QEnums, QSignals, etc...
1313
///
1414
/// All resulting structures are listed in the `Structures` struct.
1515
pub mod qobject;
16-
pub use qobject::StructuredQObject;
1716

1817
use crate::parser::cxxqtdata::ParsedCxxQtData;
18+
pub use qobject::StructuredQObject;
1919
use syn::{Error, Result};
2020

2121
/// The list of all structures that could be associated from the parsed data.
2222
/// Most importantly, this includes the list of qobjects.
2323
pub struct Structures<'a> {
2424
/// The list of qobjects
25-
pub qobjects: Vec<qobject::StructuredQObject<'a>>,
25+
pub qobjects: Vec<StructuredQObject<'a>>,
2626
}
2727

2828
impl<'a> Structures<'a> {
@@ -32,28 +32,129 @@ impl<'a> Structures<'a> {
3232
let mut qobjects: Vec<_> = cxxqtdata
3333
.qobjects
3434
.values()
35-
.map(|qobject| StructuredQObject {
36-
declaration: qobject,
37-
qenums: Vec::new(),
38-
})
35+
.map(StructuredQObject::from_qobject)
3936
.collect();
4037

4138
for qenum in &cxxqtdata.qenums {
4239
if let Some(qobject_ident) = &qenum.qobject {
43-
if let Some(qobject) = qobjects
40+
let qobject = qobjects
4441
.iter_mut()
45-
.find(|qobject| qobject.declaration.name.rust_unqualified() == qobject_ident)
46-
{
47-
qobject.qenums.push(qenum);
48-
} else {
49-
return Err(Error::new_spanned(
50-
qobject_ident,
51-
format!("Unknown QObject: {qobject_ident}"),
52-
));
53-
}
42+
.find(|qobject| qobject.has_qobject_name(qobject_ident))
43+
.ok_or_else(|| {
44+
Error::new_spanned(
45+
qobject_ident,
46+
format!("Unknown QObject: {qobject_ident}"),
47+
)
48+
})?;
49+
qobject.qenums.push(qenum);
5450
}
5551
}
5652

53+
// Associate each method parsed with its appropriate qobject
54+
for method in &cxxqtdata.methods {
55+
let qobject = qobjects
56+
.iter_mut()
57+
.find(|qobject| qobject.has_qobject_name(&method.qobject_ident))
58+
.ok_or_else(|| {
59+
Error::new_spanned(
60+
&method.qobject_ident,
61+
format!("Unknown QObject: {:?}", &method.qobject_ident),
62+
)
63+
})?;
64+
qobject.methods.push(method);
65+
}
66+
67+
// Associate each signal parsed with its appropriate qobject
68+
for signal in &cxxqtdata.signals {
69+
let qobject = qobjects
70+
.iter_mut()
71+
.find(|qobject| qobject.has_qobject_name(&signal.qobject_ident))
72+
.ok_or_else(|| {
73+
Error::new_spanned(
74+
&signal.qobject_ident,
75+
format!("Unknown QObject: {:?}", &signal.qobject_ident),
76+
)
77+
})?;
78+
qobject.signals.push(signal);
79+
}
5780
Ok(Structures { qobjects })
5881
}
5982
}
83+
84+
#[cfg(test)]
85+
mod tests {
86+
use super::*;
87+
use crate::Parser;
88+
use quote::format_ident;
89+
use syn::{parse_quote, ItemMod};
90+
91+
#[test]
92+
fn test_structures() {
93+
let module: ItemMod = parse_quote! {
94+
#[cxx_qt::bridge]
95+
mod ffi {
96+
extern "RustQt" {
97+
#[qobject]
98+
type MyObject = super::MyObjectRust;
99+
100+
#[qobject]
101+
type MyOtherObject = super::MyOtherObjectRust;
102+
}
103+
104+
unsafe extern "RustQt" {
105+
#[qinvokable]
106+
fn test_fn(self: Pin<&mut MyObject>);
107+
108+
#[qinvokable]
109+
fn test_fn_two(self: Pin<&mut MyObject>);
110+
111+
#[qinvokable]
112+
fn test_fn_again(self: Pin<&mut MyOtherObject>);
113+
114+
#[qsignal]
115+
fn ready(self: Pin<&mut MyOtherObject>);
116+
}
117+
}
118+
};
119+
120+
let parser = Parser::from(module.clone()).unwrap();
121+
let structures = Structures::new(&parser.cxx_qt_data).unwrap();
122+
123+
assert_eq!(structures.qobjects.len(), 2);
124+
let my_object = &structures.qobjects[0];
125+
let my_other_object = &structures.qobjects[1];
126+
127+
assert_eq!(
128+
*my_object.declaration.name.rust_unqualified(),
129+
format_ident!("MyObject")
130+
);
131+
assert_eq!(
132+
*my_other_object.declaration.name.rust_unqualified(),
133+
format_ident!("MyOtherObject")
134+
);
135+
136+
assert_eq!(my_object.methods.len(), 2);
137+
assert_eq!(my_other_object.methods.len(), 1);
138+
139+
assert!(my_object.signals.is_empty());
140+
assert_eq!(my_other_object.signals.len(), 1);
141+
142+
// Checking methods were registered
143+
assert_eq!(
144+
*my_object.methods[0].name.rust_unqualified(),
145+
format_ident!("test_fn")
146+
);
147+
assert_eq!(
148+
*my_object.methods[1].name.rust_unqualified(),
149+
format_ident!("test_fn_two")
150+
);
151+
assert_eq!(
152+
*my_other_object.methods[0].name.rust_unqualified(),
153+
format_ident!("test_fn_again")
154+
);
155+
assert_eq!(
156+
*my_other_object.signals[0].name.rust_unqualified(),
157+
format_ident!("ready")
158+
);
159+
}
160+
}

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

+21
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,32 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6+
use crate::parser::method::ParsedMethod;
7+
use crate::parser::signals::ParsedSignal;
68
use crate::parser::{qenum::ParsedQEnum, qobject::ParsedQObject};
9+
use proc_macro2::Ident;
710

811
/// The StructuredQObject contains the parsed QObject and all members.
912
/// This includes QEnums, QSignals, methods, etc.
1013
pub struct StructuredQObject<'a> {
1114
pub declaration: &'a ParsedQObject,
1215
pub qenums: Vec<&'a ParsedQEnum>,
16+
pub methods: Vec<&'a ParsedMethod>,
17+
pub signals: Vec<&'a ParsedSignal>,
18+
}
19+
20+
impl<'a> StructuredQObject<'a> {
21+
pub fn has_qobject_name(&self, ident: &Ident) -> bool {
22+
self.declaration.name.rust_unqualified() == ident
23+
}
24+
25+
/// Creates a [StructuredQObject] from a [ParsedQObject] with empty enum, method and signal collections
26+
pub fn from_qobject(qobject: &'a ParsedQObject) -> Self {
27+
Self {
28+
declaration: qobject,
29+
qenums: vec![],
30+
methods: vec![],
31+
signals: vec![],
32+
}
33+
}
1334
}

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

+28-18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ pub struct ParsedCxxQtData {
3131
pub qobjects: BTreeMap<Ident, ParsedQObject>,
3232
/// List of QEnums defined in the module, that aren't associated with a QObject
3333
pub qenums: Vec<ParsedQEnum>,
34+
/// List of methods and Q_INVOKABLES found
35+
pub methods: Vec<ParsedMethod>,
36+
/// List of the Q_SIGNALS found
37+
pub signals: Vec<ParsedSignal>,
3438
/// List of QNamespace declarations
3539
pub qnamespaces: Vec<ParsedQNamespace>,
3640
/// Blocks of extern "C++Qt"
@@ -47,6 +51,8 @@ impl ParsedCxxQtData {
4751
Self {
4852
qobjects: BTreeMap::<Ident, ParsedQObject>::default(),
4953
qenums: vec![],
54+
methods: vec![],
55+
signals: vec![],
5056
qnamespaces: vec![],
5157
extern_cxxqt_blocks: Vec::<ParsedExternCxxQt>::default(),
5258
module_ident,
@@ -204,31 +210,38 @@ impl ParsedCxxQtData {
204210
if let ForeignItem::Fn(mut foreign_fn) = item {
205211
// Test if the function is a signal
206212
if attribute_take_path(&mut foreign_fn.attrs, &["qsignal"]).is_some() {
207-
let parsed_signal_method = ParsedSignal::parse(foreign_fn, safe_call)?;
213+
let parsed_signal_method = ParsedSignal::parse(foreign_fn.clone(), safe_call)?;
214+
215+
let parsed_signal_method_self = ParsedSignal::parse(foreign_fn, safe_call)?;
216+
self.signals.push(parsed_signal_method_self);
208217

209218
self.with_qobject(&parsed_signal_method.qobject_ident)?
210219
.signals
211220
.push(parsed_signal_method);
212-
// Test if the function is an inheritance method
213-
//
214-
// Note that we need to test for qsignal first as qsignals have their own inherit meaning
221+
222+
// Test if the function is an inheritance method
223+
//
224+
// Note that we need to test for qsignal first as qsignals have their own inherit meaning
215225
} else if attribute_take_path(&mut foreign_fn.attrs, &["inherit"]).is_some() {
216226
let parsed_inherited_method =
217227
ParsedInheritedMethod::parse(foreign_fn, safe_call)?;
218228

219229
self.with_qobject(&parsed_inherited_method.qobject_ident)?
220230
.inherited_methods
221231
.push(parsed_inherited_method);
222-
// Remaining methods are either C++ methods or invokables
232+
// Remaining methods are either C++ methods or invokables
223233
} else {
224-
let parsed_method = ParsedMethod::parse(foreign_fn, safe_call)?;
234+
let parsed_method = ParsedMethod::parse(foreign_fn.clone(), safe_call)?;
235+
236+
let parsed_method_self = ParsedMethod::parse(foreign_fn, safe_call)?;
237+
self.methods.push(parsed_method_self);
238+
225239
self.with_qobject(&parsed_method.qobject_ident)?
226240
.methods
227241
.push(parsed_method);
228242
}
229243
}
230244
}
231-
232245
Ok(())
233246
}
234247

@@ -468,9 +481,10 @@ mod tests {
468481
};
469482
let result = cxx_qt_data.parse_cxx_qt_item(item).unwrap();
470483
assert!(result.is_none());
471-
assert_eq!(cxx_qt_data.qobjects[&qobject_ident()].methods.len(), 2);
472-
assert!(cxx_qt_data.qobjects[&qobject_ident()].methods[0].is_qinvokable);
473-
assert!(!cxx_qt_data.qobjects[&qobject_ident()].methods[1].is_qinvokable);
484+
485+
assert_eq!(cxx_qt_data.methods.len(), 2);
486+
assert!(cxx_qt_data.methods[0].is_qinvokable);
487+
assert!(!cxx_qt_data.methods[1].is_qinvokable)
474488
}
475489

476490
#[test]
@@ -623,10 +637,7 @@ mod tests {
623637
}
624638
};
625639
cxxqtdata.parse_cxx_qt_item(block).unwrap();
626-
627-
let qobject = cxxqtdata.qobjects.get(&qobject_ident()).unwrap();
628-
629-
let signals = &qobject.signals;
640+
let signals = &cxxqtdata.signals;
630641
assert_eq!(signals.len(), 2);
631642
assert!(signals[0].mutable);
632643
assert!(signals[1].mutable);
@@ -653,7 +664,8 @@ mod tests {
653664
fn ready(self: Pin<&mut UnknownObj>);
654665
}
655666
};
656-
assert!(cxxqtdata.parse_cxx_qt_item(block).is_err());
667+
let parsed_block = cxxqtdata.parse_cxx_qt_item(block);
668+
assert!(parsed_block.is_err());
657669
}
658670

659671
#[test]
@@ -667,9 +679,7 @@ mod tests {
667679
};
668680
cxxqtdata.parse_cxx_qt_item(block).unwrap();
669681

670-
let qobject = cxxqtdata.qobjects.get(&qobject_ident()).unwrap();
671-
672-
let signals = &qobject.signals;
682+
let signals = &cxxqtdata.signals;
673683
assert_eq!(signals.len(), 1);
674684
assert!(signals[0].mutable);
675685
assert!(!signals[0].safe);

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

+7
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ impl ParsedMethod {
8484

8585
let name = Name::from_rust_ident_and_attrs(&method.sig.ident, &method.attrs, None, None)?;
8686

87+
if name.namespace().is_some() {
88+
return Err(Error::new_spanned(
89+
method.sig.ident,
90+
"Methods / QInvokables cannot have a namespace attribute",
91+
));
92+
}
93+
8794
Ok(ParsedMethod {
8895
method,
8996
qobject_ident,

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl Parser {
5858
// Parse any namespace in the cxx_qt::bridge macro
5959
if name_value.path.is_ident("namespace") {
6060
namespace = Some(expr_to_string(&name_value.value)?);
61-
// Parse any custom file stem
61+
// Parse any custom file stem
6262
} else if name_value.path.is_ident("cxx_file_stem") {
6363
cxx_file_stem = expr_to_string(&name_value.value)?;
6464
}
@@ -156,7 +156,6 @@ mod tests {
156156
pub fn f64_type() -> Type {
157157
parse_quote! { f64 }
158158
}
159-
160159
#[test]
161160
fn test_parser_from_empty_module() {
162161
let module: ItemMod = parse_quote! {

0 commit comments

Comments
 (0)