1 use proc_macro2::{Group, TokenStream, TokenTree};
2 use syn::visit_mut::{self, VisitMut};
3 use syn::{
4 Block, ExprPath, Ident, Item, Macro, Pat, PatIdent, Path, Receiver, Signature, Token, TypePath,
5 };
6
has_self_in_sig(sig: &mut Signature) -> bool7 pub fn has_self_in_sig(sig: &mut Signature) -> bool {
8 let mut visitor = HasSelf(false);
9 visitor.visit_signature_mut(sig);
10 visitor.0
11 }
12
has_self_in_block(block: &mut Block) -> bool13 pub fn has_self_in_block(block: &mut Block) -> bool {
14 let mut visitor = HasSelf(false);
15 visitor.visit_block_mut(block);
16 visitor.0
17 }
18
has_self_in_token_stream(tokens: TokenStream) -> bool19 fn has_self_in_token_stream(tokens: TokenStream) -> bool {
20 tokens.into_iter().any(|tt| match tt {
21 TokenTree::Ident(ident) => ident == "Self",
22 TokenTree::Group(group) => has_self_in_token_stream(group.stream()),
23 _ => false,
24 })
25 }
26
mut_pat(pat: &mut Pat) -> Option<Token![mut]>27 pub fn mut_pat(pat: &mut Pat) -> Option<Token![mut]> {
28 let mut visitor = HasMutPat(None);
29 visitor.visit_pat_mut(pat);
30 visitor.0
31 }
32
contains_fn(tokens: TokenStream) -> bool33 fn contains_fn(tokens: TokenStream) -> bool {
34 tokens.into_iter().any(|tt| match tt {
35 TokenTree::Ident(ident) => ident == "fn",
36 TokenTree::Group(group) => contains_fn(group.stream()),
37 _ => false,
38 })
39 }
40
41 struct HasMutPat(Option<Token![mut]>);
42
43 impl VisitMut for HasMutPat {
visit_pat_ident_mut(&mut self, i: &mut PatIdent)44 fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) {
45 if let Some(m) = &i.mutability {
46 self.0 = Some(Token);
47 } else {
48 visit_mut::visit_pat_ident_mut(self, i);
49 }
50 }
51 }
52
53 struct HasSelf(bool);
54
55 impl VisitMut for HasSelf {
visit_expr_path_mut(&mut self, expr: &mut ExprPath)56 fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
57 self.0 |= expr.path.segments[0].ident == "Self";
58 visit_mut::visit_expr_path_mut(self, expr);
59 }
60
visit_type_path_mut(&mut self, ty: &mut TypePath)61 fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
62 self.0 |= ty.path.segments[0].ident == "Self";
63 visit_mut::visit_type_path_mut(self, ty);
64 }
65
visit_receiver_mut(&mut self, _arg: &mut Receiver)66 fn visit_receiver_mut(&mut self, _arg: &mut Receiver) {
67 self.0 = true;
68 }
69
visit_item_mut(&mut self, _: &mut Item)70 fn visit_item_mut(&mut self, _: &mut Item) {
71 // Do not recurse into nested items.
72 }
73
visit_macro_mut(&mut self, mac: &mut Macro)74 fn visit_macro_mut(&mut self, mac: &mut Macro) {
75 if !contains_fn(mac.tokens.clone()) {
76 self.0 |= has_self_in_token_stream(mac.tokens.clone());
77 }
78 }
79 }
80
81 pub struct ReplaceSelf;
82
prepend_underscore_to_self(ident: &mut Ident) -> bool83 fn prepend_underscore_to_self(ident: &mut Ident) -> bool {
84 let modified = ident == "self";
85 if modified {
86 *ident = Ident::new("__self", ident.span());
87 }
88 modified
89 }
90
91 impl ReplaceSelf {
visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool92 fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool {
93 let mut out = Vec::new();
94 let mut modified = false;
95 visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out);
96 if modified {
97 *tokens = TokenStream::from_iter(out);
98 }
99 return modified;
100
101 fn visit_token_stream_impl(
102 visitor: &mut ReplaceSelf,
103 tokens: TokenStream,
104 modified: &mut bool,
105 out: &mut Vec<TokenTree>,
106 ) {
107 for tt in tokens {
108 match tt {
109 TokenTree::Ident(mut ident) => {
110 *modified |= prepend_underscore_to_self(&mut ident);
111 out.push(TokenTree::Ident(ident));
112 }
113 TokenTree::Group(group) => {
114 let mut content = group.stream();
115 *modified |= visitor.visit_token_stream(&mut content);
116 let mut new = Group::new(group.delimiter(), content);
117 new.set_span(group.span());
118 out.push(TokenTree::Group(new));
119 }
120 other => out.push(other),
121 }
122 }
123 }
124 }
125 }
126
127 impl VisitMut for ReplaceSelf {
visit_ident_mut(&mut self, i: &mut Ident)128 fn visit_ident_mut(&mut self, i: &mut Ident) {
129 prepend_underscore_to_self(i);
130 }
131
visit_path_mut(&mut self, p: &mut Path)132 fn visit_path_mut(&mut self, p: &mut Path) {
133 if p.segments.len() == 1 {
134 // Replace `self`, but not `self::function`.
135 self.visit_ident_mut(&mut p.segments[0].ident);
136 }
137 for segment in &mut p.segments {
138 self.visit_path_arguments_mut(&mut segment.arguments);
139 }
140 }
141
visit_item_mut(&mut self, i: &mut Item)142 fn visit_item_mut(&mut self, i: &mut Item) {
143 // Visit `macro_rules!` because locally defined macros can refer to
144 // `self`.
145 //
146 // Visit `futures::select` and similar select macros, which commonly
147 // appear syntactically like an item despite expanding to an expression.
148 //
149 // Otherwise, do not recurse into nested items.
150 if let Item::Macro(i) = i {
151 if i.mac.path.is_ident("macro_rules")
152 || i.mac.path.segments.last().unwrap().ident == "select"
153 {
154 self.visit_macro_mut(&mut i.mac);
155 }
156 }
157 }
158
visit_macro_mut(&mut self, mac: &mut Macro)159 fn visit_macro_mut(&mut self, mac: &mut Macro) {
160 // We can't tell in general whether `self` inside a macro invocation
161 // refers to the self in the argument list or a different self
162 // introduced within the macro. Heuristic: if the macro input contains
163 // `fn`, then `self` is more likely to refer to something other than the
164 // outer function's self argument.
165 if !contains_fn(mac.tokens.clone()) {
166 self.visit_token_stream(&mut mac.tokens);
167 }
168 }
169 }
170