• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use proc_macro2::{Group, Span, 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(m);
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(pub Span);
82 
83 impl ReplaceSelf {
84     #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))]
prepend_underscore_to_self(&self, ident: &mut Ident) -> bool85     fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool {
86         let modified = ident == "self";
87         if modified {
88             *ident = Ident::new("__self", ident.span());
89             #[cfg(self_span_hack)]
90             ident.set_span(self.0);
91         }
92         modified
93     }
94 
visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool95     fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool {
96         let mut out = Vec::new();
97         let mut modified = false;
98         visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out);
99         if modified {
100             *tokens = TokenStream::from_iter(out);
101         }
102         return modified;
103 
104         fn visit_token_stream_impl(
105             visitor: &mut ReplaceSelf,
106             tokens: TokenStream,
107             modified: &mut bool,
108             out: &mut Vec<TokenTree>,
109         ) {
110             for tt in tokens {
111                 match tt {
112                     TokenTree::Ident(mut ident) => {
113                         *modified |= visitor.prepend_underscore_to_self(&mut ident);
114                         out.push(TokenTree::Ident(ident));
115                     }
116                     TokenTree::Group(group) => {
117                         let mut content = group.stream();
118                         *modified |= visitor.visit_token_stream(&mut content);
119                         let mut new = Group::new(group.delimiter(), content);
120                         new.set_span(group.span());
121                         out.push(TokenTree::Group(new));
122                     }
123                     other => out.push(other),
124                 }
125             }
126         }
127     }
128 }
129 
130 impl VisitMut for ReplaceSelf {
visit_ident_mut(&mut self, i: &mut Ident)131     fn visit_ident_mut(&mut self, i: &mut Ident) {
132         self.prepend_underscore_to_self(i);
133     }
134 
visit_path_mut(&mut self, p: &mut Path)135     fn visit_path_mut(&mut self, p: &mut Path) {
136         if p.segments.len() == 1 {
137             // Replace `self`, but not `self::function`.
138             self.visit_ident_mut(&mut p.segments[0].ident);
139         }
140         for segment in &mut p.segments {
141             self.visit_path_arguments_mut(&mut segment.arguments);
142         }
143     }
144 
visit_item_mut(&mut self, i: &mut Item)145     fn visit_item_mut(&mut self, i: &mut Item) {
146         // Visit `macro_rules!` because locally defined macros can refer to
147         // `self`.
148         //
149         // Visit `futures::select` and similar select macros, which commonly
150         // appear syntactically like an item despite expanding to an expression.
151         //
152         // Otherwise, do not recurse into nested items.
153         if let Item::Macro(i) = i {
154             if i.mac.path.is_ident("macro_rules")
155                 || i.mac.path.segments.last().unwrap().ident == "select"
156             {
157                 self.visit_macro_mut(&mut i.mac);
158             }
159         }
160     }
161 
visit_macro_mut(&mut self, mac: &mut Macro)162     fn visit_macro_mut(&mut self, mac: &mut Macro) {
163         // We can't tell in general whether `self` inside a macro invocation
164         // refers to the self in the argument list or a different self
165         // introduced within the macro. Heuristic: if the macro input contains
166         // `fn`, then `self` is more likely to refer to something other than the
167         // outer function's self argument.
168         if !contains_fn(mac.tokens.clone()) {
169             self.visit_token_stream(&mut mac.tokens);
170         }
171     }
172 }
173