rextendr icon indicating copy to clipboard operation
rextendr copied to clipboard

Cannot use `extendrsrc` with a macro that contains `#[extendr]`

Open JosiahParry opened this issue 3 months ago • 3 comments

```{extendrsrc macro}
macro_rules! make_heck_fn {
    ($fn_name:ident) => {
        #[extendr]
        /// @export
        fn $fn_name(x: Strings) -> Strings {
            x.into_iter()
                .map(|xi| match xi.is_na() {
                    true => Rstr::na(),
                    false => Rstr::from(xi.as_str().$fn_name()),
                })
                .collect::<Strings>()
        }
    };
}
```
Error in `purrr::map2()`:
ℹ In index: 1.
Caused by error in `extract_meta()`:
! Rust code contains invalid attribute macros.
• ✖ No valid `fn` or `impl` block found in the following sample:
•         #[extendr]
•           fn $fn_name(x: Strings) -> Strings {
•               
Backtrace:
  1. global .main()
  2. execute(...)
  3. rmarkdown::render(...)
  4. knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
  5. knitr:::process_file(text, output)
     ...
       at rextendr/R/make_module_macro.R:22:3
 24. purrr::map2(start, end, ~extract_meta(clean_lns[.x:.y]))
 25. purrr:::map2_("list", .x, .y, .f, ..., .progress = .progress)
 29. rextendr (local) .f(.x[[i]], .y[[i]], ...)
 30. rextendr:::extract_meta(clean_lns[.x:.y])
                                                                                                             
Execution halted

JosiahParry avatar Apr 09 '24 14:04 JosiahParry

~It should not work like this if I am not mistaken, the contents of extendrsrc go into a generated rust function.~

It could also be that it does not understand macros (which is definitely on another level).

Ilia-Kosenkov avatar Apr 09 '24 15:04 Ilia-Kosenkov

Its worth noting that it does compile the macro iff #[extendr] is not present and it can even be called later down (pretty neat)!

JosiahParry avatar Apr 09 '24 15:04 JosiahParry

Ok I get it. The issue here is two-fold. We generate exports based on simple scanning for #[extendr] attribute. Here we find it in the macros. We do not do macros expansion so it captures macros declaration verbatim, which is garbage in this case (because of function name interpolation). We can solve it as follows:

  • Introduce syntactic trick e.g. ##[extendr] to disable metadata capture (and replace all of these with #[extendr] before compiling)
  • Introduce (if it is not added yet) support for providing extendr export block explicitly (like you do in package code)
  • By combining all of these you can actually achieve somewhat similar results, but at the cost of perhaps having another macros expanded within export block (if that even works)

Ilia-Kosenkov avatar Apr 09 '24 15:04 Ilia-Kosenkov