diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 41698195..881f0df9 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -328,9 +328,9 @@ impl<'a> Visitor<'a> for StrVisitor { } } -impl<'a> Deserialize<'a> for &'a str { +impl<'de: 'a, 'a> Deserialize<'de> for &'a str { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> + where D: Deserializer<'de> { deserializer.deserialize_str(StrVisitor) } @@ -360,9 +360,9 @@ impl<'a> Visitor<'a> for BytesVisitor { } } -impl<'a> Deserialize<'a> for &'a [u8] { +impl<'de: 'a, 'a> Deserialize<'de> for &'a [u8] { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> + where D: Deserializer<'de> { deserializer.deserialize_bytes(BytesVisitor) } diff --git a/serde/src/de/private.rs b/serde/src/de/private.rs index 0e001962..8121ee15 100644 --- a/serde/src/de/private.rs +++ b/serde/src/de/private.rs @@ -1,7 +1,24 @@ +#[cfg(any(feature = "std", feature = "collections"))] +use core::{fmt, str}; + use core::marker::PhantomData; +#[cfg(feature = "collections")] +use collections::borrow::ToOwned; + +#[cfg(feature = "std")] +use std::borrow::Cow; +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::borrow::Cow; + +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::{String, Vec}; + use de::{Deserialize, Deserializer, Error, Visitor}; +#[cfg(any(feature = "std", feature = "collections"))] +use de::Unexpected; + #[cfg(any(feature = "std", feature = "collections"))] pub use de::content::{Content, ContentRefDeserializer, ContentDeserializer, TaggedContentVisitor, TagOrContentField, TagOrContentFieldVisitor, InternallyTaggedUnitVisitor, @@ -42,3 +59,118 @@ pub fn missing_field<'de, V, E>(field: &'static str) -> Result let deserializer = MissingFieldDeserializer(field, PhantomData); Deserialize::deserialize(deserializer) } + +#[cfg(any(feature = "std", feature = "collections"))] +pub fn borrow_cow_str<'de: 'a, 'a, D>(deserializer: D) -> Result, D::Error> + where D: Deserializer<'de> +{ + struct CowStrVisitor; + + impl<'a> Visitor<'a> for CowStrVisitor { + type Value = Cow<'a, str>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result + where E: Error + { + Ok(Cow::Owned(v.to_owned())) + } + + fn visit_borrowed_str(self, v: &'a str) -> Result + where E: Error + { + Ok(Cow::Borrowed(v)) + } + + fn visit_string(self, v: String) -> Result + where E: Error + { + Ok(Cow::Owned(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where E: Error + { + match str::from_utf8(v) { + Ok(s) => Ok(Cow::Owned(s.to_owned())), + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), + } + } + + fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result + where E: Error + { + match str::from_utf8(v) { + Ok(s) => Ok(Cow::Borrowed(s)), + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), + } + } + + fn visit_byte_buf(self, v: Vec) -> Result + where E: Error + { + match String::from_utf8(v) { + Ok(s) => Ok(Cow::Owned(s)), + Err(e) => Err(Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self)), + } + } + } + + deserializer.deserialize_str(CowStrVisitor) +} + +#[cfg(any(feature = "std", feature = "collections"))] +pub fn borrow_cow_bytes<'de: 'a, 'a, D>(deserializer: D) -> Result, D::Error> + where D: Deserializer<'de> +{ + struct CowBytesVisitor; + + impl<'a> Visitor<'a> for CowBytesVisitor { + type Value = Cow<'a, [u8]>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a byte array") + } + + fn visit_str(self, v: &str) -> Result + where E: Error + { + Ok(Cow::Owned(v.as_bytes().to_vec())) + } + + fn visit_borrowed_str(self, v: &'a str) -> Result + where E: Error + { + Ok(Cow::Borrowed(v.as_bytes())) + } + + fn visit_string(self, v: String) -> Result + where E: Error + { + Ok(Cow::Owned(v.into_bytes())) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where E: Error + { + Ok(Cow::Owned(v.to_vec())) + } + + fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result + where E: Error + { + Ok(Cow::Borrowed(v)) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where E: Error + { + Ok(Cow::Owned(v)) + } + } + + deserializer.deserialize_str(CowBytesVisitor) +} diff --git a/serde_codegen_internals/Cargo.toml b/serde_codegen_internals/Cargo.toml index cd167bfe..6f006914 100644 --- a/serde_codegen_internals/Cargo.toml +++ b/serde_codegen_internals/Cargo.toml @@ -12,7 +12,8 @@ readme = "../README.md" include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] [dependencies] -syn = { version = "0.11", default-features = false, features = ["parsing"] } +syn = { version = "0.11.10", default-features = false, features = ["parsing"] } +synom = "0.11" [badges] travis-ci = { repository = "serde-rs/serde" } diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index 11a2eb2a..13b429b9 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -2,6 +2,8 @@ use Ctxt; use syn; use syn::MetaItem::{List, NameValue, Word}; use syn::NestedMetaItem::{Literal, MetaItem}; +use synom::IResult; +use std::collections::BTreeSet; use std::str::FromStr; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints @@ -528,6 +530,7 @@ pub struct Field { deserialize_with: Option, ser_bound: Option>, de_bound: Option>, + borrowed_lifetimes: BTreeSet, } /// Represents the default to use for a field when deserializing. @@ -554,6 +557,7 @@ impl Field { let mut deserialize_with = Attr::none(cx, "deserialize_with"); let mut ser_bound = Attr::none(cx, "bound"); let mut de_bound = Attr::none(cx, "bound"); + let mut borrowed_lifetimes = Attr::none(cx, "borrow"); let ident = match field.ident { Some(ref ident) => ident.to_string(), @@ -651,6 +655,27 @@ impl Field { } } + // Parse `#[serde(borrow)]` + MetaItem(Word(ref name)) if name == "borrow" => { + if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, &field.ty) { + borrowed_lifetimes.set(borrowable); + } + } + + // Parse `#[serde(borrow = "'a + 'b")]` + MetaItem(NameValue(ref name, ref lit)) if name == "borrow" => { + if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, name.as_ref(), lit) { + if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, &field.ty) { + for lifetime in &lifetimes { + if !borrowable.contains(lifetime) { + cx.error(format!("field `{}` does not have lifetime {}", ident, lifetime.ident)); + } + } + borrowed_lifetimes.set(lifetimes); + } + } + } + MetaItem(ref meta_item) => { cx.error(format!("unknown serde field attribute `{}`", meta_item.name())); } @@ -668,6 +693,30 @@ impl Field { default.set_if_none(Default::Default); } + let mut borrowed_lifetimes = borrowed_lifetimes.get().unwrap_or_default(); + if !borrowed_lifetimes.is_empty() { + // Cow and Cow<[u8]> never borrow by default: + // + // impl<'de, 'a, T: ?Sized> Deserialize<'de> for Cow<'a, T> + // + // A #[serde(borrow)] attribute enables borrowing that corresponds + // roughly to these impls: + // + // impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, str> + // impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, [u8]> + if is_cow(&field.ty, "str") { + let path = syn::parse_path("_serde::de::private::borrow_cow_str").unwrap(); + deserialize_with.set_if_none(path); + } else if is_cow(&field.ty, "[u8]") { + let path = syn::parse_path("_serde::de::private::borrow_cow_bytes").unwrap(); + deserialize_with.set_if_none(path); + } + } else if is_rptr(&field.ty, "str") || is_rptr(&field.ty, "[u8]") { + // Types &str and &[u8] are always implicitly borrowed. No need for + // a #[serde(borrow)]. + borrowed_lifetimes = borrowable_lifetimes(cx, &ident, &field.ty).unwrap(); + } + let ser_name = ser_name.get(); let ser_renamed = ser_name.is_some(); let de_name = de_name.get(); @@ -687,6 +736,7 @@ impl Field { deserialize_with: deserialize_with.get(), ser_bound: ser_bound.get(), de_bound: de_bound.get(), + borrowed_lifetimes: borrowed_lifetimes, } } @@ -734,6 +784,10 @@ impl Field { pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> { self.de_bound.as_ref().map(|vec| &vec[..]) } + + pub fn borrowed_lifetimes(&self) -> &BTreeSet { + &self.borrowed_lifetimes + } } type SerAndDe = (Option, Option); @@ -836,3 +890,174 @@ fn parse_lit_into_ty(cx: &Ctxt, cx.error(format!("failed to parse type: {} = {:?}", attr_name, string)) }) } + +// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of +// lifetimes separated by `+`. +fn parse_lit_into_lifetimes(cx: &Ctxt, + attr_name: &str, + lit: &syn::Lit) + -> Result, ()> { + let string = try!(get_string_from_lit(cx, attr_name, attr_name, lit)); + if string.is_empty() { + cx.error("at least one lifetime must be borrowed"); + return Err(()); + } + + named!(lifetimes -> Vec, + separated_nonempty_list!(punct!("+"), syn::parse::lifetime) + ); + + if let IResult::Done(rest, o) = lifetimes(&string) { + if rest.trim().is_empty() { + let mut set = BTreeSet::new(); + for lifetime in o { + if !set.insert(lifetime.clone()) { + cx.error(format!("duplicate borrowed lifetime `{}`", lifetime.ident)); + } + } + return Ok(set); + } + } + Err(cx.error(format!("failed to parse borrowed lifetimes: {:?}", string))) +} + +// Whether the type looks like it might be `std::borrow::Cow` where elem="T". +// This can have false negatives and false positives. +// +// False negative: +// +// use std::borrow::Cow as Pig; +// +// #[derive(Deserialize)] +// struct S<'a> { +// #[serde(borrow)] +// pig: Pig<'a, str>, +// } +// +// False positive: +// +// type str = [i16]; +// +// #[derive(Deserialize)] +// struct S<'a> { +// #[serde(borrow)] +// cow: Cow<'a, str>, +// } +fn is_cow(ty: &syn::Ty, elem: &str) -> bool { + let path = match *ty { + syn::Ty::Path(None, ref path) => path, + _ => { + return false; + } + }; + let seg = match path.segments.last() { + Some(seg) => seg, + None => { + return false; + } + }; + let params = match seg.parameters { + syn::PathParameters::AngleBracketed(ref params) => params, + _ => { + return false; + } + }; + seg.ident == "Cow" + && params.lifetimes.len() == 1 + && params.types == vec![syn::parse_type(elem).unwrap()] + && params.bindings.is_empty() +} + +// Whether the type looks like it might be `&T` where elem="T". This can have +// false negatives and false positives. +// +// False negative: +// +// type Yarn = str; +// +// #[derive(Deserialize)] +// struct S<'a> { +// r: &'a Yarn, +// } +// +// False positive: +// +// type str = [i16]; +// +// #[derive(Deserialize)] +// struct S<'a> { +// r: &'a str, +// } +fn is_rptr(ty: &syn::Ty, elem: &str) -> bool { + match *ty { + syn::Ty::Rptr(Some(_), ref mut_ty) => { + mut_ty.mutability == syn::Mutability::Immutable + && mut_ty.ty == syn::parse_type(elem).unwrap() + } + _ => false, + } +} + +// All lifetimes that this type could borrow from a Deserializer. +// +// For example a type `S<'a, 'b>` could borrow `'a` and `'b`. On the other hand +// a type `for<'a> fn(&'a str)` could not borrow `'a` from the Deserializer. +// +// This is used when there is an explicit or implicit `#[serde(borrow)]` +// attribute on the field so there must be at least one borrowable lifetime. +fn borrowable_lifetimes(cx: &Ctxt, + name: &str, + ty: &syn::Ty) + -> Result, ()> { + let mut lifetimes = BTreeSet::new(); + collect_lifetimes(ty, &mut lifetimes); + if lifetimes.is_empty() { + Err(cx.error(format!("field `{}` has no lifetimes to borrow", name))) + } else { + Ok(lifetimes) + } +} + +fn collect_lifetimes(ty: &syn::Ty, out: &mut BTreeSet) { + match *ty { + syn::Ty::Slice(ref elem) | + syn::Ty::Array(ref elem, _) | + syn::Ty::Paren(ref elem) => { + collect_lifetimes(elem, out); + } + syn::Ty::Ptr(ref elem) => { + collect_lifetimes(&elem.ty, out); + } + syn::Ty::Rptr(ref lifetime, ref elem) => { + out.extend(lifetime.iter().cloned()); + collect_lifetimes(&elem.ty, out); + } + syn::Ty::Tup(ref elems) => { + for elem in elems { + collect_lifetimes(elem, out); + } + } + syn::Ty::Path(ref qself, ref path) => { + if let Some(ref qself) = *qself { + collect_lifetimes(&qself.ty, out); + } + for seg in &path.segments { + if let syn::PathParameters::AngleBracketed(ref params) = seg.parameters { + out.extend(params.lifetimes.iter().cloned()); + for ty in ¶ms.types { + collect_lifetimes(ty, out); + } + for binding in ¶ms.bindings { + collect_lifetimes(&binding.ty, out); + } + } + } + } + syn::Ty::BareFn(_) | + syn::Ty::Never | + syn::Ty::TraitObject(_) | + syn::Ty::ImplTrait(_) | + syn::Ty::Infer | + syn::Ty::Mac(_) => {} + } +} diff --git a/serde_codegen_internals/src/lib.rs b/serde_codegen_internals/src/lib.rs index c5e8885c..d5baf00b 100644 --- a/serde_codegen_internals/src/lib.rs +++ b/serde_codegen_internals/src/lib.rs @@ -1,4 +1,6 @@ extern crate syn; +#[macro_use] +extern crate synom; pub mod ast; pub mod attr; diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 6ab9f95e..b67ad285 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -6,20 +6,20 @@ use fragment::{Fragment, Expr, Stmts, Match}; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; +use std::collections::BTreeSet; + pub fn expand_derive_deserialize(item: &syn::DeriveInput) -> Result { - let item = { - let ctxt = internals::Ctxt::new(); - let item = Item::from_ast(&ctxt, item); - check_no_str(&ctxt, &item); - try!(ctxt.check()); - item - }; + let ctxt = internals::Ctxt::new(); + let item = Item::from_ast(&ctxt, item); + try!(ctxt.check()); let ident = &item.ident; let generics = build_generics(&item); - let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&generics); + let borrowed = borrowed_lifetimes(&item); + let params = Parameters { generics: generics, borrowed: borrowed }; + let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(¶ms); let dummy_const = Ident::new(format!("_IMPL_DESERIALIZE_FOR_{}", ident)); - let body = Stmts(deserialize_body(&item, &generics)); + let body = Stmts(deserialize_body(&item, ¶ms)); Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] @@ -37,6 +37,11 @@ pub fn expand_derive_deserialize(item: &syn::DeriveInput) -> Result, +} + // All the generics in the input, plus a bound `T: Deserialize` for each generic // field type that will be deserialized by us, plus a bound `T: Default` for // each generic field type that will be set to a default value. @@ -84,13 +89,27 @@ fn requires_default(attrs: &attr::Field) -> bool { attrs.default() == &attr::Default::Default } -fn deserialize_body(item: &Item, generics: &syn::Generics) -> Fragment { +// The union of lifetimes borrowed by each field of the item. +// +// These turn into bounds on the `'de` lifetime of the Deserialize impl. If +// lifetimes `'a` and `'b` are borrowed but `'c` is not, the impl is: +// +// impl<'de: 'a + 'b, 'a, 'b, 'c> Deserialize<'de> for S<'a, 'b, 'c> +fn borrowed_lifetimes(item: &Item) -> BTreeSet { + let mut lifetimes = BTreeSet::new(); + for field in item.body.all_fields() { + lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned()); + } + lifetimes +} + +fn deserialize_body(item: &Item, params: &Parameters) -> Fragment { if let Some(from_type) = item.attrs.from_type() { deserialize_from(from_type) } else { match item.body { Body::Enum(ref variants) => { - deserialize_item_enum(&item.ident, generics, variants, &item.attrs) + deserialize_item_enum(&item.ident, params, variants, &item.attrs) } Body::Struct(Style::Struct, ref fields) => { if fields.iter().any(|field| field.ident.is_none()) { @@ -98,7 +117,7 @@ fn deserialize_body(item: &Item, generics: &syn::Generics) -> Fragment { } deserialize_struct(&item.ident, None, - generics, + params, fields, &item.attrs, None) @@ -110,7 +129,7 @@ fn deserialize_body(item: &Item, generics: &syn::Generics) -> Fragment { } deserialize_tuple(&item.ident, None, - generics, + params, fields, &item.attrs, None) @@ -164,12 +183,12 @@ fn deserialize_unit_struct(ident: &syn::Ident, item_attrs: &attr::Item) -> Fragm fn deserialize_tuple(ident: &syn::Ident, variant_ident: Option<&syn::Ident>, - generics: &syn::Generics, + params: &Parameters, fields: &[Field], item_attrs: &attr::Item, deserializer: Option) -> Fragment { - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(generics); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let is_enum = variant_ident.is_some(); let type_path = match variant_ident { @@ -184,12 +203,12 @@ fn deserialize_tuple(ident: &syn::Ident, let nfields = fields.len(); let visit_newtype_struct = if !is_enum && nfields == 1 { - Some(deserialize_newtype_struct(ident, &type_path, generics, &fields[0])) + Some(deserialize_newtype_struct(ident, &type_path, params, &fields[0])) } else { None }; - let visit_seq = Stmts(deserialize_seq(ident, &type_path, generics, fields, false, item_attrs)); + let visit_seq = Stmts(deserialize_seq(ident, &type_path, params, fields, false, item_attrs)); let visitor_expr = quote! { __Visitor { @@ -245,7 +264,7 @@ fn deserialize_tuple(ident: &syn::Ident, fn deserialize_seq(ident: &syn::Ident, type_path: &Tokens, - generics: &syn::Generics, + params: &Parameters, fields: &[Field], is_struct: bool, item_attrs: &attr::Item) @@ -273,7 +292,7 @@ fn deserialize_seq(ident: &syn::Ident, } Some(path) => { let (wrapper, wrapper_ty) = wrap_deserialize_with( - ident, generics, field.ty, path); + ident, params, field.ty, path); quote!({ #wrapper _serde::export::Option::map( @@ -314,7 +333,7 @@ fn deserialize_seq(ident: &syn::Ident, fn deserialize_newtype_struct(ident: &syn::Ident, type_path: &Tokens, - generics: &syn::Generics, + params: &Parameters, field: &Field) -> Tokens { let value = match field.attrs.deserialize_with() { @@ -326,7 +345,7 @@ fn deserialize_newtype_struct(ident: &syn::Ident, } Some(path) => { let (wrapper, wrapper_ty) = - wrap_deserialize_with(ident, generics, field.ty, path); + wrap_deserialize_with(ident, params, field.ty, path); quote!({ #wrapper try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value @@ -345,7 +364,7 @@ fn deserialize_newtype_struct(ident: &syn::Ident, fn deserialize_struct(ident: &syn::Ident, variant_ident: Option<&syn::Ident>, - generics: &syn::Generics, + params: &Parameters, fields: &[Field], item_attrs: &attr::Item, deserializer: Option) @@ -353,7 +372,7 @@ fn deserialize_struct(ident: &syn::Ident, let is_enum = variant_ident.is_some(); let is_untagged = deserializer.is_some(); - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(generics); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let type_path = match variant_ident { Some(variant_ident) => quote!(#ident::#variant_ident), @@ -364,10 +383,10 @@ fn deserialize_struct(ident: &syn::Ident, None => format!("struct {}", ident), }; - let visit_seq = Stmts(deserialize_seq(ident, &type_path, generics, fields, true, item_attrs)); + let visit_seq = Stmts(deserialize_seq(ident, &type_path, params, fields, true, item_attrs)); let (field_visitor, fields_stmt, visit_map) = - deserialize_struct_visitor(ident, type_path, generics, fields, item_attrs); + deserialize_struct_visitor(ident, type_path, params, fields, item_attrs); let field_visitor = Stmts(field_visitor); let fields_stmt = Stmts(fields_stmt); let visit_map = Stmts(visit_map); @@ -446,41 +465,41 @@ fn deserialize_struct(ident: &syn::Ident, } fn deserialize_item_enum(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variants: &[Variant], item_attrs: &attr::Item) -> Fragment { match *item_attrs.tag() { attr::EnumTag::External => { - deserialize_externally_tagged_enum(ident, generics, variants, item_attrs) + deserialize_externally_tagged_enum(ident, params, variants, item_attrs) } attr::EnumTag::Internal { ref tag } => { deserialize_internally_tagged_enum(ident, - generics, + params, variants, item_attrs, tag) } attr::EnumTag::Adjacent { ref tag, ref content } => { deserialize_adjacently_tagged_enum(ident, - generics, + params, variants, item_attrs, tag, content) } attr::EnumTag::None => { - deserialize_untagged_enum(ident, generics, variants, item_attrs) + deserialize_untagged_enum(ident, params, variants, item_attrs) } } } fn deserialize_externally_tagged_enum(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variants: &[Variant], item_attrs: &attr::Item) -> Fragment { - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(generics); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let type_name = item_attrs.name().deserialize_name(); @@ -509,7 +528,7 @@ fn deserialize_externally_tagged_enum(ident: &syn::Ident, let variant_name = field_i(i); let block = Match(deserialize_externally_tagged_variant(ident, - generics, + params, variant, item_attrs)); @@ -571,7 +590,7 @@ fn deserialize_externally_tagged_enum(ident: &syn::Ident, } fn deserialize_internally_tagged_enum(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variants: &[Variant], item_attrs: &attr::Item, tag: &str) @@ -600,7 +619,7 @@ fn deserialize_internally_tagged_enum(ident: &syn::Ident, let block = Match(deserialize_internally_tagged_variant( ident, - generics, + params, variant, item_attrs, quote!(_serde::de::private::ContentDeserializer::<__D::Error>::new(__tagged.content)), @@ -627,13 +646,13 @@ fn deserialize_internally_tagged_enum(ident: &syn::Ident, } fn deserialize_adjacently_tagged_enum(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variants: &[Variant], item_attrs: &attr::Item, tag: &str, content: &str) -> Fragment { - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(generics); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let variant_names_idents: Vec<_> = variants.iter() .enumerate() @@ -658,7 +677,7 @@ fn deserialize_adjacently_tagged_enum(ident: &syn::Ident, let block = Match(deserialize_untagged_variant( ident, - generics, + params, variant, item_attrs, quote!(__deserializer), @@ -866,7 +885,7 @@ fn deserialize_adjacently_tagged_enum(ident: &syn::Ident, } fn deserialize_untagged_enum(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variants: &[Variant], item_attrs: &attr::Item) -> Fragment { @@ -875,7 +894,7 @@ fn deserialize_untagged_enum(ident: &syn::Ident, .map(|variant| { Expr(deserialize_untagged_variant( ident, - generics, + params, variant, item_attrs, quote!(_serde::de::private::ContentRefDeserializer::<__D::Error>::new(&__content)), @@ -904,7 +923,7 @@ fn deserialize_untagged_enum(ident: &syn::Ident, } fn deserialize_externally_tagged_variant(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variant: &Variant, item_attrs: &attr::Item) -> Fragment { @@ -920,13 +939,13 @@ fn deserialize_externally_tagged_variant(ident: &syn::Ident, Style::Newtype => { deserialize_externally_tagged_newtype_variant(ident, variant_ident, - generics, + params, &variant.fields[0]) } Style::Tuple => { deserialize_tuple(ident, Some(variant_ident), - generics, + params, &variant.fields, item_attrs, None) @@ -934,7 +953,7 @@ fn deserialize_externally_tagged_variant(ident: &syn::Ident, Style::Struct => { deserialize_struct(ident, Some(variant_ident), - generics, + params, &variant.fields, item_attrs, None) @@ -943,7 +962,7 @@ fn deserialize_externally_tagged_variant(ident: &syn::Ident, } fn deserialize_internally_tagged_variant(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variant: &Variant, item_attrs: &attr::Item, deserializer: Tokens) @@ -961,7 +980,7 @@ fn deserialize_internally_tagged_variant(ident: &syn::Ident, } Style::Newtype | Style::Struct => { deserialize_untagged_variant(ident, - generics, + params, variant, item_attrs, deserializer) @@ -971,7 +990,7 @@ fn deserialize_internally_tagged_variant(ident: &syn::Ident, } fn deserialize_untagged_variant(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, variant: &Variant, item_attrs: &attr::Item, deserializer: Tokens) @@ -994,14 +1013,14 @@ fn deserialize_untagged_variant(ident: &syn::Ident, Style::Newtype => { deserialize_untagged_newtype_variant(ident, variant_ident, - generics, + params, &variant.fields[0], deserializer) } Style::Tuple => { deserialize_tuple(ident, Some(variant_ident), - generics, + params, &variant.fields, item_attrs, Some(deserializer)) @@ -1009,7 +1028,7 @@ fn deserialize_untagged_variant(ident: &syn::Ident, Style::Struct => { deserialize_struct(ident, Some(variant_ident), - generics, + params, &variant.fields, item_attrs, Some(deserializer)) @@ -1019,7 +1038,7 @@ fn deserialize_untagged_variant(ident: &syn::Ident, fn deserialize_externally_tagged_newtype_variant(ident: &syn::Ident, variant_ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, field: &Field) -> Fragment { match field.attrs.deserialize_with() { @@ -1033,7 +1052,7 @@ fn deserialize_externally_tagged_newtype_variant(ident: &syn::Ident, } Some(path) => { let (wrapper, wrapper_ty) = - wrap_deserialize_with(ident, generics, field.ty, path); + wrap_deserialize_with(ident, params, field.ty, path); quote_block! { #wrapper _serde::export::Result::map( @@ -1046,7 +1065,7 @@ fn deserialize_externally_tagged_newtype_variant(ident: &syn::Ident, fn deserialize_untagged_newtype_variant(ident: &syn::Ident, variant_ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, field: &Field, deserializer: Tokens) -> Fragment { @@ -1061,7 +1080,7 @@ fn deserialize_untagged_newtype_variant(ident: &syn::Ident, } Some(path) => { let (wrapper, wrapper_ty) = - wrap_deserialize_with(ident, generics, field.ty, path); + wrap_deserialize_with(ident, params, field.ty, path); quote_block! { #wrapper _serde::export::Result::map( @@ -1186,7 +1205,7 @@ fn deserialize_field_visitor(fields: Vec<(String, Ident)>, fn deserialize_struct_visitor(ident: &syn::Ident, struct_path: Tokens, - generics: &syn::Generics, + params: &Parameters, fields: &[Field], item_attrs: &attr::Item) -> (Fragment, Fragment, Fragment) { @@ -1205,14 +1224,14 @@ fn deserialize_struct_visitor(ident: &syn::Ident, let field_visitor = deserialize_field_visitor(field_names_idents, item_attrs, false); - let visit_map = deserialize_map(ident, struct_path, generics, fields, item_attrs); + let visit_map = deserialize_map(ident, struct_path, params, fields, item_attrs); (field_visitor, fields_stmt, visit_map) } fn deserialize_map(ident: &syn::Ident, struct_path: Tokens, - generics: &syn::Generics, + params: &Parameters, fields: &[Field], item_attrs: &attr::Item) -> Fragment { @@ -1247,7 +1266,7 @@ fn deserialize_map(ident: &syn::Ident, } Some(path) => { let (wrapper, wrapper_ty) = wrap_deserialize_with( - ident, generics, field.ty, path); + ident, params, field.ty, path); quote!({ #wrapper try!(_serde::de::MapVisitor::visit_value::<#wrapper_ty>(&mut __visitor)).value @@ -1355,11 +1374,11 @@ fn field_i(i: usize) -> Ident { /// This function wraps the expression in `#[serde(deserialize_with="...")]` in /// a trait to prevent it from accessing the internal `Deserialize` state. fn wrap_deserialize_with(ident: &syn::Ident, - generics: &syn::Generics, + params: &Parameters, field_ty: &syn::Ty, deserialize_with: &syn::Path) -> (Tokens, Tokens) { - let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(generics); + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params); let wrapper = quote! { struct __DeserializeWith #de_impl_generics #where_clause { @@ -1420,34 +1439,16 @@ fn expr_is_missing(field: &Field, item_attrs: &attr::Item) -> Fragment { } } -fn check_no_str(cx: &internals::Ctxt, item: &Item) { - let fail = || { - cx.error("Serde does not support deserializing fields of type &str; consider using \ - String instead"); - }; - - for field in item.body.all_fields() { - if field.attrs.skip_deserializing() || field.attrs.deserialize_with().is_some() { - continue; - } - - if let syn::Ty::Rptr(_, ref inner) = *field.ty { - if let syn::Ty::Path(_, ref path) = inner.ty { - if path.segments.len() == 1 && path.segments[0].ident == "str" { - fail(); - return; - } - } - } - } -} - -struct DeImplGenerics<'a>(&'a syn::Generics); +struct DeImplGenerics<'a>(&'a Parameters); impl<'a> ToTokens for DeImplGenerics<'a> { fn to_tokens(&self, tokens: &mut Tokens) { - let mut generics = self.0.clone(); - generics.lifetimes.insert(0, syn::LifetimeDef::new("'de")); + let mut generics = self.0.generics.clone(); + generics.lifetimes.insert(0, syn::LifetimeDef { + attrs: Vec::new(), + lifetime: syn::Lifetime::new("'de"), + bounds: self.0.borrowed.iter().cloned().collect(), + }); let (impl_generics, _, _) = generics.split_for_impl(); impl_generics.to_tokens(tokens); } @@ -1464,9 +1465,9 @@ impl<'a> ToTokens for DeTyGenerics<'a> { } } -fn split_with_de_lifetime(generics: &syn::Generics) -> (DeImplGenerics, DeTyGenerics, syn::TyGenerics, &syn::WhereClause) { - let de_impl_generics = DeImplGenerics(generics); - let de_ty_generics = DeTyGenerics(generics); - let (_, ty_generics, where_clause) = generics.split_for_impl(); +fn split_with_de_lifetime(params: &Parameters) -> (DeImplGenerics, DeTyGenerics, syn::TyGenerics, &syn::WhereClause) { + let de_impl_generics = DeImplGenerics(¶ms); + let de_ty_generics = DeTyGenerics(¶ms.generics); + let (_, ty_generics, where_clause) = params.generics.split_for_impl(); (de_impl_generics, de_ty_generics, ty_generics, where_clause) } diff --git a/serde_test/src/de.rs b/serde_test/src/de.rs index b2d6dd12..2ec45f46 100644 --- a/serde_test/src/de.rs +++ b/serde_test/src/de.rs @@ -105,8 +105,10 @@ impl<'de, 'a, I> de::Deserializer<'de> for &'a mut Deserializer Some(Token::F64(v)) => visitor.visit_f64(v), Some(Token::Char(v)) => visitor.visit_char(v), Some(Token::Str(v)) => visitor.visit_str(v), + Some(Token::BorrowedStr(v)) => visitor.visit_borrowed_str(v), Some(Token::String(v)) => visitor.visit_string(v), Some(Token::Bytes(v)) => visitor.visit_bytes(v), + Some(Token::BorrowedBytes(v)) => visitor.visit_borrowed_bytes(v), Some(Token::ByteBuf(v)) => visitor.visit_byte_buf(v), Some(Token::Option(false)) => visitor.visit_none(), Some(Token::Option(true)) => visitor.visit_some(self), diff --git a/serde_test/src/token.rs b/serde_test/src/token.rs index 82ccd9f2..05e15583 100644 --- a/serde_test/src/token.rs +++ b/serde_test/src/token.rs @@ -39,12 +39,18 @@ pub enum Token<'a> { /// A serialized `str`. Str(&'a str), + /// A borrowed `str`. + BorrowedStr(&'a str), + /// A serialized `String`. String(String), /// A serialized `[u8]` Bytes(&'a [u8]), + /// A borrowed `[u8]`. + BorrowedBytes(&'a [u8]), + /// A serialized `ByteBuf` ByteBuf(Vec), diff --git a/test_suite/tests/compile-fail/borrow/bad_lifetimes.rs b/test_suite/tests/compile-fail/borrow/bad_lifetimes.rs new file mode 100644 index 00000000..b4a5e4a3 --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/bad_lifetimes.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +struct Test<'a> { + #[serde(borrow = "zzz")] //~^^ HELP: failed to parse borrowed lifetimes: "zzz" + s: &'a str, +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/borrow/duplicate_lifetime.rs b/test_suite/tests/compile-fail/borrow/duplicate_lifetime.rs new file mode 100644 index 00000000..542ddc2a --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/duplicate_lifetime.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +struct Test<'a> { + #[serde(borrow = "'a + 'a")] //~^^ HELP: duplicate borrowed lifetime `'a` + s: &'a str, +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/borrow/empty_lifetimes.rs b/test_suite/tests/compile-fail/borrow/empty_lifetimes.rs new file mode 100644 index 00000000..c1d43b57 --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/empty_lifetimes.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +struct Test<'a> { + #[serde(borrow = "")] //~^^ HELP: at least one lifetime must be borrowed + s: &'a str, +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/borrow/no_lifetimes.rs b/test_suite/tests/compile-fail/borrow/no_lifetimes.rs new file mode 100644 index 00000000..47b3c0d4 --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/no_lifetimes.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +struct Test { + #[serde(borrow)] //~^^ HELP: field `s` has no lifetimes to borrow + s: String, +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/borrow/wrong_lifetime.rs b/test_suite/tests/compile-fail/borrow/wrong_lifetime.rs new file mode 100644 index 00000000..5cf8999f --- /dev/null +++ b/test_suite/tests/compile-fail/borrow/wrong_lifetime.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate serde_derive; + +#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked +struct Test<'a> { + #[serde(borrow = "'b")] //~^^ HELP: field `s` does not have lifetime 'b + s: &'a str, +} + +fn main() {} diff --git a/test_suite/tests/compile-fail/str_ref_deser.rs b/test_suite/tests/compile-fail/str_ref_deser.rs deleted file mode 100644 index 51f03f5c..00000000 --- a/test_suite/tests/compile-fail/str_ref_deser.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[macro_use] -extern crate serde_derive; - -#[derive(Serialize, Deserialize)] //~ ERROR: proc-macro derive panicked -struct Test<'a> { - s: &'a str, //~^^ HELP: Serde does not support deserializing fields of type &str -} - -fn main() {} diff --git a/test_suite/tests/test_borrow.rs b/test_suite/tests/test_borrow.rs new file mode 100644 index 00000000..dd75f70b --- /dev/null +++ b/test_suite/tests/test_borrow.rs @@ -0,0 +1,199 @@ +#[macro_use] +extern crate serde_derive; + +extern crate serde; +use serde::{Deserialize, Deserializer}; + +extern crate serde_test; +use serde_test::{ + Error, + Token, + assert_de_tokens, + assert_de_tokens_error, +}; + +use std::borrow::Cow; + +#[test] +fn test_borrowed_str() { + assert_de_tokens( + &"borrowed", + &[ + Token::BorrowedStr("borrowed"), + ] + ); +} + +#[test] +fn test_borrowed_str_from_string() { + assert_de_tokens_error::<&str>( + &[ + Token::String("borrowed".to_owned()), + ], + Error::Message("invalid type: string \"borrowed\", expected a borrowed string".to_owned()), + ); +} + +#[test] +fn test_borrowed_str_from_str() { + assert_de_tokens_error::<&str>( + &[ + Token::Str("borrowed"), + ], + Error::Message("invalid type: string \"borrowed\", expected a borrowed string".to_owned()), + ); +} + +#[test] +fn test_string_from_borrowed_str() { + assert_de_tokens( + &"owned".to_owned(), + &[ + Token::BorrowedStr("owned"), + ] + ); +} + +#[test] +fn test_borrowed_bytes() { + assert_de_tokens( + &&b"borrowed"[..], + &[ + Token::BorrowedBytes(b"borrowed"), + ] + ); +} + +#[test] +fn test_borrowed_bytes_from_bytebuf() { + assert_de_tokens_error::<&[u8]>( + &[ + Token::ByteBuf(b"borrowed".to_vec()), + ], + Error::Message("invalid type: byte array, expected a borrowed byte array".to_owned()), + ); +} + +#[test] +fn test_borrowed_bytes_from_bytes() { + assert_de_tokens_error::<&[u8]>( + &[ + Token::Bytes(b"borrowed"), + ], + Error::Message("invalid type: byte array, expected a borrowed byte array".to_owned()), + ); +} + +#[test] +fn test_tuple() { + assert_de_tokens( + &("str", &b"bytes"[..]), + &[ + Token::TupleStart(2), + + Token::TupleSep, + Token::BorrowedStr("str"), + + Token::TupleSep, + Token::BorrowedBytes(b"bytes"), + + Token::TupleEnd, + ] + ); +} + +#[test] +fn test_struct() { + #[derive(Deserialize, Debug, PartialEq)] + struct Borrowing<'a, 'b> { + bs: &'a str, + bb: &'b [u8], + } + + assert_de_tokens( + &Borrowing { bs: "str", bb: b"bytes" }, + &[ + Token::StructStart("Borrowing", 2), + + Token::StructSep, + Token::BorrowedStr("bs"), + Token::BorrowedStr("str"), + + Token::StructSep, + Token::BorrowedStr("bb"), + Token::BorrowedBytes(b"bytes"), + + Token::StructEnd, + ] + ); +} + +#[test] +fn test_cow() { + #[derive(Deserialize)] + struct Cows<'a, 'b> { + copied: Cow<'a, str>, + + #[serde(borrow)] + borrowed: Cow<'b, str>, + } + + let tokens = vec![ + Token::StructStart("Cows", 2), + + Token::StructSep, + Token::Str("copied"), + Token::BorrowedStr("copied"), + + Token::StructSep, + Token::Str("borrowed"), + Token::BorrowedStr("borrowed"), + + Token::StructEnd, + ]; + + let mut de = serde_test::Deserializer::new(tokens.into_iter()); + let cows = Cows::deserialize(&mut de).unwrap(); + assert_eq!(de.next_token(), None); + + match cows.copied { + Cow::Owned(ref s) if s == "copied" => {} + _ => panic!("expected a copied string"), + } + + match cows.borrowed { + Cow::Borrowed("borrowed") => {} + _ => panic!("expected a borrowed string"), + } +} + +#[test] +fn test_lifetimes() { + #[derive(Deserialize)] + struct Cows<'a, 'b> { + _copied: Cow<'a, str>, + + #[serde(borrow)] + _borrowed: Cow<'b, str>, + } + + // Tests that `'de: 'a` is not required by the Deserialize impl. + fn _cows_lifetimes<'de: 'b, 'a, 'b, D>(deserializer: D) -> Cows<'a, 'b> + where D: Deserializer<'de> + { + Deserialize::deserialize(deserializer).unwrap() + } + + #[derive(Deserialize)] + struct Wrap<'a, 'b> { + #[serde(borrow = "'b")] + _cows: Cows<'a, 'b>, + } + + // Tests that `'de: 'a` is not required by the Deserialize impl. + fn _wrap_lifetimes<'de: 'b, 'a, 'b, D>(deserializer: D) -> Wrap<'a, 'b> + where D: Deserializer<'de> + { + Deserialize::deserialize(deserializer).unwrap() + } +}