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

Fix handling of Self keyword in type definition #1830

Merged
merged 2 commits into from Jan 25, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion serde_derive/Cargo.toml
Expand Up @@ -22,7 +22,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.58", features = ["visit"] }
syn = { version = "1.0.58", features = ["visit", "visit-mut"] }

[dev-dependencies]
serde = { version = "1.0", path = "../serde" }
Expand Down
8 changes: 6 additions & 2 deletions serde_derive/src/de.rs
Expand Up @@ -8,13 +8,17 @@ use bound;
use dummy;
use fragment::{Expr, Fragment, Match, Stmts};
use internals::ast::{Container, Data, Field, Style, Variant};
use internals::{attr, ungroup, Ctxt, Derive};
use internals::{attr, replace_receiver, ungroup, Ctxt, Derive};
use pretend;

use std::collections::BTreeSet;
use std::ptr;

pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
pub fn expand_derive_deserialize(
input: &mut syn::DeriveInput,
) -> Result<TokenStream, Vec<syn::Error>> {
replace_receiver(input);

let ctxt = Ctxt::new();
let cont = match Container::from_ast(&ctxt, input, Derive::Deserialize) {
Some(cont) => cont,
Expand Down
4 changes: 4 additions & 0 deletions serde_derive/src/internals/mod.rs
Expand Up @@ -4,8 +4,12 @@ pub mod attr;
mod ctxt;
pub use self::ctxt::Ctxt;

mod receiver;
pub use self::receiver::replace_receiver;

mod case;
mod check;
mod respan;
mod symbol;

use syn::Type;
Expand Down
190 changes: 190 additions & 0 deletions serde_derive/src/internals/receiver.rs
@@ -0,0 +1,190 @@
use super::respan::respan;
use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned};
use std::{iter::FromIterator, mem};
use syn::{
parse_quote,
punctuated::Punctuated,
visit_mut::{self, VisitMut},
DeriveInput, ExprPath, Macro, Path, PathArguments, QSelf, Type, TypePath,
};

pub fn replace_receiver(input: &mut DeriveInput) {
let self_ty = {
let ident = &input.ident;
let ty_generics = input.generics.split_for_impl().1;
parse_quote!(#ident #ty_generics)
};
let mut visitor = ReplaceReceiver(&self_ty);
visitor.visit_generics_mut(&mut input.generics);
visitor.visit_data_mut(&mut input.data);
}

struct ReplaceReceiver<'a>(&'a TypePath);

impl ReplaceReceiver<'_> {
fn self_ty(&self, span: Span) -> TypePath {
respan(self.0, span)
}

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

// Make borrow checker happy
{
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;
}

let span = path.segments[0].ident.span();
*qself = Some(QSelf {
lt_token: Token![<](span),
ty: Box::new(self.self_ty(span).into()),
position: 0,
as_token: None,
gt_token: Token![>](span),
});

path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap());

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 path.leading_colon.is_some() {
return;
}

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

let self_ty = self.self_ty(path.segments[0].ident.span());
let variant = mem::replace(path, self_ty.path);
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(<Token![::]>::default());
}
}
}
if variant.segments.len() > 1 {
path.segments.push_punct(<Token![::]>::default());
path.segments.extend(variant.segments.into_pairs().skip(1));
}
}

fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool {
let mut out = Vec::new();
let mut modified = false;
let mut iter = tokens.clone().into_iter().peekable();
while let Some(tt) = iter.next() {
match tt {
TokenTree::Ident(ident) => {
if ident == "Self" {
modified = true;
let self_ty = self.self_ty(ident.span());
match iter.peek() {
Some(TokenTree::Punct(p))
if p.as_char() == ':' && p.spacing() == Spacing::Joint => {}
_ => {
out.extend(quote!(#self_ty));
continue;
}
}
let next = iter.next().unwrap();
match iter.peek() {
Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
let span = ident.span();
out.extend(quote_spanned!(span=> <#self_ty>));
}
_ => out.extend(quote!(#self_ty)),
}
out.push(next);
} else {
out.push(TokenTree::Ident(ident));
}
}
TokenTree::Group(group) => {
let mut content = group.stream();
modified |= self.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),
}
}
if modified {
*tokens = TokenStream::from_iter(out);
}
modified
}
}

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

// `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() {
self.self_to_qself(&mut expr.qself, &mut expr.path);
}
visit_mut::visit_expr_path_mut(self, expr);
}

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

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,
})
}
22 changes: 22 additions & 0 deletions serde_derive/src/internals/respan.rs
@@ -0,0 +1,22 @@
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::parse::Parse;

