-
-
Notifications
You must be signed in to change notification settings - Fork 5
Conversation
@dtolnay okay I feel like I'm already getting stuck on groking the next two steps. I'm reading through the docs for proc-macro-hack and I'm already feeling like I'm a little in over my head. Maybe its best to summaries the pieces of mashup I understand and then start questions from there. Essentially mashup is a bit like macro inception. Its a macro that generates macros out of necessity for collecting statements declaring components that should be concatenated. I think I understand the latter part more than the former. In the collection phase the impl's parse fn is called and collects a name it will use as an exported new macro name, Before we get there's a declaration site and impl site where some magic happens after what I assume to be a whittling down of a block of syntax down to a single line of syntax in mashup and mashup_parser Work gets handled off to the actual impl here. There's some interaction that I don't quite understand that enables a form of derive macro which is the hack part of the proc-macro-hack crate that has my head spinning. What I was initially thinking was that I could skip the need for understanding that and go straight to the transition to stable proc macro. My initial approach was ( in the impl crate ) to create a new entrypoint for that would also delegate to the Those interfaces typically take two streams and return a new here's a rough sketch of what I think that may look like #[proc_macro_attribute]
pub fn mashup_macro_stable(_: TokenStream, input: TokenStream) -> TokenStream {
let input = parse(input);
let mut macros = String::new();
for (name, concat) in input {
macros += &make_macro(name, concat);
}
macros.parse().unwrap()
} next up is figuring out how to wire that into the consumer ( mashup ) crate. with something like
but that doesn't quiet work as I'll get an error like the following
I feel like I'm on the brink of understanding here am also feeling a little stuck. Can you drop another hint/point of direction form what I described above? If it would be helpful I could commit what I have so you can see the errors I'm looking at in travis. |
After dropping proc-macro-hack there is no longer a need for separate declaration crate and implementation crate. There would be just one crate #[proc_macro]
pub fn mashup(input: TokenStream) -> TokenStream {
/* ... */
} which takes in the |
Oh this may be simpler that I thought! I'll give this another look tomorrow |
doh! at least I thought that would be simple. Now running into another issue that seems like it's by design. It seems that procedural macros cannot expand to macro definitions that seems kind of like a show stopper for porting this to a first class proc macro because expanding to macro definitions is this macros specialty! |
Ah right. Time for some more indirection! Procedural macros in item position can expand to invocations of derive macros, and derive macros can expand to macro_rules macro definitions. mashup! {
m[...] = ...;
qrst[...] = ...;
}
// expands to
#[derive(mashup::EnumHack)]
enum _mashup_1m_4qrst { // some mangled name based on the substitution macro names
Value = (stringify! {
macro_rules! m {
...
}
macro_rules! qrst {
...
}
}, 0).1,
}
// expands to
macro_rules! m {
...
}
macro_rules! qrst {
...
} |
@dtolnay I have no idea what you look like in real life but I can only imagine you wear a purple a wizard hat. I'll take a shot at that approach |
we may need one more level of indirection :/ I'm now seeing "error[E0658]: procedural macros cannot be expanded to statements (see issue #38356)". I think that's seems to come from rustc here. for ctx here's what I switched to returning from the new mashup! proc macro. format!(r#"
#[derive(mashup::EnumHack)]
enum {} {{
Value = (stringify! {{
{}
}}, 0).1,
}}
"#, dummy, macros) I'm also borrowing some of the source from the proc macro hack repo. In particular this Parse impl which actually depends on some proc macro2 types. Likely because Parse hasn't been implemented yet for likewise Im starting to understand a bit about the proc macro hack crate as it uses a trick similar to the one I'm trying to perform #[doc(hidden)]
#[proc_macro_derive(EnumHack)]
pub fn enum_hack(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let inner = parse_macro_input!(input as EnumHack);
proc_macro::TokenStream::from(inner.token_stream)
} |
It may also be an issue with the call syntax. There's some references here that describe what you can and can't do with proc macros |
I came up with a fresh batch of hacks today (as you noticed in the proc-macro-hack repo) which makes it possible to use function-like procedural macros in expression position. That makes it so that we may no longer need substitution macros at all. I put together a prototype of an approach without substitution macros in https://github.com/dtolnay/paste. Could you take a look at whether it would work for your use case? I believe it behaves correctly with respect to 2018-style macro imports as far as I have been able to test. |
Holy smokes. I'll try this out tomorrow in an app tomorrow and let you know. In learned alot about macros in the last few weeks. Thank you so much for being patient with me. |
@dtolnay did a test run. Over here https://github.com/softprops/lando/pull/38/files the first thing I noticed (which may just be me miss understanding new crate resolution semantics ) was that a consuming crate doesn't automatically get transient reference paste so I may need to re-export from my crate as I would pre 1.30.0 below is the output from a consumer of my crate ( which depends on
|
after re-exporting
I'm seeing a different error. It may be due to me using the wrong paste method.
below is the what my expr! {
gateway! { @module ([<lib env!("CARGO_PKG_NAME")>],[<initlib env!("CARGO_PKG_NAME")>], [<PyInit_lib env!("CARGO_PKG_NAME")>])
@handlers ($($handler => $target),*) }
} |
when I try with
|
Looks like you are missing a helper/src/lib.rsextern crate paste;
#[doc(hidden)]
pub use paste::expr as paste_expr;
#[macro_export]
macro_rules! helper {
() => {
$crate::paste_expr! {
[<lib env!("CARGO_PKG_NAME")>]
}
};
} testing/src/main.rsextern crate helper;
fn libtesting() {
println!("success");
}
fn main() {
helper::helper!()();
} |
I was able to work around the scoping issue but now I'm stuck on this expr/item error here It may have something to with the expansion order or something error: macro expansion ignores token `{` and any following
--> <::paste::expr macros>:2:1
|
2 | {
| ^
|
note: caused by the macro expansion here; the usage of `expr!` is likely invalid in item context
--> src/lib.rs:7:1
|
7 | / gateway!(|request, _| {
8 | | println!("{:?}", request.path_parameters());
9 | | Ok(lando::Response::new(format!(
10| | "hello {}",
... |
16| | )))
17| | });
| |___^
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) |
Is there test coverage of
If |
added an example branch on the repo i'm testing in here softprops/serverless-lando#6 |
what's strange is that demo app is essential the same as what should be getting picked up by a doc test here
|
Since - pub use paste::expr as paste_expr;
+ pub use paste::item as paste_item;
- $crate::paste_expr! {
+ $crate::paste_item! { After that I see |
Thanks for all of the hand holding in this. I thought I had tried
I moved all of my fake doc mains to a separate hidden line in examples I can confirm though that this fixed the issue for me. All of that for the ability to not have to |
here's what that last commit looked like for future reference softprops/lando@572e95e |
The end result of this should hopefully be the follow up on #17 (review) and close out #16
I expect I'll have a lot of questions so I'm going to let this pr be a point of reference with a checklist to keep track of where I am along the way