Rust: Attribute macro helper attributes

Created on 25 Oct 2019  路  12Comments  路  Source: rust-lang/rust

The reference states that custom derive macros support defined helper attributes:

#[proc_macro_derive(HelperAttr, attributes(helper))]
pub fn derive_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}

However, the same does not appear to apply to proc_macro_attribute. Please extend it to do so.

#[proc_macro_attribute(attributes(helper))]
pub fn my_attribute(attr: TokenStream, item: TokenStream) -> TokenStream { .. }

Motivation is limited, but this could provide simpler specification here.

Related: #53012

A-attributes A-macros C-feature-request T-lang

Most helpful comment

Any updates on this?

All 12 comments

cc @petrochenkov

This is not technically necessary because proc macro attributes can remove these attributes from their input unlike derives, but should be supportable pretty easily as a convenience feature once https://github.com/rust-lang/rust/pull/64694 lands.

Good point; I do think it would be useful for the language to do this automatically as an ergonomics thing from the POV of a macro author myself. Nominating based on @petrochenkov's comment to see what the preliminary interest is in the language team. We may or may not ask for an RFC based on the discussion.

Can we clarify what the expected behavior is here?

If you include attributes(helper), does it just strip out that attribute from wherever it appears in the final output?

I'm generally positive on the idea but would like to see a more complete write-up, at minimum. I mildly prefer a small RFC but I think a PR that links to a good write-up would probably suffice.

(Presumably we would then create a tracking issue.)

This is not technically necessary because proc macro attributes can remove these attributes from their input unlike derives, but should be supportable pretty easily as a convenience feature once #64694 lands.

Is this always true? I just tried this with a proc macro attribute on a function, and the compiler attempted to expand the "helper" attribute and failed before the macro got a chance to strip it. In my scenario the helper attribute was placed on a statement in the function, if that makes any difference.

@calebzulawski
Could you give a minimized reproduction? This shouldn't happen in theory.

I just tried a minimal example and it worked as you describe, I imagine it's likely I wasn't properly stripping out the helper in my larger example. So is this safe behavior to rely on right now? Similar questions were asked in rust-lang/reference#578 and rust-lang/reference#692 but it doesn't look like there's a clear answer. Perhaps helper attribute syntax would be nice but I think documenting the behavior might go a long way.

Having gone through and stumbled over this myself just recently (I actually thought adding a remark to the docs would make sense: https://github.com/rust-lang/reference/pull/716) I must say I really think this needs no compiler support. Here are just a few simple lines (using the syn crate) that completely obliterate all attributes from the AST given to it: https://github.com/Extrawurst/alpaca-rust/commit/46dbba66cf73f6d05f7fdda2d6e1b9d6e30384c2#diff-8f097454b072a95a96c470f9c3f84c5fR136-R151 but that could easily be extended to just the attributes that you want to remove.

Motivation is limited

This will make things easier when multiple attribute macros use the same helper attributes. With proc_macro_derive you can write

#[derive(Serialize,Deserialize)]
struct Foo {
   #[serde(rename = "bar")]
   x: i32
}

And none of the derives has to worry that serde attribute will be stolen by some other macro.
In attribute macros this is probably trickier - you could for example check other attributes that are applied to struct, and skip cleaning helper attributes if you know that this macro will use them. The downside is that this can break easily if user renames attribute or imports it from unrelated liblary.

Any updates on this?

I find stabilized rfc2565 which seems related to this issue.

But I find no way to define #[path_param] and #[query_param] in rfc

 fn get_person(
        &self,
        #[path_param = "name"] name: String, // <-- formal function parameter.
        #[query_param = "limit"] limit: Option<u32>, // <-- here too.
    ) {
        ...
    }

@petrochenkov

Was this page helpful?
0 / 5 - 0 ratings