pub(crate) fn respan<T>(node: &T, span: Span) -> T
where
T: ToTokens + Parse,
{
let tokens = node.to_token_stream();
let respanned = respan_tokens(tokens, span);
syn::parse2(respanned).unwrap()
}

fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream {
tokens
.into_iter()
.map(|mut token| {
token.set_span(span);
token
})
.collect()
}
4 changes: 2 additions & 2 deletions serde_derive/src/lib.rs
Expand Up @@ -86,8 +86,8 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream {

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
de::expand_derive_deserialize(&input)
let mut input = parse_macro_input!(input as DeriveInput);
de::expand_derive_deserialize(&mut input)
.unwrap_or_else(to_compile_errors)
.into()
}
Expand Down
2 changes: 1 addition & 1 deletion serde_derive_internals/Cargo.toml
Expand Up @@ -16,7 +16,7 @@ path = "lib.rs"
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.33", default-features = false, features = ["derive", "parsing", "printing", "clone-impls"] }
syn = { version = "1.0.33", default-features = false, features = ["derive", "parsing", "printing", "clone-impls", "visit-mut"] }

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
101 changes: 101 additions & 0 deletions test_suite/tests/test_self.rs
@@ -0,0 +1,101 @@
use serde::{Deserialize, Serialize};

#[test]
fn test_self() {
macro_rules! mac {
($($tt:tt)*) => {
$($tt)*
};
}

pub trait Trait {
type Assoc;
}

#[derive(Deserialize, Serialize)]
pub struct Generics<T: Trait<Assoc = Self>>
where
Self: Trait<Assoc = Self>,
<Self as Trait>::Assoc: Sized,
mac!(Self): Trait<Assoc = mac!(Self)>,
{
_f: T,
}

impl<T: Trait<Assoc = Self>> Trait for Generics<T> {
type Assoc = Self;
}

#[derive(Deserialize, Serialize)]
pub struct Struct {
_f1: Box<Self>,
_f2: Box<<Self as Trait>::Assoc>,
_f3: Box<mac!(Self)>,
_f4: [(); Self::ASSOC],
_f5: [(); Self::assoc()],
_f6: [(); mac!(Self::assoc())],
}

impl Struct {
const ASSOC: usize = 1;
const fn assoc() -> usize {
0
}
}

impl Trait for Struct {
type Assoc = Self;
}

#[derive(Deserialize, Serialize)]
struct Tuple(
Box<Self>,
Box<<Self as Trait>::Assoc>,
Box<mac!(Self)>,
[(); Self::ASSOC],
[(); Self::assoc()],
[(); mac!(Self::assoc())],
);

impl Tuple {
const ASSOC: usize = 1;
const fn assoc() -> usize {
0
}
}

impl Trait for Tuple {
type Assoc = Self;
}

#[derive(Deserialize, Serialize)]
enum Enum {
Struct {
_f1: Box<Self>,
_f2: Box<<Self as Trait>::Assoc>,
_f3: Box<mac!(Self)>,
_f4: [(); Self::ASSOC],
_f5: [(); Self::assoc()],
_f6: [(); mac!(Self::assoc())],
},
Tuple(
Box<Self>,
Box<<Self as Trait>::Assoc>,
Box<mac!(Self)>,
[(); Self::ASSOC],
[(); Self::assoc()],
[(); mac!(Self::assoc())],
),
}

impl Enum {
const ASSOC: usize = 1;
const fn assoc() -> usize {
0
}
}

impl Trait for Enum {
type Assoc = Self;
}
}