Skip to content

Commit 49cd39a

Browse files
Add a simple extension trait derive
1 parent eaff1af commit 49cd39a

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed
+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use proc_macro2::Ident;
2+
use quote::quote;
3+
use syn::parse::{Parse, ParseStream};
4+
use syn::punctuated::Punctuated;
5+
use syn::spanned::Spanned;
6+
use syn::{
7+
braced, parse_macro_input, Attribute, Generics, ImplItem, Pat, PatIdent, Path, Signature,
8+
Token, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, Visibility,
9+
};
10+
11+
pub(crate) fn extension(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12+
// Parse the input tokens into a syntax tree
13+
let Extension { attrs, generics, vis, trait_, self_ty, items } =
14+
parse_macro_input!(input as Extension);
15+
let headers: Vec<_> = items
16+
.iter()
17+
.map(|item| match item {
18+
ImplItem::Fn(f) => TraitItem::Fn(TraitItemFn {
19+
attrs: scrub_attrs(&f.attrs),
20+
sig: scrub_header(f.sig.clone()),
21+
default: None,
22+
semi_token: Some(Token![;](f.block.span())),
23+
}),
24+
ImplItem::Const(ct) => TraitItem::Const(TraitItemConst {
25+
attrs: scrub_attrs(&ct.attrs),
26+
const_token: ct.const_token,
27+
ident: ct.ident.clone(),
28+
generics: ct.generics.clone(),
29+
colon_token: ct.colon_token,
30+
ty: ct.ty.clone(),
31+
default: None,
32+
semi_token: ct.semi_token,
33+
}),
34+
ImplItem::Type(ty) => TraitItem::Type(TraitItemType {
35+
attrs: scrub_attrs(&ty.attrs),
36+
type_token: ty.type_token,
37+
ident: ty.ident.clone(),
38+
generics: ty.generics.clone(),
39+
colon_token: None,
40+
bounds: Punctuated::new(),
41+
default: None,
42+
semi_token: ty.semi_token,
43+
}),
44+
ImplItem::Macro(mac) => TraitItem::Macro(TraitItemMacro {
45+
attrs: scrub_attrs(&mac.attrs),
46+
mac: mac.mac.clone(),
47+
semi_token: mac.semi_token,
48+
}),
49+
ImplItem::Verbatim(stream) => TraitItem::Verbatim(stream.clone()),
50+
_ => unimplemented!(),
51+
})
52+
.collect();
53+
54+
quote! {
55+
#(#attrs)*
56+
#vis trait #trait_ {
57+
#(#headers)*
58+
}
59+
60+
impl #generics #trait_ for #self_ty {
61+
#(#items)*
62+
}
63+
}
64+
.into()
65+
}
66+
67+
/// Only keep `#[doc]` attrs.
68+
fn scrub_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
69+
attrs.into_iter().cloned().filter(|attr| attr.path().segments[0].ident == "doc").collect()
70+
}
71+
72+
/// Scrub arguments so that they're valid for trait signatures.
73+
fn scrub_header(mut sig: Signature) -> Signature {
74+
for (idx, input) in sig.inputs.iter_mut().enumerate() {
75+
match input {
76+
syn::FnArg::Receiver(rcvr) => {
77+
// `mut self` -> `self`
78+
if rcvr.reference.is_none() {
79+
rcvr.mutability.take();
80+
}
81+
}
82+
syn::FnArg::Typed(arg) => match &mut *arg.pat {
83+
Pat::Ident(arg) => {
84+
// `ref mut ident @ pat` -> `ident`
85+
arg.by_ref.take();
86+
arg.mutability.take();
87+
arg.subpat.take();
88+
}
89+
_ => {
90+
// `pat` -> `__arg0`
91+
arg.pat = Box::new(
92+
PatIdent {
93+
attrs: vec![],
94+
by_ref: None,
95+
mutability: None,
96+
ident: Ident::new(&format!("__arg{idx}"), arg.pat.span()),
97+
subpat: None,
98+
}
99+
.into(),
100+
)
101+
}
102+
},
103+
}
104+
}
105+
sig
106+
}
107+
108+
struct Extension {
109+
attrs: Vec<Attribute>,
110+
vis: Visibility,
111+
generics: Generics,
112+
trait_: Path,
113+
self_ty: Type,
114+
items: Vec<ImplItem>,
115+
}
116+
117+
impl Parse for Extension {
118+
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
119+
let attrs = input.call(Attribute::parse_outer)?;
120+
let vis = input.parse()?;
121+
let _: Token![impl] = input.parse()?;
122+
let generics = input.parse()?;
123+
let trait_ = input.parse()?;
124+
let _: Token![for] = input.parse()?;
125+
let self_ty = input.parse()?;
126+
127+
let content;
128+
let _brace_token = braced!(content in input);
129+
let mut items = Vec::new();
130+
while !content.is_empty() {
131+
items.push(content.parse()?);
132+
}
133+
134+
Ok(Extension { attrs, generics, vis, trait_, self_ty, items })
135+
}
136+
}

compiler/rustc_macros/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use proc_macro::TokenStream;
1414

1515
mod current_version;
1616
mod diagnostics;
17+
mod extension;
1718
mod hash_stable;
1819
mod lift;
1920
mod query;
@@ -40,6 +41,11 @@ pub fn symbols(input: TokenStream) -> TokenStream {
4041
symbols::symbols(input.into()).into()
4142
}
4243

44+
#[proc_macro_attribute]
45+
pub fn extension(_attr: TokenStream, input: TokenStream) -> TokenStream {
46+
extension::extension(input)
47+
}
48+
4349
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);
4450
decl_derive!(
4551
[HashStable_Generic, attributes(stable_hasher)] =>

0 commit comments

Comments
 (0)