在Rust的proc_macro_attribute实现中过滤嵌套的#[fix]属性
过滤proc宏中函数参数的指定属性
直接操作TokenStream的TokenTree来过滤属性容易出错,毕竟Rust语法结构嵌套复杂。更简洁可靠的方式是用syn库解析成抽象语法树(AST),修改后再转回TokenStream,这和标准库#[derive(Default)]处理#[default]属性的逻辑一致。
实现步骤
- 添加依赖
在Cargo.toml中加入syn和quote(proc宏开发的标准工具链):
[dependencies] syn = { version = "2.0", features = ["full", "parsing"] } quote = "1.0" proc-macro2 = "1.0"
- 解析并修改语法树
用syn将输入解析为模块结构,遍历其中的函数,过滤掉参数上的#[fix]属性:
use proc_macro::TokenStream; use syn::{parse_macro_input, ItemMod, ItemFn, FnArg}; use quote::quote; #[proc_macro_attribute] pub fn custom(_attr: TokenStream, input: TokenStream) -> TokenStream { // 解析输入为模块项 let mut module = parse_macro_input!(input as ItemMod); // 遍历模块内的所有子项 if let Some((_, items)) = &mut module.content { for item in items { // 匹配并处理函数项 if let syn::Item::Fn(func) = item { filter_fix_attrs(func); } } } // 将修改后的语法树转回TokenStream quote!(#module).into() } // 过滤函数参数上的#[fix]属性 fn filter_fix_attrs(func: &mut ItemFn) { for arg in &mut func.sig.inputs { // 匹配带属性的普通参数(跳过self相关参数) if let FnArg::Typed(pat_type) = arg { // 保留所有非#[fix]的属性 pat_type.attrs.retain(|attr| !attr.path().is_ident("fix")); } } }
- 测试效果
使用示例中的代码:
#[custom] mod MyModule { pub fn my_func( #[fix] arg1: i32, arg2: String // 不受处理影响 ) { // 函数逻辑 } }
宏展开后会自动移除#[fix]属性,最终代码为:
mod MyModule { pub fn my_func( arg1: i32, arg2: String ) { // 函数逻辑 } }
关键说明
- syn的
parse_macro_input!宏能安全解析TokenStream为指定语法树类型,若输入语法错误会给出友好的编译提示。 - 用
retain方法过滤属性是最简洁的方式,直接保留非目标属性。 - 该逻辑可轻松扩展到其他场景(如独立函数、impl块内的方法),只需调整遍历的语法树节点类型即可。
内容的提问来源于stack exchange,提问作者Yuri Astrakhan
相关产品推荐
相关产品推荐

