diff --git a/crates/cxx-qt-gen/src/generator/cpp/inherit.rs b/crates/cxx-qt-gen/src/generator/cpp/inherit.rs index bb2e05b40..21faf90be 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/inherit.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/inherit.rs @@ -27,7 +27,7 @@ pub fn generate( 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)? { + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &method.common_attrs.cfgs)? { continue; } diff --git a/crates/cxx-qt-gen/src/generator/cpp/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/mod.rs index f5c74ce6b..92e27c3e4 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/mod.rs @@ -72,8 +72,10 @@ impl GeneratedCppBlocks { .iter() .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) - { + match try_eval_attributes( + opt.cfg_evaluator.as_ref(), + &qobject.declaration.common_attrs.cfgs, + ) { Ok(true) => { Some(GeneratedCppQObject::from(qobject, &parser.type_names, opt)) } diff --git a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs index 9e75e6e4b..15e87c182 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qenum.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qenum.rs @@ -39,7 +39,7 @@ pub fn generate_declaration( opt: &GeneratedOpt, ) -> Result { // Skip if the cfg attributes are not resolved to true - if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &qenum.cfgs)? { + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &qenum.common_attrs.cfgs)? { return Ok(String::new()); } @@ -75,7 +75,7 @@ pub fn generate_on_qobject<'a>( 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)? { + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &qenum.common_attrs.cfgs)? { continue; } diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index 1dc4808da..4e5d26b1a 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -89,7 +89,7 @@ pub fn generate_cpp_signal( 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)? { + if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &signal.common_attrs.cfgs)? { return Ok(generated); } diff --git a/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs b/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs index 7e4ce6be9..eff650f41 100644 --- a/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs @@ -7,10 +7,9 @@ use crate::{ generator::rust::{fragment::GeneratedRustFragment, signals::generate_rust_signal}, naming::TypeNames, parser::externcxxqt::ParsedExternCxxQt, - syntax::path::path_compare_str, }; use quote::quote; -use syn::{parse_quote, Attribute, Result}; +use syn::{parse_quote, Item, Result}; impl GeneratedRustFragment { pub fn from_extern_cxx_qt( @@ -22,76 +21,79 @@ impl GeneratedRustFragment { } else { quote! {} }; + let extern_block_docs = &extern_cxxqt_block.common_attrs.docs; // Add the pass through blocks let unsafety = &extern_cxxqt_block.unsafety; let items = &extern_cxxqt_block.passthrough_items; - let mut generated = extern_cxxqt_block - .qobjects - .iter() - .map(|ty| -> Result { - let mut generated = vec![]; - let qobject_names = QObjectNames::from_extern_qobject(ty, type_names)?; - generated.push(GeneratedRustFragment::generate_casting_impl( - &qobject_names, - type_names, - &ty.name, - &ty.base_class, - )?); + let mut qobject_items: Vec = vec![]; + let mut cxx_qt_mod = vec![]; + let mut cxx_mod = vec![]; + for obj in &extern_cxxqt_block.qobjects { + let qobject_names = QObjectNames::from_extern_qobject(obj, type_names)?; - let namespace = if let Some(namespace) = &ty.name.namespace() { - quote! { #[namespace = #namespace ] } - } else { - quote! {} - }; - let cpp_name = &ty.name.cxx_unqualified(); - let rust_name = &ty.name.rust_unqualified(); - let vis = &ty.declaration.vis; - let ident = &ty.name.rust_unqualified(); - let cxx_name = if &rust_name.to_string() == cpp_name { - quote! {} - } else { - let cxx_name = cpp_name.to_string(); - quote! { - #[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 - .iter() - .filter(|attr| path_compare_str(attr.meta.path(), &["doc"])) - .collect(); - generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! { - #extern_block_namespace - #unsafety extern "C++" { - #namespace - #cxx_name - #(#cfgs)* - #(#docs)* - #vis type #ident; - } - })); - Ok(GeneratedRustFragment::flatten(generated)) - }) - .collect::>>()?; + let casting = GeneratedRustFragment::generate_casting_impl( + &qobject_names, + type_names, + &obj.name, + &obj.base_class, + )?; + cxx_mod.extend(casting.cxx_mod_contents); + cxx_qt_mod.extend(casting.cxx_qt_mod_contents); - if !items.is_empty() { - generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! { - #extern_block_namespace - #unsafety extern "C++" { - #(#items)* + let namespace = if let Some(namespace) = &obj.name.namespace() { + quote! { #[namespace = #namespace ] } + } else { + quote! {} + }; + let cpp_name = &obj.name.cxx_unqualified(); + let rust_name = &obj.name.rust_unqualified(); + let vis = &obj.declaration.vis; + let ident = &obj.name.rust_unqualified(); + let cxx_name = if &rust_name.to_string() == cpp_name { + quote! {} + } else { + let cxx_name = cpp_name.to_string(); + quote! { + #[cxx_name = #cxx_name] } - })); + }; + // TODO: (cfg everywhere) Can we make extract_docs return references, and then use here? + let cfgs = obj.common_attrs.cfgs.iter().collect::>(); + let docs = obj.common_attrs.docs.iter().collect::>(); + qobject_items.push(parse_quote! { + #namespace + #cxx_name + #(#cfgs)* + #(#docs)* + #vis type #ident; + }); } + let passthrough_items = if !items.is_empty() { + quote! { + #(#items)* + } + } else { + quote! {} + }; + + cxx_mod.push(parse_quote! { + #extern_block_namespace + #(#extern_block_docs)* + #unsafety extern "C++" { + #(#qobject_items)* + + #passthrough_items + } + }); + + let mut generated = vec![GeneratedRustFragment { + cxx_mod_contents: cxx_mod, + cxx_qt_mod_contents: cxx_qt_mod, + }]; + // Build the signals for signal in &extern_cxxqt_block.signals { let qobject_name = type_names.lookup(&signal.qobject_ident)?; diff --git a/crates/cxx-qt-gen/src/generator/rust/inherit.rs b/crates/cxx-qt-gen/src/generator/rust/inherit.rs index b61786949..6e2504bd0 100644 --- a/crates/cxx-qt-gen/src/generator/rust/inherit.rs +++ b/crates/cxx-qt-gen/src/generator/rust/inherit.rs @@ -48,8 +48,8 @@ pub fn generate( if method.safe { std::mem::swap(&mut unsafe_call, &mut unsafe_block); } - let doc_comments = &method.docs; - let cfgs = &method.cfgs; + let doc_comments = &method.common_attrs.docs; + let cfgs = &method.common_attrs.cfgs; let namespace = qobject_names.namespace_tokens(); syn::parse2(quote_spanned! { diff --git a/crates/cxx-qt-gen/src/generator/rust/qenum.rs b/crates/cxx-qt-gen/src/generator/rust/qenum.rs index d791aaf52..ba4158883 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qenum.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qenum.rs @@ -16,8 +16,8 @@ pub fn generate_cxx_mod_contents(qenums: &[ParsedQEnum]) -> Vec { let item = &qenum.item; let vis = &item.vis; let variants = &item.variants; - let docs = &qenum.docs; - let cfgs = &qenum.cfgs; + let docs = &qenum.common_attrs.docs; + let cfgs = &qenum.common_attrs.cfgs; let cxx_namespace = if namespace.is_none() { quote! {} diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index aa9b9bd2c..9da5c4a54 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::generator::structuring::StructuredQObject; +use crate::parser::CommonAttrs; use crate::{ generator::{ naming::{namespace::NamespaceName, qobject::QObjectNames}, @@ -15,7 +16,7 @@ use crate::{ naming::TypeNames, }; use quote::quote; -use syn::{parse_quote, Attribute, Result}; +use syn::{parse_quote, Result}; impl GeneratedRustFragment { pub fn from_qobject( @@ -28,7 +29,7 @@ impl GeneratedRustFragment { let namespace_idents = NamespaceName::from(qobject); let mut generated = vec![ - generate_qobject_definitions(&qobject_names, &qobject.cfgs)?, + generate_qobject_definitions(&qobject_names, &qobject.common_attrs)?, generate_rust_properties( &qobject.properties, &qobject_names, @@ -57,7 +58,7 @@ impl GeneratedRustFragment { &qobject_names, &namespace_idents, type_names, - &qobject.cfgs, + &qobject.common_attrs.cfgs, )?); } @@ -75,9 +76,9 @@ impl GeneratedRustFragment { &qobject_names, &namespace_idents, type_names, - &qobject.cfgs, + &qobject.common_attrs.cfgs, )?, - cxxqttype::generate(&qobject_names, type_names, &qobject.cfgs)?, + cxxqttype::generate(&qobject_names, type_names, &qobject.common_attrs.cfgs)?, ]); Ok(GeneratedRustFragment::flatten(generated)) @@ -87,8 +88,11 @@ impl GeneratedRustFragment { /// Generate the C++ and Rust CXX definitions for the QObject fn generate_qobject_definitions( qobject_idents: &QObjectNames, - cfgs: &[Attribute], + common_attrs: &CommonAttrs, ) -> Result { + let docs = &common_attrs.docs; + let cfgs = &common_attrs.cfgs; + let cpp_class_name_rust = &qobject_idents.name.rust_unqualified(); let cpp_class_name_cpp = &qobject_idents.name.cxx_unqualified(); @@ -106,6 +110,15 @@ fn generate_qobject_definitions( } }; + let maybe_docs = if docs.is_empty() { + quote! {} + } else { + quote! { + #[doc = "\n"] + #(#docs)* + } + }; + Ok(GeneratedRustFragment { cxx_mod_contents: vec![ parse_quote! { @@ -116,6 +129,7 @@ fn generate_qobject_definitions( #[doc = "Use this type when referring to the QObject as a pointer"] #[doc = "\n"] #[doc = "See the book for more information: "] + #maybe_docs #namespace #cxx_name #(#cfgs)* @@ -130,6 +144,7 @@ fn generate_qobject_definitions( // but to apply it to only certain types, it is needed here too #namespace #(#cfgs)* + #(#docs)* 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 2b407522c..fbe21a386 100644 --- a/crates/cxx-qt-gen/src/generator/rust/signals.rs +++ b/crates/cxx-qt-gen/src/generator/rust/signals.rs @@ -100,8 +100,8 @@ pub fn generate_rust_signal( } else { Some(quote! { unsafe }) }; - let doc_comments = &signal.docs; - let cfgs = &signal.cfgs; + let doc_comments = &signal.common_attrs.docs; + let cfgs = &signal.common_attrs.cfgs; let namespace = if let Some(namespace) = qobject_name.namespace() { quote_spanned! { span=> #[namespace = #namespace ] } } else { diff --git a/crates/cxx-qt-gen/src/parser/externcxxqt.rs b/crates/cxx-qt-gen/src/parser/externcxxqt.rs index 4364c9027..8b76e7239 100644 --- a/crates/cxx-qt-gen/src/parser/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/parser/externcxxqt.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::parser::CommonAttrs; use crate::{ parser::{ externqobject::ParsedExternQObject, require_attributes, signals::ParsedSignal, @@ -19,6 +20,8 @@ use syn::{ pub struct ParsedExternCxxQt { /// The namespace of the type in C++. pub namespace: Option, + /// All the universal top level attributes for the block + pub common_attrs: CommonAttrs, /// Whether this block has an unsafe token pub unsafety: Option, /// Items which can be passed into the extern "C++Qt" block @@ -35,10 +38,10 @@ impl ParsedExternCxxQt { module_ident: &Ident, parent_namespace: Option<&str>, ) -> Result { - // TODO: support cfg on foreign mod blocks - let attrs = require_attributes( + // TODO: (cfg everywhere) support cfg on foreign mod blocks + let (attrs, common_attrs) = require_attributes( &foreign_mod.attrs, - &["namespace", "auto_cxx_name", "auto_rust_name"], + &["doc", "namespace", "auto_cxx_name", "auto_rust_name"], )?; let auto_case = CaseConversion::from_attrs(&attrs)?; @@ -52,6 +55,7 @@ impl ParsedExternCxxQt { let mut extern_cxx_block = ParsedExternCxxQt { namespace, + common_attrs, unsafety: foreign_mod.unsafety, ..Default::default() }; @@ -67,7 +71,7 @@ impl ParsedExternCxxQt { ForeignItem::Type(foreign_ty) => { // Test that there is a #[qobject] attribute on any type // - // TODO: what happens to any docs here? + // TODO: (cfg everywhere) what happens to any docs here? if attribute_get_path(&foreign_ty.attrs, &["qobject"]).is_some() { let extern_ty = ParsedExternQObject::parse(foreign_ty, module_ident, parent_namespace)?; diff --git a/crates/cxx-qt-gen/src/parser/externqobject.rs b/crates/cxx-qt-gen/src/parser/externqobject.rs index b7ea93faa..969cf6368 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::{parse_base_type, require_attributes, CaseConversion}; +use crate::parser::{parse_base_type, require_attributes, CaseConversion, CommonAttrs}; use syn::{ForeignItemType, Ident, Result}; /// A representation of a QObject to be generated in an extern C++ block @@ -14,15 +14,17 @@ pub struct ParsedExternQObject { pub declaration: ForeignItemType, /// The base class of the struct pub base_class: Option, + /// Common attrs for this object + pub common_attrs: CommonAttrs, } impl ParsedExternQObject { const ALLOWED_ATTRS: [&'static str; 7] = [ + "cfg", + "doc", "cxx_name", "rust_name", "namespace", - "cfg", - "doc", "qobject", "base", ]; @@ -32,7 +34,8 @@ impl ParsedExternQObject { module_ident: &Ident, parent_namespace: Option<&str>, ) -> Result { - let attributes = require_attributes(&ty.attrs, &Self::ALLOWED_ATTRS)?; + // TODO: (cfg everywhere) should these have docs kept with them in the generated code? + let (attributes, common_attrs) = require_attributes(&ty.attrs, &Self::ALLOWED_ATTRS)?; let base_class = parse_base_type(&attributes)?; @@ -46,6 +49,7 @@ impl ParsedExternQObject { )?, declaration: ty, base_class, + common_attrs, }) } } diff --git a/crates/cxx-qt-gen/src/parser/externrustqt.rs b/crates/cxx-qt-gen/src/parser/externrustqt.rs index 6da520bdf..e72789344 100644 --- a/crates/cxx-qt-gen/src/parser/externrustqt.rs +++ b/crates/cxx-qt-gen/src/parser/externrustqt.rs @@ -37,8 +37,8 @@ impl ParsedExternRustQt { module_ident: &Ident, parent_namespace: Option<&str>, ) -> Result { - // TODO: support cfg on foreign mod blocks - let attrs = require_attributes( + // TODO: (cfg everywhere) support cfg on foreign mod blocks + let (attrs, _common_attrs) = require_attributes( &foreign_mod.attrs, &["namespace", "auto_cxx_name", "auto_rust_name"], )?; diff --git a/crates/cxx-qt-gen/src/parser/inherit.rs b/crates/cxx-qt-gen/src/parser/inherit.rs index 9ebb5af17..97aa6fed9 100644 --- a/crates/cxx-qt-gen/src/parser/inherit.rs +++ b/crates/cxx-qt-gen/src/parser/inherit.rs @@ -3,43 +3,36 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::parser::{ - extract_cfgs, extract_docs, method::MethodFields, require_attributes, CaseConversion, -}; +use crate::parser::{method::MethodFields, require_attributes, CaseConversion, CommonAttrs}; use core::ops::Deref; use quote::format_ident; use std::ops::DerefMut; -use syn::{Attribute, ForeignItemFn, Ident, Result}; +use syn::{ForeignItemFn, Ident, Result}; /// Describes a method found in an extern "RustQt" with #[inherit] pub struct ParsedInheritedMethod { /// The common fields which are available on all callable types pub method_fields: MethodFields, - /// All the docs (each line) of the inherited method - pub docs: Vec, - /// Cfgs for the inherited method - pub cfgs: Vec, + /// All the universal attributes for the inherited method + pub common_attrs: CommonAttrs, } impl ParsedInheritedMethod { const ALLOWED_ATTRS: [&'static str; 6] = [ + "cfg", + "doc", "cxx_name", "rust_name", "qinvokable", - "doc", "inherit", - "cfg", ]; pub fn parse(method: ForeignItemFn, auto_case: CaseConversion) -> Result { - require_attributes(&method.attrs, &Self::ALLOWED_ATTRS)?; - let docs = extract_docs(&method.attrs); - let cfgs = extract_cfgs(&method.attrs); + let (_attrs, common_attrs) = require_attributes(&method.attrs, &Self::ALLOWED_ATTRS)?; Ok(Self { method_fields: MethodFields::parse(method, auto_case)?, - docs, - cfgs, + common_attrs, }) } diff --git a/crates/cxx-qt-gen/src/parser/method.rs b/crates/cxx-qt-gen/src/parser/method.rs index 89c708da5..24230bbef 100644 --- a/crates/cxx-qt-gen/src/parser/method.rs +++ b/crates/cxx-qt-gen/src/parser/method.rs @@ -5,7 +5,7 @@ use crate::parser::CaseConversion; use crate::{ naming::Name, - parser::{extract_cfgs, parameter::ParsedFunctionParameter, require_attributes}, + parser::{parameter::ParsedFunctionParameter, require_attributes}, syntax::{foreignmod, types}, }; use core::ops::Deref; @@ -121,8 +121,7 @@ impl ParsedMethod { unsafe_block: bool, ) -> Result { let fields = MethodFields::parse(method, auto_case)?; - let attrs = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?; - let cfgs = extract_cfgs(&fields.method.attrs); + let (attrs, common_attrs) = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?; // Determine if the method is invokable let is_qinvokable = attrs.contains_key("qinvokable"); @@ -134,7 +133,7 @@ impl ParsedMethod { specifiers, is_qinvokable, is_pure, - cfgs, + cfgs: common_attrs.cfgs, unsafe_block, }) } diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 97b11ec1f..f1a669258 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -87,22 +87,23 @@ impl CaseConversion { } } -/// Iterate the attributes of the method to extract cfg attributes -pub fn extract_cfgs(attrs: &[Attribute]) -> Vec { +/// Helper function to extract all of a particular attribute from the slice +fn extract_attr(attrs: &[Attribute], target: &str) -> Vec { attrs .iter() - .filter(|attr| path_compare_str(attr.meta.path(), &["cfg"])) + .filter(|attr| path_compare_str(attr.meta.path(), &[target])) .cloned() .collect() } +/// Iterate the attributes of the method to extract cfg attributes +pub fn extract_cfgs(attrs: &[Attribute]) -> Vec { + extract_attr(attrs, "cfg") +} + /// Iterate the attributes of the method to extract Doc attributes (doc comments are parsed as this) pub fn extract_docs(attrs: &[Attribute]) -> Vec { - attrs - .iter() - .filter(|attr| path_compare_str(attr.meta.path(), &["doc"])) - .cloned() - .collect() + extract_attr(attrs, "doc") } /// Splits a path by :: separators e.g. "cxx_qt::bridge" becomes ["cxx_qt", "bridge"] @@ -115,12 +116,21 @@ fn split_path(path_str: &str) -> Vec<&str> { path } +/// Attributes which should be passed through, and are available on most things +#[derive(Clone, Debug, Default)] +pub struct CommonAttrs { + pub docs: Vec, + pub cfgs: Vec, +} + /// Collects a Map of all attributes found from the allowed list /// Will error if an attribute which is not in the allowed list is found +/// +/// Has the option to allow a set of common attributes such as doc, cfg, etc... pub fn require_attributes<'a>( attrs: &'a [Attribute], allowed: &'a [&str], -) -> Result> { +) -> Result<(BTreeMap<&'a str, &'a Attribute>, CommonAttrs)> { let mut output = BTreeMap::default(); for attr in attrs { let index = allowed @@ -138,7 +148,11 @@ pub fn require_attributes<'a>( )); } } - Ok(output) + let common = CommonAttrs { + docs: extract_docs(attrs), + cfgs: extract_cfgs(attrs), + }; + Ok((output, common)) } // Extract base identifier from attribute @@ -196,7 +210,7 @@ pub struct Parser { impl Parser { fn parse_mod_attributes(module: &mut ItemMod) -> Result> { - let attrs = require_attributes(&module.attrs, &["doc", "cxx_qt::bridge"])?; + let (attrs, _common_attrs) = require_attributes(&module.attrs, &["doc", "cxx_qt::bridge"])?; let mut namespace = None; // Check for the cxx_qt::bridge attribute diff --git a/crates/cxx-qt-gen/src/parser/qenum.rs b/crates/cxx-qt-gen/src/parser/qenum.rs index 069831a1b..9357e22d6 100644 --- a/crates/cxx-qt-gen/src/parser/qenum.rs +++ b/crates/cxx-qt-gen/src/parser/qenum.rs @@ -3,10 +3,10 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::parser::{extract_cfgs, extract_docs, CaseConversion}; +use crate::parser::{CaseConversion, CommonAttrs}; use crate::{naming::Name, parser::require_attributes, syntax::path::path_compare_str}; use quote::ToTokens; -use syn::{Attribute, Ident, ItemEnum, Result, Variant}; +use syn::{Ident, ItemEnum, Result, Variant}; pub struct ParsedQEnum { /// The name of the QObject @@ -17,10 +17,8 @@ pub struct ParsedQEnum { pub qobject: Option, /// The original enum item pub item: ItemEnum, - /// Docs from the qenum - pub docs: Vec, - /// Cfgs from the qenum - pub cfgs: Vec, + /// All the universal attributes for the enum + pub common_attrs: CommonAttrs, } impl ParsedQEnum { @@ -60,9 +58,7 @@ impl ParsedQEnum { parent_namespace: Option<&str>, module: &Ident, ) -> Result { - require_attributes(&qenum.attrs, &Self::ALLOWED_ATTRS)?; - let cfgs = extract_cfgs(&qenum.attrs); - let docs = extract_docs(&qenum.attrs); + let (_attrs, common_attrs) = require_attributes(&qenum.attrs, &Self::ALLOWED_ATTRS)?; if qenum.variants.is_empty() { return Err(syn::Error::new_spanned( @@ -96,8 +92,7 @@ impl ParsedQEnum { name, qobject, variants, - docs, - cfgs, + common_attrs, item: qenum, }) } diff --git a/crates/cxx-qt-gen/src/parser/qnamespace.rs b/crates/cxx-qt-gen/src/parser/qnamespace.rs index ab483ec20..e4e89db33 100644 --- a/crates/cxx-qt-gen/src/parser/qnamespace.rs +++ b/crates/cxx-qt-gen/src/parser/qnamespace.rs @@ -15,7 +15,7 @@ pub struct ParsedQNamespace { impl ParsedQNamespace { pub fn parse(mac: ItemMacro) -> Result { - let attrs = require_attributes(&mac.attrs, &["qml_element"])?; + let (attrs, _common_attrs) = require_attributes(&mac.attrs, &["qml_element"])?; let namespace_literal: LitStr = syn::parse2(mac.mac.tokens)?; let namespace = namespace_literal.value(); if namespace.contains(char::is_whitespace) { diff --git a/crates/cxx-qt-gen/src/parser/qobject.rs b/crates/cxx-qt-gen/src/parser/qobject.rs index 30e1ed051..1286f2fda 100644 --- a/crates/cxx-qt-gen/src/parser/qobject.rs +++ b/crates/cxx-qt-gen/src/parser/qobject.rs @@ -3,15 +3,14 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::parser::CommonAttrs; use crate::{ naming::Name, - parser::{extract_cfgs, property::ParsedQProperty, require_attributes}, + parser::{parse_base_type, property::ParsedQProperty, require_attributes, CaseConversion}, syntax::{expr::expr_to_string, foreignmod::ForeignTypeIdentAlias, path::path_compare_str}, }; #[cfg(test)] use quote::format_ident; - -use crate::parser::{parse_base_type, CaseConversion}; use syn::{Attribute, Error, Ident, Meta, Result}; /// Metadata for registering QML element @@ -42,17 +41,17 @@ 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, + /// All the universal attributes for the object + pub common_attrs: CommonAttrs, } impl ParsedQObject { const ALLOWED_ATTRS: [&'static str; 11] = [ + "cfg", + "doc", "cxx_name", "rust_name", "namespace", - "cfg", - "doc", "qobject", "base", "qml_element", @@ -74,7 +73,10 @@ impl ParsedQObject { ident_left: format_ident!("MyObject"), ident_right: format_ident!("MyObjectRust"), }, - cfgs: vec![], + common_attrs: CommonAttrs { + docs: vec![], + cfgs: vec![], + }, } } @@ -85,9 +87,8 @@ impl ParsedQObject { module: &Ident, auto_case: CaseConversion, ) -> Result { - let attributes = require_attributes(&declaration.attrs, &Self::ALLOWED_ATTRS)?; - // TODO: handle docs through to generation - let cfgs = extract_cfgs(&declaration.attrs); + let (attributes, common_attrs) = + require_attributes(&declaration.attrs, &Self::ALLOWED_ATTRS)?; let has_qobject_macro = attributes.contains_key("qobject"); @@ -125,12 +126,12 @@ impl ParsedQObject { properties, qml_metadata, has_qobject_macro, - cfgs, + common_attrs, }) } fn parse_qml_metadata(name: &Name, attrs: &[Attribute]) -> Result> { - let attributes = require_attributes(attrs, &Self::ALLOWED_ATTRS)?; + let (attributes, _common_attributes) = require_attributes(attrs, &Self::ALLOWED_ATTRS)?; if let Some(attr) = attributes.get("qml_element") { // Extract the name of the qml_element from macro, else use the c++ name // This will use the name provided by cxx_name if that attr was present diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index 81e264ebb..668f826f5 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -2,14 +2,14 @@ // SPDX-FileContributor: Andrew Hayzen // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::parser::CaseConversion; +use crate::parser::CommonAttrs; use crate::{ - parser::{extract_cfgs, extract_docs, method::MethodFields, require_attributes}, + parser::{method::MethodFields, require_attributes, CaseConversion}, syntax::path::path_compare_str, }; use core::ops::Deref; use std::ops::DerefMut; -use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility}; +use syn::{spanned::Spanned, Error, ForeignItemFn, Result, Visibility}; #[derive(Clone)] /// Describes an individual Signal @@ -20,15 +20,13 @@ pub struct ParsedSignal { pub inherit: bool, /// Whether the signal is private pub private: bool, - /// All the doc attributes (each line) of the signal - pub docs: Vec, - /// Cfgs for signal - pub cfgs: Vec, + /// All the universal attributes for the signal + pub common_attrs: CommonAttrs, } impl ParsedSignal { const ALLOWED_ATTRS: [&'static str; 6] = - ["cfg", "cxx_name", "rust_name", "inherit", "doc", "qsignal"]; + ["cfg", "doc", "cxx_name", "rust_name", "inherit", "qsignal"]; #[cfg(test)] /// Test fn for creating a mocked signal from a method body @@ -37,10 +35,8 @@ impl ParsedSignal { } pub fn parse(method: ForeignItemFn, auto_case: CaseConversion) -> Result { - let docs = extract_docs(&method.attrs); - let cfgs = extract_cfgs(&method.attrs); let fields = MethodFields::parse(method, auto_case)?; - let attrs = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?; + let (attrs, common_attrs) = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?; if !fields.mutable { return Err(Error::new( @@ -61,8 +57,7 @@ impl ParsedSignal { method_fields: fields, inherit, private, - docs, - cfgs, + common_attrs, }) } } diff --git a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs index bb4fa2c2e..01dca4a00 100644 --- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs @@ -92,6 +92,7 @@ pub mod ffi { } #[namespace = ""] + /// Top level docs for a module unsafe extern "C++Qt" { #[qobject] type QPushButton; @@ -102,6 +103,7 @@ pub mod ffi { #[namespace = "mynamespace"] #[cxx_name = "ExternObjectCpp"] #[qobject] + /// An external object with some docs on it type ExternObject; #[qsignal] @@ -130,6 +132,7 @@ pub mod ffi { #[qobject] #[namespace = "second_object"] #[qproperty(i32, property_name, cxx_name = "propertyName")] + /// The second QObject with some different docs on it type SecondObject = super::SecondObjectRust; } diff --git a/crates/cxx-qt-gen/test_outputs/inheritance.rs b/crates/cxx-qt-gen/test_outputs/inheritance.rs index a44c252d0..6189f7af1 100644 --- a/crates/cxx-qt-gen/test_outputs/inheritance.rs +++ b/crates/cxx-qt-gen/test_outputs/inheritance.rs @@ -103,8 +103,6 @@ mod inheritance { } extern "C++" { type QPushButton; - } - extern "C++" { include ! (< QtWidgets / QPushButton >); } extern "C++" { diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs index 5e0f5b3a7..d74076746 100644 --- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs @@ -201,11 +201,14 @@ pub mod ffi { #[doc = "Use this type when referring to the QObject as a pointer"] #[doc = "\n"] #[doc = "See the book for more information: "] + #[doc = "\n"] + #[doc = " The second QObject with some different docs on it"] #[namespace = "second_object"] type SecondObject; } extern "Rust" { #[namespace = "second_object"] + #[doc = " The second QObject with some different docs on it"] type SecondObjectRust; } extern "Rust" { @@ -381,11 +384,6 @@ pub mod ffi { #[namespace = "rust::cxxqt1"] unsafe fn cxx_qt_ffi_QPushButton_downcastPtr(base: *const QObject) -> *const QPushButton; } - #[namespace = ""] - unsafe extern "C++" { - #[namespace = "cxx_qt::multi_object"] - type QPushButton; - } extern "C++" { #[doc(hidden)] #[cxx_name = "upcastPtr"] @@ -400,9 +398,13 @@ pub mod ffi { ) -> *const ExternObject; } #[namespace = ""] + #[doc = " Top level docs for a module"] unsafe extern "C++" { + #[namespace = "cxx_qt::multi_object"] + type QPushButton; #[namespace = "mynamespace"] #[cxx_name = "ExternObjectCpp"] + #[doc = " An external object with some docs on it"] type ExternObject; } unsafe extern "C++" { diff --git a/crates/cxx-qt-gen/test_outputs/signals.rs b/crates/cxx-qt-gen/test_outputs/signals.rs index 9c30d80ea..56c6fc613 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.rs +++ b/crates/cxx-qt-gen/test_outputs/signals.rs @@ -196,8 +196,6 @@ mod ffi { #[namespace = "cxx_qt::my_object"] #[doc = " QTimer"] type QTimer; - } - unsafe extern "C++" { include ! (< QtCore / QTimer >); } unsafe extern "C++" {