Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of Self #177

Merged
merged 1 commit into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 2 additions & 68 deletions pin-project-internal/src/pin_project/derive.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use std::mem;

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{
parse::{Parse, ParseBuffer, ParseStream},
punctuated::Punctuated,
visit_mut::{self, VisitMut},
visit_mut::VisitMut,
*,
};

Expand Down Expand Up @@ -224,7 +221,7 @@ impl Context {
{
let ty_generics = generics.split_for_impl().1;
let self_ty = syn::parse_quote!(#ident #ty_generics);
let mut visitor = ReplaceSelf::new(&self_ty);
let mut visitor = ReplaceReceiver::new(&self_ty);
visitor.visit_where_clause_mut(generics.make_where_clause());
}

Expand Down Expand Up @@ -799,66 +796,3 @@ impl Context {
})
}
}

// Replace `Self` with `self_ty`.
// Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs

struct ReplaceSelf<'a> {
self_ty: &'a Type,
}

impl<'a> ReplaceSelf<'a> {
fn new(self_ty: &'a Type) -> Self {
Self { self_ty }
}

fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
if path.leading_colon.is_some() {
return;
}

let first = &path.segments[0];
if first.ident != "Self" || !first.arguments.is_empty() {
return;
}

match path.segments.pairs().next().unwrap().punct() {
Some(colon) => path.leading_colon = Some(**colon),
None => return,
}

*qself = Some(QSelf {
lt_token: token::Lt::default(),
ty: Box::new(self.self_ty.clone()),
position: 0,
as_token: None,
gt_token: token::Gt::default(),
});

let segments = mem::replace(&mut path.segments, Punctuated::new());
path.segments = segments.into_pairs().skip(1).collect();
}
}

impl VisitMut for ReplaceSelf<'_> {
// `Self` -> `Receiver`
fn visit_type_mut(&mut self, ty: &mut Type) {
if let Type::Path(node) = ty {
if node.qself.is_none() && node.path.is_ident("Self") {
*ty = self.self_ty.clone();
} else {
self.visit_type_path_mut(node);
}
} else {
visit_mut::visit_type_mut(self, ty);
}
}

// `Self::Assoc` -> `<Receiver>::Assoc`
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
if ty.qself.is_none() {
self.self_to_qself(&mut ty.qself, &mut ty.path);
}
visit_mut::visit_type_path_mut(self, ty);
}
}
133 changes: 5 additions & 128 deletions pin-project-internal/src/pinned_drop.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use std::mem;

use proc_macro2::{Group, Span, TokenStream, TokenTree};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{
punctuated::Punctuated,
spanned::Spanned,
visit_mut::{self, VisitMut},
*,
};
use syn::{spanned::Spanned, visit_mut::VisitMut, *};

use crate::utils::{parse_as_empty, CURRENT_PRIVATE_MODULE};
use crate::utils::{
parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, CURRENT_PRIVATE_MODULE,
};

pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream {
if let Err(e) = parse_as_empty(args).and_then(|()| parse(&mut input)) {
Expand Down Expand Up @@ -202,121 +197,3 @@ fn expand_item(item: &mut ItemImpl) {
__drop_inner(self);
}};
}

// Replace `self` and `Self` with `__self` and `self_ty`.
// Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs

struct ReplaceReceiver<'a> {
self_ty: &'a Type,
}

impl<'a> ReplaceReceiver<'a> {
fn new(self_ty: &'a Type) -> Self {
Self { self_ty }
}

fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
if path.leading_colon.is_some() {
return;
}

let first = &path.segments[0];
if first.ident != "Self" || !first.arguments.is_empty() {
return;
}

match path.segments.pairs().next().unwrap().punct() {
Some(colon) => path.leading_colon = Some(**colon),
None => return,
}

*qself = Some(QSelf {
lt_token: token::Lt::default(),
ty: Box::new(self.self_ty.clone()),
position: 0,
as_token: None,
gt_token: token::Gt::default(),
});

let segments = mem::replace(&mut path.segments, Punctuated::new());
path.segments = segments.into_pairs().skip(1).collect();
}
}

impl VisitMut for ReplaceReceiver<'_> {
// `Self` -> `Receiver`
fn visit_type_mut(&mut self, ty: &mut Type) {
if let Type::Path(node) = ty {
if node.qself.is_none() && node.path.is_ident("Self") {
*ty = self.self_ty.clone();
} else {
self.visit_type_path_mut(node);
}
} else {
visit_mut::visit_type_mut(self, ty);
}
}

// `Self::Assoc` -> `<Receiver>::Assoc`
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
if ty.qself.is_none() {
self.self_to_qself(&mut ty.qself, &mut ty.path);
}
visit_mut::visit_type_path_mut(self, ty);
}

// `Self::method` -> `<Receiver>::method`
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
if expr.qself.is_none() {
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
self.self_to_qself(&mut expr.qself, &mut expr.path);
}
visit_mut::visit_expr_path_mut(self, expr);
}

