diff --git a/.github/workflows/github-cxx-qt-tests.yml b/.github/workflows/github-cxx-qt-tests.yml index 4c77695dc..c73c28eaf 100644 --- a/.github/workflows/github-cxx-qt-tests.yml +++ b/.github/workflows/github-cxx-qt-tests.yml @@ -71,31 +71,54 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + # Note we need to match the LLVM and Rust versions + # + # See versions from the table in this link + # https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#get-coverage-of-cc-code-linked-to-rust-librarybinary + - name: Install llvm 17 + run: | + sudo apt-get update && sudo apt-get install -y llvm-17 + test -d /usr/lib/llvm-17/bin/ - name: Setup toolchain run: | + # Note that the llvm version needs to match, see the link above rustup default 1.77.2 cargo install grcov - rustup component add llvm-tools rustfmt + rustup component add rustfmt + # Ensure we do not have any existing coverage files + - run: rm -f coverage/*.profraw - name: build env: + CARGO_INCREMENTAL: 0 RUSTFLAGS: -Cinstrument-coverage LLVM_PROFILE_FILE: coverage/coverage_data-%p-%m.profraw run: cargo build --package cxx-qt-gen - name: test env: + CARGO_INCREMENTAL: 0 RUSTFLAGS: -Cinstrument-coverage LLVM_PROFILE_FILE: coverage/coverage_data-%p-%m.profraw run: cargo test --lib --package cxx-qt-gen - name: generate-report - run: grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing -o ./target/debug/lcov.info --excl-start CODECOV_EXCLUDE_START --excl-stop CODECOV_EXCLUDE_STOP + # Note that --llvm-path is important here to ensure the matching llvm version to the Rust version (1.77.x) + # Note that --keep-only is important here to ensure crates.io paths don't conflict + run: grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing --llvm --llvm-path /usr/lib/llvm-17/bin/ --keep-only "crates/*" -o ./target/debug/lcov.info --excl-start CODECOV_EXCLUDE_START --excl-stop CODECOV_EXCLUDE_STOP - name: upload-report uses: codecov/codecov-action@v5 with: directory: ./target/debug/ + disable-search: true files: lcov.info fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} verbose: true + - name: Upload GitHub Actions artifacts of lcov + if: always() + uses: actions/upload-artifact@v4 + with: + name: lcov + path: ./target/debug/lcov.info + if-no-files-found: ignore build-wasm: name: Ubuntu 24.04 (wasm_32) Qt 6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6587cd297..4ef49c360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for further types: `QUuid` - New example: Basic greeter app - Support for further types: `qreal`, `qint64`, `qintptr`, `qsizetype`, `quint64`, `quintptr` +- Support for `cfg` attributes through to C++ generation ### Fixed diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index d56a8f44c..c76f4f138 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -43,7 +43,7 @@ use std::{ }; use cxx_qt_gen::{ - parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks, + parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks, GeneratedOpt, GeneratedRustBlocks, Parser, }; @@ -103,6 +103,9 @@ impl GeneratedCpp { // The include path we inject needs any prefix (eg the crate name) too let include_ident = format!("{include_prefix}/{file_ident}"); + let mut cxx_qt_opt = GeneratedOpt::default(); + cxx_qt_opt.cfg_evaluator = Box::new(cfg_evaluator::CargoEnvCfgEvaluator); + // Loop through the items looking for any CXX or CXX-Qt blocks let mut found_bridge = false; for item in &file.items { @@ -132,7 +135,7 @@ impl GeneratedCpp { let parser = Parser::from(m.clone()) .map_err(GeneratedError::from) .map_err(to_diagnostic)?; - let generated_cpp = GeneratedCppBlocks::from(&parser) + let generated_cpp = GeneratedCppBlocks::from(&parser, &cxx_qt_opt) .map_err(GeneratedError::from) .map_err(to_diagnostic)?; let generated_rust = GeneratedRustBlocks::from(&parser) diff --git a/crates/cxx-qt-gen/Cargo.toml b/crates/cxx-qt-gen/Cargo.toml index 1b33cce8e..0191461bf 100644 --- a/crates/cxx-qt-gen/Cargo.toml +++ b/crates/cxx-qt-gen/Cargo.toml @@ -20,6 +20,7 @@ exclude = ["update_expected.sh"] rust-version = "1.64.0" [dependencies] +cxx-gen.workspace = true proc-macro2.workspace = true syn.workspace = true quote.workspace = true diff --git a/crates/cxx-qt-gen/src/generator/cfg.rs b/crates/cxx-qt-gen/src/generator/cfg.rs new file mode 100644 index 000000000..c04a578a6 --- /dev/null +++ b/crates/cxx-qt-gen/src/generator/cfg.rs @@ -0,0 +1,197 @@ +// SPDX-FileCopyrightText: CXX Authors +// SPDX-FileContributor: Andrew Hayzen +// SPDX-FileContributor: David Tolnay +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::syntax::cfg::{parse_attribute, CfgExpr}; +use cxx_gen::{CfgEvaluator, CfgResult}; +use quote::quote; +use syn::{Attribute, Error, LitStr}; + +pub(crate) fn try_eval_attributes( + cfg_evaluator: &dyn CfgEvaluator, + attrs: &[Attribute], +) -> Result { + // Build a single CfgExpr from the Attributes + let cfg_expr = attrs + .iter() + .map(parse_attribute) + .collect::, Error>>()? + .into_iter() + .reduce(|mut acc, e| { + acc.merge(e); + acc + }); + + // Evaluate the CfgExpr against the CfgEvaluator + if let Some(cfg_expr) = cfg_expr { + try_eval(cfg_evaluator, &cfg_expr).map_err(|errs| { + errs.into_iter() + .reduce(|mut acc, e| { + acc.combine(e); + acc + }) + .expect("There should be at least one error") + }) + } else { + Ok(true) + } +} + +fn try_eval(cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr) -> Result> { + match expr { + CfgExpr::Unconditional => Ok(true), + CfgExpr::Eq(ident, string) => { + let key = ident.to_string(); + let value = string.as_ref().map(LitStr::value); + match cfg_evaluator.eval(&key, value.as_deref()) { + CfgResult::True => Ok(true), + CfgResult::False => Ok(false), + CfgResult::Undetermined { msg } => { + let span = quote!(#ident #string); + Err(vec![Error::new_spanned(span, msg)]) + } + } + } + CfgExpr::All(list) => { + let mut all_errors = Vec::new(); + for subexpr in list { + match try_eval(cfg_evaluator, subexpr) { + Ok(true) => {} + Ok(false) => return Ok(false), + Err(errors) => all_errors.extend(errors), + } + } + if all_errors.is_empty() { + Ok(true) + } else { + Err(all_errors) + } + } + CfgExpr::Any(list) => { + let mut all_errors = Vec::new(); + for subexpr in list { + match try_eval(cfg_evaluator, subexpr) { + Ok(true) => return Ok(true), + Ok(false) => {} + Err(errors) => all_errors.extend(errors), + } + } + if all_errors.is_empty() { + Ok(false) + } else { + Err(all_errors) + } + } + CfgExpr::Not(subexpr) => match try_eval(cfg_evaluator, subexpr) { + Ok(value) => Ok(!value), + Err(errors) => Err(errors), + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::{generator::UnsupportedCfgEvaluator, tests::CfgEvaluatorTest}; + use syn::{parse_quote, ItemMod}; + + fn assert_eval_insert(module: ItemMod, cfgs: &[&str], [before, after]: [bool; 2]) { + let mut cfg_evaluator = Box::new(CfgEvaluatorTest::default()); + assert_eq!( + try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs).unwrap(), + before + ); + + for chunk in cfgs.chunks(2) { + assert_eq!( + try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs).unwrap(), + before + ); + + if let [key, value] = chunk { + cfg_evaluator.cfgs.insert(key, Some(value)); + } + } + + assert_eq!( + try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs).unwrap(), + after + ); + } + + fn assert_eval_insert_false_true(module: ItemMod, cfgs: &[&str]) { + assert_eval_insert(module, cfgs, [false, true]); + } + + #[test] + fn test_try_eval_attributes_eq() { + assert_eval_insert_false_true( + parse_quote! { + #[cfg(a = "1")] + #[cfg(b = "2")] + mod module; + }, + &["c", "3", "a", "1", "b", "2"], + ); + } + + #[test] + fn test_try_eval_attributes_any() { + assert_eval_insert_false_true( + parse_quote! { + #[cfg(any(a = "1", b = "2"))] + mod module; + }, + &["c", "3", "a", "1"], + ); + } + + #[test] + fn test_try_eval_attributes_all() { + assert_eval_insert_false_true( + parse_quote! { + #[cfg(all(a = "1", b = "2"))] + mod module; + }, + &["c", "3", "a", "1", "b", "2"], + ); + } + + #[test] + fn test_try_eval_attributes_not() { + assert_eval_insert( + parse_quote! { + #[cfg(not(a = "1"))] + mod module; + }, + &["c", "3", "a", "1"], + [true, false], + ); + } + + #[test] + fn test_try_eval_unconditional() { + let cfg_expr = CfgExpr::Unconditional; + let cfg_evaluator = Box::new(UnsupportedCfgEvaluator); + assert_eq!(try_eval(cfg_evaluator.as_ref(), &cfg_expr).unwrap(), true); + } + + #[test] + fn test_try_eval_attributes_undetermined_err() { + let module: ItemMod = parse_quote! { + #[cfg(a = "1")] + #[cfg(all(a = "1", b = "2"))] + #[cfg(any(a = "1", b = "2"))] + #[cfg(not(a = "1"))] + mod module; + }; + let cfg_evaluator = Box::new(UnsupportedCfgEvaluator); + assert!(try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs[0..1]).is_err()); + assert!(try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs[1..2]).is_err()); + assert!(try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs[2..3]).is_err()); + assert!(try_eval_attributes(cfg_evaluator.as_ref(), &module.attrs[3..4]).is_err()); + } +} diff --git a/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs b/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs index 09453dbbd..aa5ffcf5d 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs @@ -4,8 +4,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - generator::cpp::signal::generate_cpp_signal, naming::TypeNames, - parser::externcxxqt::ParsedExternCxxQt, CppFragment, + generator::{cpp::signal::generate_cpp_signal, GeneratedOpt}, + naming::TypeNames, + parser::externcxxqt::ParsedExternCxxQt, + CppFragment, }; use std::collections::BTreeSet; use syn::Result; @@ -23,18 +25,20 @@ pub struct GeneratedCppExternCxxQtBlocks { pub fn generate( blocks: &[ParsedExternCxxQt], type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result> { let mut out = vec![]; for block in blocks { for signal in &block.signals { - let mut block = GeneratedCppExternCxxQtBlocks::default(); let qobject_name = type_names.lookup(&signal.qobject_ident)?; - let data = generate_cpp_signal(signal, qobject_name, type_names)?; - block.includes = data.includes; - block.forward_declares = data.forward_declares; - block.fragments = data.fragments; + let data = generate_cpp_signal(signal, qobject_name, type_names, opt)?; debug_assert!(data.methods.is_empty()); + let block = GeneratedCppExternCxxQtBlocks { + includes: data.includes, + forward_declares: data.forward_declares, + fragments: data.fragments, + }; out.push(block); } } @@ -70,9 +74,10 @@ mod tests { .unwrap()]; // Unknown types - assert!(generate(&blocks, &TypeNames::default()).is_err()); + let opt = GeneratedOpt::default(); + assert!(generate(&blocks, &TypeNames::default(), &opt).is_err()); - let generated = generate(&blocks, &TypeNames::mock()).unwrap(); + let generated = generate(&blocks, &TypeNames::mock(), &opt).unwrap(); assert_eq!(generated.len(), 2); } @@ -97,7 +102,7 @@ mod tests { let mut type_names = TypeNames::default(); type_names.mock_insert("ObjRust", None, Some("ObjCpp"), Some("mynamespace")); - let generated = generate(&blocks, &type_names).unwrap(); + let generated = generate(&blocks, &type_names, &GeneratedOpt::default()).unwrap(); assert_eq!(generated.len(), 1); } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/inherit.rs b/crates/cxx-qt-gen/src/generator/cpp/inherit.rs index c6dbcf98a..66ef414c0 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/inherit.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/inherit.rs @@ -5,7 +5,11 @@ use indoc::formatdoc; use crate::{ - generator::cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks}, + generator::{ + cfg::try_eval_attributes, + cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks}, + GeneratedOpt, + }, naming::cpp::syn_type_to_cpp_return_type, naming::TypeNames, parser::inherit::ParsedInheritedMethod, @@ -17,10 +21,16 @@ pub fn generate( inherited_methods: &[&ParsedInheritedMethod], base_class: &Option, type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result { let mut result = GeneratedCppQObjectBlocks::default(); for &method in inherited_methods { + // Skip if the cfg attributes are not resolved to true + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &method.cfgs)? { + continue; + } + let return_type = syn_type_to_cpp_return_type(&method.method.sig.output, type_names)?; // Note that no qobject macro with no base class is an error // @@ -47,6 +57,7 @@ pub fn generate( #[cfg(test)] mod tests { + use crate::tests::CfgEvaluatorTest; use pretty_assertions::assert_str_eq; use syn::{parse_quote, ForeignItemFn}; @@ -62,7 +73,12 @@ mod tests { let method = ParsedInheritedMethod::parse(method, Safety::Safe, CaseConversion::none())?; let inherited_methods = vec![&method]; let base_class = base_class.map(|s| s.to_owned()); - generate(&inherited_methods, &base_class, &TypeNames::default()) + generate( + &inherited_methods, + &base_class, + &TypeNames::default(), + &GeneratedOpt::default(), + ) } fn assert_generated_eq(expected: &str, generated: &GeneratedCppQObjectBlocks) { @@ -71,6 +87,25 @@ mod tests { assert_str_eq!(header, expected); } + #[test] + fn test_cfg() { + let method = parse_quote! { + #[cfg(test_cfg_disabled)] + fn test(self: &T, a: B, b: C); + }; + let method = + ParsedInheritedMethod::parse(method, Safety::Safe, CaseConversion::none()).unwrap(); + let inherited_methods = vec![&method]; + let base_class = Some("TestBaseClass".to_owned()); + let opt = GeneratedOpt { + cfg_evaluator: Box::new(CfgEvaluatorTest::default()), + }; + let generated = + generate(&inherited_methods, &base_class, &TypeNames::default(), &opt).unwrap(); + + assert!(generated.methods.is_empty()); + } + #[test] fn test_immutable() { let generated = generate_from_foreign( diff --git a/crates/cxx-qt-gen/src/generator/cpp/method.rs b/crates/cxx-qt-gen/src/generator/cpp/method.rs index 4281db665..2aac55b80 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/method.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/method.rs @@ -3,11 +3,15 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::generator::cpp::get_cpp_params; use crate::{ - generator::cpp::{ - fragment::{CppFragment, CppNamedType}, - qobject::GeneratedCppQObjectBlocks, + generator::{ + cfg::try_eval_attributes, + cpp::{ + fragment::{CppFragment, CppNamedType}, + get_cpp_params, + qobject::GeneratedCppQObjectBlocks, + GeneratedOpt, + }, }, naming::cpp::{syn_return_type_to_cpp_except, syn_type_to_cpp_return_type}, naming::TypeNames, @@ -18,9 +22,15 @@ use syn::Result; pub fn generate_cpp_methods( invokables: &Vec<&ParsedMethod>, type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result { let mut generated = GeneratedCppQObjectBlocks::default(); for &invokable in invokables { + // Skip if the cfg attributes are not resolved to true + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &invokable.cfgs)? { + continue; + } + let return_cxx_ty = syn_type_to_cpp_return_type(&invokable.method.sig.output, type_names)?; let parameters: Vec = get_cpp_params(&invokable.method, type_names)?; @@ -77,10 +87,29 @@ mod tests { use super::*; use crate::generator::cpp::property::tests::require_header; + use crate::tests::CfgEvaluatorTest; use pretty_assertions::assert_str_eq; use std::collections::HashSet; use syn::{parse_quote, ForeignItemFn}; + #[test] + fn test_generate_cpp_invokables_cfg() { + let method: ForeignItemFn = parse_quote! { + #[cfg(test_cfg_disabled)] + #[cxx_name = "voidInvokable"] + fn void_invokable(self: &MyObject); + }; + let invokables = vec![ParsedMethod::mock_qinvokable(&method)]; + let type_names = TypeNames::mock(); + let opt = GeneratedOpt { + cfg_evaluator: Box::new(CfgEvaluatorTest::default()), + }; + let generated = + generate_cpp_methods(&invokables.iter().collect(), &type_names, &opt).unwrap(); + + assert!(generated.methods.is_empty()); + } + #[test] fn test_generate_cpp_invokables() { let method1: ForeignItemFn = parse_quote! { @@ -122,7 +151,12 @@ mod tests { let mut type_names = TypeNames::mock(); type_names.mock_insert("QColor", None, None, None); - let generated = generate_cpp_methods(&invokables.iter().collect(), &type_names).unwrap(); + let generated = generate_cpp_methods( + &invokables.iter().collect(), + &type_names, + &GeneratedOpt::default(), + ) + .unwrap(); // methods assert_eq!(generated.methods.len(), 5); @@ -168,7 +202,8 @@ mod tests { type_names.mock_insert("A", None, Some("A1"), None); type_names.mock_insert("B", None, Some("B2"), None); - let generated = generate_cpp_methods(&invokables, &type_names).unwrap(); + let generated = + generate_cpp_methods(&invokables, &type_names, &GeneratedOpt::default()).unwrap(); // methods assert_eq!(generated.methods.len(), 1); diff --git a/crates/cxx-qt-gen/src/generator/cpp/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/mod.rs index 0f26e6281..f5c74ce6b 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/mod.rs @@ -20,10 +20,13 @@ mod utils; use std::collections::BTreeSet; -use crate::generator::cpp::fragment::CppNamedType; +use crate::generator::{cfg::try_eval_attributes, cpp::fragment::CppNamedType}; use crate::naming::cpp::syn_type_to_cpp_type; use crate::naming::TypeNames; -use crate::{generator::structuring, parser::Parser}; +use crate::{ + generator::{structuring, GeneratedOpt}, + parser::Parser, +}; use externcxxqt::GeneratedCppExternCxxQtBlocks; use qobject::GeneratedCppQObject; use syn::{FnArg, ForeignItemFn, Pat, PatIdent, PatType, Result}; @@ -42,7 +45,7 @@ pub struct GeneratedCppBlocks { impl GeneratedCppBlocks { /// Create a [GeneratedCppBlocks] from the given [Parser] object - pub fn from(parser: &Parser) -> Result { + pub fn from(parser: &Parser, opt: &GeneratedOpt) -> Result { let structures = structuring::Structures::new(&parser.cxx_qt_data)?; let mut includes = BTreeSet::new(); @@ -58,7 +61,8 @@ impl GeneratedCppBlocks { .cxx_qt_data .qenums .iter() - .map(|parsed_qenum| qenum::generate_declaration(parsed_qenum, &mut includes)), + .map(|parsed_qenum| qenum::generate_declaration(parsed_qenum, &mut includes, opt)) + .collect::>>()?, ); Ok(GeneratedCppBlocks { forward_declares, @@ -66,11 +70,22 @@ impl GeneratedCppBlocks { qobjects: structures .qobjects .iter() - .map(|qobject| GeneratedCppQObject::from(qobject, &parser.type_names)) + .filter_map(|qobject| { + // Skip if the cfg attributes are not resolved to true + match try_eval_attributes(opt.cfg_evaluator.as_ref(), &qobject.declaration.cfgs) + { + Ok(true) => { + Some(GeneratedCppQObject::from(qobject, &parser.type_names, opt)) + } + Ok(false) => None, + Err(err) => Some(Err(err)), + } + }) .collect::>>()?, extern_cxx_qt: externcxxqt::generate( &parser.cxx_qt_data.extern_cxxqt_blocks, &parser.type_names, + opt, )?, }) } @@ -112,6 +127,24 @@ mod tests { use crate::parser::Parser; use syn::{parse_quote, ItemMod}; + #[test] + fn test_generated_qobject_cfg_error() { + let module: ItemMod = parse_quote! { + #[cxx_qt::bridge] + mod ffi { + extern "RustQt" { + #[qobject] + #[cfg(unknown)] + type MyObject = super::MyObjectRust; + } + } + }; + let parser = Parser::from(module).unwrap(); + // Uses an UnsupportedCfgEvaluator which will cause an error + let opt = GeneratedOpt::default(); + assert!(GeneratedCppBlocks::from(&parser, &opt).is_err()); + } + #[test] fn test_generated_cpp_blocks() { let module: ItemMod = parse_quote! { @@ -125,7 +158,8 @@ mod tests { }; let parser = Parser::from(module).unwrap(); - let cpp = GeneratedCppBlocks::from(&parser).unwrap(); + let opt = GeneratedOpt::default(); + let cpp = GeneratedCppBlocks::from(&parser, &opt).unwrap(); assert_eq!(cpp.qobjects.len(), 1); assert_eq!(cpp.qobjects[0].name.namespace(), None); } @@ -143,7 +177,8 @@ mod tests { }; let parser = Parser::from(module).unwrap(); - let cpp = GeneratedCppBlocks::from(&parser).unwrap(); + let opt = GeneratedOpt::default(); + let cpp = GeneratedCppBlocks::from(&parser, &opt).unwrap(); assert_eq!(cpp.qobjects[0].name.namespace(), Some("cxx_qt")); } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs index f7713b612..aa8c1a83b 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs @@ -7,6 +7,7 @@ use crate::generator::structuring::StructuredQObject; use crate::generator::{ cpp::{qobject::GeneratedCppQObjectBlocks, signal::generate_cpp_signals}, naming::{property::QPropertyNames, qobject::QObjectNames}, + GeneratedOpt, }; use crate::{ naming::cpp::syn_type_to_cpp_type, naming::TypeNames, parser::property::ParsedQProperty, @@ -23,6 +24,7 @@ pub fn generate_cpp_properties( qobject_idents: &QObjectNames, type_names: &TypeNames, structured_qobject: &StructuredQObject, + opt: &GeneratedOpt, ) -> Result { let mut generated = GeneratedCppQObjectBlocks::default(); let mut signals = vec![]; @@ -53,6 +55,7 @@ pub fn generate_cpp_properties( &signals.iter().collect(), qobject_idents, type_names, + opt, )?); Ok(generated) @@ -111,6 +114,7 @@ pub mod tests { &qobject_idents, &type_names, &structured_qobject, + &GeneratedOpt::default(), ) } @@ -175,6 +179,7 @@ pub mod tests { &qobject_idents, &type_names, structured_qobject, + &GeneratedOpt::default(), ) .unwrap(); @@ -213,6 +218,7 @@ pub mod tests { &qobject_idents, &type_names, structured_qobject, + &GeneratedOpt::default(), ) .unwrap(); @@ -255,6 +261,7 @@ pub mod tests { &qobject_idents, &type_names, structured_qobject, + &GeneratedOpt::default(), ) .unwrap(); @@ -305,6 +312,7 @@ pub mod tests { &qobject_idents, &type_names, &structured_qobject, + &GeneratedOpt::default(), ) .unwrap(); @@ -487,6 +495,7 @@ pub mod tests { &qobject_idents, &type_names, &structured_qobject, + &GeneratedOpt::default(), ) .unwrap(); diff --git a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs index cb183b1ff..9e75e6e4b 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs @@ -8,7 +8,11 @@ use std::collections::BTreeSet; use indoc::formatdoc; use syn::Result; -use crate::{parser::qenum::ParsedQEnum, writer::cpp::namespaced}; +use crate::{ + generator::{cfg::try_eval_attributes, GeneratedOpt}, + parser::qenum::ParsedQEnum, + writer::cpp::namespaced, +}; use super::{qobject::GeneratedCppQObjectBlocks, utils::Indent}; @@ -29,7 +33,16 @@ fn generate_definition(qenum: &ParsedQEnum) -> String { "#, enum_values = enum_values.indented(2) } } -pub fn generate_declaration(qenum: &ParsedQEnum, includes: &mut BTreeSet) -> String { +pub fn generate_declaration( + qenum: &ParsedQEnum, + includes: &mut BTreeSet, + opt: &GeneratedOpt, +) -> Result { + // Skip if the cfg attributes are not resolved to true + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &qenum.cfgs)? { + return Ok(String::new()); + } + let is_standalone = qenum.qobject.is_none(); if is_standalone { // required for Q_NAMESPACE and Q_ENUM_NS if we're not on a QObject @@ -38,7 +51,7 @@ pub fn generate_declaration(qenum: &ParsedQEnum, includes: &mut BTreeSet let enum_definition = generate_definition(qenum).indented(2); let enum_name = &qenum.name.cxx_unqualified(); - namespaced( + Ok(namespaced( qenum.name.namespace().unwrap_or_default(), // The declaration must still include Q_NAMESPACE, as otherwise moc will complain. // This is redundant with `qnamespace!`, which is now only required if you want to specify @@ -51,15 +64,21 @@ pub fn generate_declaration(qenum: &ParsedQEnum, includes: &mut BTreeSet } else { enum_definition }, - ) + )) } pub fn generate_on_qobject<'a>( qenums: impl Iterator, + opt: &GeneratedOpt, ) -> Result { let mut generated = GeneratedCppQObjectBlocks::default(); for qenum in qenums { + // Skip if the cfg attributes are not resolved to true + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &qenum.cfgs)? { + continue; + } + let mut qualified_name = qenum.name.cxx_qualified(); let enum_name = qenum.name.cxx_unqualified(); // TODO: this is a workaround for cxx_qualified not returning a fully-qualified @@ -90,11 +109,34 @@ mod tests { use std::assert_eq; use super::*; + use crate::tests::CfgEvaluatorTest; use indoc::indoc; use pretty_assertions::assert_str_eq; use quote::format_ident; use syn::parse_quote; + #[test] + fn test_cfg() { + let qenums = [ParsedQEnum::parse( + parse_quote! { + #[cfg(test_cfg_disabled)] + enum MyEnum { + A, B, C + } + }, + Some(format_ident!("MyObject")), + None, + &format_ident!("qobject"), + ) + .unwrap()]; + let opt = GeneratedOpt { + cfg_evaluator: Box::new(CfgEvaluatorTest::default()), + }; + let generated = generate_on_qobject(qenums.iter(), &opt).unwrap(); + + assert!(generated.methods.is_empty()); + } + #[test] fn generates() { let qenums = [ParsedQEnum::parse( @@ -109,7 +151,7 @@ mod tests { ) .unwrap()]; - let generated = generate_on_qobject(qenums.iter()).unwrap(); + let generated = generate_on_qobject(qenums.iter(), &GeneratedOpt::default()).unwrap(); assert_eq!(generated.includes.len(), 1); assert!(generated.includes.contains("#include ")); assert_eq!(generated.metaobjects.len(), 1); diff --git a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs index 937ff9203..47183a1ee 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs @@ -13,6 +13,7 @@ use crate::{ structuring::StructuredQObject, }, naming::Name, + GeneratedOpt, }; use crate::{naming::TypeNames, parser::qobject::ParsedQObject}; use std::collections::BTreeSet; @@ -99,6 +100,7 @@ impl GeneratedCppQObject { pub fn from( structured_qobject: &StructuredQObject, type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result { let qobject = structured_qobject.declaration; @@ -136,24 +138,29 @@ impl GeneratedCppQObject { &qobject_idents, type_names, structured_qobject, + opt, )?); generated.blocks.append(&mut generate_cpp_methods( &structured_qobject.methods, type_names, + opt, )?); generated.blocks.append(&mut generate_cpp_signals( &structured_qobject.signals, &qobject_idents, type_names, + opt, )?); generated.blocks.append(&mut inherit::generate( &structured_qobject.inherited_methods, &qobject.base_class.as_ref().map(|ident| ident.to_string()), type_names, + opt, )?); generated.blocks.append(&mut qenum::generate_on_qobject( structured_qobject.qenums.iter().cloned(), + opt, )?); let mut class_initializers = vec![]; @@ -201,9 +208,12 @@ mod tests { let parser = Parser::from(module).unwrap(); let structures = Structures::new(&parser.cxx_qt_data).unwrap(); - let cpp = - GeneratedCppQObject::from(structures.qobjects.first().unwrap(), &TypeNames::mock()) - .unwrap(); + let cpp = GeneratedCppQObject::from( + structures.qobjects.first().unwrap(), + &TypeNames::mock(), + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyObject"); assert_eq!(cpp.rust_struct.cxx_unqualified(), "MyObjectRust"); assert_eq!(cpp.namespace_internals, "cxx_qt_MyObject"); @@ -240,8 +250,12 @@ mod tests { None, ); - let cpp = - GeneratedCppQObject::from(structures.qobjects.first().unwrap(), &type_names).unwrap(); + let cpp = GeneratedCppQObject::from( + structures.qobjects.first().unwrap(), + &type_names, + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(cpp.namespace_internals, "cxx_qt::cxx_qt_MyObject"); assert_eq!(cpp.blocks.base_classes.len(), 2); assert_eq!(cpp.blocks.base_classes[0], "QStringListModel"); @@ -270,8 +284,12 @@ mod tests { let mut type_names = TypeNames::default(); type_names.mock_insert("MyNamedObject", None, None, None); type_names.mock_insert("MyNamedObjectRust", None, None, None); - let cpp = - GeneratedCppQObject::from(structures.qobjects.first().unwrap(), &type_names).unwrap(); + let cpp = GeneratedCppQObject::from( + structures.qobjects.first().unwrap(), + &type_names, + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyNamedObject"); assert_eq!(cpp.blocks.metaobjects.len(), 1); assert_eq!( @@ -286,9 +304,12 @@ mod tests { let parser = Parser::from(module).unwrap(); let structures = Structures::new(&parser.cxx_qt_data).unwrap(); - let cpp = - GeneratedCppQObject::from(structures.qobjects.first().unwrap(), &TypeNames::mock()) - .unwrap(); + let cpp = GeneratedCppQObject::from( + structures.qobjects.first().unwrap(), + &TypeNames::mock(), + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyObject"); assert_eq!(cpp.blocks.metaobjects.len(), 2); assert_eq!( @@ -314,9 +335,12 @@ mod tests { let parser = Parser::from(module).unwrap(); let structures = Structures::new(&parser.cxx_qt_data).unwrap(); - let cpp = - GeneratedCppQObject::from(structures.qobjects.first().unwrap(), &TypeNames::mock()) - .unwrap(); + let cpp = GeneratedCppQObject::from( + structures.qobjects.first().unwrap(), + &TypeNames::mock(), + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyObject"); assert_eq!(cpp.blocks.metaobjects.len(), 2); assert_eq!( diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index a15fa4bf8..1dc4808da 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -5,11 +5,13 @@ use crate::{ generator::{ + cfg::try_eval_attributes, cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks}, naming::{ qobject::QObjectNames, signals::{QSignalHelperNames, QSignalNames}, }, + GeneratedOpt, }, naming::{cpp::syn_type_to_cpp_type, Name, TypeNames}, parser::{parameter::ParsedFunctionParameter, signals::ParsedSignal}, @@ -82,9 +84,15 @@ pub fn generate_cpp_signal( signal: &ParsedSignal, qobject_name: &Name, type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result { let mut generated = CppSignalFragment::default(); + // Skip if the cfg attributes are not resolved to true + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &signal.cfgs)? { + return Ok(generated); + } + // Add the include we need generated .includes @@ -192,16 +200,19 @@ pub fn generate_cpp_signals( signals: &Vec<&ParsedSignal>, qobject_idents: &QObjectNames, type_names: &TypeNames, + opt: &GeneratedOpt, ) -> Result { let mut generated = GeneratedCppQObjectBlocks::default(); for &signal in signals { - let mut block = GeneratedCppQObjectBlocks::default(); - let data = generate_cpp_signal(signal, &qobject_idents.name, type_names)?; - block.includes = data.includes; - block.forward_declares_namespaced = data.forward_declares; - block.fragments = data.fragments; - block.methods = data.methods; + let data = generate_cpp_signal(signal, &qobject_idents.name, type_names, opt)?; + let mut block = GeneratedCppQObjectBlocks { + includes: data.includes, + forward_declares_namespaced: data.forward_declares, + fragments: data.fragments, + methods: data.methods, + ..Default::default() + }; generated.append(&mut block); } @@ -214,10 +225,30 @@ mod tests { use crate::generator::cpp::property::tests::{require_header, require_pair}; use crate::generator::naming::qobject::tests::create_qobjectname; + use crate::tests::CfgEvaluatorTest; use indoc::indoc; use pretty_assertions::assert_str_eq; use syn::{parse_quote, ForeignItemFn}; + #[test] + fn test_generate_cpp_signal_cfg() { + let method: ForeignItemFn = parse_quote! { + #[cfg(test_cfg_disabled)] + #[cxx_name = "dataChanged"] + fn data_changed(self: Pin<&mut MyObject>, trivial: i32, opaque: UniquePtr); + }; + let signal = ParsedSignal::mock(&method); + let signals = vec![&signal]; + let qobject_idents = create_qobjectname(); + let type_names = TypeNames::mock(); + let opt = GeneratedOpt { + cfg_evaluator: Box::new(CfgEvaluatorTest::default()), + }; + let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names, &opt).unwrap(); + + assert_eq!(generated.methods.len(), 0); + } + #[test] fn test_generate_cpp_signals() { let method: ForeignItemFn = parse_quote! { @@ -230,7 +261,13 @@ mod tests { let mut type_names = TypeNames::mock(); type_names.mock_insert("QColor", None, None, None); - let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names).unwrap(); + let generated = generate_cpp_signals( + &signals, + &qobject_idents, + &type_names, + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(generated.methods.len(), 1); let header = require_header(&generated.methods[0]).unwrap(); @@ -311,7 +348,13 @@ mod tests { let mut type_names = TypeNames::mock(); type_names.mock_insert("A", None, Some("A1"), None); - let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names).unwrap(); + let generated = generate_cpp_signals( + &signals, + &qobject_idents, + &type_names, + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(generated.methods.len(), 1); let header = require_header(&generated.methods[0]).unwrap(); @@ -388,8 +431,13 @@ mod tests { let signals = vec![&signal]; let qobject_idents = create_qobjectname(); - let generated = - generate_cpp_signals(&signals, &qobject_idents, &TypeNames::mock()).unwrap(); + let generated = generate_cpp_signals( + &signals, + &qobject_idents, + &TypeNames::mock(), + &GeneratedOpt::default(), + ) + .unwrap(); assert_eq!(generated.methods.len(), 0); assert_eq!(generated.fragments.len(), 1); @@ -464,7 +512,9 @@ mod tests { let mut type_names = TypeNames::default(); type_names.mock_insert("MyObject", None, None, None); let qobject_name = type_names.lookup(&signal.qobject_ident).unwrap(); - let generated = generate_cpp_signal(&signal, qobject_name, &type_names).unwrap(); + let generated = + generate_cpp_signal(&signal, qobject_name, &type_names, &GeneratedOpt::default()) + .unwrap(); assert_eq!(generated.methods.len(), 0); @@ -541,7 +591,9 @@ mod tests { let mut type_names = TypeNames::default(); type_names.mock_insert("MyObject", None, Some("ObjCpp"), Some("mynamespace")); let qobject_name = type_names.lookup(&signal.qobject_ident).unwrap(); - let generated = generate_cpp_signal(&signal, qobject_name, &type_names).unwrap(); + let generated = + generate_cpp_signal(&signal, qobject_name, &type_names, &GeneratedOpt::default()) + .unwrap(); assert_eq!(generated.methods.len(), 0); diff --git a/crates/cxx-qt-gen/src/generator/mod.rs b/crates/cxx-qt-gen/src/generator/mod.rs index 58d50ecca..96655c783 100644 --- a/crates/cxx-qt-gen/src/generator/mod.rs +++ b/crates/cxx-qt-gen/src/generator/mod.rs @@ -3,13 +3,42 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use cxx_gen::{CfgEvaluator, CfgResult}; #[cfg(test)] use syn::{parse_quote, ItemMod}; + +pub mod cfg; pub mod cpp; pub mod naming; pub mod rust; pub mod structuring; +/// Options for C++ code generation. +#[non_exhaustive] +pub struct GeneratedOpt { + /// Impl for handling conditional compilation attributes. + pub cfg_evaluator: Box, +} + +impl Default for GeneratedOpt { + fn default() -> Self { + Self { + cfg_evaluator: Box::new(UnsupportedCfgEvaluator), + } + } +} + +pub(super) struct UnsupportedCfgEvaluator; + +impl CfgEvaluator for UnsupportedCfgEvaluator { + fn eval(&self, name: &str, value: Option<&str>) -> CfgResult { + let _ = name; + let _ = value; + let msg = "cfg attribute is not supported".to_owned(); + CfgResult::Undetermined { msg } + } +} + #[cfg(test)] /// Mocks a module containing a singleton type pub fn mock_qml_singleton() -> ItemMod { @@ -25,3 +54,28 @@ pub fn mock_qml_singleton() -> ItemMod { } } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::CfgEvaluatorTest; + + #[test] + fn test_cfg_unsupported() { + let evaluator = UnsupportedCfgEvaluator {}; + let result = evaluator.eval("test", Some("test")); + assert!(matches!(result, CfgResult::Undetermined { .. })); + } + + #[test] + fn test_cfg_test() { + let mut evaluator = CfgEvaluatorTest::default(); + let result_false = evaluator.eval("test", Some("test")); + assert!(matches!(result_false, CfgResult::False)); + + evaluator.cfgs.insert("test", Some("test")); + let result_true = evaluator.eval("test", Some("test")); + assert!(matches!(result_true, CfgResult::True)); + } +} diff --git a/crates/cxx-qt-gen/src/generator/rust/constructor.rs b/crates/cxx-qt-gen/src/generator/rust/constructor.rs index 02e8875a4..433f729ea 100644 --- a/crates/cxx-qt-gen/src/generator/rust/constructor.rs +++ b/crates/cxx-qt-gen/src/generator/rust/constructor.rs @@ -20,8 +20,8 @@ use crate::{ use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ - parse_quote, parse_quote_spanned, spanned::Spanned, Error, Expr, FnArg, Ident, Item, Lifetime, - Result, Type, + parse_quote, parse_quote_spanned, spanned::Spanned, Attribute, Error, Expr, FnArg, Ident, Item, + Lifetime, Result, Type, }; const CONSTRUCTOR_ARGUMENTS: &str = "CxxQtConstructorArguments"; @@ -64,6 +64,7 @@ fn argument_members(args: &[Type]) -> Vec { fn generate_default_constructor( qobject_idents: &QObjectNames, namespace: &NamespaceName, + cfgs: &[Attribute], ) -> GeneratedRustFragment { let rust_struct_ident = qobject_idents.rust_struct.rust_unqualified(); @@ -78,12 +79,14 @@ fn generate_default_constructor( extern "Rust" { #[cxx_name = "createRs"] #[namespace = #namespace_internals] + #(#cfgs)* fn #create_rs_ident() -> Box<#rust_struct_ident>; } }], cxx_qt_mod_contents: vec![parse_quote! { #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] + #(#cfgs)* pub fn #create_rs_ident() -> std::boxed::Box<#rust_struct_ident> { // Wrapping the call to Default::default in a Box::new call leads // to a nicer error message, as it's not trying to infer trait bounds @@ -99,6 +102,7 @@ fn generate_arguments_struct( struct_name: &Name, lifetime: &Option, argument_list: &[Type], + cfgs: &[Attribute], ) -> Item { let argument_members = argument_members(argument_list); let not_empty = if argument_list.is_empty() { @@ -113,6 +117,7 @@ fn generate_arguments_struct( #[namespace = #namespace_internals] #[cxx_name = #cxx_name] #[doc(hidden)] + #(#cfgs)* struct #rust_name #lifetime { #(#argument_members,)* #not_empty // Make sure there's always at least one struct member, as CXX @@ -125,6 +130,7 @@ fn generate_arguments_initialization( struct_name: &Ident, instance_name: Ident, argument_list: &[Type], + cfgs: &[Attribute], ) -> Expr { let init_arguments = extract_arguments_from_tuple(argument_list, instance_name); let not_empty = if argument_list.is_empty() { @@ -134,6 +140,7 @@ fn generate_arguments_initialization( }; parse_quote! { + #(#cfgs)* #struct_name { #(#init_arguments,)* #not_empty @@ -183,9 +190,10 @@ pub fn generate( qobject_names: &QObjectNames, namespace: &NamespaceName, type_names: &TypeNames, + cfgs: &[Attribute], ) -> Result { if constructors.is_empty() { - return Ok(generate_default_constructor(qobject_names, namespace)); + return Ok(generate_default_constructor(qobject_names, namespace, cfgs)); } let module_ident = qobject_names.name.require_module()?; @@ -320,16 +328,19 @@ pub fn generate( &new_arguments_rust, format_ident!("new_arguments"), &constructor.new_arguments, + cfgs, ); let init_initalize_arguments = generate_arguments_initialization( &initialize_arguments_rust, format_ident!("initialize_arguments"), &constructor.initialize_arguments, + cfgs, ); let init_base_arguments = generate_arguments_initialization( &base_arguments_rust, format_ident!("base_arguments"), &constructor.base_arguments, + cfgs, ); let extract_new_arguments = extract_arguments_from_struct( @@ -356,12 +367,13 @@ pub fn generate( initialize: #initialize_arguments_rust #initialize_lifetime, } }, - generate_arguments_struct(&namespace.internal, &Name::new(base_arguments_rust.clone()).with_cxx_name(base_arguments_cxx.to_string()), &base_lifetime, &constructor.base_arguments), - generate_arguments_struct(&namespace.internal, &Name::new(new_arguments_rust.clone()).with_cxx_name(new_arguments_cxx.to_string()), &new_lifetime, &constructor.new_arguments), - generate_arguments_struct(&namespace.internal, &Name::new(initialize_arguments_rust.clone()).with_cxx_name(initialize_arguments_cxx.to_string()), &initialize_lifetime, &constructor.initialize_arguments), + generate_arguments_struct(&namespace.internal, &Name::new(base_arguments_rust.clone()).with_cxx_name(base_arguments_cxx.to_string()), &base_lifetime, &constructor.base_arguments, cfgs), + generate_arguments_struct(&namespace.internal, &Name::new(new_arguments_rust.clone()).with_cxx_name(new_arguments_cxx.to_string()), &new_lifetime, &constructor.new_arguments, cfgs), + generate_arguments_struct(&namespace.internal, &Name::new(initialize_arguments_rust.clone()).with_cxx_name(initialize_arguments_cxx.to_string()), &initialize_lifetime, &constructor.initialize_arguments, cfgs), parse_quote_spanned! { constructor.imp.span() => #[allow(clippy::needless_lifetimes)] + #(#cfgs)* extern "Rust" { #[namespace = #namespace_internals] #[cxx_name = #route_arguments_cxx] @@ -381,6 +393,7 @@ pub fn generate( result.cxx_qt_mod_contents.append(&mut vec![parse_quote_spanned! { constructor.imp.span() => #[doc(hidden)] + #(#cfgs)* // Use the catch-all lifetime here, as if a lifetime argument is specified, it should // be used in either the argument list itself, or the returned, routed arguments. // So it must be used by this function somewhere. @@ -407,6 +420,7 @@ pub fn generate( #[allow(unused_variables)] #[allow(clippy::extra_unused_lifetimes)] #[allow(clippy::unnecessary_box_returns)] + #(#cfgs)* // If we use the lifetime here for casting to the specific Constructor type, then // clippy for some reason thinks that the lifetime is unused even though it is used // by the `as` expression. @@ -420,6 +434,7 @@ pub fn generate( #[doc(hidden)] #[allow(unused_variables)] #[allow(clippy::extra_unused_lifetimes)] + #(#cfgs)* // If we use the lifetime here for casting to the specific Constructor type, then // clippy for some reason thinks that the lifetime is unused even though it is used // by the `as` expression. @@ -469,7 +484,14 @@ mod tests { type_names.mock_insert("QString", None, None, None); type_names.mock_insert("QObject", None, None, None); - generate(constructors, &mock_name(), &mock_namespace(), &type_names).unwrap() + generate( + constructors, + &mock_name(), + &mock_namespace(), + &type_names, + &vec![], + ) + .unwrap() } #[test] @@ -805,6 +827,7 @@ mod tests { &mock_name(), &mock_namespace(), &TypeNames::mock(), + &vec![], ) .is_err()); } diff --git a/crates/cxx-qt-gen/src/generator/rust/cxxqttype.rs b/crates/cxx-qt-gen/src/generator/rust/cxxqttype.rs index 11af3db35..757887690 100644 --- a/crates/cxx-qt-gen/src/generator/rust/cxxqttype.rs +++ b/crates/cxx-qt-gen/src/generator/rust/cxxqttype.rs @@ -3,14 +3,16 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{generator::naming::qobject::QObjectNames, naming::TypeNames}; -use syn::{parse_quote, Result}; - -use super::fragment::GeneratedRustFragment; +use crate::{ + generator::{naming::qobject::QObjectNames, rust::fragment::GeneratedRustFragment}, + naming::TypeNames, +}; +use syn::{parse_quote, Attribute, Result}; pub fn generate( qobject_names: &QObjectNames, type_names: &TypeNames, + cfgs: &[Attribute], ) -> Result { let cpp_struct_ident = &qobject_names.name.rust_unqualified(); let rust_struct_ident = &qobject_names.rust_struct.rust_unqualified(); @@ -27,6 +29,7 @@ pub fn generate( Ok(GeneratedRustFragment { cxx_mod_contents: vec![ parse_quote! { + #(#cfgs)* unsafe extern "C++" { #[doc(hidden)] #(#rust_fn_attrs)* @@ -34,6 +37,7 @@ pub fn generate( } }, parse_quote! { + #(#cfgs)* unsafe extern "C++" { #[doc(hidden)] #(#rust_mut_fn_attrs)* @@ -43,6 +47,7 @@ pub fn generate( ], cxx_qt_mod_contents: vec![ parse_quote! { + #(#cfgs)* impl ::core::ops::Deref for #qualified_impl { type Target = #rust_struct_ident; @@ -52,6 +57,7 @@ pub fn generate( } }, parse_quote! { + #(#cfgs)* impl ::cxx_qt::CxxQtType for #qualified_impl { type Rust = #rust_struct_ident; @@ -81,7 +87,7 @@ mod tests { let qobject = create_parsed_qobject(); let qobject_names = QObjectNames::from_qobject(&qobject, &TypeNames::mock()).unwrap(); - let generated = generate(&qobject_names, &TypeNames::mock()).unwrap(); + let generated = generate(&qobject_names, &TypeNames::mock(), &vec![]).unwrap(); assert_eq!(generated.cxx_mod_contents.len(), 2); assert_eq!(generated.cxx_qt_mod_contents.len(), 2); diff --git a/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs b/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs index 8b1999389..43d14a500 100644 --- a/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs @@ -46,6 +46,12 @@ impl GeneratedRustFragment { #[cxx_name = #cxx_name] } }; + let cfgs: Vec<&Attribute> = ty + .declaration + .attrs + .iter() + .filter(|attr| path_compare_str(attr.meta.path(), &["cfg"])) + .collect(); let docs: Vec<&Attribute> = ty .declaration .attrs @@ -55,6 +61,7 @@ impl GeneratedRustFragment { quote! { #namespace #cxx_name + #(#cfgs)* #(#docs)* #vis type #ident; } diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index 2b559eda7..46c8415f3 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -15,7 +15,7 @@ use crate::{ naming::TypeNames, }; use quote::quote; -use syn::{parse_quote, Ident, Result}; +use syn::{parse_quote, Attribute, Ident, Result}; impl GeneratedRustFragment { // Might need to be refactored to use a StructuredQObject instead (confirm with Leon) @@ -29,7 +29,12 @@ impl GeneratedRustFragment { let namespace_idents = NamespaceName::from(qobject); let mut generated = vec![ - generate_qobject_definitions(&qobject_names, qobject.base_class.clone(), type_names)?, + generate_qobject_definitions( + &qobject_names, + qobject.base_class.clone(), + type_names, + &qobject.cfgs, + )?, generate_rust_properties( &qobject.properties, &qobject_names, @@ -58,6 +63,7 @@ impl GeneratedRustFragment { &qobject_names, &namespace_idents, type_names, + &qobject.cfgs, )?); } @@ -67,8 +73,9 @@ impl GeneratedRustFragment { &qobject_names, &namespace_idents, type_names, + &qobject.cfgs, )?, - cxxqttype::generate(&qobject_names, type_names)?, + cxxqttype::generate(&qobject_names, type_names, &qobject.cfgs)?, ]); Ok(GeneratedRustFragment::flatten(generated)) @@ -80,6 +87,7 @@ fn generate_qobject_definitions( qobject_idents: &QObjectNames, base: Option, type_names: &TypeNames, + cfgs: &[Attribute], ) -> Result { let cpp_class_name_rust = &qobject_idents.name.rust_unqualified(); let cpp_class_name_cpp = &qobject_idents.name.cxx_unqualified(); @@ -103,13 +111,17 @@ fn generate_qobject_definitions( let base_upcast = if let Some(base) = base { let base_name = type_names.lookup(&base)?.rust_qualified(); vec![ - parse_quote! { impl cxx_qt::Upcast<#base_name> for #cpp_struct_qualified {} }, + parse_quote! { + #(#cfgs)* + impl cxx_qt::Upcast<#base_name> for #cpp_struct_qualified {} + }, // Until we can actually implement the Upcast trait properly, we just need to silence // the warning that the base class is otherwise unused. // This can be done with an unnamed import and the right attributes parse_quote! { #[allow(unused_imports)] #[allow(dead_code)] + #(#cfgs)* use #base_name as _; }, ] @@ -129,6 +141,7 @@ fn generate_qobject_definitions( #[doc = "See the book for more information: "] #namespace #cxx_name + #(#cfgs)* type #cpp_class_name_rust; } }, @@ -139,6 +152,7 @@ fn generate_qobject_definitions( // A Namespace from cxx_qt::bridge would be automatically applied to all children // but to apply it to only certain types, it is needed here too #namespace + #(#cfgs)* type #rust_struct_name_rust; } }, diff --git a/crates/cxx-qt-gen/src/generator/rust/signals.rs b/crates/cxx-qt-gen/src/generator/rust/signals.rs index 4f25f5074..dfe22deed 100644 --- a/crates/cxx-qt-gen/src/generator/rust/signals.rs +++ b/crates/cxx-qt-gen/src/generator/rust/signals.rs @@ -136,26 +136,28 @@ pub fn generate_rust_signal( cxx_mod_contents.extend(vec![ parse_quote! { - unsafe extern "C++" { - #[doc(hidden)] - #[namespace = #namespace_str] - type #signal_handler_alias = cxx_qt::signalhandler::CxxQtSignalHandler; + #(#cfgs)* + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = #namespace_str] + type #signal_handler_alias = cxx_qt::signalhandler::CxxQtSignalHandler; - #[doc(hidden)] - #[namespace = #namespace_str] - #[cxx_name = #free_connect_ident_cpp] - fn #free_connect_ident_rust(self_value: #self_type_cxx, signal_handler: #signal_handler_alias, conn_type: CxxQtConnectionType) -> CxxQtQMetaObjectConnection; - } + #[doc(hidden)] + #[namespace = #namespace_str] + #[cxx_name = #free_connect_ident_cpp] + fn #free_connect_ident_rust(self_value: #self_type_cxx, signal_handler: #signal_handler_alias, conn_type: CxxQtConnectionType) -> CxxQtQMetaObjectConnection; + } }, parse_quote! { - #[namespace = #namespace_str] - extern "Rust" { - #[doc(hidden)] - fn #signal_handler_drop(handler: #signal_handler_alias); + #(#cfgs)* + #[namespace = #namespace_str] + extern "Rust" { + #[doc(hidden)] + fn #signal_handler_drop(handler: #signal_handler_alias); - #[doc(hidden)] - #unsafe_call fn #signal_handler_call(handler: &mut #signal_handler_alias, self_value: #self_type_cxx, #(#parameters_cxx),*); - } + #[doc(hidden)] + #unsafe_call fn #signal_handler_call(handler: &mut #signal_handler_alias, self_value: #self_type_cxx, #(#parameters_cxx),*); + } }, ]); @@ -163,6 +165,7 @@ pub fn generate_rust_signal( cxx_mod_contents, cxx_qt_mod_contents: vec![ parse_quote! { + #(#cfgs)* impl #qualified_impl { #[doc = "Connect the given function pointer to the signal "] #[doc = #signal_name_cpp] @@ -178,6 +181,7 @@ pub fn generate_rust_signal( } }, parse_quote! { + #(#cfgs)* impl #qualified_impl { #[doc = "Connect the given function pointer to the signal "] #[doc = #signal_name_cpp] @@ -195,19 +199,23 @@ pub fn generate_rust_signal( } }, parse_quote! { + #(#cfgs)* #[doc(hidden)] pub struct #closure_struct {} }, parse_quote! { + #(#cfgs)* impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for #closure_struct { type Id = cxx::type_id!(#signal_handler_alias_namespaced_str); type FnType = dyn FnMut(#self_type_qualified, #(#parameters_qualified_type),*) + Send; } }, parse_quote! { + #(#cfgs)* use core::mem::drop as #signal_handler_drop; }, parse_quote! { + #(#cfgs)* fn #signal_handler_call( handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler<#closure_struct>, self_value: #self_type_qualified, @@ -217,9 +225,11 @@ pub fn generate_rust_signal( } }, parse_quote! { + #(#cfgs)* cxx_qt::static_assertions::assert_eq_align!(cxx_qt::signalhandler::CxxQtSignalHandler<#closure_struct>, usize); }, parse_quote! { + #(#cfgs)* cxx_qt::static_assertions::assert_eq_size!(cxx_qt::signalhandler::CxxQtSignalHandler<#closure_struct>, [usize; 2]); }, ], diff --git a/crates/cxx-qt-gen/src/generator/rust/threading.rs b/crates/cxx-qt-gen/src/generator/rust/threading.rs index 6333937c3..d0f2aae7f 100644 --- a/crates/cxx-qt-gen/src/generator/rust/threading.rs +++ b/crates/cxx-qt-gen/src/generator/rust/threading.rs @@ -10,7 +10,7 @@ use crate::{ }, naming::TypeNames, }; -use syn::{parse_quote, Result}; +use syn::{parse_quote, Attribute, Result}; use super::fragment::GeneratedRustFragment; @@ -18,6 +18,7 @@ pub fn generate( qobject_names: &QObjectNames, namespace_ident: &NamespaceName, type_names: &TypeNames, + cfgs: &[Attribute], ) -> Result { let module_ident = qobject_names.name.require_module()?; @@ -59,11 +60,13 @@ pub fn generate( // #[doc(hidden)] #[namespace = #cxx_qt_thread_namespace] + #(#cfgs)* type #cxx_qt_thread_ident = cxx_qt::CxxQtThread<#cpp_struct_ident>; include!("cxx-qt/thread.h"); #[doc(hidden)] #(#thread_fn_attrs)* + #(#cfgs)* fn #thread_fn_name(qobject: &#cpp_struct_ident) -> #cxx_qt_thread_ident; // SAFETY: @@ -71,6 +74,7 @@ pub fn generate( // - FnOnce: QMetaObject::invokeMethod() should call the function at most once. #[doc(hidden)] #(#thread_queue_attrs)* + #(#cfgs)* fn #thread_queue_name( cxx_qt_thread: &#cxx_qt_thread_ident, func: fn(Pin<&mut #cpp_struct_ident>, Box<#cxx_qt_thread_queued_fn_ident>), @@ -79,26 +83,31 @@ pub fn generate( #[doc(hidden)] #(#thread_clone_attrs)* + #(#cfgs)* fn #thread_clone_name(cxx_qt_thread: &#cxx_qt_thread_ident) -> #cxx_qt_thread_ident; #[doc(hidden)] #(#thread_drop_attrs)* + #(#cfgs)* fn #thread_drop_name(cxx_qt_thread: &mut #cxx_qt_thread_ident); #[doc(hidden)] #(#thread_is_destroyed_attrs)* + #(#cfgs)* fn #thread_is_destroyed_name(cxx_qt_thread: &#cxx_qt_thread_ident) -> bool; } }, parse_quote! { extern "Rust" { #[namespace = #namespace_internals] + #(#cfgs)* type #cxx_qt_thread_queued_fn_ident; } }, ], cxx_qt_mod_contents: vec![ parse_quote! { + #(#cfgs)* impl cxx_qt::Threading for #qualified_impl { type BoxedQueuedFn = #cxx_qt_thread_queued_fn_ident; type ThreadingTypeId = cxx::type_id!(#cxx_qt_thread_ident_type_id_str); @@ -153,6 +162,7 @@ pub fn generate( }, parse_quote! { #[doc(hidden)] + #(#cfgs)* pub struct #cxx_qt_thread_queued_fn_ident { // An opaque Rust type is required to be Sized. // https://github.com/dtolnay/cxx/issues/665 @@ -179,7 +189,13 @@ mod tests { let qobject_names = QObjectNames::from_qobject(&qobject, &TypeNames::mock()).unwrap(); let namespace_ident = NamespaceName::from(&qobject); - let generated = generate(&qobject_names, &namespace_ident, &TypeNames::mock()).unwrap(); + let generated = generate( + &qobject_names, + &namespace_ident, + &TypeNames::mock(), + &vec![], + ) + .unwrap(); assert_eq!(generated.cxx_mod_contents.len(), 2); assert_eq!(generated.cxx_qt_mod_contents.len(), 2); diff --git a/crates/cxx-qt-gen/src/lib.rs b/crates/cxx-qt-gen/src/lib.rs index 1a282884d..89e83e6b9 100644 --- a/crates/cxx-qt-gen/src/lib.rs +++ b/crates/cxx-qt-gen/src/lib.rs @@ -17,6 +17,7 @@ mod writer; pub use generator::{ cpp::{fragment::CppFragment, GeneratedCppBlocks}, rust::GeneratedRustBlocks, + GeneratedOpt, }; pub use parser::Parser; pub use syntax::{parse_qt_file, CxxQtFile, CxxQtItem}; @@ -30,12 +31,14 @@ mod tests { use crate::generator::cpp::property::tests::require_pair; use clang_format::{clang_format_with_style, ClangFormatStyle}; + use cxx_gen::{CfgEvaluator, CfgResult}; use generator::{cpp::GeneratedCppBlocks, rust::GeneratedRustBlocks}; use parser::Parser; use pretty_assertions::assert_str_eq; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use std::{ + collections::HashMap, env, fs::OpenOptions, io::Write, @@ -43,6 +46,21 @@ mod tests { }; use writer::{cpp::write_cpp, rust::write_rust}; + #[derive(Default)] + pub struct CfgEvaluatorTest<'a> { + pub cfgs: HashMap<&'a str, Option<&'a str>>, + } + + impl<'a> CfgEvaluator for CfgEvaluatorTest<'a> { + fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult { + if self.cfgs.get(name) == Some(&query_value) { + CfgResult::True + } else { + CfgResult::False + } + } + } + /// Helper to ensure that a given syn item is the same as the given TokenStream pub fn assert_tokens_eq(item: &T, tokens: TokenStream) { // For understanding what's going on, it is nicer to use format_rs_source @@ -158,7 +176,14 @@ mod tests { ) { let parser = Parser::from(syn::parse_str(input).unwrap()).unwrap(); - let generated_cpp = GeneratedCppBlocks::from(&parser).unwrap(); + let mut cfg_evaluator = CfgEvaluatorTest::default(); + cfg_evaluator.cfgs.insert("crate", Some("cxx-qt-gen")); + cfg_evaluator.cfgs.insert("enabled", None); + + let opt = GeneratedOpt { + cfg_evaluator: Box::new(cfg_evaluator), + }; + let generated_cpp = GeneratedCppBlocks::from(&parser, &opt).unwrap(); let (mut header, mut source) = require_pair(&write_cpp(&generated_cpp, "directory/file_ident")).unwrap(); header = sanitize_code(header); @@ -192,6 +217,11 @@ mod tests { }; } + #[test] + fn generates_cfgs() { + test_code_generation!("cfgs"); + } + #[test] fn generates_invokables() { test_code_generation!("invokables"); diff --git a/crates/cxx-qt-gen/src/parser/externqobject.rs b/crates/cxx-qt-gen/src/parser/externqobject.rs index ed6f2af9c..97565b22c 100644 --- a/crates/cxx-qt-gen/src/parser/externqobject.rs +++ b/crates/cxx-qt-gen/src/parser/externqobject.rs @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::naming::Name; -use crate::parser::CaseConversion; +use crate::parser::{require_attributes, CaseConversion}; use syn::{ForeignItemType, Ident, Result}; /// A representation of a QObject to be generated in an extern C++ block @@ -15,11 +15,23 @@ pub struct ParsedExternQObject { } impl ParsedExternQObject { + const ALLOWED_ATTRS: [&'static str; 6] = [ + "cxx_name", + "rust_name", + "namespace", + "cfg", + "doc", + "qobject", + // TODO: support base, qproperty etc here? + ]; + pub fn parse( ty: ForeignItemType, module_ident: &Ident, parent_namespace: Option<&str>, ) -> Result { + require_attributes(&ty.attrs, &Self::ALLOWED_ATTRS)?; + Ok(Self { name: Name::from_ident_and_attrs( &ty.ident, diff --git a/crates/cxx-qt-gen/src/parser/qobject.rs b/crates/cxx-qt-gen/src/parser/qobject.rs index 9e35546a8..7a4225cd5 100644 --- a/crates/cxx-qt-gen/src/parser/qobject.rs +++ b/crates/cxx-qt-gen/src/parser/qobject.rs @@ -5,7 +5,7 @@ use crate::{ naming::Name, - parser::{property::ParsedQProperty, require_attributes}, + parser::{extract_cfgs, property::ParsedQProperty, require_attributes}, syntax::{expr::expr_to_string, foreignmod::ForeignTypeIdentAlias, path::path_compare_str}, }; #[cfg(test)] @@ -42,13 +42,16 @@ pub struct ParsedQObject { pub has_qobject_macro: bool, /// The original declaration entered by the user, i.e. a type alias with a list of attributes pub declaration: ForeignTypeIdentAlias, + /// Cfgs for the object + pub cfgs: Vec, } impl ParsedQObject { - const ALLOWED_ATTRS: [&'static str; 10] = [ + const ALLOWED_ATTRS: [&'static str; 11] = [ "cxx_name", "rust_name", "namespace", + "cfg", "doc", "qobject", "base", @@ -71,6 +74,7 @@ impl ParsedQObject { ident_left: format_ident!("MyObject"), ident_right: format_ident!("MyObjectRust"), }, + cfgs: vec![], } } @@ -83,8 +87,8 @@ impl ParsedQObject { ) -> Result { let attributes = require_attributes(&declaration.attrs, &Self::ALLOWED_ATTRS)?; // TODO: handle docs through to generation - // - // TODO: handle cfgs on qobject + let cfgs = extract_cfgs(&declaration.attrs); + let has_qobject_macro = attributes.contains_key("qobject"); let base_class = attributes @@ -134,6 +138,7 @@ impl ParsedQObject { properties, qml_metadata, has_qobject_macro, + cfgs, }) } diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index a200b966b..f7cdfff04 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -26,7 +26,8 @@ pub struct ParsedSignal { } impl ParsedSignal { - const ALLOWED_ATTRS: [&'static str; 5] = ["cxx_name", "rust_name", "inherit", "doc", "qsignal"]; + const ALLOWED_ATTRS: [&'static str; 6] = + ["cfg", "cxx_name", "rust_name", "inherit", "doc", "qsignal"]; #[cfg(test)] /// Test fn for creating a mocked signal from a method body diff --git a/crates/cxx-qt-gen/src/syntax/cfg.rs b/crates/cxx-qt-gen/src/syntax/cfg.rs new file mode 100644 index 000000000..2f4ed367b --- /dev/null +++ b/crates/cxx-qt-gen/src/syntax/cfg.rs @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: CXX Authors +// SPDX-FileContributor: David Tolnay +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Originally found in the CXX repository +// https://github.com/dtolnay/cxx/blob/e26474acf4284235895d526c5ed12575cd9c0cce/syntax/cfg.rs + +use proc_macro2::Ident; +use std::mem; +use syn::parse::{Error, ParseStream, Result}; +use syn::{parenthesized, spanned::Spanned, token, Attribute, LitStr, Token}; + +#[derive(Clone)] +pub(crate) enum CfgExpr { + Unconditional, + Eq(Ident, Option), + All(Vec), + Any(Vec), + Not(Box), +} + +impl CfgExpr { + pub(crate) fn merge(&mut self, expr: CfgExpr) { + if let CfgExpr::Unconditional = self { + *self = expr; + } else if let CfgExpr::All(list) = self { + list.push(expr); + } else { + let prev = mem::replace(self, CfgExpr::Unconditional); + *self = CfgExpr::All(vec![prev, expr]); + } + } +} + +pub(crate) fn parse_attribute(attr: &Attribute) -> Result { + // Ensure that the attribute is a cfg attribute + if attr.path().require_ident()? != "cfg" { + return Err(Error::new(attr.span(), "Expected #[cfg(...)] attribute")); + } + + attr.parse_args_with(|input: ParseStream| { + let cfg_expr = input.call(parse_single)?; + input.parse::>()?; + Ok(cfg_expr) + }) +} + +fn parse_single(input: ParseStream) -> Result { + let ident: Ident = input.parse()?; + let lookahead = input.lookahead1(); + if input.peek(token::Paren) { + let content; + parenthesized!(content in input); + if ident == "all" { + let list = content.call(parse_multiple)?; + Ok(CfgExpr::All(list)) + } else if ident == "any" { + let list = content.call(parse_multiple)?; + Ok(CfgExpr::Any(list)) + } else if ident == "not" { + let expr = content.call(parse_single)?; + content.parse::>()?; + Ok(CfgExpr::Not(Box::new(expr))) + } else { + Err(Error::new(ident.span(), "unrecognized cfg expression")) + } + } else if lookahead.peek(Token![=]) { + input.parse::()?; + let string: LitStr = input.parse()?; + Ok(CfgExpr::Eq(ident, Some(string))) + } else if lookahead.peek(Token![,]) || input.is_empty() { + Ok(CfgExpr::Eq(ident, None)) + } else { + // CODECOV_EXCLUDE_START + Err(lookahead.error()) + // CODECOV_EXCLUDE_STOP + } +} + +fn parse_multiple(input: ParseStream) -> Result> { + let mut vec = Vec::new(); + while !input.is_empty() { + let expr = input.call(parse_single)?; + vec.push(expr); + if input.is_empty() { + break; + } + input.parse::()?; + } + Ok(vec) +} + +#[cfg(test)] +mod tests { + use super::*; + + use syn::{parse_quote, ItemMod}; + + #[test] + fn test_merge() { + let mut cfg = CfgExpr::Unconditional; + + let module: ItemMod = parse_quote! { + #[cfg(all(a, b))] + #[cfg(not(a))] + mod test; + }; + let cfg_all = parse_attribute(&module.attrs[0]).unwrap(); + assert!(matches!(cfg_all, CfgExpr::All(ref items) if items.len() == 2)); + assert!(matches!(cfg, CfgExpr::Unconditional)); + + // Merge all into unconditional + cfg.merge(cfg_all.clone()); + assert!(matches!(cfg, CfgExpr::All(ref items) if items.len() == 2)); + + // Merge all with all + cfg.merge(cfg_all.clone()); + assert!(matches!(cfg, CfgExpr::All(ref items) if items.len() == 3)); + + // Merge not with other + let mut cfg_not = parse_attribute(&module.attrs[1]).unwrap(); + assert!(matches!(cfg_not, CfgExpr::Not(..))); + cfg_not.merge(cfg_all); + assert!(matches!(cfg_not, CfgExpr::All(items) if items.len() == 2)); + } + + #[test] + fn test_parse_attribute() { + let module: ItemMod = parse_quote! { + #[cfg(a = "b")] + #[cfg(a)] + #[unknown] + mod test; + }; + let cfg_eq = parse_attribute(&module.attrs[0]).unwrap(); + assert!(matches!(cfg_eq, CfgExpr::Eq(.., Some(..)))); + + let cfg_single = parse_attribute(&module.attrs[1]).unwrap(); + assert!(matches!(cfg_single, CfgExpr::Eq(.., None))); + + let cfg_unknown = parse_attribute(&module.attrs[2]); + assert!(cfg_unknown.is_err()); + } + + #[test] + fn test_parse_attribute_parenthesis() { + let module: ItemMod = parse_quote! { + #[cfg(all(a, b))] + #[cfg(any(a, b))] + #[cfg(not(a))] + #[cfg(unknown(a))] + mod test; + }; + + let cfg_all = parse_attribute(&module.attrs[0]).unwrap(); + assert!(matches!(cfg_all, CfgExpr::All(items) if items.len() == 2)); + + let cfg_any = parse_attribute(&module.attrs[1]).unwrap(); + assert!(matches!(cfg_any, CfgExpr::Any(items) if items.len() == 2)); + + let cfg_not = parse_attribute(&module.attrs[2]).unwrap(); + assert!(matches!(cfg_not, CfgExpr::Not(..))); + + let cfg_unknown = parse_attribute(&module.attrs[3]); + assert!(cfg_unknown.is_err()); + } +} diff --git a/crates/cxx-qt-gen/src/syntax/mod.rs b/crates/cxx-qt-gen/src/syntax/mod.rs index 9677ae0ca..e46f661a4 100644 --- a/crates/cxx-qt-gen/src/syntax/mod.rs +++ b/crates/cxx-qt-gen/src/syntax/mod.rs @@ -4,6 +4,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pub mod attribute; +pub mod cfg; pub mod expr; pub mod foreignmod; pub mod lifetimes; diff --git a/crates/cxx-qt-gen/src/writer/cpp/header.rs b/crates/cxx-qt-gen/src/writer/cpp/header.rs index 9972233bc..d05559864 100644 --- a/crates/cxx-qt-gen/src/writer/cpp/header.rs +++ b/crates/cxx-qt-gen/src/writer/cpp/header.rs @@ -175,6 +175,7 @@ pub fn write_cpp_header(generated: &GeneratedCppBlocks, include_path: &str) -> S mod tests { use super::*; + use crate::generator::GeneratedOpt; use crate::tests::format_cpp; use crate::writer::cpp::tests::{ create_generated_cpp, create_generated_cpp_multi_qobjects, @@ -250,7 +251,7 @@ mod tests { let parser = Parser::from(module.clone()).unwrap(); - let generated = GeneratedCppBlocks::from(&parser).unwrap(); + let generated = GeneratedCppBlocks::from(&parser, &GeneratedOpt::default()).unwrap(); let header = write_cpp_header(&generated, "cxx-qt-gen/ffi"); let expected = indoc! {r#" #pragma once diff --git a/crates/cxx-qt-gen/test_inputs/cfgs.rs b/crates/cxx-qt-gen/test_inputs/cfgs.rs new file mode 100644 index 000000000..fa29ee3b0 --- /dev/null +++ b/crates/cxx-qt-gen/test_inputs/cfgs.rs @@ -0,0 +1,136 @@ +#[cxx_qt::bridge] +mod ffi { + // Enabled C++Qt QObject + // - disabled and enabled qsignal + unsafe extern "C++Qt" { + #[qobject] + #[cfg(enabled)] + type QObjectExternEnabled; + + #[qsignal] + #[cfg(not(enabled))] + fn signal_disabled1(self: Pin<&mut QObjectExternEnabled>); + + #[qsignal] + #[cfg(enabled)] + fn signal_enabled1(self: Pin<&mut QObjectExternEnabled>); + } + + // Disabled C++Qt QObject + // - disabled and enabled qsignal + unsafe extern "C++Qt" { + #[qobject] + #[cfg(not(enabled))] + type QObjectExternDisabled; + + #[qsignal] + #[cfg(not(enabled))] + fn signal_disabled2(self: Pin<&mut QObjectExternDisabled>); + + #[qsignal] + #[cfg(enabled)] + fn signal_enabled2(self: Pin<&mut QObjectExternDisabled>); + } + + // Enabled RustQt QObject + // - disabled and enabled qenum + + #[qenum(QObjectEnabled)] + #[cfg(not(enabled))] + enum EnumDisabled1 { + A, + } + + #[qenum(QObjectEnabled)] + #[cfg(enabled)] + enum EnumEnabled1 { + A, + } + + // Enabled RustQt QObject + // - disabled and enabled inherit + // - disabled and enabled invokable + // - disabled and enabled signal + unsafe extern "RustQt" { + #[qobject] + #[cfg(enabled)] + // TODO: should we allow for disabling properties? + // #[qproperty(i32, property_disabled, cfg(not(enabled)))] + type QObjectEnabled = super::QObjectEnabledRust; + + #[inherit] + #[cfg(not(enabled))] + fn inherit_disabled(self: &QObjectEnabled); + + #[inherit] + #[cfg(enabled)] + fn inherit_enabled(self: &QObjectEnabled); + + #[qinvokable] + #[cfg(not(enabled))] + fn invokable_disabled(self: &QObjectEnabled); + + #[qinvokable] + #[cfg(enabled)] + fn invokable_enabled(self: &QObjectEnabled); + + #[qsignal] + #[cfg(not(enabled))] + fn signal_disabled(self: Pin<&mut QObjectEnabled>); + + #[qsignal] + #[cfg(enabled)] + fn signal_enabled(self: Pin<&mut QObjectEnabled>); + } + + // Dislabed RustQt QObject + // - disabled and enabled qenum + + #[qenum(QObjectDisabled)] + #[cfg(not(enabled))] + enum EnumDisabled2 { + A, + } + + #[qenum(QObjectDisabled)] + #[cfg(enabled)] + enum EnumEnabled2 { + A, + } + + // Disabled RustQt QObject + // - disabled and enabled inherit + // - disabled and enabled invokable + // - disabled and enabled signal + unsafe extern "RustQt" { + #[qobject] + #[cfg(not(enabled))] + // TODO: should we allow for disabling properties? + // #[qproperty(i32, property_disabled, cfg(not(enabled)))] + type QObjectDisabled = super::QObjectDisabledRust; + + #[inherit] + #[cfg(not(enabled))] + fn inherit_disabled(self: &QObjectDisabled); + + #[inherit] + #[cfg(enabled)] + fn inherit_enabled(self: &QObjectDisabled); + + #[qinvokable] + #[cfg(not(enabled))] + fn invokable_disabled(self: &QObjectDisabled); + + #[qinvokable] + #[cfg(enabled)] + fn invokable_enabled(self: &QObjectDisabled); + + #[qsignal] + #[cfg(not(enabled))] + fn signal_disabled(self: Pin<&mut QObjectDisabled>); + + #[qsignal] + #[cfg(enabled)] + fn signal_enabled(self: Pin<&mut QObjectDisabled>); + } +} diff --git a/crates/cxx-qt-gen/test_inputs/cfgs.rs.license b/crates/cxx-qt-gen/test_inputs/cfgs.rs.license new file mode 100644 index 000000000..c6752c743 --- /dev/null +++ b/crates/cxx-qt-gen/test_inputs/cfgs.rs.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +SPDX-FileContributor: Andrew Hayzen + +SPDX-License-Identifier: MIT OR Apache-2.0 + diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.cpp b/crates/cxx-qt-gen/test_outputs/cfgs.cpp new file mode 100644 index 000000000..b1231344f --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.cpp @@ -0,0 +1,179 @@ +#include "directory/file_ident.cxxqt.h" + +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust::cxxqt1 { +template<> +SignalHandler< + ::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalParamssignal_enabled1*>:: + ~SignalHandler() noexcept +{ + if (data[0] == nullptr && data[1] == nullptr) { + return; + } + + drop_QObjectExternEnabled_signal_handler_signal_enabled1(::std::move(*this)); +} + +template<> +template<> +void +SignalHandler< + ::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalParamssignal_enabled1*>:: +operator()(QObjectExternEnabled& self) +{ + call_QObjectExternEnabled_signal_handler_signal_enabled1(*this, self); +} + +static_assert( + alignof( + SignalHandler<::rust::cxxqtgen1:: + QObjectExternEnabledCxxQtSignalParamssignal_enabled1*>) <= + alignof(::std::size_t), + "unexpected aligment"); +static_assert( + sizeof( + SignalHandler<::rust::cxxqtgen1:: + QObjectExternEnabledCxxQtSignalParamssignal_enabled1*>) == + sizeof(::std::size_t[2]), + "unexpected size"); +} // namespace rust::cxxqt1 + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectExternEnabled_signal_enabled1Connect( + QObjectExternEnabled& self, + ::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalHandlersignal_enabled1 + closure, + ::Qt::ConnectionType type) +{ + return ::QObject::connect( + &self, + &QObjectExternEnabled::signal_enabled1, + &self, + [&, closure = ::std::move(closure)]() mutable { + closure.template operator()(self); + }, + type); +} +} // namespace rust::cxxqtgen1 + +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust::cxxqt1 { +template<> +SignalHandler< + ::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalParamssignal_enabled2*>:: + ~SignalHandler() noexcept +{ + if (data[0] == nullptr && data[1] == nullptr) { + return; + } + + drop_QObjectExternDisabled_signal_handler_signal_enabled2(::std::move(*this)); +} + +template<> +template<> +void +SignalHandler< + ::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalParamssignal_enabled2*>:: +operator()(QObjectExternDisabled& self) +{ + call_QObjectExternDisabled_signal_handler_signal_enabled2(*this, self); +} + +static_assert( + alignof( + SignalHandler<::rust::cxxqtgen1:: + QObjectExternDisabledCxxQtSignalParamssignal_enabled2*>) <= + alignof(::std::size_t), + "unexpected aligment"); +static_assert( + sizeof( + SignalHandler<::rust::cxxqtgen1:: + QObjectExternDisabledCxxQtSignalParamssignal_enabled2*>) == + sizeof(::std::size_t[2]), + "unexpected size"); +} // namespace rust::cxxqt1 + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectExternDisabled_signal_enabled2Connect( + QObjectExternDisabled& self, + ::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalHandlersignal_enabled2 + closure, + ::Qt::ConnectionType type) +{ + return ::QObject::connect( + &self, + &QObjectExternDisabled::signal_enabled2, + &self, + [&, closure = ::std::move(closure)]() mutable { + closure.template operator()(self); + }, + type); +} +} // namespace rust::cxxqtgen1 + +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust::cxxqt1 { +template<> +SignalHandler< + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalParamssignal_enabled*>:: + ~SignalHandler() noexcept +{ + if (data[0] == nullptr && data[1] == nullptr) { + return; + } + + drop_QObjectEnabled_signal_handler_signal_enabled(::std::move(*this)); +} + +template<> +template<> +void +SignalHandler< + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalParamssignal_enabled*>:: +operator()(QObjectEnabled& self) +{ + call_QObjectEnabled_signal_handler_signal_enabled(*this, self); +} + +static_assert( + alignof(SignalHandler< + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalParamssignal_enabled*>) <= + alignof(::std::size_t), + "unexpected aligment"); +static_assert( + sizeof(SignalHandler< + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalParamssignal_enabled*>) == + sizeof(::std::size_t[2]), + "unexpected size"); +} // namespace rust::cxxqt1 + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectEnabled_signal_enabledConnect( + QObjectEnabled& self, + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalHandlersignal_enabled closure, + ::Qt::ConnectionType type) +{ + return ::QObject::connect( + &self, + &QObjectEnabled::signal_enabled, + &self, + [&, closure = ::std::move(closure)]() mutable { + closure.template operator()(self); + }, + type); +} +} // namespace rust::cxxqtgen1 + +QObjectEnabled::QObjectEnabled(QObject* parent) + : QObject(parent) + , ::rust::cxxqt1::CxxQtType( + ::cxx_qt_QObjectEnabled::createRs()) +{ +} diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.cpp.license b/crates/cxx-qt-gen/test_outputs/cfgs.cpp.license new file mode 100644 index 000000000..c6752c743 --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.cpp.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +SPDX-FileContributor: Andrew Hayzen + +SPDX-License-Identifier: MIT OR Apache-2.0 + diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.h b/crates/cxx-qt-gen/test_outputs/cfgs.h new file mode 100644 index 000000000..440bccaba --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +class QObjectEnabled; + +namespace rust::cxxqtgen1 { +using QObjectEnabledCxxQtSignalHandlersignal_enabled = + ::rust::cxxqt1::SignalHandler< + struct QObjectEnabledCxxQtSignalParamssignal_enabled*>; +} // namespace rust::cxxqtgen1 + +enum class EnumEnabled1 : ::std::int32_t +{ + A +}; + +enum class EnumEnabled2 : ::std::int32_t +{ + A +}; + +namespace rust::cxxqtgen1 { +using QObjectExternEnabledCxxQtSignalHandlersignal_enabled1 = + ::rust::cxxqt1::SignalHandler< + struct QObjectExternEnabledCxxQtSignalParamssignal_enabled1*>; +} // namespace rust::cxxqtgen1 + +namespace rust::cxxqtgen1 { +using QObjectExternDisabledCxxQtSignalHandlersignal_enabled2 = + ::rust::cxxqt1::SignalHandler< + struct QObjectExternDisabledCxxQtSignalParamssignal_enabled2*>; +} // namespace rust::cxxqtgen1 + +#include "directory/file_ident.cxx.h" + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectExternEnabled_signal_enabled1Connect( + QObjectExternEnabled& self, + ::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalHandlersignal_enabled1 + closure, + ::Qt::ConnectionType type); +} // namespace rust::cxxqtgen1 + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectExternDisabled_signal_enabled2Connect( + QObjectExternDisabled& self, + ::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalHandlersignal_enabled2 + closure, + ::Qt::ConnectionType type); +} // namespace rust::cxxqtgen1 + +namespace rust::cxxqtgen1 { +::QMetaObject::Connection +QObjectEnabled_signal_enabledConnect( + QObjectEnabled& self, + ::rust::cxxqtgen1::QObjectEnabledCxxQtSignalHandlersignal_enabled closure, + ::Qt::ConnectionType type); +} // namespace rust::cxxqtgen1 + +class QObjectEnabled + : public QObject + , public ::rust::cxxqt1::CxxQtType +{ + Q_OBJECT +public: +#ifdef Q_MOC_RUN + enum class EnumEnabled1 : ::std::int32_t{ A }; + Q_ENUM(EnumEnabled1) +#else + using EnumEnabled1 = ::EnumEnabled1; + Q_ENUM(EnumEnabled1) +#endif + + virtual ~QObjectEnabled() = default; + +public: + Q_INVOKABLE void invokable_enabled() const noexcept; + Q_SIGNAL void signal_enabled(); + template + void inherit_enabledCxxQtInherit(Args... args) const + { + return QObject::inherit_enabled(args...); + } + explicit QObjectEnabled(QObject* parent = nullptr); +}; + +static_assert(::std::is_base_of::value, + "QObjectEnabled must inherit from QObject"); + +Q_DECLARE_METATYPE(QObjectEnabled*) diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.h.license b/crates/cxx-qt-gen/test_outputs/cfgs.h.license new file mode 100644 index 000000000..c6752c743 --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.h.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +SPDX-FileContributor: Andrew Hayzen + +SPDX-License-Identifier: MIT OR Apache-2.0 + diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.rs b/crates/cxx-qt-gen/test_outputs/cfgs.rs new file mode 100644 index 000000000..b07954b71 --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.rs @@ -0,0 +1,1131 @@ +#[cxx::bridge(namespace = "")] +#[allow(unused_unsafe)] +mod ffi { + unsafe extern "C++" { + include ! (< QtCore / QObject >); + include!("cxx-qt/connection.h"); + #[doc(hidden)] + #[namespace = "Qt"] + #[rust_name = "CxxQtConnectionType"] + #[allow(dead_code)] + type ConnectionType = cxx_qt::ConnectionType; + #[doc(hidden)] + #[namespace = "rust::cxxqt1"] + #[rust_name = "CxxQtQMetaObjectConnection"] + #[allow(dead_code)] + type QMetaObjectConnection = cxx_qt::QMetaObjectConnection; + } + unsafe extern "C++" { + include!("directory/file_ident.cxxqt.h"); + } + #[repr(i32)] + #[cfg(not(enabled))] + enum EnumDisabled1 { + A, + } + extern "C++" { + #[cfg(not(enabled))] + type EnumDisabled1; + } + #[repr(i32)] + #[cfg(enabled)] + enum EnumEnabled1 { + A, + } + extern "C++" { + #[cfg(enabled)] + type EnumEnabled1; + } + #[repr(i32)] + #[cfg(not(enabled))] + enum EnumDisabled2 { + A, + } + extern "C++" { + #[cfg(not(enabled))] + type EnumDisabled2; + } + #[repr(i32)] + #[cfg(enabled)] + enum EnumEnabled2 { + A, + } + extern "C++" { + #[cfg(enabled)] + type EnumEnabled2; + } + unsafe extern "C++" { + #[doc = "The C++ type for the QObject "] + #[doc = "QObjectEnabledRust"] + #[doc = "\n"] + #[doc = "Use this type when referring to the QObject as a pointer"] + #[doc = "\n"] + #[doc = "See the book for more information: "] + #[cfg(enabled)] + type QObjectEnabled; + } + extern "Rust" { + #[cfg(enabled)] + type QObjectEnabledRust; + } + extern "Rust" { + #[cxx_name = "invokable_disabled"] + #[cfg(not(enabled))] + #[doc(hidden)] + fn invokable_disabled(self: &QObjectEnabled); + } + extern "Rust" { + #[cxx_name = "invokable_enabled"] + #[cfg(enabled)] + #[doc(hidden)] + fn invokable_enabled(self: &QObjectEnabled); + } + unsafe extern "C++" { + #[cxx_name = "inherit_disabledCxxQtInherit"] + #[cfg(not(enabled))] + fn inherit_disabled(self: &QObjectEnabled); + } + unsafe extern "C++" { + #[cxx_name = "inherit_enabledCxxQtInherit"] + #[cfg(enabled)] + fn inherit_enabled(self: &QObjectEnabled); + } + unsafe extern "C++" { + #[cxx_name = "signal_disabled"] + #[cfg(not(enabled))] + fn signal_disabled(self: Pin<&mut QObjectEnabled>); + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectEnabledCxxQtSignalHandlersignal_disabled = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectEnabledCxxQtSignalClosuresignal_disabled, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectEnabled_signal_disabledConnect"] + fn QObjectEnabled_connect_signal_disabled( + self_value: Pin<&mut QObjectEnabled>, + signal_handler: QObjectEnabledCxxQtSignalHandlersignal_disabled, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(not(enabled))] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectEnabled_signal_handler_signal_disabled( + handler: QObjectEnabledCxxQtSignalHandlersignal_disabled, + ); + #[doc(hidden)] + fn call_QObjectEnabled_signal_handler_signal_disabled( + handler: &mut QObjectEnabledCxxQtSignalHandlersignal_disabled, + self_value: Pin<&mut QObjectEnabled>, + ); + } + unsafe extern "C++" { + #[cxx_name = "signal_enabled"] + #[cfg(enabled)] + fn signal_enabled(self: Pin<&mut QObjectEnabled>); + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectEnabledCxxQtSignalHandlersignal_enabled = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectEnabledCxxQtSignalClosuresignal_enabled, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectEnabled_signal_enabledConnect"] + fn QObjectEnabled_connect_signal_enabled( + self_value: Pin<&mut QObjectEnabled>, + signal_handler: QObjectEnabledCxxQtSignalHandlersignal_enabled, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(enabled)] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectEnabled_signal_handler_signal_enabled( + handler: QObjectEnabledCxxQtSignalHandlersignal_enabled, + ); + #[doc(hidden)] + fn call_QObjectEnabled_signal_handler_signal_enabled( + handler: &mut QObjectEnabledCxxQtSignalHandlersignal_enabled, + self_value: Pin<&mut QObjectEnabled>, + ); + } + extern "Rust" { + #[cxx_name = "createRs"] + #[namespace = "cxx_qt_QObjectEnabled"] + #[cfg(enabled)] + fn create_rs_QObjectEnabledRust() -> Box; + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[cxx_name = "unsafeRust"] + #[namespace = "rust::cxxqt1"] + fn cxx_qt_ffi_QObjectEnabled_unsafeRust(outer: &QObjectEnabled) -> &QObjectEnabledRust; + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[cxx_name = "unsafeRustMut"] + #[namespace = "rust::cxxqt1"] + fn cxx_qt_ffi_QObjectEnabled_unsafeRustMut( + outer: Pin<&mut QObjectEnabled>, + ) -> Pin<&mut QObjectEnabledRust>; + } + unsafe extern "C++" { + #[doc = "The C++ type for the QObject "] + #[doc = "QObjectDisabledRust"] + #[doc = "\n"] + #[doc = "Use this type when referring to the QObject as a pointer"] + #[doc = "\n"] + #[doc = "See the book for more information: "] + #[cfg(not(enabled))] + type QObjectDisabled; + } + extern "Rust" { + #[cfg(not(enabled))] + type QObjectDisabledRust; + } + extern "Rust" { + #[cxx_name = "invokable_disabled"] + #[cfg(not(enabled))] + #[doc(hidden)] + fn invokable_disabled(self: &QObjectDisabled); + } + extern "Rust" { + #[cxx_name = "invokable_enabled"] + #[cfg(enabled)] + #[doc(hidden)] + fn invokable_enabled(self: &QObjectDisabled); + } + unsafe extern "C++" { + #[cxx_name = "inherit_disabledCxxQtInherit"] + #[cfg(not(enabled))] + fn inherit_disabled(self: &QObjectDisabled); + } + unsafe extern "C++" { + #[cxx_name = "inherit_enabledCxxQtInherit"] + #[cfg(enabled)] + fn inherit_enabled(self: &QObjectDisabled); + } + unsafe extern "C++" { + #[cxx_name = "signal_disabled"] + #[cfg(not(enabled))] + fn signal_disabled(self: Pin<&mut QObjectDisabled>); + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectDisabledCxxQtSignalHandlersignal_disabled = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectDisabledCxxQtSignalClosuresignal_disabled, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectDisabled_signal_disabledConnect"] + fn QObjectDisabled_connect_signal_disabled( + self_value: Pin<&mut QObjectDisabled>, + signal_handler: QObjectDisabledCxxQtSignalHandlersignal_disabled, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(not(enabled))] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectDisabled_signal_handler_signal_disabled( + handler: QObjectDisabledCxxQtSignalHandlersignal_disabled, + ); + #[doc(hidden)] + fn call_QObjectDisabled_signal_handler_signal_disabled( + handler: &mut QObjectDisabledCxxQtSignalHandlersignal_disabled, + self_value: Pin<&mut QObjectDisabled>, + ); + } + unsafe extern "C++" { + #[cxx_name = "signal_enabled"] + #[cfg(enabled)] + fn signal_enabled(self: Pin<&mut QObjectDisabled>); + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectDisabledCxxQtSignalHandlersignal_enabled = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectDisabledCxxQtSignalClosuresignal_enabled, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectDisabled_signal_enabledConnect"] + fn QObjectDisabled_connect_signal_enabled( + self_value: Pin<&mut QObjectDisabled>, + signal_handler: QObjectDisabledCxxQtSignalHandlersignal_enabled, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(enabled)] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectDisabled_signal_handler_signal_enabled( + handler: QObjectDisabledCxxQtSignalHandlersignal_enabled, + ); + #[doc(hidden)] + fn call_QObjectDisabled_signal_handler_signal_enabled( + handler: &mut QObjectDisabledCxxQtSignalHandlersignal_enabled, + self_value: Pin<&mut QObjectDisabled>, + ); + } + extern "Rust" { + #[cxx_name = "createRs"] + #[namespace = "cxx_qt_QObjectDisabled"] + #[cfg(not(enabled))] + fn create_rs_QObjectDisabledRust() -> Box; + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[cxx_name = "unsafeRust"] + #[namespace = "rust::cxxqt1"] + fn cxx_qt_ffi_QObjectDisabled_unsafeRust(outer: &QObjectDisabled) -> &QObjectDisabledRust; + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[cxx_name = "unsafeRustMut"] + #[namespace = "rust::cxxqt1"] + fn cxx_qt_ffi_QObjectDisabled_unsafeRustMut( + outer: Pin<&mut QObjectDisabled>, + ) -> Pin<&mut QObjectDisabledRust>; + } + unsafe extern "C++" { + #[cfg(enabled)] + type QObjectExternEnabled; + } + unsafe extern "C++" { + #[cxx_name = "signal_disabled1"] + #[cfg(not(enabled))] + fn signal_disabled1(self: Pin<&mut QObjectExternEnabled>); + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectExternEnabledCxxQtSignalHandlersignal_disabled1 = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectExternEnabled_signal_disabled1Connect"] + fn QObjectExternEnabled_connect_signal_disabled1( + self_value: Pin<&mut QObjectExternEnabled>, + signal_handler: QObjectExternEnabledCxxQtSignalHandlersignal_disabled1, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(not(enabled))] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectExternEnabled_signal_handler_signal_disabled1( + handler: QObjectExternEnabledCxxQtSignalHandlersignal_disabled1, + ); + #[doc(hidden)] + fn call_QObjectExternEnabled_signal_handler_signal_disabled1( + handler: &mut QObjectExternEnabledCxxQtSignalHandlersignal_disabled1, + self_value: Pin<&mut QObjectExternEnabled>, + ); + } + unsafe extern "C++" { + #[cxx_name = "signal_enabled1"] + #[cfg(enabled)] + fn signal_enabled1(self: Pin<&mut QObjectExternEnabled>); + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectExternEnabledCxxQtSignalHandlersignal_enabled1 = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectExternEnabled_signal_enabled1Connect"] + fn QObjectExternEnabled_connect_signal_enabled1( + self_value: Pin<&mut QObjectExternEnabled>, + signal_handler: QObjectExternEnabledCxxQtSignalHandlersignal_enabled1, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(enabled)] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectExternEnabled_signal_handler_signal_enabled1( + handler: QObjectExternEnabledCxxQtSignalHandlersignal_enabled1, + ); + #[doc(hidden)] + fn call_QObjectExternEnabled_signal_handler_signal_enabled1( + handler: &mut QObjectExternEnabledCxxQtSignalHandlersignal_enabled1, + self_value: Pin<&mut QObjectExternEnabled>, + ); + } + unsafe extern "C++" { + #[cfg(not(enabled))] + type QObjectExternDisabled; + } + unsafe extern "C++" { + #[cxx_name = "signal_disabled2"] + #[cfg(not(enabled))] + fn signal_disabled2(self: Pin<&mut QObjectExternDisabled>); + } + #[cfg(not(enabled))] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectExternDisabledCxxQtSignalHandlersignal_disabled2 = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectExternDisabled_signal_disabled2Connect"] + fn QObjectExternDisabled_connect_signal_disabled2( + self_value: Pin<&mut QObjectExternDisabled>, + signal_handler: QObjectExternDisabledCxxQtSignalHandlersignal_disabled2, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(not(enabled))] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectExternDisabled_signal_handler_signal_disabled2( + handler: QObjectExternDisabledCxxQtSignalHandlersignal_disabled2, + ); + #[doc(hidden)] + fn call_QObjectExternDisabled_signal_handler_signal_disabled2( + handler: &mut QObjectExternDisabledCxxQtSignalHandlersignal_disabled2, + self_value: Pin<&mut QObjectExternDisabled>, + ); + } + unsafe extern "C++" { + #[cxx_name = "signal_enabled2"] + #[cfg(enabled)] + fn signal_enabled2(self: Pin<&mut QObjectExternDisabled>); + } + #[cfg(enabled)] + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + type QObjectExternDisabledCxxQtSignalHandlersignal_enabled2 = + cxx_qt::signalhandler::CxxQtSignalHandler< + super::QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >; + #[doc(hidden)] + #[namespace = "rust::cxxqtgen1"] + #[cxx_name = "QObjectExternDisabled_signal_enabled2Connect"] + fn QObjectExternDisabled_connect_signal_enabled2( + self_value: Pin<&mut QObjectExternDisabled>, + signal_handler: QObjectExternDisabledCxxQtSignalHandlersignal_enabled2, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[cfg(enabled)] + #[namespace = "rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QObjectExternDisabled_signal_handler_signal_enabled2( + handler: QObjectExternDisabledCxxQtSignalHandlersignal_enabled2, + ); + #[doc(hidden)] + fn call_QObjectExternDisabled_signal_handler_signal_enabled2( + handler: &mut QObjectExternDisabledCxxQtSignalHandlersignal_enabled2, + self_value: Pin<&mut QObjectExternDisabled>, + ); + } +} +#[cfg(not(enabled))] +impl ffi::QObjectEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_disabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectEnabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectEnabled_connect_signal_disabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectEnabledCxxQtSignalClosuresignal_disabled, + >::new(Box::new(closure)), + conn_type, + )) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_disabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectEnabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectEnabled_connect_signal_disabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectEnabledCxxQtSignalClosuresignal_disabled, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[cfg(not(enabled))] +#[doc(hidden)] +pub struct QObjectEnabledCxxQtSignalClosuresignal_disabled {} +#[cfg(not(enabled))] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectEnabledCxxQtSignalClosuresignal_disabled +{ + type Id = cxx::type_id!("::rust::cxxqtgen1::QObjectEnabledCxxQtSignalHandlersignal_disabled"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + Send; +} +#[cfg(not(enabled))] +use core::mem::drop as drop_QObjectEnabled_signal_handler_signal_disabled; +#[cfg(not(enabled))] +fn call_QObjectEnabled_signal_handler_signal_disabled( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectEnabledCxxQtSignalClosuresignal_disabled, + >, + self_value: core::pin::Pin<&mut ffi::QObjectEnabled>, +) { + handler.closure()(self_value); +} +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler, + usize +); +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler, + [usize; 2] +); +#[cfg(enabled)] +impl ffi::QObjectEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_enabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectEnabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectEnabled_connect_signal_enabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectEnabledCxxQtSignalClosuresignal_enabled, + >::new(Box::new(closure)), + conn_type, + )) + } +} +#[cfg(enabled)] +impl ffi::QObjectEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_enabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectEnabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectEnabled_connect_signal_enabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectEnabledCxxQtSignalClosuresignal_enabled, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[cfg(enabled)] +#[doc(hidden)] +pub struct QObjectEnabledCxxQtSignalClosuresignal_enabled {} +#[cfg(enabled)] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectEnabledCxxQtSignalClosuresignal_enabled +{ + type Id = cxx::type_id!("::rust::cxxqtgen1::QObjectEnabledCxxQtSignalHandlersignal_enabled"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectEnabled>) + Send; +} +#[cfg(enabled)] +use core::mem::drop as drop_QObjectEnabled_signal_handler_signal_enabled; +#[cfg(enabled)] +fn call_QObjectEnabled_signal_handler_signal_enabled( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectEnabledCxxQtSignalClosuresignal_enabled, + >, + self_value: core::pin::Pin<&mut ffi::QObjectEnabled>, +) { + handler.closure()(self_value); +} +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler, + usize +); +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler, + [usize; 2] +); +#[doc(hidden)] +#[allow(clippy::unnecessary_box_returns)] +#[cfg(enabled)] +pub fn create_rs_QObjectEnabledRust() -> std::boxed::Box { + std::boxed::Box::new(core::default::Default::default()) +} +#[cfg(enabled)] +impl ::core::ops::Deref for ffi::QObjectEnabled { + type Target = QObjectEnabledRust; + fn deref(&self) -> &Self::Target { + ffi::cxx_qt_ffi_QObjectEnabled_unsafeRust(self) + } +} +#[cfg(enabled)] +impl ::cxx_qt::CxxQtType for ffi::QObjectEnabled { + type Rust = QObjectEnabledRust; + fn rust(&self) -> &Self::Rust { + ffi::cxx_qt_ffi_QObjectEnabled_unsafeRust(self) + } + fn rust_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut Self::Rust> { + ffi::cxx_qt_ffi_QObjectEnabled_unsafeRustMut(self) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_disabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectDisabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectDisabled_connect_signal_disabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectDisabledCxxQtSignalClosuresignal_disabled, + >::new(Box::new(closure)), + conn_type, + )) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_disabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectDisabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectDisabled_connect_signal_disabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectDisabledCxxQtSignalClosuresignal_disabled, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[cfg(not(enabled))] +#[doc(hidden)] +pub struct QObjectDisabledCxxQtSignalClosuresignal_disabled {} +#[cfg(not(enabled))] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectDisabledCxxQtSignalClosuresignal_disabled +{ + type Id = cxx::type_id!("::rust::cxxqtgen1::QObjectDisabledCxxQtSignalHandlersignal_disabled"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + Send; +} +#[cfg(not(enabled))] +use core::mem::drop as drop_QObjectDisabled_signal_handler_signal_disabled; +#[cfg(not(enabled))] +fn call_QObjectDisabled_signal_handler_signal_disabled( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectDisabledCxxQtSignalClosuresignal_disabled, + >, + self_value: core::pin::Pin<&mut ffi::QObjectDisabled>, +) { + handler.closure()(self_value); +} +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler, + usize +); +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler, + [usize; 2] +); +#[cfg(enabled)] +impl ffi::QObjectDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_enabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectDisabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectDisabled_connect_signal_enabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectDisabledCxxQtSignalClosuresignal_enabled, + >::new(Box::new(closure)), + conn_type, + )) + } +} +#[cfg(enabled)] +impl ffi::QObjectDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_enabled< + F: FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectDisabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectDisabled_connect_signal_enabled( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectDisabledCxxQtSignalClosuresignal_enabled, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[cfg(enabled)] +#[doc(hidden)] +pub struct QObjectDisabledCxxQtSignalClosuresignal_enabled {} +#[cfg(enabled)] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectDisabledCxxQtSignalClosuresignal_enabled +{ + type Id = cxx::type_id!("::rust::cxxqtgen1::QObjectDisabledCxxQtSignalHandlersignal_enabled"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectDisabled>) + Send; +} +#[cfg(enabled)] +use core::mem::drop as drop_QObjectDisabled_signal_handler_signal_enabled; +#[cfg(enabled)] +fn call_QObjectDisabled_signal_handler_signal_enabled( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectDisabledCxxQtSignalClosuresignal_enabled, + >, + self_value: core::pin::Pin<&mut ffi::QObjectDisabled>, +) { + handler.closure()(self_value); +} +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler, + usize +); +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler, + [usize; 2] +); +#[doc(hidden)] +#[allow(clippy::unnecessary_box_returns)] +#[cfg(not(enabled))] +pub fn create_rs_QObjectDisabledRust() -> std::boxed::Box { + std::boxed::Box::new(core::default::Default::default()) +} +#[cfg(not(enabled))] +impl ::core::ops::Deref for ffi::QObjectDisabled { + type Target = QObjectDisabledRust; + fn deref(&self) -> &Self::Target { + ffi::cxx_qt_ffi_QObjectDisabled_unsafeRust(self) + } +} +#[cfg(not(enabled))] +impl ::cxx_qt::CxxQtType for ffi::QObjectDisabled { + type Rust = QObjectDisabledRust; + fn rust(&self) -> &Self::Rust { + ffi::cxx_qt_ffi_QObjectDisabled_unsafeRust(self) + } + fn rust_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut Self::Rust> { + ffi::cxx_qt_ffi_QObjectDisabled_unsafeRustMut(self) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectExternEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled1"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_disabled1< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternEnabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternEnabled_connect_signal_disabled1( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >::new(Box::new(closure)), + conn_type, + ), + ) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectExternEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled1"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_disabled1< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternEnabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternEnabled_connect_signal_disabled1( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + ), + ) + } +} +#[cfg(not(enabled))] +#[doc(hidden)] +pub struct QObjectExternEnabledCxxQtSignalClosuresignal_disabled1 {} +#[cfg(not(enabled))] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectExternEnabledCxxQtSignalClosuresignal_disabled1 +{ + type Id = + cxx::type_id!("::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalHandlersignal_disabled1"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + Send; +} +#[cfg(not(enabled))] +use core::mem::drop as drop_QObjectExternEnabled_signal_handler_signal_disabled1; +#[cfg(not(enabled))] +fn call_QObjectExternEnabled_signal_handler_signal_disabled1( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >, + self_value: core::pin::Pin<&mut ffi::QObjectExternEnabled>, +) { + handler.closure()(self_value); +} +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >, + usize +); +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_disabled1, + >, + [usize; 2] +); +#[cfg(enabled)] +impl ffi::QObjectExternEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled1"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_enabled1< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternEnabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectExternEnabled_connect_signal_enabled1( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >::new(Box::new(closure)), + conn_type, + )) + } +} +#[cfg(enabled)] +impl ffi::QObjectExternEnabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled1"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_enabled1< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternEnabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from(ffi::QObjectExternEnabled_connect_signal_enabled1( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[cfg(enabled)] +#[doc(hidden)] +pub struct QObjectExternEnabledCxxQtSignalClosuresignal_enabled1 {} +#[cfg(enabled)] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectExternEnabledCxxQtSignalClosuresignal_enabled1 +{ + type Id = + cxx::type_id!("::rust::cxxqtgen1::QObjectExternEnabledCxxQtSignalHandlersignal_enabled1"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectExternEnabled>) + Send; +} +#[cfg(enabled)] +use core::mem::drop as drop_QObjectExternEnabled_signal_handler_signal_enabled1; +#[cfg(enabled)] +fn call_QObjectExternEnabled_signal_handler_signal_enabled1( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >, + self_value: core::pin::Pin<&mut ffi::QObjectExternEnabled>, +) { + handler.closure()(self_value); +} +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >, + usize +); +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternEnabledCxxQtSignalClosuresignal_enabled1, + >, + [usize; 2] +); +#[cfg(not(enabled))] +impl ffi::QObjectExternDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled2"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_disabled2< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternDisabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternDisabled_connect_signal_disabled2( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >::new(Box::new(closure)), + conn_type, + ), + ) + } +} +#[cfg(not(enabled))] +impl ffi::QObjectExternDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_disabled2"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_disabled2< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternDisabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternDisabled_connect_signal_disabled2( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + ), + ) + } +} +#[cfg(not(enabled))] +#[doc(hidden)] +pub struct QObjectExternDisabledCxxQtSignalClosuresignal_disabled2 {} +#[cfg(not(enabled))] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectExternDisabledCxxQtSignalClosuresignal_disabled2 +{ + type Id = + cxx::type_id!("::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalHandlersignal_disabled2"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + Send; +} +#[cfg(not(enabled))] +use core::mem::drop as drop_QObjectExternDisabled_signal_handler_signal_disabled2; +#[cfg(not(enabled))] +fn call_QObjectExternDisabled_signal_handler_signal_disabled2( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >, + self_value: core::pin::Pin<&mut ffi::QObjectExternDisabled>, +) { + handler.closure()(self_value); +} +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >, + usize +); +#[cfg(not(enabled))] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_disabled2, + >, + [usize; 2] +); +#[cfg(enabled)] +impl ffi::QObjectExternDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled2"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_signal_enabled2< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternDisabled>, + mut closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternDisabled_connect_signal_enabled2( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >::new(Box::new(closure)), + conn_type, + ), + ) + } +} +#[cfg(enabled)] +impl ffi::QObjectExternDisabled { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "signal_enabled2"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_signal_enabled2< + F: FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + 'static + Send, + >( + self: core::pin::Pin<&mut ffi::QObjectExternDisabled>, + mut closure: F, + ) -> cxx_qt::QMetaObjectConnectionGuard { + cxx_qt::QMetaObjectConnectionGuard::from( + ffi::QObjectExternDisabled_connect_signal_enabled2( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::< + QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >::new(Box::new(closure)), + cxx_qt::ConnectionType::AutoConnection, + ), + ) + } +} +#[cfg(enabled)] +#[doc(hidden)] +pub struct QObjectExternDisabledCxxQtSignalClosuresignal_enabled2 {} +#[cfg(enabled)] +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure + for QObjectExternDisabledCxxQtSignalClosuresignal_enabled2 +{ + type Id = + cxx::type_id!("::rust::cxxqtgen1::QObjectExternDisabledCxxQtSignalHandlersignal_enabled2"); + type FnType = dyn FnMut(core::pin::Pin<&mut ffi::QObjectExternDisabled>) + Send; +} +#[cfg(enabled)] +use core::mem::drop as drop_QObjectExternDisabled_signal_handler_signal_enabled2; +#[cfg(enabled)] +fn call_QObjectExternDisabled_signal_handler_signal_enabled2( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >, + self_value: core::pin::Pin<&mut ffi::QObjectExternDisabled>, +) { + handler.closure()(self_value); +} +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >, + usize +); +#[cfg(enabled)] +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler< + QObjectExternDisabledCxxQtSignalClosuresignal_enabled2, + >, + [usize; 2] +); diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.rs.license b/crates/cxx-qt-gen/test_outputs/cfgs.rs.license new file mode 100644 index 000000000..c6752c743 --- /dev/null +++ b/crates/cxx-qt-gen/test_outputs/cfgs.rs.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +SPDX-FileContributor: Andrew Hayzen + +SPDX-License-Identifier: MIT OR Apache-2.0 + diff --git a/scripts/grcov_cxx_qt.sh b/scripts/grcov_cxx_qt.sh index 1103d872b..9dbb9c045 100755 --- a/scripts/grcov_cxx_qt.sh +++ b/scripts/grcov_cxx_qt.sh @@ -5,19 +5,34 @@ # # SPDX-License-Identifier: MIT OR Apache-2.0 -# Assumes you have grcov and llvm-tools +# Assumes you have grcov and llvm in a system path # Install: # cargo install grcov -# rustup component add llvm-tools set -ex + # Ensure we are in the right directory SCRIPT=$(realpath "$0") SCRIPTPATH=$(dirname "$SCRIPT") cd "$SCRIPTPATH/../" +# Ensure coverage folder is cleared +rm -f "$SCRIPTPATH"/coverage/*.profraw + +# Check that the llvm path exists +# +# We can use rustup component add llvm-tools but this can be out of sync +# See versions from the table in this link +# https://github.com/taiki-e/cargo-llvm-cov?tab=readme-ov-file#get-coverage-of-cc-code-linked-to-rust-librarybinary +if [ ! -d /usr/lib/llvm-17/bin/ ]; then + echo "LLVM 17 not found" +fi + +export CARGO_INCREMENTAL=0 export RUSTFLAGS="-Cinstrument-coverage" export LLVM_PROFILE_FILE="$SCRIPTPATH/coverage/coverage_data-%p-%m.profraw" cargo build --package cxx-qt-gen cargo test --package cxx-qt-gen -grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing -o ./target/debug/ --excl-start CODECOV_EXCLUDE_START --excl-stop CODECOV_EXCLUDE_STOP -echo "Coverage html report generated in $(realpath "$SCRIPTPATH"/../target/debug/html)" \ No newline at end of file +# Note that --llvm-path is important here to ensure the matching llvm version to the Rust version (1.77.x) +# Note that --keep-only is important here to ensure crates.io paths don't conflict +grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --llvm --llvm-path=/usr/lib/llvm-17/bin/ --keep-only "crates/*" -o ./target/debug/ --excl-start CODECOV_EXCLUDE_START --excl-stop CODECOV_EXCLUDE_STOP +echo "Coverage html report generated in $(realpath "$SCRIPTPATH"/../target/debug/html)"