diff --git a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs index bab4f55ee..c1ac31fd3 100644 --- a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs +++ b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs @@ -8,7 +8,10 @@ use crate::syntax::foreignmod::{foreign_mod_to_foreign_item_types, ForeignTypeId use crate::syntax::path::path_from_idents; use crate::syntax::safety::Safety; use crate::{ - parser::{inherit::ParsedInheritedMethod, qobject::ParsedQObject, signals::ParsedSignal}, + parser::{ + externcxxqt::ParsedExternCxxQt, inherit::ParsedInheritedMethod, method::ParsedMethod, + qobject::ParsedQObject, signals::ParsedSignal, + }, syntax::expr::expr_to_string, }; use quote::format_ident; @@ -18,8 +21,6 @@ use syn::{ Result, Type, TypePath, }; -use super::method::ParsedMethod; - #[derive(Default)] pub struct ParsedCxxMappings { /// Map of the cxx_name of any types defined in CXX extern blocks @@ -60,6 +61,8 @@ pub struct ParsedCxxQtData { // 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: BTreeMap, + /// Blocks of extern "C++Qt" + pub extern_cxx_blocks: Vec, /// The namespace of the CXX-Qt module pub namespace: String, /// The ident of the module, used for mappings @@ -73,6 +76,7 @@ impl ParsedCxxQtData { cxx_mappings: ParsedCxxMappings::default(), qualified_mappings: BTreeMap::::default(), qobjects: BTreeMap::::default(), + extern_cxx_blocks: Vec::::default(), module_ident, namespace, } @@ -233,7 +237,12 @@ impl ParsedCxxQtData { self.parse_foreign_mod_rust_qt(foreign_mod)?; return Ok(None); } - // TODO: look for "C++Qt" later + "C++Qt" => { + self.populate_mappings_from_foreign_mod_item(&foreign_mod)?; + self.extern_cxx_blocks + .push(ParsedExternCxxQt::parse(foreign_mod)?); + return Ok(None); + } _others => {} } } @@ -561,6 +570,29 @@ mod tests { assert!(cxx_qt_data.qobjects[&qobject_ident()].threading); } + #[test] + fn test_find_and_merge_cxx_qt_item_extern_cxx_qt() { + let mut cxx_qt_data = create_parsed_cxx_qt_data(); + + let item: Item = parse_quote! { + #[namespace = "rust"] + unsafe extern "C++Qt" { + type QPushButton; + + #[qsignal] + fn clicked(self: Pin<&mut QPushButton>, checked: bool); + } + }; + let result = cxx_qt_data.parse_cxx_qt_item(item).unwrap(); + assert!(result.is_none()); + + assert_eq!(cxx_qt_data.extern_cxx_blocks.len(), 1); + assert_eq!(cxx_qt_data.extern_cxx_blocks[0].attrs.len(), 1); + assert_eq!(cxx_qt_data.extern_cxx_blocks[0].passthrough_items.len(), 1); + assert_eq!(cxx_qt_data.extern_cxx_blocks[0].signals.len(), 1); + assert!(cxx_qt_data.extern_cxx_blocks[0].unsafety.is_some()); + } + #[test] fn test_cxx_mappings_cxx_name_empty() { let mut cxx_qt_data = create_parsed_cxx_qt_data(); diff --git a/crates/cxx-qt-gen/src/parser/externcxxqt.rs b/crates/cxx-qt-gen/src/parser/externcxxqt.rs new file mode 100644 index 000000000..9f12335c5 --- /dev/null +++ b/crates/cxx-qt-gen/src/parser/externcxxqt.rs @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{ + parser::signals::ParsedSignal, + syntax::{attribute::attribute_find_path, safety::Safety}, +}; +use syn::{Attribute, ForeignItem, ItemForeignMod, Result, Token}; + +/// Representation of an extern "C++Qt" block +#[derive(Default)] +pub struct ParsedExternCxxQt { + /// Attributes for the extern "C++Qt" block + pub attrs: Vec, + /// Whether this block has an unsafe token + pub unsafety: Option, + /// Items which can be passed into the extern "C++Qt" block + pub passthrough_items: Vec, + /// Signals that need generation in the extern "C++Qt" block + pub signals: Vec, +} + +impl ParsedExternCxxQt { + pub fn parse(mut foreign_mod: ItemForeignMod) -> Result { + let mut extern_cxx_block = ParsedExternCxxQt { + attrs: foreign_mod.attrs.clone(), + unsafety: foreign_mod.unsafety, + ..Default::default() + }; + + let safe_call = if foreign_mod.unsafety.is_some() { + Safety::Safe + } else { + Safety::Unsafe + }; + + // Parse any signals, other items are passed through + for item in foreign_mod.items.drain(..) { + if let ForeignItem::Fn(foreign_fn) = &item { + // Test if the function is a signal + if let Some(index) = attribute_find_path(&foreign_fn.attrs, &["qsignal"]) { + let mut foreign_fn = foreign_fn.clone(); + // Remove the signals attribute + foreign_fn.attrs.remove(index); + + extern_cxx_block + .signals + .push(ParsedSignal::parse(foreign_fn, safe_call)?); + continue; + } + } + + extern_cxx_block.passthrough_items.push(item); + } + + Ok(extern_cxx_block) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use syn::parse_quote; + + #[test] + fn test_find_and_merge_cxx_qt_item_extern_cxx_qt() { + let extern_cxx_qt = ParsedExternCxxQt::parse(parse_quote! { + #[namespace = "rust"] + unsafe extern "C++Qt" { + type QPushButton; + + #[qsignal] + fn clicked(self: Pin<&mut QPushButton>, checked: bool); + } + }) + .unwrap(); + + assert_eq!(extern_cxx_qt.attrs.len(), 1); + assert_eq!(extern_cxx_qt.passthrough_items.len(), 1); + assert_eq!(extern_cxx_qt.signals.len(), 1); + assert!(extern_cxx_qt.unsafety.is_some()); + } +} diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 4275f0dc8..a68a18de0 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -5,6 +5,7 @@ pub mod constructor; pub mod cxxqtdata; +pub mod externcxxqt; pub mod inherit; pub mod method; pub mod parameter; @@ -71,7 +72,7 @@ impl Parser { // Loop through items and load into qobject or others and populate mappings for item in items.1.drain(..) { // Try to find any CXX-Qt items, if found add them to the relevant - // qobject. Otherwise return them to be added to other + // qobject or extern C++Qt block. Otherwise return them to be added to other if let Some(other) = cxx_qt_data.parse_cxx_qt_item(item)? { // Load any CXX name mappings cxx_qt_data.populate_mappings_from_item(&other)?;