fn visit_macro_mut(&mut self, node: &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(node.tokens.clone()) {
node.tokens = fold_token_stream(node.tokens.clone());
}
}

fn visit_item_mut(&mut self, _: &mut Item) {
// Do not recurse into nested items.
}
}

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,
})
}

fn fold_token_stream(tokens: TokenStream) -> TokenStream {
tokens
.into_iter()
.map(|tt| match tt {
TokenTree::Ident(mut ident) => {
prepend_underscore_to_self(&mut ident);
TokenTree::Ident(ident)
}
TokenTree::Group(group) => {
let content = fold_token_stream(group.stream());
TokenTree::Group(Group::new(group.delimiter(), content))
}
other => other,
})
.collect()
}

fn prepend_underscore_to_self(ident: &mut Ident) {
if ident == "self" {
*ident = Ident::new("__self", ident.span());
}
}
153 changes: 152 additions & 1 deletion pin-project-internal/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use proc_macro2::TokenStream;
use std::mem;

use proc_macro2::{Group, TokenStream, TokenTree};
use quote::{format_ident, quote_spanned};
use syn::{
parse::{ParseBuffer, ParseStream},
punctuated::Punctuated,
token::{self, Comma},
visit_mut::{self, VisitMut},
*,
};

Expand Down Expand Up @@ -155,3 +158,151 @@ impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> {
Ok(content)
}
}

// Replace `self`/`Self` with `__self`/`self_ty`.
// Based on https://github.com/dtolnay/async-trait/blob/0.1.22/src/receiver.rs

pub(crate) struct ReplaceReceiver<'a> {
self_ty: &'a Type,
}

impl<'a> ReplaceReceiver<'a> {
pub(crate) fn new(self_ty: &'a Type) -> Self {
Self { self_ty }
}

fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
if path.leading_colon.is_some() {
return;
}

let first = &path.segments[0];
if first.ident != "Self" || !first.arguments.is_empty() {
return;
}

if path.segments.len() == 1 {
self.self_to_expr_path(path);
return;
}

*qself = Some(QSelf {
lt_token: token::Lt::default(),
ty: Box::new(self.self_ty.clone()),
position: 0,
as_token: None,
gt_token: token::Gt::default(),
});

match path.segments.pairs().next().unwrap().punct() {
Some(&&colon) => path.leading_colon = Some(colon),
None => return,
}

let segments = mem::replace(&mut path.segments, Punctuated::new());
path.segments = segments.into_pairs().skip(1).collect();
}

fn self_to_expr_path(&self, path: &mut Path) {
if let Type::Path(self_ty) = &self.self_ty {
*path = self_ty.path.clone();
for segment in &mut path.segments {
if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
bracketed.colon2_token = Some(Default::default());
}
}
}
} else {
let span = path.segments[0].ident.span();
let msg = "Self type of this impl is unsupported in expression position";
let error = Error::new(span, msg).to_compile_error();
*path = parse_quote!(::core::marker::PhantomData::<#error>);
}
}
}

impl VisitMut for ReplaceReceiver<'_> {
// `Self` -> `Receiver`
fn visit_type_mut(&mut self, ty: &mut Type) {
if let Type::Path(node) = ty {
if node.qself.is_none() && node.path.is_ident("Self") {
*ty = self.self_ty.clone();
} else {
self.visit_type_path_mut(node);
}
} else {
visit_mut::visit_type_mut(self, ty);
}
}

// `Self::Assoc` -> `<Receiver>::Assoc`
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
if ty.qself.is_none() {
self.self_to_qself(&mut ty.qself, &mut ty.path);
}
visit_mut::visit_type_path_mut(self, ty);
}

// `Self::method` -> `<Receiver>::method`
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
if expr.qself.is_none() {
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
self.self_to_qself(&mut expr.qself, &mut expr.path);
}
visit_mut::visit_expr_path_mut(self, expr);
}

fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) {
if expr.path.is_ident("Self") {
self.self_to_expr_path(&mut expr.path);
}
visit_mut::visit_expr_struct_mut(self, expr);
}

fn visit_macro_mut(&mut self, node: &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(node.tokens.clone()) {
node.tokens = fold_token_stream(node.tokens.clone());
}
}

fn visit_item_mut(&mut self, _: &mut Item) {
// Do not recurse into nested items.
}
}

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,
})
}

fn fold_token_stream(tokens: TokenStream) -> TokenStream {
tokens
.into_iter()
.map(|tt| match tt {
TokenTree::Ident(mut ident) => {
prepend_underscore_to_self(&mut ident);
TokenTree::Ident(ident)
}
TokenTree::Group(group) => {
let content = fold_token_stream(group.stream());
TokenTree::Group(Group::new(group.delimiter(), content))
}
other => other,
})
.collect()
}

pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) {
if ident == "self" {
*ident = Ident::new("__self", ident.span());
}
}
Loading