From b3b62415b064af5f58a81acdd9aec16604a40988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 26 Aug 2024 19:59:40 +0200 Subject: [PATCH] glib-macros: Properties: copy property docs to getter We take the docs directly preceding a `#[property]` and copy them into the generated getter method. --- glib-macros/src/lib.rs | 6 ++++ glib-macros/src/properties.rs | 60 ++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index 69006bd9919c..866e60677b20 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -1367,6 +1367,10 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream { /// * `connect_$property_notify()` /// * `notify_$property()` /// +/// # Documentation +/// +/// Doc comments preceding a `#[property]` attribute will be copied to the generated getter method. +/// /// ## Extension trait /// You can choose to move the method definitions to a trait by using `#[properties(wrapper_type = super::MyType, ext_trait = MyTypePropertiesExt)]`. /// The trait name is optional, and defaults to `MyTypePropertiesExt`, where `MyType` is extracted from the wrapper type. @@ -1430,7 +1434,9 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream { /// pub struct Foo { /// #[property(get, set = Self::set_fizz)] /// fizz: RefCell, +/// /// The author's name /// #[property(name = "author-name", get, set, type = String, member = name)] +/// /// The author's childhood nickname /// #[property(name = "author-nick", get, set, type = String, member = nick)] /// author: RefCell, /// #[property(get, set, explicit_notify, lax_validation)] diff --git a/glib-macros/src/properties.rs b/glib-macros/src/properties.rs index 07c91891fc13..8b4c97db3ed8 100644 --- a/glib-macros/src/properties.rs +++ b/glib-macros/src/properties.rs @@ -12,7 +12,7 @@ use syn::parse::Parse; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::Token; -use syn::{parse_quote_spanned, LitStr}; +use syn::{parse_quote_spanned, Attribute, LitStr}; pub struct PropsMacroInput { wrapper_ty: syn::Path, @@ -255,6 +255,7 @@ struct PropDesc { field_ident: syn::Ident, ty: syn::Type, name: syn::LitStr, + comments: Vec, override_class: Option, override_interface: Option, nullable: bool, @@ -271,6 +272,7 @@ impl PropDesc { attrs_span: proc_macro2::Span, field_ident: syn::Ident, field_ty: syn::Type, + comments: Vec, attrs: ReceivedAttrs, ) -> syn::Result { let ReceivedAttrs { @@ -321,6 +323,7 @@ impl PropDesc { field_ident, ty, name, + comments, override_class, override_interface, nullable, @@ -524,26 +527,33 @@ fn expand_set_property_fn(props: &[PropDesc]) -> TokenStream2 { } fn parse_fields(fields: syn::Fields) -> syn::Result> { - fields - .into_iter() - .flat_map(|field| { - let syn::Field { - ident, attrs, ty, .. - } = field; - attrs - .into_iter() - .filter(|a| a.path().is_ident("property")) - .map(move |prop_attrs| { - let span = prop_attrs.span(); - PropDesc::new( - span, - ident.as_ref().unwrap().clone(), - ty.clone(), - prop_attrs.parse_args()?, - ) - }) - }) - .collect::>() + let mut properties = vec![]; + + for field in fields.into_iter() { + let syn::Field { + ident, attrs, ty, .. + } = field; + // Store the comments until the next `#[property]` we see and then attach them to it. + let mut comments: Vec = vec![]; + for prop_attr in attrs.iter() { + if prop_attr.path().is_ident("doc") { + comments.push(prop_attr.clone()); + } else if prop_attr.path().is_ident("property") { + let span = prop_attr.span(); + let existing_comments = comments; + comments = vec![]; + properties.push(PropDesc::new( + span, + ident.as_ref().unwrap().clone(), + ty.clone(), + existing_comments, + prop_attr.parse_args()?, + )?); + } + } + } + + Ok(properties) } /// Converts a glib property name to a correct rust ident @@ -567,7 +577,7 @@ fn expand_impl_getset_properties(props: &[PropDesc]) -> Vec { let ident = name_to_ident(name); let ty = &p.ty; - let getter = p.get.is_some().then(|| { + let mut getter: Option = p.get.is_some().then(|| { let span = p.attrs_span; parse_quote_spanned!(span=> #[must_use] @@ -578,6 +588,12 @@ fn expand_impl_getset_properties(props: &[PropDesc]) -> Vec { ) }); + if let Some(ref mut getter) = getter { + for lit in &p.comments { + getter.attrs.push(lit.clone()); + } + } + let setter = (p.set.is_some() && !p.is_construct_only).then(|| { let ident = format_ident!("set_{}", ident); let target_ty = quote!(<<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec>::SetValue);