Skip to content

Commit

Permalink
Merge pull request #1499 from sgrif/sg-custom-serde-path
Browse files Browse the repository at this point in the history
Allow `#[serde(crate = "...")]` to override `extern crate serde`
  • Loading branch information
dtolnay authored Apr 3, 2019
2 parents 295730b + f3c6b9f commit 465392b
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 16 deletions.
13 changes: 7 additions & 6 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
let body = Stmts(deserialize_body(&cont, &params));
let delife = params.borrowed.de_lifetime();
let serde = cont.attrs.serde_path();

let impl_block = if let Some(remote) = cont.attrs.remote() {
let vis = &input.vis;
let used = pretend::pretend_used(&cont);
quote! {
impl #de_impl_generics #ident #ty_generics #where_clause {
#vis fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<#remote #ty_generics, __D::Error>
#vis fn deserialize<__D>(__deserializer: __D) -> #serde::export::Result<#remote #ty_generics, __D::Error>
where
__D: _serde::Deserializer<#delife>,
__D: #serde::Deserializer<#delife>,
{
#used
#body
Expand All @@ -47,10 +48,10 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream

quote! {
#[automatically_derived]
impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
impl #de_impl_generics #serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
fn deserialize<__D>(__deserializer: __D) -> #serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<#delife>,
__D: #serde::Deserializer<#delife>,
{
#body
}
Expand All @@ -60,7 +61,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream
}
};

Ok(dummy::wrap_in_const("DESERIALIZE", ident, impl_block))
Ok(dummy::wrap_in_const(cont.attrs.custom_serde_path(), "DESERIALIZE", ident, impl_block))
}

fn precondition(cx: &Ctxt, cont: &Container) {
Expand Down
17 changes: 13 additions & 4 deletions serde_derive/src/dummy.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
use proc_macro2::{Ident, Span, TokenStream};

use syn;
use try;

pub fn wrap_in_const(trait_: &str, ty: &Ident, code: TokenStream) -> TokenStream {
pub fn wrap_in_const(serde_path: Option<&syn::Path>, trait_: &str, ty: &Ident, code: TokenStream) -> TokenStream {
let try_replacement = try::replacement();

let dummy_const = Ident::new(
&format!("_IMPL_{}_FOR_{}", trait_, unraw(ty)),
Span::call_site(),
);

quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
let use_serde = serde_path.map(|path| {
quote!(use #path as _serde;)
}).unwrap_or_else(|| {
quote! {
#[allow(unknown_lints)]
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(rust_2018_idioms)]
extern crate serde as _serde;
}
});

quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
#use_serde
#try_replacement
#code
};
Expand Down
21 changes: 21 additions & 0 deletions serde_derive/src/internals/attr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use internals::Ctxt;
use proc_macro2::{Group, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::str::FromStr;
use syn;
Expand Down Expand Up @@ -218,6 +219,7 @@ pub struct Container {
remote: Option<syn::Path>,
identifier: Identifier,
has_flatten: bool,
serde_path: Option<syn::Path>,
}

/// Styles of representing an enum.
Expand Down Expand Up @@ -298,6 +300,7 @@ impl Container {
let mut remote = Attr::none(cx, "remote");
let mut field_identifier = BoolAttr::none(cx, "field_identifier");
let mut variant_identifier = BoolAttr::none(cx, "variant_identifier");
let mut serde_path = Attr::none(cx, "crate");

for meta_items in item.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
Expand Down Expand Up @@ -582,6 +585,13 @@ impl Container {
variant_identifier.set_true(word);
}

// Parse `#[serde(crate = "foo")]`
Meta(NameValue(ref m)) if m.ident == "crate" => {
if let Ok(path) = parse_lit_into_path(cx, &m.ident, &m.lit) {
serde_path.set(&m.ident, path)
}
}

Meta(ref meta_item) => {
cx.error_spanned_by(
meta_item.name(),
Expand Down Expand Up @@ -613,6 +623,7 @@ impl Container {
remote: remote.get(),
identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
has_flatten: false,
serde_path: serde_path.get(),
}
}

Expand Down Expand Up @@ -671,6 +682,16 @@ impl Container {
pub fn mark_has_flatten(&mut self) {
self.has_flatten = true;
}

pub fn custom_serde_path(&self) -> Option<&syn::Path> {
self.serde_path.as_ref()
}

pub fn serde_path(&self) -> Cow<syn::Path> {
self.custom_serde_path()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(parse_quote!(_serde)))
}
}

fn decide_tag(
Expand Down
13 changes: 7 additions & 6 deletions serde_derive/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream,
let params = Parameters::new(&cont);
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
let body = Stmts(serialize_body(&cont, &params));
let serde = cont.attrs.serde_path();

let impl_block = if let Some(remote) = cont.attrs.remote() {
let vis = &input.vis;
let used = pretend::pretend_used(&cont);
quote! {
impl #impl_generics #ident #ty_generics #where_clause {
#vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
#vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> #serde::export::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
__S: #serde::Serializer,
{
#used
#body
Expand All @@ -40,18 +41,18 @@ pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream,
} else {
quote! {
#[automatically_derived]
impl #impl_generics _serde::Serialize for #ident #ty_generics #where_clause {
fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
impl #impl_generics #serde::Serialize for #ident #ty_generics #where_clause {
fn serialize<__S>(&self, __serializer: __S) -> #serde::export::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
__S: #serde::Serializer,
{
#body
}
}
}
};

Ok(dummy::wrap_in_const("SERIALIZE", ident, impl_block))
Ok(dummy::wrap_in_const(cont.attrs.custom_serde_path(), "SERIALIZE", ident, impl_block))
}

fn precondition(cx: &Ctxt, cont: &Container) {
Expand Down
39 changes: 39 additions & 0 deletions test_suite/tests/test_serde_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#[test]
fn test_gen_custom_serde() {
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(crate = "fake_serde")]
struct Foo;

// Would be overlapping if serde::Serialize were implemented
impl AssertNotSerdeSerialize for Foo {}
// Would be overlapping if serde::Deserialize were implemented
impl<'a> AssertNotSerdeDeserialize<'a> for Foo {}

fake_serde::assert::<Foo>();
}

mod fake_serde {
pub use serde::*;

pub fn assert<T>()
where
T: Serialize,
T: for<'a> Deserialize<'a>,
{}

pub trait Serialize {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
}

pub trait Deserialize<'a>: Sized {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error>;
}
}

trait AssertNotSerdeSerialize {}

impl<T: serde::Serialize> AssertNotSerdeSerialize for T {}

trait AssertNotSerdeDeserialize<'a> {}

impl<'a, T: serde::Deserialize<'a>> AssertNotSerdeDeserialize<'a> for T {}

0 comments on commit 465392b

Please sign in to comment.