| use proc_macro2::{Group, Span, TokenStream, TokenTree}; |
| use std::iter::FromIterator; |
| use syn::visit_mut::{self, VisitMut}; |
| use syn::{ |
| Block, ExprPath, Ident, Item, Macro, Pat, PatIdent, PatPath, Path, Receiver, Signature, Token, |
| TypePath, |
| }; |
| |
| pub fn has_self_in_sig(sig: &mut Signature) -> bool { |
| let mut visitor = HasSelf(false); |
| visitor.visit_signature_mut(sig); |
| visitor.0 |
| } |
| |
| pub fn has_self_in_block(block: &mut Block) -> bool { |
| let mut visitor = HasSelf(false); |
| visitor.visit_block_mut(block); |
| visitor.0 |
| } |
| |
| fn has_self_in_token_stream(tokens: TokenStream) -> bool { |
| tokens.into_iter().any(|tt| match tt { |
| TokenTree::Ident(ident) => ident == "Self", |
| TokenTree::Group(group) => has_self_in_token_stream(group.stream()), |
| _ => false, |
| }) |
| } |
| |
| pub fn mut_pat(pat: &mut Pat) -> Option<Token![mut]> { |
| let mut visitor = HasMutPat(None); |
| visitor.visit_pat_mut(pat); |
| visitor.0 |
| } |
| |
| fn contains_fn(tokens: TokenStream) -> bool { |
| tokens.into_iter().any(|tt| match tt { |
| TokenTree::Ident(ident) => ident == "fn", |
| TokenTree::Group(group) => contains_fn(group.stream()), |
| _ => false, |
| }) |
| } |
| |
| struct HasMutPat(Option<Token![mut]>); |
| |
| impl VisitMut for HasMutPat { |
| fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) { |
| if let Some(m) = i.mutability { |
| self.0 = Some(m); |
| } else { |
| visit_mut::visit_pat_ident_mut(self, i); |
| } |
| } |
| } |
| |
| struct HasSelf(bool); |
| |
| impl VisitMut for HasSelf { |
| fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { |
| self.0 |= expr.path.segments[0].ident == "Self"; |
| visit_mut::visit_expr_path_mut(self, expr); |
| } |
| |
| fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { |
| self.0 |= pat.path.segments[0].ident == "Self"; |
| visit_mut::visit_pat_path_mut(self, pat); |
| } |
| |
| fn visit_type_path_mut(&mut self, ty: &mut TypePath) { |
| self.0 |= ty.path.segments[0].ident == "Self"; |
| visit_mut::visit_type_path_mut(self, ty); |
| } |
| |
| fn visit_receiver_mut(&mut self, _arg: &mut Receiver) { |
| self.0 = true; |
| } |
| |
| fn visit_item_mut(&mut self, _: &mut Item) { |
| // Do not recurse into nested items. |
| } |
| |
| fn visit_macro_mut(&mut self, mac: &mut Macro) { |
| if !contains_fn(mac.tokens.clone()) { |
| self.0 |= has_self_in_token_stream(mac.tokens.clone()); |
| } |
| } |
| } |
| |
| pub struct ReplaceSelf(pub Span); |
| |
| impl ReplaceSelf { |
| #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))] |
| fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool { |
| let modified = ident == "self"; |
| if modified { |
| *ident = Ident::new("__self", ident.span()); |
| #[cfg(self_span_hack)] |
| ident.set_span(self.0); |
| } |
| modified |
| } |
| |
| fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool { |
| let mut out = Vec::new(); |
| let mut modified = false; |
| visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out); |
| if modified { |
| *tokens = TokenStream::from_iter(out); |
| } |
| return modified; |
| |
| fn visit_token_stream_impl( |
| visitor: &mut ReplaceSelf, |
| tokens: TokenStream, |
| modified: &mut bool, |
| out: &mut Vec<TokenTree>, |
| ) { |
| for tt in tokens { |
| match tt { |
| TokenTree::Ident(mut ident) => { |
| *modified |= visitor.prepend_underscore_to_self(&mut ident); |
| out.push(TokenTree::Ident(ident)); |
| } |
| TokenTree::Group(group) => { |
| let mut content = group.stream(); |
| *modified |= visitor.visit_token_stream(&mut content); |
| let mut new = Group::new(group.delimiter(), content); |
| new.set_span(group.span()); |
| out.push(TokenTree::Group(new)); |
| } |
| other => out.push(other), |
| } |
| } |
| } |
| } |
| } |
| |
| impl VisitMut for ReplaceSelf { |
| fn visit_ident_mut(&mut self, i: &mut Ident) { |
| self.prepend_underscore_to_self(i); |
| } |
| |
| fn visit_path_mut(&mut self, p: &mut Path) { |
| if p.segments.len() == 1 { |
| // Replace `self`, but not `self::function`. |
| self.visit_ident_mut(&mut p.segments[0].ident); |
| } |
| for segment in &mut p.segments { |
| self.visit_path_arguments_mut(&mut segment.arguments); |
| } |
| } |
| |
| fn visit_item_mut(&mut self, i: &mut Item) { |
| // Visit `macro_rules!` because locally defined macros can refer to |
| // `self`. |
| // |
| // Visit `futures::select` and similar select macros, which commonly |
| // appear syntactically like an item despite expanding to an expression. |
| // |
| // Otherwise, do not recurse into nested items. |
| if let Item::Macro(i) = i { |
| if i.mac.path.is_ident("macro_rules") |
| || i.mac.path.segments.last().unwrap().ident == "select" |
| { |
| self.visit_macro_mut(&mut i.mac) |
| } |
| } |
| } |
| |
| fn visit_macro_mut(&mut self, mac: &mut Macro) { |
| // We can't tell in general whether `self` inside a macro invocation |
| // refers to the self in the argument list or a different self |
| // introduced within the macro. Heuristic: if the macro input contains |
| // `fn`, then `self` is more likely to refer to something other than the |
| // outer function's self argument. |
| if !contains_fn(mac.tokens.clone()) { |
| self.visit_token_stream(&mut mac.tokens); |
| } |
| } |
| } |