diff options
Diffstat (limited to 'third_party/rust/async-trait/src/receiver.rs')
-rw-r--r-- | third_party/rust/async-trait/src/receiver.rs | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/third_party/rust/async-trait/src/receiver.rs b/third_party/rust/async-trait/src/receiver.rs new file mode 100644 index 0000000000..2230db6ea2 --- /dev/null +++ b/third_party/rust/async-trait/src/receiver.rs @@ -0,0 +1,179 @@ +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); + } + } +} |