Skip to content

Commit ffb4d90

Browse files
committed
Improve handling of Self
1 parent 8a695f1 commit ffb4d90

File tree

10 files changed

+440
-362
lines changed

10 files changed

+440
-362
lines changed

pin-project-internal/src/pin_project/derive.rs

+2-68
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
use std::mem;
2-
31
use proc_macro2::{Span, TokenStream};
42
use quote::{format_ident, quote, quote_spanned};
53
use syn::{
64
parse::{Parse, ParseBuffer, ParseStream},
7-
punctuated::Punctuated,
8-
visit_mut::{self, VisitMut},
5+
visit_mut::VisitMut,
96
*,
107
};
118

@@ -224,7 +221,7 @@ impl Context {
224221
{
225222
let ty_generics = generics.split_for_impl().1;
226223
let self_ty = syn::parse_quote!(#ident #ty_generics);
227-
let mut visitor = ReplaceSelf::new(&self_ty);
224+
let mut visitor = ReplaceReceiver::new(&self_ty);
228225
visitor.visit_where_clause_mut(generics.make_where_clause());
229226
}
230227

@@ -799,66 +796,3 @@ impl Context {
799796
})
800797
}
801798
}
802-
803-
// Replace `Self` with `self_ty`.
804-
// Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs
805-
806-
struct ReplaceSelf<'a> {
807-
self_ty: &'a Type,
808-
}
809-
810-
impl<'a> ReplaceSelf<'a> {
811-
fn new(self_ty: &'a Type) -> Self {
812-
Self { self_ty }
813-
}
814-
815-
fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
816-
if path.leading_colon.is_some() {
817-
return;
818-
}
819-
820-
let first = &path.segments[0];
821-
if first.ident != "Self" || !first.arguments.is_empty() {
822-
return;
823-
}
824-
825-
match path.segments.pairs().next().unwrap().punct() {
826-
Some(colon) => path.leading_colon = Some(**colon),
827-
None => return,
828-
}
829-
830-
*qself = Some(QSelf {
831-
lt_token: token::Lt::default(),
832-
ty: Box::new(self.self_ty.clone()),
833-
position: 0,
834-
as_token: None,
835-
gt_token: token::Gt::default(),
836-
});
837-
838-
let segments = mem::replace(&mut path.segments, Punctuated::new());
839-
path.segments = segments.into_pairs().skip(1).collect();
840-
}
841-
}
842-
843-
impl VisitMut for ReplaceSelf<'_> {
844-
// `Self` -> `Receiver`
845-
fn visit_type_mut(&mut self, ty: &mut Type) {
846-
if let Type::Path(node) = ty {
847-
if node.qself.is_none() && node.path.is_ident("Self") {
848-
*ty = self.self_ty.clone();
849-
} else {
850-
self.visit_type_path_mut(node);
851-
}
852-
} else {
853-
visit_mut::visit_type_mut(self, ty);
854-
}
855-
}
856-
857-
// `Self::Assoc` -> `<Receiver>::Assoc`
858-
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
859-
if ty.qself.is_none() {
860-
self.self_to_qself(&mut ty.qself, &mut ty.path);
861-
}
862-
visit_mut::visit_type_path_mut(self, ty);
863-
}
864-
}

pin-project-internal/src/pinned_drop.rs

+5-128
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
use std::mem;
2-
3-
use proc_macro2::{Group, Span, TokenStream, TokenTree};
1+
use proc_macro2::{Span, TokenStream};
42
use quote::{quote, quote_spanned, ToTokens};
5-
use syn::{
6-
punctuated::Punctuated,
7-
spanned::Spanned,
8-
visit_mut::{self, VisitMut},
9-
*,
10-
};
3+
use syn::{spanned::Spanned, visit_mut::VisitMut, *};
114

12-
use crate::utils::{parse_as_empty, CURRENT_PRIVATE_MODULE};
5+
use crate::utils::{
6+
parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, CURRENT_PRIVATE_MODULE,
7+
};
138

