Paste : Passing a non-existing identifier to a macro input

Hi,

I'm writing a macro that would generate different structs, in order to create wrappers around C linked lists.
I wrote a first macro that is working well.

Here's the code invoking the macro :

linked_list_iter!(
  sys, // namespace
  libvlc_module_description, // C raw type
  ModuleDescription, // Rust base struct name
  {
    name: (String, str),
    shortname: (String, str),
    longname: (String, str),
    help: (String, str),
  } // fields
);

And here's the working first version :

macro_rules! linked_list_iter {
  (
    $namespace:path,
    $c_type: ident,
    $name: ident,
    {
      $($(#[$field_meta:meta])*
      $field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+
    }
  ) => {
    paste::item! {
      use std::marker::PhantomData;
      use crate::$namespace::[<$c_type _t>];
      use crate::$namespace::[<$c_type _list_release>];

      #[derive(Clone, PartialEq, Eq, Hash, Debug)]
      pub struct $name {
        $(
          $(#[$field_meta:meta])*
          $field_vis $field_name : Option<$field_type>,
        )*
      }

      #[derive(Clone, PartialEq, Eq, Hash, Debug)]
      pub struct [<$name Ref>]<'a> {
        $(
          $(#[$field_meta:meta])*
          $field_vis $field_name : Option<Cow<'a, $field_b_type>>,
        )*
      }

      impl<'a> [<$name Ref>]<'a> {
        /// Convert to owned strings.
        pub fn into_owned(&'a self) -> $name {
          $name {
            $($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)*
          }
        }
      }

      pub struct [<$name ListIter>]<'a> {
        ptr: *mut [<$c_type _t>],
        _phantomdata: PhantomData<&'a [<$c_type _t>]>,
      }

      impl<'a> Iterator for [<$name ListIter>]<'a> {
        type Item = [<$name Ref>]<'a>;

        fn next(&mut self) -> Option<Self::Item> {
          unsafe {
            if self.ptr.is_null() {
              return None;
            }
            let p = self.ptr;
            self.ptr = (*p).p_next;
            Some([<$name Ref>] {
              $($field_name: from_cstr_ref((*p).[<psz_ $field_name>]),)*
            })
          }
        }
      }

      pub struct [<$name List>] {
        ptr: *mut [<$c_type _t>]
      }

      impl [<$name List>] {
        /// Returns raw pointer
        pub fn raw(&self) -> *mut [<$c_type _t>] {
          self.ptr
        }
      }

      impl Drop for [<$name List>] {
        fn drop(&mut self) {
          unsafe{ [<$c_type _list_release>](self.ptr) };
        }
      }

      impl<'a> IntoIterator for &'a [<$name List>] {
        type Item = [<$name Ref>]<'a>;
        type IntoIter = [<$name ListIter>]<'a>;

        fn into_iter(self) -> Self::IntoIter {
          [<$name ListIter>]{ptr: self.ptr, _phantomdata: PhantomData}
        }
      }
    }
  }
}

Now, I'd prefer to generate these structs inside a module, so I modified a little bit the macro call :

linked_list_iter!(
  sys, // namespace
  libvlc_module_description, // C raw type
  module_description, // Rust module name
  {
    name: (String, str),
    shortname: (String, str),
    longname: (String, str),
    help: (String, str),
  } // fields
);

And the macro :

macro_rules! linked_list_iter {
  (
    $namespace:path,
    $c_type:ident,
    $name:ident,
    {
      $($(#[$field_meta:meta])*
      $field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+
    }
  ) => {
    paste::item! {
      mod $name {
        use std::borrow::Cow;
        use std::marker::PhantomData;
        use crate::tools::from_cstr_ref;

        use crate::$namespace::[<$c_type _t>];
        use crate::$namespace::[<$c_type _list_release>];

        #[derive(Clone, PartialEq, Eq, Hash, Debug)]
        pub struct Item {
          $(
            $(#[$field_meta:meta])*
            $field_vis $field_name : Option<$field_type>,
          )*
        }

        #[derive(Clone, PartialEq, Eq, Hash, Debug)]
        pub struct ItemRef<'a> {
          $(
            $(#[$field_meta:meta])*
            $field_vis $field_name : Option<Cow<'a, $field_b_type>>,
          )*
        }

        impl<'a> ItemRef<'a> {
          /// Convert to owned strings.
          pub fn into_owned(&'a self) -> $name {
            $name {
              $($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)*
            }
          }
        }

        pub struct ListIter<'a> {
          ptr: *mut [<$c_type _t>],
          _phantomdata: PhantomData<&'a [<$c_type _t>]>,
        }

        impl<'a> Iterator for ListIter<'a> {
          type Item = ItemRef<'a>;

          fn next(&mut self) -> Option<Self::Item> {
            unsafe {
              if self.ptr.is_null() {
                return None;
              }
              let p = self.ptr;
              self.ptr = (*p).p_next;
              Some(ItemRef {
                $($field_name: from_cstr_ref((*p).[<psz_ $field_name>]),)*
              })
            }
          }
        }

        pub struct List {
          ptr: *mut [<$c_type _t>]
        }

        impl List {
          /// Returns raw pointer
          pub fn raw(&self) -> *mut [<$c_type _t>] {
            self.ptr
          }
        }

        impl Drop for List {
          fn drop(&mut self) {
            unsafe{ [<$c_type _list_release>](self.ptr) };
          }
        }

        impl<'a> IntoIterator for &'a List {
          type Item = ItemRef<'a>;
          type IntoIter = ListIter<'a>;

          fn into_iter(self) -> Self::IntoIter {
            ListIter{ptr: self.ptr, _phantomdata: PhantomData}
          }
        }
      }

      // backward compatibility types
      type [<$name:camel>] = $name::Item;
      type [<$name:camel Ref>]<'a> = $name::ItemRef<'a>;
      type [<$name:camel List>] = $name::List;
      type [<$name:camel ListIter>]<'a> = $name::ListIter<'a>;
    }
  }
}

But I have an error in the macro call, complaining about "module_description" not found :

error[E0412]: cannot find type `module_description` in this scope
  --> src/core.rs:32:3
   |
32 |   module_description, // Rust base struct name
   |   ^^^^^^^^^^^^^^^^^^ not found in this scope

error[E0422]: cannot find struct, variant or union type `module_description` in this scope
  --> src/core.rs:32:3
   |
32 |   module_description, // Rust base struct name
   |   ^^^^^^^^^^^^^^^^^^ not found in this scope

Some errors have detailed explanations: E0412, E0422.
For more information about an error, try `rustc --explain E0412`.

Any idea on why this error occurs ?

Regards,

--
Pierre

This still uses $name; I guess you should change it to use Item instead.


In general, you can debug macro invocations like this by looking at the output from cargo expand and then pasting the expansion into the source file. Then the error message will properly point out the place where the problem occurs. (In a bigger project, use cargo expand path::to::module to only get one module. Another approach that might work would be to use trace_macros!(true); … trace_macros!(false); around a macro invocation, that prints the output of the macro, too.)

1 Like

Thanks a lot !
Cargo expand is very useful.