Skip to main content

derive_tools_meta/derive/
deref_mut.rs

1use macro_tools ::
2{
3  diag, struct_like ::StructLike, Result, qt, attr, syn, proc_macro2, return_syn_err, syn_err, Spanned,
4};
5
6///
7/// Derive macro to implement `DerefMut` when-ever it's possible to do automatically.
8///
9/// Fix(issue-5): Changed from `generic_params::decompose` to `split_for_impl`.
10/// Root cause: `decompose()` returns `Punctuated` types incompatible with `quote!` macro, causing "expected one of..." errors with generic types.
11/// Pitfall: Always use `split_for_impl()` for trait implementations, not `decompose()`. `split_for_impl()` returns properly formatted `ImplGenerics` and `TypeGenerics`.
12///
13pub fn deref_mut(input: proc_macro ::TokenStream) -> Result< proc_macro2 ::TokenStream >
14{
15  let original_input = input.clone();
16  let parsed = syn ::parse :: < StructLike >(input)?;
17  let has_debug = attr ::has_debug(parsed.attrs().iter())?;
18  let item_name = &parsed.ident();
19
20  let (generics_impl, generics_ty, generics_where_option) = parsed.generics().split_for_impl();
21
22  let result =  match parsed 
23  {
24  StructLike ::Unit(ref _item) =>
25  {
26   return_syn_err!(parsed.span(), "Expects a structure with one field");
27 }
28  StructLike ::Struct(ref item) =>
29  {
30   let fields_count = item.fields.len();
31   let mut target_field_type = None;
32   let mut target_field_name = None;
33   let mut deref_mut_attr_count = 0;
34
35   if fields_count == 0 
36   {
37  return_syn_err!(item.span(), "DerefMut cannot be derived for structs with no fields.");
38 } else  if fields_count == 1 
39  {
40  // Single field struct: automatically deref_mut to that field
41  let field = item.fields.iter().next().expect("Expects a single field to derive DerefMut");
42  target_field_type = Some(field.ty.clone());
43  target_field_name.clone_from(&field.ident);
44 } else {
45  // Multi-field struct: require #[ deref_mut ] attribute on one field
46  for field in &item.fields 
47  {
48   if attr ::has_deref_mut(field.attrs.iter())? 
49   {
50  deref_mut_attr_count += 1;
51  target_field_type = Some(field.ty.clone());
52  target_field_name.clone_from(&field.ident);
53 }
54 }
55
56  if deref_mut_attr_count == 0 
57  {
58   return_syn_err!(
59  item.span(),
60  "DerefMut cannot be derived for multi-field structs without a `#[ deref_mut ]` attribute on one field."
61 );
62 } else  if deref_mut_attr_count > 1 
63  {
64   return_syn_err!(item.span(), "Only one field can have the `#[ deref_mut ]` attribute.");
65 }
66 }
67
68   let field_type =
69  target_field_type.ok_or_else(|| syn_err!(item.span(), "Could not determine target field type for DerefMut."))?;
70   let field_name = target_field_name;
71
72   generate(
73  item_name,
74  &generics_impl,
75  &generics_ty,
76  generics_where_option,
77  &field_type,
78  field_name.as_ref(),
79 )
80 }
81  StructLike ::Enum(ref item) =>
82  {
83   return_syn_err!(
84  item.span(),
85  "DerefMut cannot be derived for enums. It is only applicable to structs with a single field."
86 );
87 }
88 };
89
90  if has_debug 
91  {
92  let about = format!("derive: DerefMut\nstructure: {item_name}");
93  diag ::report_print(about, &original_input, &result);
94 }
95
96  Ok(result)
97}
98
99/// Generates `DerefMut` implementation for structs.
100///
101/// Fix(issue-5): Updated signature to use `ImplGenerics`, `TypeGenerics`, and `WhereClause`.
102/// Root cause: `Punctuated` types don't format correctly in `quote!` macro for generic impl blocks.
103/// Pitfall: Always use `split_for_impl()` types (`ImplGenerics`, `TypeGenerics`, `WhereClause`) for trait implementations.
104///
105/// Example of generated code :
106/// ```text
107/// impl< T > ::core ::ops ::DerefMut for IsTransparent< T >
108/// {
109///   fn deref_mut( &mut self ) -> &mut T
110/// ///   {
111/// ///     &mut self.0
112/// /// }
113/// /// }
114/// ```
115fn generate(
116  item_name: &syn ::Ident,
117  generics_impl: &syn ::ImplGenerics< '_ >,
118  generics_ty: &syn ::TypeGenerics< '_ >,
119  generics_where_option: Option< &syn ::WhereClause >,
120  field_type: &syn ::Type,
121  field_name: Option< &syn ::Ident >,
122) -> proc_macro2 ::TokenStream {
123  let body =  if let Some(field_name) = field_name
124  {
125  qt! { &mut self.#field_name }
126 } else {
127  qt! { &mut self.0 }
128 };
129
130  qt! {
131  #[ automatically_derived ]
132  impl #generics_impl ::core ::ops ::DerefMut for #item_name #generics_ty
133  #generics_where_option
134  {
135   fn deref_mut( &mut self ) -> &mut #field_type
136   {
137  #body
138 }
139 }
140 }
141}