149
pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream {
1510
if let Err(e) = parse_as_empty(args).and_then(|()| parse(&mut input)) {
@@ -202,121 +197,3 @@ fn expand_item(item: &mut ItemImpl) {
202197
__drop_inner(self);
203198
}};
204199
}
205-
206-
// Replace `self` and `Self` with `__self` and `self_ty`.
207-
// Based on https://github.com/dtolnay/async-trait/blob/1.0.15/src/receiver.rs
208-
209-
struct ReplaceReceiver<'a> {
210-
self_ty: &'a Type,
211-
}
212-
213-
impl<'a> ReplaceReceiver<'a> {
214-
fn new(self_ty: &'a Type) -> Self {
215-
Self { self_ty }
216-
}
217-
218-
fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
219-
if path.leading_colon.is_some() {
220-
return;
221-
}
222-
223-
let first = &path.segments[0];
224-
if first.ident != "Self" || !first.arguments.is_empty() {
225-
return;
226-
}
227-
228-
match path.segments.pairs().next().unwrap().punct() {
229-
Some(colon) => path.leading_colon = Some(**colon),
230-
None => return,
231-
}
232-
233-
*qself = Some(QSelf {
234-
lt_token: token::Lt::default(),
235-
ty: Box::new(self.self_ty.clone()),
236-
position: 0,
237-
as_token: None,
238-
gt_token: token::Gt::default(),
239-
});
240-
241-
let segments = mem::replace(&mut path.segments, Punctuated::new());
242-
path.segments = segments.into_pairs().skip(1).collect();
243-
}
244-
}
245-
246-
impl VisitMut for ReplaceReceiver<'_> {
247-
// `Self` -> `Receiver`
248-
fn visit_type_mut(&mut self, ty: &mut Type) {
249-
if let Type::Path(node) = ty {
250-
if node.qself.is_none() && node.path.is_ident("Self") {
251-
*ty = self.self_ty.clone();
252-
} else {
253-
self.visit_type_path_mut(node);
254-
}
255-
} else {
256-
visit_mut::visit_type_mut(self, ty);
257-
}
258-
}
259-
260-
// `Self::Assoc` -> `<Receiver>::Assoc`
261-
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
262-
if ty.qself.is_none() {
263-
self.self_to_qself(&mut ty.qself, &mut ty.path);
264-
}
265-
visit_mut::visit_type_path_mut(self, ty);
266-
}
267-
268-
// `Self::method` -> `<Receiver>::method`
269-
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
270-
if expr.qself.is_none() {
271-
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
272-
self.self_to_qself(&mut expr.qself, &mut expr.path);
273-
}
274-
visit_mut::visit_expr_path_mut(self, expr);
275-
}
276-
277-
fn visit_macro_mut(&mut self, node: &mut Macro) {
278-
// We can't tell in general whether `self` inside a macro invocation
279-
// refers to the self in the argument list or a different self
280-
// introduced within the macro. Heuristic: if the macro input contains
281-
// `fn`, then `self` is more likely to refer to something other than the
282-
// outer function's self argument.
283-
if !contains_fn(node.tokens.clone()) {
284-
node.tokens = fold_token_stream(node.tokens.clone());
285-
}
286-
}
287-
288-
fn visit_item_mut(&mut self, _: &mut Item) {
289-
// Do not recurse into nested items.
290-
}
291-
}
292-
293-
fn contains_fn(tokens: TokenStream) -> bool {
294-
tokens.into_iter().any(|tt| match tt {
295-
TokenTree::Ident(ident) => ident == "fn",
296-
TokenTree::Group(group) => contains_fn(group.stream()),
297-
_ => false,
298-
})
299-
}
300-
301-
fn fold_token_stream(tokens: TokenStream) -> TokenStream {
302-
tokens
303-
.into_iter()
304-
.map(|tt| match tt {
305-
TokenTree::Ident(mut ident) => {
306-
prepend_underscore_to_self(&mut ident);
307-
TokenTree::Ident(ident)
308-
}
309-
TokenTree::Group(group) => {
310-
let content = fold_token_stream(group.stream());
311-
TokenTree::Group(Group::new(group.delimiter(), content))
312-
}
313-
other => other,
314-
})
315-
.collect()
316-
}
317-
318-
fn prepend_underscore_to_self(ident: &mut Ident) {
319-
if ident == "self" {
320-
*ident = Ident::new("__self", ident.span());
321-
}
322-
}

pin-project-internal/src/utils.rs

+152-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use proc_macro2::TokenStream;
1+
use std::mem;
2+
3+
use proc_macro2::{Group, TokenStream, TokenTree};
24
use quote::{format_ident, quote_spanned};
35
use syn::{
46
parse::{ParseBuffer, ParseStream},
57
punctuated::Punctuated,
68
token::{self, Comma},
9+
visit_mut::{self, VisitMut},
710
*,
811
};
912

