Skip to content

Commit e5533b5

Browse files
committed
Handle self inside macro invocation
1 parent e2f544f commit e5533b5

File tree

1 file changed

+45
-5
lines changed

1 file changed

+45
-5
lines changed

src/receiver.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use proc_macro2::{Group, TokenStream, TokenTree};
12
use std::mem;
23
use syn::punctuated::Punctuated;
34
use syn::visit_mut::{self, VisitMut};
45
use syn::{
5-
ArgSelf, ArgSelfRef, Block, ExprPath, Ident, Item, MethodSig, Path, QSelf, Type, TypePath,
6+
ArgSelf, ArgSelfRef, Block, ExprPath, Ident, Item, Macro, MethodSig, Path, QSelf, Type,
7+
TypePath,
68
};
79

810
pub fn has_self_in_sig(sig: &mut MethodSig) -> bool {
@@ -126,10 +128,7 @@ impl VisitMut for ReplaceReceiver {
126128
// `Self::method` -> `<Receiver>::method`
127129
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
128130
if expr.qself.is_none() {
129-
if expr.path.is_ident("self") {
130-
let ident = &mut expr.path.segments[0].ident;
131-
*ident = Ident::new("_self", ident.span());
132-
}
131+
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
133132
self.self_to_qself_expr(&mut expr.qself, &mut expr.path);
134133
}
135134
visit_mut::visit_expr_path_mut(self, expr);
@@ -138,4 +137,45 @@ impl VisitMut for ReplaceReceiver {
138137
fn visit_item_mut(&mut self, _: &mut Item) {
139138
// Do not recurse into nested items.
140139
}
140+
141+
fn visit_macro_mut(&mut self, i: &mut Macro) {
142+
// We can't tell in general whether `self` inside a macro invocation
143+
// refers to the self in the argument list or a different self
144+
// introduced within the macro. Heuristic: if the macro input contains
145+
// `fn`, then `self` is more likely to refer to something other than the
146+
// outer function's self argument.
147+
if !contains_fn(i.tts.clone()) {
148+
i.tts = fold_token_stream(i.tts.clone());
149+
}
150+
}
151+
}
152+
153+
fn contains_fn(tts: TokenStream) -> bool {
154+
tts.into_iter().any(|tt| match tt {
155+
TokenTree::Ident(ident) => ident == "fn",
156+
TokenTree::Group(group) => contains_fn(group.stream()),
157+
_ => false,
158+
})
159+
}
160+
161+
fn fold_token_stream(tts: TokenStream) -> TokenStream {
162+
tts.into_iter()
163+
.map(|tt| match tt {
164+
TokenTree::Ident(mut ident) => {
165+
prepend_underscore_to_self(&mut ident);
166+
TokenTree::Ident(ident)
167+
}
168+
TokenTree::Group(group) => {
169+
let content = fold_token_stream(group.stream());
170+
TokenTree::Group(Group::new(group.delimiter(), content))
171+
}
172+
other => other,
173+
})
174+
.collect()
175+
}
176+
177+
fn prepend_underscore_to_self(ident: &mut Ident) {
178+
if ident == "self" {
179+
*ident = Ident::new("_self", ident.span());
180+
}
141181
}

0 commit comments

Comments
 (0)