blob: 2230db6ea24c20ebf25ae996da2c04298c57a74d [file] [log] [blame]
Joel Galenson5448f372021-04-01 15:10:30 -07001use proc_macro2::{Group, Span, TokenStream, TokenTree};
Andrew Walbrand1b91c72020-08-11 17:12:08 +01002use std::iter::FromIterator;
Andrew Walbrand1b91c72020-08-11 17:12:08 +01003use syn::visit_mut::{self, VisitMut};
4use syn::{
Jeff Vander Stoepf833bce2021-04-21 14:43:55 +02005 Block, ExprPath, Ident, Item, Macro, Pat, PatIdent, PatPath, Path, Receiver, Signature, Token,
Joel Galenson5448f372021-04-01 15:10:30 -07006 TypePath,
Andrew Walbrand1b91c72020-08-11 17:12:08 +01007};
8
9pub fn has_self_in_sig(sig: &mut Signature) -> bool {
10 let mut visitor = HasSelf(false);
11 visitor.visit_signature_mut(sig);
12 visitor.0
13}
14
Andrew Walbrand1b91c72020-08-11 17:12:08 +010015pub fn has_self_in_block(block: &mut Block) -> bool {
16 let mut visitor = HasSelf(false);
17 visitor.visit_block_mut(block);
18 visitor.0
19}
20
Haibo Huang62e9b292020-09-01 20:28:34 -070021fn has_self_in_token_stream(tokens: TokenStream) -> bool {
22 tokens.into_iter().any(|tt| match tt {
23 TokenTree::Ident(ident) => ident == "Self",
24 TokenTree::Group(group) => has_self_in_token_stream(group.stream()),
25 _ => false,
26 })
27}
28
Joel Galenson5448f372021-04-01 15:10:30 -070029pub fn mut_pat(pat: &mut Pat) -> Option<Token![mut]> {
30 let mut visitor = HasMutPat(None);
31 visitor.visit_pat_mut(pat);
32 visitor.0
33}
34
35fn contains_fn(tokens: TokenStream) -> bool {
36 tokens.into_iter().any(|tt| match tt {
37 TokenTree::Ident(ident) => ident == "fn",
38 TokenTree::Group(group) => contains_fn(group.stream()),
39 _ => false,
40 })
41}
42
43struct HasMutPat(Option<Token![mut]>);
44
45impl VisitMut for HasMutPat {
46 fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) {
47 if let Some(m) = i.mutability {
48 self.0 = Some(m);
49 } else {
50 visit_mut::visit_pat_ident_mut(self, i);
51 }
52 }
53}
54
Andrew Walbrand1b91c72020-08-11 17:12:08 +010055struct HasSelf(bool);
56
57impl VisitMut for HasSelf {
58 fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
59 self.0 |= expr.path.segments[0].ident == "Self";
60 visit_mut::visit_expr_path_mut(self, expr);
61 }
62
63 fn visit_pat_path_mut(&mut self, pat: &mut PatPath) {
64 self.0 |= pat.path.segments[0].ident == "Self";
65 visit_mut::visit_pat_path_mut(self, pat);
66 }
67
68 fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
69 self.0 |= ty.path.segments[0].ident == "Self";
70 visit_mut::visit_type_path_mut(self, ty);
71 }
72
73 fn visit_receiver_mut(&mut self, _arg: &mut Receiver) {
74 self.0 = true;
75 }
76
77 fn visit_item_mut(&mut self, _: &mut Item) {
78 // Do not recurse into nested items.
79 }
Haibo Huang62e9b292020-09-01 20:28:34 -070080
81 fn visit_macro_mut(&mut self, mac: &mut Macro) {
82 if !contains_fn(mac.tokens.clone()) {
83 self.0 |= has_self_in_token_stream(mac.tokens.clone());
84 }
85 }
Andrew Walbrand1b91c72020-08-11 17:12:08 +010086}
87
Joel Galenson5448f372021-04-01 15:10:30 -070088pub struct ReplaceSelf(pub Span);
Andrew Walbrand1b91c72020-08-11 17:12:08 +010089
Joel Galenson5448f372021-04-01 15:10:30 -070090impl ReplaceSelf {
91 #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))]
92 fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool {
93 let modified = ident == "self";
Andrew Walbrand1b91c72020-08-11 17:12:08 +010094 if modified {
Joel Galenson5448f372021-04-01 15:10:30 -070095 *ident = Ident::new("__self", ident.span());
96 #[cfg(self_span_hack)]
97 ident.set_span(self.0);
Andrew Walbrand1b91c72020-08-11 17:12:08 +010098 }
99 modified
100 }
Joel Galenson5448f372021-04-01 15:10:30 -0700101
102 fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool {
103 let mut out = Vec::new();
104 let mut modified = false;
105 visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out);
106 if modified {
107 *tokens = TokenStream::from_iter(out);
108 }
109 return modified;
110
111 fn visit_token_stream_impl(
112 visitor: &mut ReplaceSelf,
113 tokens: TokenStream,
114 modified: &mut bool,
115 out: &mut Vec<TokenTree>,
116 ) {
117 for tt in tokens {
118 match tt {
119 TokenTree::Ident(mut ident) => {
120 *modified |= visitor.prepend_underscore_to_self(&mut ident);
121 out.push(TokenTree::Ident(ident));
122 }
123 TokenTree::Group(group) => {
124 let mut content = group.stream();
125 *modified |= visitor.visit_token_stream(&mut content);
126 let mut new = Group::new(group.delimiter(), content);
127 new.set_span(group.span());
128 out.push(TokenTree::Group(new));
129 }
130 other => out.push(other),
131 }
132 }
133 }
134 }
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100135}
136
Joel Galenson5448f372021-04-01 15:10:30 -0700137impl VisitMut for ReplaceSelf {
138 fn visit_ident_mut(&mut self, i: &mut Ident) {
139 self.prepend_underscore_to_self(i);
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100140 }
141
Jeff Vander Stoepf833bce2021-04-21 14:43:55 +0200142 fn visit_path_mut(&mut self, p: &mut Path) {
143 if p.segments.len() == 1 {
144 // Replace `self`, but not `self::function`.
145 self.visit_ident_mut(&mut p.segments[0].ident);
146 }
147 for segment in &mut p.segments {
148 self.visit_path_arguments_mut(&mut segment.arguments);
149 }
150 }
151
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100152 fn visit_item_mut(&mut self, i: &mut Item) {
Joel Galenson5448f372021-04-01 15:10:30 -0700153 // Visit `macro_rules!` because locally defined macros can refer to
Jeff Vander Stoepf833bce2021-04-21 14:43:55 +0200154 // `self`.
155 //
156 // Visit `futures::select` and similar select macros, which commonly
157 // appear syntactically like an item despite expanding to an expression.
158 //
159 // Otherwise, do not recurse into nested items.
Joel Galenson5448f372021-04-01 15:10:30 -0700160 if let Item::Macro(i) = i {
Jeff Vander Stoepf833bce2021-04-21 14:43:55 +0200161 if i.mac.path.is_ident("macro_rules")
162 || i.mac.path.segments.last().unwrap().ident == "select"
163 {
Joel Galensonaa9cbeb2021-08-09 10:24:16 -0700164 self.visit_macro_mut(&mut i.mac);
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100165 }
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100166 }
167 }
168
Haibo Huang62e9b292020-09-01 20:28:34 -0700169 fn visit_macro_mut(&mut self, mac: &mut Macro) {
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100170 // We can't tell in general whether `self` inside a macro invocation
171 // refers to the self in the argument list or a different self
172 // introduced within the macro. Heuristic: if the macro input contains
173 // `fn`, then `self` is more likely to refer to something other than the
174 // outer function's self argument.
Haibo Huang62e9b292020-09-01 20:28:34 -0700175 if !contains_fn(mac.tokens.clone()) {
176 self.visit_token_stream(&mut mac.tokens);
Andrew Walbrand1b91c72020-08-11 17:12:08 +0100177 }
178 }
179}