Change:
As this This is the example proc-macro: This is the test: Run both of result and result are: Formal parameter attributes disappear in proc-macro. Toolchain:
cc
I have a fix in

Discussion:
self.s.word("<"); self.commasep(Inconsistent, &generic_params, |s, param| { <ins> s.print_outer_attributes_inline(&param.attrs); </ins> match param.kind { ast::GenericParamKind::Lifetime => { <del> s.print_outer_attributes_inline(&param.attrs); </del> let lt = ast::Lifetime { id: param.id, ident: param.ident }; s.print_lifetime_bounds(lt, &param.bounds) } ast::GenericParamKind::Type { ref default } => { <del> s.print_outer_attributes_inline(&param.attrs); </del> s.print_ident(param.ident); s.print_type_bounds(":", &param.bounds); <del> match default { Some(ref default) => { s.s.space(); s.word_space("="); s.print_type(default) } _ => {} </del> <ins> if let Some(ref default) = default { s.s.space(); s.word_space("="); s.print_type(default) </ins> } } ast::GenericParamKind::Const { ref ty } => { <del> s.print_outer_attributes_inline(&param.attrs); </del> s.word_space("const"); s.print_ident(param.ident); s.s.space();

crate fn print_arg(&mut self, input: &ast::Arg, is_closure: bool) { self.ibox(INDENT_UNIT); <ins> self.print_outer_attributes_inline(&input.attrs); </ins> match input.ty.node { ast::TyKind::Infer if is_closure => self.print_pat(&input.pat), _ => {

<ins> // force-host // no-prefer-dynamic #![crate_type = "proc-macro"] extern crate proc_macro; use proc_macro::TokenStream; macro_rules! checker { ($attr_name:ident, $expected:literal) => { #[proc_macro_attribute] pub fn $attr_name(attr: TokenStream, input: TokenStream) -> TokenStream { assert!(attr.to_string().is_empty()); assert_eq!(input.to_string(), $expected); TokenStream::new() } } } checker!(attr_extern, r#"extern "C" { fn ffi(#[a1] arg1: i32, #[a2] ...); }"#); checker!(attr_extern_cvar, r#"unsafe extern "C" fn cvar(arg1: i32, #[a1] mut args: ...) { }"#); checker!(attr_alias, "type Alias = fn(#[a1] u8, #[a2] ...);"); checker!(attr_free, "fn free(#[a1] arg1: u8) { let lam = |#[a2] W(x), #[a3] y| (); }"); checker!(attr_inherent_1, "fn inherent1(#[a1] self, #[a2] arg1: u8) { }"); checker!(attr_inherent_2, "fn inherent2(#[a1] &self, #[a2] arg1: u8) { }"); checker!(attr_inherent_3, "fn inherent3<'a>(#[a1] &'a mut self, #[a2] arg1: u8) { }"); checker!(attr_inherent_4, "fn inherent4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8) { }"); checker!(attr_trait_1, "fn trait1(#[a1] self, #[a2] arg1: u8);"); checker!(attr_trait_2, "fn trait2(#[a1] &self, #[a2] arg1: u8);"); checker!(attr_trait_3, "fn trait3<'a>(#[a1] &'a mut self, #[a2] arg1: u8);"); checker!(attr_trait_4, "fn trait4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8, #[a3] Vec<u8>);"); </ins>

<ins> // aux-build:param-attrs.rs // check-pass #![feature(param_attrs)] #![feature(c_variadic)] extern crate param_attrs; use param_attrs::*; struct W(u8); #[attr_extern] extern "C" { fn ffi(#[a1] arg1: i32, #[a2] ...); } #[attr_extern_cvar] unsafe extern "C" fn cvar(arg1: i32, #[a1] mut args: ...) {} #[attr_alias] type Alias = fn(#[a1] u8, #[a2] ...); #[attr_free] fn free(#[a1] arg1: u8) { let lam = |#[a2] W(x), #[a3] y| (); } impl W { #[attr_inherent_1] fn inherent1(#[a1] self, #[a2] arg1: u8) {} #[attr_inherent_2] fn inherent2(#[a1] &self, #[a2] arg1: u8) {} #[attr_inherent_3] fn inherent3<'a>(#[a1] &'a mut self, #[a2] arg1: u8) {} #[attr_inherent_4] fn inherent4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8) {} } trait A { #[attr_trait_1] fn trait1(#[a1] self, #[a2] arg1: u8); #[attr_trait_2] fn trait2(#[a1] &self, #[a2] arg1: u8); #[attr_trait_3] fn trait3<'a>(#[a1] &'a mut self, #[a2] arg1: u8); #[attr_trait_4] fn trait4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8, #[a3] Vec<u8>); } fn main() {} </ins>