miniextendr_macros/
receiver_body.rs1use proc_macro2::{Group, Ident, TokenStream, TokenTree};
4use syn::visit_mut::{self, VisitMut};
5
6pub(crate) fn rewrite(body: &mut syn::Block, receiver: &Ident) {
7 ReceiverBody { receiver }.visit_block_mut(body);
8}
9
10struct ReceiverBody<'a> {
11 receiver: &'a Ident,
12}
13
14impl VisitMut for ReceiverBody<'_> {
15 fn visit_expr_path_mut(&mut self, expr: &mut syn::ExprPath) {
16 if expr.qself.is_none() && expr.path.is_ident("self") {
17 expr.path = self.receiver.clone().into();
18 } else {
19 visit_mut::visit_expr_path_mut(self, expr);
20 }
21 }
22
23 fn visit_item_mut(&mut self, item: &mut syn::Item) {
24 if let syn::Item::Macro(item) = item {
27 self.visit_macro_mut(&mut item.mac);
28 }
29 }
30
31 fn visit_macro_mut(&mut self, mac: &mut syn::Macro) {
32 mac.tokens = rewrite_macro_tokens(mac.tokens.clone(), self.receiver);
35 }
36}
37
38fn rewrite_macro_tokens(tokens: TokenStream, receiver: &Ident) -> TokenStream {
39 let tokens: Vec<_> = tokens.into_iter().collect();
40 tokens.iter().enumerate().map(|(i, token)| match token {
41 TokenTree::Ident(ident) if ident == "self" => {
42 let module_path = #[allow(non_exhaustive_omitted_patterns)] match tokens.get(i + 1) {
Some(TokenTree::Punct(p)) if p.as_char() == ':' => true,
_ => false,
}matches!(tokens.get(i + 1), Some(TokenTree::Punct(p)) if p.as_char() == ':')
43 && #[allow(non_exhaustive_omitted_patterns)] match tokens.get(i + 2) {
Some(TokenTree::Punct(p)) if p.as_char() == ':' => true,
_ => false,
}matches!(tokens.get(i + 2), Some(TokenTree::Punct(p)) if p.as_char() == ':');
44 if module_path {
45 token.clone()
46 } else {
47 TokenTree::Ident(receiver.clone())
48 }
49 }
50 TokenTree::Group(group) => {
51 let mut rewritten = Group::new(group.delimiter(), rewrite_macro_tokens(group.stream(), receiver));
52 rewritten.set_span(group.span());
53 TokenTree::Group(rewritten)
54 }
55 _ => token.clone(),
56 }).collect()
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use quote::quote;
63
64 #[test]
65 fn preserves_module_paths_and_nested_receivers() {
66 let mut body: syn::Block = syn::parse_quote!({
67 struct Local(i32);
68 impl Local {
69 fn read(&self) -> i32 {
70 self.0
71 }
72 }
73 use self::support::OFFSET;
74 let read = || self.value;
75 assert_eq!(self.value, self::support::value());
76 self.value + read() + Local(OFFSET).read()
77 });
78 rewrite(&mut body, &syn::parse_quote!(__receiver));
79 let expected: syn::Block = syn::parse_quote!({
80 struct Local(i32);
81 impl Local {
82 fn read(&self) -> i32 {
83 self.0
84 }
85 }
86 use self::support::OFFSET;
87 let read = || __receiver.value;
88 assert_eq!(__receiver.value, self::support::value());
89 __receiver.value + read() + Local(OFFSET).read()
90 });
91 assert_eq!(quote!(#body).to_string(), quote!(#expected).to_string());
92 }
93
94 #[test]
95 fn local_macros_can_capture_the_outer_receiver() {
96 let mut body: syn::Block = syn::parse_quote!({
97 macro_rules! read {
98 () => {
99 self.value + self::OFFSET
100 };
101 }
102 read!()
103 });
104 rewrite(&mut body, &syn::parse_quote!(__receiver));
105 let expected: syn::Block = syn::parse_quote!({
106 macro_rules! read {
107 () => {
108 __receiver.value + self::OFFSET
109 };
110 }
111 read!()
112 });
113 assert_eq!(quote!(#body).to_string(), quote!(#expected).to_string());
114 }
115}