@@ -155,3 +158,151 @@ impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> {
155158
Ok(content)
156159
}
157160
}
161+
162+
// Replace `self`/`Self` with `__self`/`self_ty`.
163+
// Based on https://github.com/dtolnay/async-trait/blob/0.1.22/src/receiver.rs
164+
165+
pub(crate) struct ReplaceReceiver<'a> {
166+
self_ty: &'a Type,
167+
}
168+
169+
impl<'a> ReplaceReceiver<'a> {
170+
pub(crate) fn new(self_ty: &'a Type) -> Self {
171+
Self { self_ty }
172+
}
173+
174+
fn self_to_qself(&mut self, qself: &mut Option<QSelf>, path: &mut Path) {
175+
if path.leading_colon.is_some() {
176+
return;
177+
}
178+
179+
let first = &path.segments[0];
180+
if first.ident != "Self" || !first.arguments.is_empty() {
181+
return;
182+
}
183+
184+
if path.segments.len() == 1 {
185+
self.self_to_expr_path(path);
186+
return;
187+
}
188+
189+
*qself = Some(QSelf {
190+
lt_token: token::Lt::default(),
191+
ty: Box::new(self.self_ty.clone()),
192+
position: 0,
193+
as_token: None,
194+
gt_token: token::Gt::default(),
195+
});
196+
197+
match path.segments.pairs().next().unwrap().punct() {
198+
Some(&&colon) => path.leading_colon = Some(colon),
199+
None => return,
200+
}
201+
202+
let segments = mem::replace(&mut path.segments, Punctuated::new());
203+
path.segments = segments.into_pairs().skip(1).collect();
204+
}
205+
206+
fn self_to_expr_path(&self, path: &mut Path) {
207+
if let Type::Path(self_ty) = &self.self_ty {
208+
*path = self_ty.path.clone();
209+
for segment in &mut path.segments {
210+
if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
211+
if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() {
212+
bracketed.colon2_token = Some(Default::default());
213+
}
214+
}
215+
}
216+
} else {
217+
let span = path.segments[0].ident.span();
218+
let msg = "Self type of this impl is unsupported in expression position";
219+
let error = Error::new(span, msg).to_compile_error();
220+
*path = parse_quote!(::core::marker::PhantomData::<#error>);
221+
}
222+
}
223+
}
224+
225+
impl VisitMut for ReplaceReceiver<'_> {
226+
// `Self` -> `Receiver`
227+
fn visit_type_mut(&mut self, ty: &mut Type) {
228+
if let Type::Path(node) = ty {
229+
if node.qself.is_none() && node.path.is_ident("Self") {
230+
*ty = self.self_ty.clone();
231+
} else {
232+
self.visit_type_path_mut(node);
233+
}
234+
} else {
235+
visit_mut::visit_type_mut(self, ty);
236+
}
237+
}
238+
239+
// `Self::Assoc` -> `<Receiver>::Assoc`
240+
fn visit_type_path_mut(&mut self, ty: &mut TypePath) {
241+
if ty.qself.is_none() {
242+
self.self_to_qself(&mut ty.qself, &mut ty.path);
243+
}
244+
visit_mut::visit_type_path_mut(self, ty);
245+
}
246+
247+
// `Self::method` -> `<Receiver>::method`
248+
fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) {
249+
if expr.qself.is_none() {
250+
prepend_underscore_to_self(&mut expr.path.segments[0].ident);
251+
self.self_to_qself(&mut expr.qself, &mut expr.path);
252+
}
253+
visit_mut::visit_expr_path_mut(self, expr);
254+
}
255+
256+
fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) {
257+
if expr.path.is_ident("Self") {
258+
self.self_to_expr_path(&mut expr.path);
259+
}
260+
visit_mut::visit_expr_struct_mut(self, expr);
261+
}
262+
263+
fn visit_macro_mut(&mut self, node: &mut Macro) {
264+
// We can't tell in general whether `self` inside a macro invocation
265+
// refers to the self in the argument list or a different self
266+
// introduced within the macro. Heuristic: if the macro input contains
267+
// `fn`, then `self` is more likely to refer to something other than the
268+
// outer function's self argument.
269+
if !contains_fn(node.tokens.clone()) {
270+
node.tokens = fold_token_stream(node.tokens.clone());
271+
}
272+
}
273+
274+
fn visit_item_mut(&mut self, _: &mut Item) {
275+
// Do not recurse into nested items.
276+
}
277+
}
278+
279+
fn contains_fn(tokens: TokenStream) -> bool {
280+
tokens.into_iter().any(|tt| match tt {
281+
TokenTree::Ident(ident) => ident == "fn",
282+
TokenTree::Group(group) => contains_fn(group.stream()),
283+
_ => false,
284+
})
285+
}
286+
287+
fn fold_token_stream(tokens: TokenStream) -> TokenStream {
288+
tokens
289+
.into_iter()
290+
.map(|tt| match tt {
291+
TokenTree::Ident(mut ident) => {
292+
prepend_underscore_to_self(&mut ident);
293+
TokenTree::Ident(ident)
294+
}
295+
TokenTree::Group(group) => {
296+
let content = fold_token_stream(group.stream());
297+
TokenTree::Group(Group::new(group.delimiter(), content))
298+
}
299+
other => other,
300+
})
301+
.collect()
302+
}
303+
304+
pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) {
305+
if ident == "self" {
306+
*ident = Ident::new("__self", ident.span());
307+
}
308+
}

0 commit comments

Comments
 (0)