mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-29 01:27:56 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ba406726f | |||
| 7e9826e17b | |||
| f4dcc5c918 | |||
| 8b1887c440 | |||
| bbfb1d3504 | |||
| e106feb5ec | |||
| 696f6f56db | |||
| b7b636a23f | |||
| 183b91775e | |||
| 0e70f59021 | |||
| 4d9b76db73 | |||
| 9af132f594 | |||
| 6c063569c0 | |||
| 7e9b98401d | |||
| f301e09e02 | |||
| b80e722f81 | |||
| 1714c262c4 | |||
| a42cdafdcd | |||
| eb4c3f16f7 | |||
| ce86f351d6 | |||
| 0b90f6c96a | |||
| 2198463218 | |||
| be57a5e00a | |||
| b1b09eba60 | |||
| eb1e8c140d | |||
| 43da87939d | |||
| 06d99a13a6 | |||
| 49a911d7de | |||
| 27d6628785 | |||
| e4e2956e79 | |||
| ea2f7b81d9 | |||
| f0dfdb5247 | |||
| 6a5da85fcd | |||
| 0750eee4ff | |||
| ef551a517c | |||
| 1c5ea24f76 | |||
| 88d73e5250 | |||
| 1ff2a972c6 | |||
| bb72fe2726 | |||
| 38c130a303 | |||
| c7393614ff | |||
| f7636428ed | |||
| bd4a0981ba |
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.153" # remember to update html_root_url and serde_derive dependency
|
version = "1.0.155" # remember to update html_root_url and serde_derive dependency
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
categories = ["encoding", "no-std"]
|
categories = ["encoding", "no-std"]
|
||||||
@@ -15,7 +15,7 @@ repository = "https://github.com/serde-rs/serde"
|
|||||||
rust-version = "1.19"
|
rust-version = "1.19"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_derive = { version = "=1.0.153", optional = true, path = "../serde_derive" }
|
serde_derive = { version = "=1.0.155", optional = true, path = "../serde_derive" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_derive = { version = "1.0", path = "../serde_derive" }
|
serde_derive = { version = "1.0", path = "../serde_derive" }
|
||||||
|
|||||||
@@ -114,6 +114,12 @@ fn main() {
|
|||||||
println!("cargo:rustc-cfg=no_std_atomic");
|
println!("cargo:rustc-cfg=no_std_atomic");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support for core::ffi::CStr and alloc::ffi::CString stabilized in Rust 1.64.
|
||||||
|
// https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html#c-compatible-ffi-types-in-core-and-alloc
|
||||||
|
if minor < 64 {
|
||||||
|
println!("cargo:rustc-cfg=no_core_cstr");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rustc_minor_version() -> Option<u32> {
|
fn rustc_minor_version() -> Option<u32> {
|
||||||
|
|||||||
@@ -666,10 +666,10 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a [u8] {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
|
||||||
struct CStringVisitor;
|
struct CStringVisitor;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
|
||||||
impl<'de> Visitor<'de> for CStringVisitor {
|
impl<'de> Visitor<'de> for CStringVisitor {
|
||||||
type Value = CString;
|
type Value = CString;
|
||||||
|
|
||||||
@@ -720,7 +720,7 @@ impl<'de> Visitor<'de> for CStringVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
|
||||||
impl<'de> Deserialize<'de> for CString {
|
impl<'de> Deserialize<'de> for CString {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
@@ -747,7 +747,7 @@ macro_rules! forwarded_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(no_de_boxed_c_str)))]
|
#[cfg(all(any(feature = "std", all(not(no_core_cstr), feature = "alloc")), not(no_de_boxed_c_str)))]
|
||||||
forwarded_impl!((), Box<CStr>, CString::into_boxed_c_str);
|
forwarded_impl!((), Box<CStr>, CString::into_boxed_c_str);
|
||||||
|
|
||||||
#[cfg(not(no_core_reverse))]
|
#[cfg(not(no_core_reverse))]
|
||||||
|
|||||||
+12
-2
@@ -93,7 +93,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Serde types in rustdoc of other crates get linked to here.
|
// Serde types in rustdoc of other crates get linked to here.
|
||||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.153")]
|
#![doc(html_root_url = "https://docs.rs/serde/1.0.155")]
|
||||||
// Support using Serde without the standard library!
|
// Support using Serde without the standard library!
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
// Unstable functionality only if the user asks for it. For tracking and
|
// Unstable functionality only if the user asks for it. For tracking and
|
||||||
@@ -219,13 +219,23 @@ mod lib {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
|
pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
|
||||||
|
|
||||||
|
#[cfg(all(not(no_core_cstr), not(feature = "std")))]
|
||||||
|
pub use core::ffi::CStr;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::ffi::CStr;
|
||||||
|
|
||||||
|
#[cfg(all(not(no_core_cstr), feature = "alloc", not(feature = "std")))]
|
||||||
|
pub use alloc::ffi::{CString};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use std::ffi::CString;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::{error, net};
|
pub use std::{error, net};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::collections::{HashMap, HashSet};
|
pub use std::collections::{HashMap, HashSet};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::ffi::{CStr, CString, OsStr, OsString};
|
pub use std::ffi::{OsStr, OsString};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use std::hash::{BuildHasher, Hash};
|
pub use std::hash::{BuildHasher, Hash};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl<'a> Serialize for fmt::Arguments<'a> {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(feature = "std", not(no_core_cstr)))]
|
||||||
impl Serialize for CStr {
|
impl Serialize for CStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
@@ -83,7 +83,7 @@ impl Serialize for CStr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(any(feature = "std", all(not(no_core_cstr), feature = "alloc")))]
|
||||||
impl Serialize for CString {
|
impl Serialize for CString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.153" # remember to update html_root_url
|
version = "1.0.155" # remember to update html_root_url
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
categories = ["no-std"]
|
categories = ["no-std"]
|
||||||
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
||||||
|
|||||||
@@ -954,6 +954,7 @@ fn deserialize_struct(
|
|||||||
lifetime: _serde::__private::PhantomData,
|
lifetime: _serde::__private::PhantomData,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let need_seed = deserializer.is_none();
|
||||||
let dispatch = if let Some(deserializer) = deserializer {
|
let dispatch = if let Some(deserializer) = deserializer {
|
||||||
quote! {
|
quote! {
|
||||||
_serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
|
_serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
|
||||||
@@ -999,14 +1000,14 @@ fn deserialize_struct(
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let visitor_seed = if is_enum && cattrs.has_flatten() {
|
let visitor_seed = if need_seed && is_enum && cattrs.has_flatten() {
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
|
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
|
||||||
type Value = #this_type #ty_generics;
|
type Value = #this_type #ty_generics;
|
||||||
|
|
||||||
fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result<Self::Value, __D::Error>
|
fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result<Self::Value, __D::Error>
|
||||||
where
|
where
|
||||||
__D: _serde::Deserializer<'de>,
|
__D: _serde::Deserializer<#delife>,
|
||||||
{
|
{
|
||||||
_serde::Deserializer::deserialize_map(__deserializer, self)
|
_serde::Deserializer::deserialize_map(__deserializer, self)
|
||||||
}
|
}
|
||||||
@@ -1256,7 +1257,7 @@ fn deserialize_externally_tagged_enum(
|
|||||||
// This is an empty enum like `enum Impossible {}` or an enum in which
|
// This is an empty enum like `enum Impossible {}` or an enum in which
|
||||||
// all variants have `#[serde(skip_deserializing)]`.
|
// all variants have `#[serde(skip_deserializing)]`.
|
||||||
quote! {
|
quote! {
|
||||||
// FIXME: Once we drop support for Rust 1.15:
|
// FIXME: Once feature(exhaustive_patterns) is stable:
|
||||||
// let _serde::__private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data);
|
// let _serde::__private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data);
|
||||||
// _serde::__private::Err(__err)
|
// _serde::__private::Err(__err)
|
||||||
_serde::__private::Result::map(
|
_serde::__private::Result::map(
|
||||||
@@ -2400,7 +2401,9 @@ fn deserialize_struct_as_struct_visitor(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let fields_stmt = {
|
let fields_stmt = {
|
||||||
let field_names = field_names_idents.iter().flat_map(|(_, _, aliases)| aliases);
|
let field_names = field_names_idents
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, _, aliases)| aliases);
|
||||||
|
|
||||||
quote_block! {
|
quote_block! {
|
||||||
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
|
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
|
||||||
@@ -2536,7 +2539,7 @@ fn deserialize_map(
|
|||||||
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
|
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
|
||||||
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
|
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
|
||||||
quote! {
|
quote! {
|
||||||
// FIXME: Once we drop support for Rust 1.15:
|
// FIXME: Once feature(exhaustive_patterns) is stable:
|
||||||
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
|
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
|
||||||
_serde::__private::Option::map(
|
_serde::__private::Option::map(
|
||||||
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
|
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
|
||||||
@@ -2769,7 +2772,7 @@ fn deserialize_map_in_place(
|
|||||||
|
|
||||||
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
|
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
|
||||||
quote! {
|
quote! {
|
||||||
// FIXME: Once we drop support for Rust 1.15:
|
// FIXME: Once feature(exhaustive_patterns) is stable:
|
||||||
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
|
// let _serde::__private::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
|
||||||
_serde::__private::Option::map(
|
_serde::__private::Option::map(
|
||||||
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
|
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
|
||||||
|
|||||||
@@ -39,10 +39,6 @@ pub fn wrap_in_const(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn unraw(ident: &Ident) -> String {
|
fn unraw(ident: &Ident) -> String {
|
||||||
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
|
ident.to_string().trim_start_matches("r#").to_owned()
|
||||||
// in 1.33. We currently support rustc back to 1.15 so we need to continue
|
|
||||||
// to use the deprecated one.
|
|
||||||
ident.to_string().trim_left_matches("r#").to_owned()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+188
-234
@@ -1,16 +1,16 @@
|
|||||||
use internals::respan::respan;
|
|
||||||
use internals::symbol::*;
|
use internals::symbol::*;
|
||||||
use internals::{ungroup, Ctxt};
|
use internals::{ungroup, Ctxt};
|
||||||
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
|
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
use std::iter::FromIterator;
|
||||||
use syn;
|
use syn;
|
||||||
use syn::parse::{self, Parse, ParseStream};
|
use syn::parse::ParseStream;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::Ident;
|
|
||||||
use syn::Meta::{List, NameValue, Path};
|
use syn::Meta::{List, NameValue, Path};
|
||||||
use syn::NestedMeta::{Lit, Meta};
|
use syn::NestedMeta::{Lit, Meta};
|
||||||
|
use syn::{Ident, Lifetime};
|
||||||
|
|
||||||
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
|
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
|
||||||
// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
|
// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
|
||||||
@@ -141,12 +141,8 @@ pub struct Name {
|
|||||||
deserialize_aliases: Vec<String>,
|
deserialize_aliases: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn unraw(ident: &Ident) -> String {
|
fn unraw(ident: &Ident) -> String {
|
||||||
// str::trim_start_matches was added in 1.30, trim_left_matches deprecated
|
ident.to_string().trim_start_matches("r#").to_owned()
|
||||||
// in 1.33. We currently support rustc back to 1.15 so we need to continue
|
|
||||||
// to use the deprecated one.
|
|
||||||
ident.to_string().trim_left_matches("r#").to_owned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
@@ -379,48 +375,43 @@ impl Container {
|
|||||||
syn::Fields::Named(_) => {
|
syn::Fields::Named(_) => {
|
||||||
default.set(word, Default::Default);
|
default.set(word, Default::Default);
|
||||||
}
|
}
|
||||||
syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
|
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
|
||||||
fields,
|
let msg =
|
||||||
"#[serde(default)] can only be used on structs with named fields",
|
"#[serde(default)] can only be used on structs with named fields";
|
||||||
),
|
cx.error_spanned_by(fields, msg);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx.error_spanned_by(
|
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => {
|
||||||
enum_token,
|
let msg = "#[serde(default)] can only be used on structs with named fields";
|
||||||
"#[serde(default)] can only be used on structs with named fields",
|
cx.error_spanned_by(enum_token, msg);
|
||||||
),
|
}
|
||||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => cx.error_spanned_by(
|
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||||
union_token,
|
let msg = "#[serde(default)] can only be used on structs with named fields";
|
||||||
"#[serde(default)] can only be used on structs with named fields",
|
cx.error_spanned_by(union_token, msg);
|
||||||
),
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Parse `#[serde(default = "...")]`
|
// Parse `#[serde(default = "...")]`
|
||||||
Meta(NameValue(m)) if m.path == DEFAULT => {
|
Meta(NameValue(m)) if m.path == DEFAULT => {
|
||||||
if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
|
if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
|
||||||
match &item.data {
|
match &item.data {
|
||||||
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
|
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
||||||
match fields {
|
syn::Fields::Named(_) => {
|
||||||
syn::Fields::Named(_) => {
|
default.set(&m.path, Default::Path(path));
|
||||||
default.set(&m.path, Default::Path(path));
|
|
||||||
}
|
|
||||||
syn::Fields::Unnamed(_) | syn::Fields::Unit => cx
|
|
||||||
.error_spanned_by(
|
|
||||||
fields,
|
|
||||||
"#[serde(default = \"...\")] can only be used on structs with named fields",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
|
||||||
|
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
|
||||||
|
cx.error_spanned_by(fields, msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => {
|
||||||
|
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
|
||||||
|
cx.error_spanned_by(enum_token, msg);
|
||||||
|
}
|
||||||
|
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||||
|
let msg = "#[serde(default = \"...\")] can only be used on structs with named fields";
|
||||||
|
cx.error_spanned_by(union_token, msg);
|
||||||
}
|
}
|
||||||
syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx
|
|
||||||
.error_spanned_by(
|
|
||||||
enum_token,
|
|
||||||
"#[serde(default = \"...\")] can only be used on structs with named fields",
|
|
||||||
),
|
|
||||||
syn::Data::Union(syn::DataUnion {
|
|
||||||
union_token, ..
|
|
||||||
}) => cx.error_spanned_by(
|
|
||||||
union_token,
|
|
||||||
"#[serde(default = \"...\")] can only be used on structs with named fields",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,16 +438,12 @@ impl Container {
|
|||||||
untagged.set_true(word);
|
untagged.set_true(word);
|
||||||
}
|
}
|
||||||
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
|
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(untagged)] can only be used on enums";
|
||||||
struct_token,
|
cx.error_spanned_by(struct_token, msg);
|
||||||
"#[serde(untagged)] can only be used on enums",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(untagged)] can only be used on enums";
|
||||||
union_token,
|
cx.error_spanned_by(union_token, msg);
|
||||||
"#[serde(untagged)] can only be used on enums",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -472,17 +459,13 @@ impl Container {
|
|||||||
internal_tag.set(&m.path, s.value());
|
internal_tag.set(&m.path, s.value());
|
||||||
}
|
}
|
||||||
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
|
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
|
||||||
fields,
|
cx.error_spanned_by(fields, msg);
|
||||||
"#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
|
||||||
union_token,
|
cx.error_spanned_by(union_token, msg);
|
||||||
"#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -496,36 +479,32 @@ impl Container {
|
|||||||
content.set(&m.path, s.value());
|
content.set(&m.path, s.value());
|
||||||
}
|
}
|
||||||
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
|
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(content = \"...\")] can only be used on enums";
|
||||||
struct_token,
|
cx.error_spanned_by(struct_token, msg);
|
||||||
"#[serde(content = \"...\")] can only be used on enums",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(content = \"...\")] can only be used on enums";
|
||||||
union_token,
|
cx.error_spanned_by(union_token, msg);
|
||||||
"#[serde(content = \"...\")] can only be used on enums",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(from = "Type")]
|
// Parse `#[serde(from = "Type")]`
|
||||||
Meta(NameValue(m)) if m.path == FROM => {
|
Meta(NameValue(m)) if m.path == FROM => {
|
||||||
if let Ok(from_ty) = parse_lit_into_ty(cx, FROM, &m.lit) {
|
if let Ok(from_ty) = parse_lit_into_ty(cx, FROM, &m.lit) {
|
||||||
type_from.set_opt(&m.path, Some(from_ty));
|
type_from.set_opt(&m.path, Some(from_ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(try_from = "Type")]
|
// Parse `#[serde(try_from = "Type")]`
|
||||||
Meta(NameValue(m)) if m.path == TRY_FROM => {
|
Meta(NameValue(m)) if m.path == TRY_FROM => {
|
||||||
if let Ok(try_from_ty) = parse_lit_into_ty(cx, TRY_FROM, &m.lit) {
|
if let Ok(try_from_ty) = parse_lit_into_ty(cx, TRY_FROM, &m.lit) {
|
||||||
type_try_from.set_opt(&m.path, Some(try_from_ty));
|
type_try_from.set_opt(&m.path, Some(try_from_ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse `#[serde(into = "Type")]
|
// Parse `#[serde(into = "Type")]`
|
||||||
Meta(NameValue(m)) if m.path == INTO => {
|
Meta(NameValue(m)) if m.path == INTO => {
|
||||||
if let Ok(into_ty) = parse_lit_into_ty(cx, INTO, &m.lit) {
|
if let Ok(into_ty) = parse_lit_into_ty(cx, INTO, &m.lit) {
|
||||||
type_into.set_opt(&m.path, Some(into_ty));
|
type_into.set_opt(&m.path, Some(into_ty));
|
||||||
@@ -573,21 +552,20 @@ impl Container {
|
|||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace(' ', "");
|
.replace(' ', "");
|
||||||
cx.error_spanned_by(
|
let msg = format!("unknown serde container attribute `{}`", path);
|
||||||
meta_item.path(),
|
cx.error_spanned_by(meta_item.path(), msg);
|
||||||
format!("unknown serde container attribute `{}`", path),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lit(lit) => {
|
Lit(lit) => {
|
||||||
cx.error_spanned_by(lit, "unexpected literal in serde container attribute");
|
let msg = "unexpected literal in serde container attribute";
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut is_packed = false;
|
let mut is_packed = false;
|
||||||
for attr in &item.attrs {
|
for attr in &item.attrs {
|
||||||
if attr.path.is_ident("repr") {
|
if attr.path == REPR {
|
||||||
let _ = attr.parse_args_with(|input: ParseStream| {
|
let _ = attr.parse_args_with(|input: ParseStream| {
|
||||||
while let Some(token) = input.parse()? {
|
while let Some(token) = input.parse()? {
|
||||||
if let TokenTree::Ident(ident) = token {
|
if let TokenTree::Ident(ident) = token {
|
||||||
@@ -725,10 +703,9 @@ fn decide_tag(
|
|||||||
syn::Fields::Named(_) | syn::Fields::Unit => {}
|
syn::Fields::Named(_) | syn::Fields::Unit => {}
|
||||||
syn::Fields::Unnamed(fields) => {
|
syn::Fields::Unnamed(fields) => {
|
||||||
if fields.unnamed.len() != 1 {
|
if fields.unnamed.len() != 1 {
|
||||||
cx.error_spanned_by(
|
let msg =
|
||||||
variant,
|
"#[serde(tag = \"...\")] cannot be used with tuple variants";
|
||||||
"#[serde(tag = \"...\")] cannot be used with tuple variants",
|
cx.error_spanned_by(variant, msg);
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,48 +715,28 @@ fn decide_tag(
|
|||||||
TagType::Internal { tag }
|
TagType::Internal { tag }
|
||||||
}
|
}
|
||||||
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
|
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
|
||||||
cx.error_spanned_by(
|
let msg = "enum cannot be both untagged and internally tagged";
|
||||||
untagged_tokens,
|
cx.error_spanned_by(untagged_tokens, msg);
|
||||||
"enum cannot be both untagged and internally tagged",
|
cx.error_spanned_by(tag_tokens, msg);
|
||||||
);
|
|
||||||
cx.error_spanned_by(
|
|
||||||
tag_tokens,
|
|
||||||
"enum cannot be both untagged and internally tagged",
|
|
||||||
);
|
|
||||||
TagType::External // doesn't matter, will error
|
TagType::External // doesn't matter, will error
|
||||||
}
|
}
|
||||||
(None, None, Some((content_tokens, _))) => {
|
(None, None, Some((content_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(tag = \"...\", content = \"...\")] must be used together";
|
||||||
content_tokens,
|
cx.error_spanned_by(content_tokens, msg);
|
||||||
"#[serde(tag = \"...\", content = \"...\")] must be used together",
|
|
||||||
);
|
|
||||||
TagType::External
|
TagType::External
|
||||||
}
|
}
|
||||||
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
|
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
let msg = "untagged enum cannot have #[serde(content = \"...\")]";
|
||||||
untagged_tokens,
|
cx.error_spanned_by(untagged_tokens, msg);
|
||||||
"untagged enum cannot have #[serde(content = \"...\")]",
|
cx.error_spanned_by(content_tokens, msg);
|
||||||
);
|
|
||||||
cx.error_spanned_by(
|
|
||||||
content_tokens,
|
|
||||||
"untagged enum cannot have #[serde(content = \"...\")]",
|
|
||||||
);
|
|
||||||
TagType::External
|
TagType::External
|
||||||
}
|
}
|
||||||
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent { tag, content },
|
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent { tag, content },
|
||||||
(Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
|
(Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
let msg = "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]";
|
||||||
untagged_tokens,
|
cx.error_spanned_by(untagged_tokens, msg);
|
||||||
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
cx.error_spanned_by(tag_tokens, msg);
|
||||||
);
|
cx.error_spanned_by(content_tokens, msg);
|
||||||
cx.error_spanned_by(
|
|
||||||
tag_tokens,
|
|
||||||
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
|
||||||
);
|
|
||||||
cx.error_spanned_by(
|
|
||||||
content_tokens,
|
|
||||||
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
|
||||||
);
|
|
||||||
TagType::External
|
TagType::External
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -798,44 +755,32 @@ fn decide_identifier(
|
|||||||
) {
|
) {
|
||||||
(_, None, None) => Identifier::No,
|
(_, None, None) => Identifier::No,
|
||||||
(_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
|
(_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
let msg =
|
||||||
field_identifier_tokens,
|
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set";
|
||||||
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
|
cx.error_spanned_by(field_identifier_tokens, msg);
|
||||||
);
|
cx.error_spanned_by(variant_identifier_tokens, msg);
|
||||||
cx.error_spanned_by(
|
|
||||||
variant_identifier_tokens,
|
|
||||||
"#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
|
|
||||||
);
|
|
||||||
Identifier::No
|
Identifier::No
|
||||||
}
|
}
|
||||||
(syn::Data::Enum(_), Some(_), None) => Identifier::Field,
|
(syn::Data::Enum(_), Some(_), None) => Identifier::Field,
|
||||||
(syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
|
(syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
|
||||||
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), Some(_), None) => {
|
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), Some(_), None) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(field_identifier)] can only be used on an enum";
|
||||||
struct_token,
|
cx.error_spanned_by(struct_token, msg);
|
||||||
"#[serde(field_identifier)] can only be used on an enum",
|
|
||||||
);
|
|
||||||
Identifier::No
|
Identifier::No
|
||||||
}
|
}
|
||||||
(syn::Data::Union(syn::DataUnion { union_token, .. }), Some(_), None) => {
|
(syn::Data::Union(syn::DataUnion { union_token, .. }), Some(_), None) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(field_identifier)] can only be used on an enum";
|
||||||
union_token,
|
cx.error_spanned_by(union_token, msg);
|
||||||
"#[serde(field_identifier)] can only be used on an enum",
|
|
||||||
);
|
|
||||||
Identifier::No
|
Identifier::No
|
||||||
}
|
}
|
||||||
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), None, Some(_)) => {
|
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), None, Some(_)) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(variant_identifier)] can only be used on an enum";
|
||||||
struct_token,
|
cx.error_spanned_by(struct_token, msg);
|
||||||
"#[serde(variant_identifier)] can only be used on an enum",
|
|
||||||
);
|
|
||||||
Identifier::No
|
Identifier::No
|
||||||
}
|
}
|
||||||
(syn::Data::Union(syn::DataUnion { union_token, .. }), None, Some(_)) => {
|
(syn::Data::Union(syn::DataUnion { union_token, .. }), None, Some(_)) => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(variant_identifier)] can only be used on an enum";
|
||||||
union_token,
|
cx.error_spanned_by(union_token, msg);
|
||||||
"#[serde(variant_identifier)] can only be used on an enum",
|
|
||||||
);
|
|
||||||
Identifier::No
|
Identifier::No
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,7 +797,12 @@ pub struct Variant {
|
|||||||
other: bool,
|
other: bool,
|
||||||
serialize_with: Option<syn::ExprPath>,
|
serialize_with: Option<syn::ExprPath>,
|
||||||
deserialize_with: Option<syn::ExprPath>,
|
deserialize_with: Option<syn::ExprPath>,
|
||||||
borrow: Option<syn::Meta>,
|
borrow: Option<BorrowAttribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BorrowAttribute {
|
||||||
|
path: syn::Path,
|
||||||
|
lifetimes: Option<BTreeSet<syn::Lifetime>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Variant {
|
impl Variant {
|
||||||
@@ -1005,16 +955,39 @@ impl Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]`
|
// Parse `#[serde(borrow)]`
|
||||||
Meta(m) if m.path() == BORROW => match &variant.fields {
|
Meta(Path(word)) if word == BORROW => match &variant.fields {
|
||||||
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
||||||
borrow.set(m.path(), m.clone());
|
borrow.set(
|
||||||
|
word,
|
||||||
|
BorrowAttribute {
|
||||||
|
path: word.clone(),
|
||||||
|
lifetimes: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
cx.error_spanned_by(
|
let msg = "#[serde(borrow)] may only be used on newtype variants";
|
||||||
variant,
|
cx.error_spanned_by(variant, msg);
|
||||||
"#[serde(borrow)] may only be used on newtype variants",
|
}
|
||||||
);
|
},
|
||||||
|
|
||||||
|
// Parse `#[serde(borrow = "'a + 'b")]`
|
||||||
|
Meta(NameValue(m)) if m.path == BORROW => match &variant.fields {
|
||||||
|
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
||||||
|
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, &m.lit) {
|
||||||
|
borrow.set(
|
||||||
|
&m.path,
|
||||||
|
BorrowAttribute {
|
||||||
|
path: m.path.clone(),
|
||||||
|
lifetimes: Some(lifetimes),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let msg = "#[serde(borrow)] may only be used on newtype variants";
|
||||||
|
cx.error_spanned_by(variant, msg);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1024,14 +997,13 @@ impl Variant {
|
|||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace(' ', "");
|
.replace(' ', "");
|
||||||
cx.error_spanned_by(
|
let msg = format!("unknown serde variant attribute `{}`", path);
|
||||||
meta_item.path(),
|
cx.error_spanned_by(meta_item.path(), msg);
|
||||||
format!("unknown serde variant attribute `{}`", path),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lit(lit) => {
|
Lit(lit) => {
|
||||||
cx.error_spanned_by(lit, "unexpected literal in serde variant attribute");
|
let msg = "unexpected literal in serde variant attribute";
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1168,16 +1140,28 @@ impl Field {
|
|||||||
None => index.to_string(),
|
None => index.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let variant_borrow = attrs
|
if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) {
|
||||||
.and_then(|variant| variant.borrow.as_ref())
|
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
|
||||||
.map(|borrow| Meta(borrow.clone()));
|
if let Some(lifetimes) = &borrow_attribute.lifetimes {
|
||||||
|
for lifetime in lifetimes {
|
||||||
|
if !borrowable.contains(lifetime) {
|
||||||
|
let msg =
|
||||||
|
format!("field `{}` does not have lifetime {}", ident, lifetime);
|
||||||
|
cx.error_spanned_by(field, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
borrowed_lifetimes.set(&borrow_attribute.path, lifetimes.clone());
|
||||||
|
} else {
|
||||||
|
borrowed_lifetimes.set(&borrow_attribute.path, borrowable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for meta_item in field
|
for meta_item in field
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|attr| get_serde_meta_items(cx, attr))
|
.flat_map(|attr| get_serde_meta_items(cx, attr))
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(variant_borrow)
|
|
||||||
{
|
{
|
||||||
match &meta_item {
|
match &meta_item {
|
||||||
// Parse `#[serde(rename = "foo")]`
|
// Parse `#[serde(rename = "foo")]`
|
||||||
@@ -1299,17 +1283,15 @@ impl Field {
|
|||||||
|
|
||||||
// Parse `#[serde(borrow = "'a + 'b")]`
|
// Parse `#[serde(borrow = "'a + 'b")]`
|
||||||
Meta(NameValue(m)) if m.path == BORROW => {
|
Meta(NameValue(m)) if m.path == BORROW => {
|
||||||
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, BORROW, &m.lit) {
|
if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, &m.lit) {
|
||||||
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
|
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
|
||||||
for lifetime in &lifetimes {
|
for lifetime in &lifetimes {
|
||||||
if !borrowable.contains(lifetime) {
|
if !borrowable.contains(lifetime) {
|
||||||
cx.error_spanned_by(
|
let msg = format!(
|
||||||
field,
|
"field `{}` does not have lifetime {}",
|
||||||
format!(
|
ident, lifetime,
|
||||||
"field `{}` does not have lifetime {}",
|
|
||||||
ident, lifetime
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
cx.error_spanned_by(field, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
borrowed_lifetimes.set(&m.path, lifetimes);
|
borrowed_lifetimes.set(&m.path, lifetimes);
|
||||||
@@ -1335,14 +1317,13 @@ impl Field {
|
|||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
.to_string()
|
.to_string()
|
||||||
.replace(' ', "");
|
.replace(' ', "");
|
||||||
cx.error_spanned_by(
|
let msg = format!("unknown serde field attribute `{}`", path);
|
||||||
meta_item.path(),
|
cx.error_spanned_by(meta_item.path(), msg);
|
||||||
format!("unknown serde field attribute `{}`", path),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lit(lit) => {
|
Lit(lit) => {
|
||||||
cx.error_spanned_by(lit, "unexpected literal in serde field attribute");
|
let msg = "unexpected literal in serde field attribute";
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1525,13 +1506,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
cx.error_spanned_by(
|
let msg = format!(
|
||||||
meta,
|
"malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
|
||||||
format!(
|
attr_name,
|
||||||
"malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
|
|
||||||
attr_name
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
cx.error_spanned_by(meta, msg);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1595,21 +1574,20 @@ fn get_lit_str2<'a>(
|
|||||||
if let syn::Lit::Str(lit) = lit {
|
if let syn::Lit::Str(lit) = lit {
|
||||||
Ok(lit)
|
Ok(lit)
|
||||||
} else {
|
} else {
|
||||||
cx.error_spanned_by(
|
let msg = format!(
|
||||||
lit,
|
"expected serde {} attribute to be a string: `{} = \"...\"`",
|
||||||
format!(
|
attr_name, meta_item_name,
|
||||||
"expected serde {} attribute to be a string: `{} = \"...\"`",
|
|
||||||
attr_name, meta_item_name
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lit_into_path(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Path, ()> {
|
fn parse_lit_into_path(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Path, ()> {
|
||||||
let string = get_lit_str(cx, attr_name, lit)?;
|
let string = get_lit_str(cx, attr_name, lit)?;
|
||||||
parse_lit_str(string).map_err(|_| {
|
string.parse().map_err(|_| {
|
||||||
cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
|
let msg = format!("failed to parse path: {:?}", string.value());
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1619,8 +1597,9 @@ fn parse_lit_into_expr_path(
|
|||||||
lit: &syn::Lit,
|
lit: &syn::Lit,
|
||||||
) -> Result<syn::ExprPath, ()> {
|
) -> Result<syn::ExprPath, ()> {
|
||||||
let string = get_lit_str(cx, attr_name, lit)?;
|
let string = get_lit_str(cx, attr_name, lit)?;
|
||||||
parse_lit_str(string).map_err(|_| {
|
string.parse().map_err(|_| {
|
||||||
cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
|
let msg = format!("failed to parse path: {:?}", string.value());
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1631,63 +1610,53 @@ fn parse_lit_into_where(
|
|||||||
lit: &syn::Lit,
|
lit: &syn::Lit,
|
||||||
) -> Result<Vec<syn::WherePredicate>, ()> {
|
) -> Result<Vec<syn::WherePredicate>, ()> {
|
||||||
let string = get_lit_str2(cx, attr_name, meta_item_name, lit)?;
|
let string = get_lit_str2(cx, attr_name, meta_item_name, lit)?;
|
||||||
if string.value().is_empty() {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
let where_string = syn::LitStr::new(&format!("where {}", string.value()), string.span());
|
string
|
||||||
|
.parse_with(Punctuated::<syn::WherePredicate, Token![,]>::parse_terminated)
|
||||||
parse_lit_str::<syn::WhereClause>(&where_string)
|
.map(Vec::from_iter)
|
||||||
.map(|wh| wh.predicates.into_iter().collect())
|
|
||||||
.map_err(|err| cx.error_spanned_by(lit, err))
|
.map_err(|err| cx.error_spanned_by(lit, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Type, ()> {
|
fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Type, ()> {
|
||||||
let string = get_lit_str(cx, attr_name, lit)?;
|
let string = get_lit_str(cx, attr_name, lit)?;
|
||||||
|
|
||||||
parse_lit_str(string).map_err(|_| {
|
string.parse().map_err(|_| {
|
||||||
cx.error_spanned_by(
|
let msg = format!("failed to parse type: {} = {:?}", attr_name, string.value());
|
||||||
lit,
|
cx.error_spanned_by(lit, msg);
|
||||||
format!("failed to parse type: {} = {:?}", attr_name, string.value()),
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of
|
// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of
|
||||||
// lifetimes separated by `+`.
|
// lifetimes separated by `+`.
|
||||||
fn parse_lit_into_lifetimes(
|
fn parse_lit_into_lifetimes(cx: &Ctxt, lit: &syn::Lit) -> Result<BTreeSet<syn::Lifetime>, ()> {
|
||||||
cx: &Ctxt,
|
let string = get_lit_str(cx, BORROW, lit)?;
|
||||||
attr_name: Symbol,
|
|
||||||
lit: &syn::Lit,
|
|
||||||
) -> Result<BTreeSet<syn::Lifetime>, ()> {
|
|
||||||
let string = get_lit_str(cx, attr_name, lit)?;
|
|
||||||
if string.value().is_empty() {
|
|
||||||
cx.error_spanned_by(lit, "at least one lifetime must be borrowed");
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BorrowedLifetimes(Punctuated<syn::Lifetime, Token![+]>);
|
if let Ok(lifetimes) = string.parse_with(|input: ParseStream| {
|
||||||
|
|
||||||
impl Parse for BorrowedLifetimes {
|
|
||||||
fn parse(input: ParseStream) -> parse::Result<Self> {
|
|
||||||
Punctuated::parse_separated_nonempty(input).map(BorrowedLifetimes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
|
|
||||||
let mut set = BTreeSet::new();
|
let mut set = BTreeSet::new();
|
||||||
for lifetime in lifetimes {
|
while !input.is_empty() {
|
||||||
|
let lifetime: Lifetime = input.parse()?;
|
||||||
if !set.insert(lifetime.clone()) {
|
if !set.insert(lifetime.clone()) {
|
||||||
cx.error_spanned_by(lit, format!("duplicate borrowed lifetime `{}`", lifetime));
|
let msg = format!("duplicate borrowed lifetime `{}`", lifetime);
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
}
|
}
|
||||||
|
if input.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input.parse::<Token![+]>()?;
|
||||||
}
|
}
|
||||||
return Ok(set);
|
Ok(set)
|
||||||
|
}) {
|
||||||
|
return if lifetimes.is_empty() {
|
||||||
|
let msg = "at least one lifetime must be borrowed";
|
||||||
|
cx.error_spanned_by(lit, msg);
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(lifetimes)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.error_spanned_by(
|
let msg = format!("failed to parse borrowed lifetimes: {:?}", string.value());
|
||||||
lit,
|
cx.error_spanned_by(lit, msg);
|
||||||
format!("failed to parse borrowed lifetimes: {:?}", string.value()),
|
|
||||||
);
|
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1842,10 +1811,8 @@ fn borrowable_lifetimes(
|
|||||||
let mut lifetimes = BTreeSet::new();
|
let mut lifetimes = BTreeSet::new();
|
||||||
collect_lifetimes(&field.ty, &mut lifetimes);
|
collect_lifetimes(&field.ty, &mut lifetimes);
|
||||||
if lifetimes.is_empty() {
|
if lifetimes.is_empty() {
|
||||||
cx.error_spanned_by(
|
let msg = format!("field `{}` has no lifetimes to borrow", name);
|
||||||
field,
|
cx.error_spanned_by(field, msg);
|
||||||
format!("field `{}` has no lifetimes to borrow", name),
|
|
||||||
);
|
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
Ok(lifetimes)
|
Ok(lifetimes)
|
||||||
@@ -1937,16 +1904,3 @@ fn collect_lifetimes_from_tokens(tokens: TokenStream, out: &mut BTreeSet<syn::Li
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lit_str<T>(s: &syn::LitStr) -> parse::Result<T>
|
|
||||||
where
|
|
||||||
T: Parse,
|
|
||||||
{
|
|
||||||
let tokens = spanned_tokens(s)?;
|
|
||||||
syn::parse2(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spanned_tokens(s: &syn::LitStr) -> parse::Result<TokenStream> {
|
|
||||||
let stream = syn::parse_str(&s.value())?;
|
|
||||||
Ok(respan(stream, s.span()))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub const DEFAULT: Symbol = Symbol("default");
|
|||||||
pub const DENY_UNKNOWN_FIELDS: Symbol = Symbol("deny_unknown_fields");
|
pub const DENY_UNKNOWN_FIELDS: Symbol = Symbol("deny_unknown_fields");
|
||||||
pub const DESERIALIZE: Symbol = Symbol("deserialize");
|
pub const DESERIALIZE: Symbol = Symbol("deserialize");
|
||||||
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
|
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
|
||||||
|
pub const EXPECTING: Symbol = Symbol("expecting");
|
||||||
pub const FIELD_IDENTIFIER: Symbol = Symbol("field_identifier");
|
pub const FIELD_IDENTIFIER: Symbol = Symbol("field_identifier");
|
||||||
pub const FLATTEN: Symbol = Symbol("flatten");
|
pub const FLATTEN: Symbol = Symbol("flatten");
|
||||||
pub const FROM: Symbol = Symbol("from");
|
pub const FROM: Symbol = Symbol("from");
|
||||||
@@ -22,6 +23,7 @@ pub const OTHER: Symbol = Symbol("other");
|
|||||||
pub const REMOTE: Symbol = Symbol("remote");
|
pub const REMOTE: Symbol = Symbol("remote");
|
||||||
pub const RENAME: Symbol = Symbol("rename");
|
pub const RENAME: Symbol = Symbol("rename");
|
||||||
pub const RENAME_ALL: Symbol = Symbol("rename_all");
|
pub const RENAME_ALL: Symbol = Symbol("rename_all");
|
||||||
|
pub const REPR: Symbol = Symbol("repr");
|
||||||
pub const SERDE: Symbol = Symbol("serde");
|
pub const SERDE: Symbol = Symbol("serde");
|
||||||
pub const SERIALIZE: Symbol = Symbol("serialize");
|
pub const SERIALIZE: Symbol = Symbol("serialize");
|
||||||
pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with");
|
pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with");
|
||||||
@@ -35,7 +37,6 @@ pub const TRY_FROM: Symbol = Symbol("try_from");
|
|||||||
pub const UNTAGGED: Symbol = Symbol("untagged");
|
pub const UNTAGGED: Symbol = Symbol("untagged");
|
||||||
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
|
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
|
||||||
pub const WITH: Symbol = Symbol("with");
|
pub const WITH: Symbol = Symbol("with");
|
||||||
pub const EXPECTING: Symbol = Symbol("expecting");
|
|
||||||
|
|
||||||
impl PartialEq<Symbol> for Ident {
|
impl PartialEq<Symbol> for Ident {
|
||||||
fn eq(&self, word: &Symbol) -> bool {
|
fn eq(&self, word: &Symbol) -> bool {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//!
|
//!
|
||||||
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.153")]
|
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.155")]
|
||||||
#![allow(unknown_lints, bare_trait_objects)]
|
#![allow(unknown_lints, bare_trait_objects)]
|
||||||
// Ignored clippy lints
|
// Ignored clippy lints
|
||||||
#![allow(
|
#![allow(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_test"
|
name = "serde_test"
|
||||||
version = "1.0.153" # remember to update html_root_url
|
version = "1.0.155" # remember to update html_root_url
|
||||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
categories = ["development-tools::testing"]
|
categories = ["development-tools::testing"]
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.153")]
|
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.155")]
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
|
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
|
||||||
// Ignored clippy lints
|
// Ignored clippy lints
|
||||||
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
|
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ unstable = ["serde/unstable"]
|
|||||||
serde = { path = "../serde" }
|
serde = { path = "../serde" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
automod = "1.0"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
serde = { path = "../serde", features = ["rc", "derive"] }
|
serde = { path = "../serde", features = ["rc", "derive"] }
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mod regression {
|
||||||
|
automod::dir!("tests/regression");
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Nested;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub enum ExternallyTagged {
|
||||||
|
Flatten {
|
||||||
|
#[serde(flatten)]
|
||||||
|
nested: Nested,
|
||||||
|
string: &'static str,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "tag")]
|
||||||
|
pub enum InternallyTagged {
|
||||||
|
Flatten {
|
||||||
|
#[serde(flatten)]
|
||||||
|
nested: Nested,
|
||||||
|
string: &'static str,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "tag", content = "content")]
|
||||||
|
pub enum AdjacentlyTagged {
|
||||||
|
Flatten {
|
||||||
|
#[serde(flatten)]
|
||||||
|
nested: Nested,
|
||||||
|
string: &'static str,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum UntaggedWorkaround {
|
||||||
|
Flatten {
|
||||||
|
#[serde(flatten)]
|
||||||
|
nested: Nested,
|
||||||
|
string: &'static str,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -257,6 +257,16 @@ fn test_gen() {
|
|||||||
}
|
}
|
||||||
assert::<VariantWithTraits2<X, X>>();
|
assert::<VariantWithTraits2<X, X>>();
|
||||||
|
|
||||||
|
type PhantomDataAlias<T> = PhantomData<T>;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(bound = "")]
|
||||||
|
struct PhantomDataWrapper<T> {
|
||||||
|
#[serde(default)]
|
||||||
|
field: PhantomDataAlias<T>,
|
||||||
|
}
|
||||||
|
assert::<PhantomDataWrapper<X>>();
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct CowStr<'a>(Cow<'a, str>);
|
struct CowStr<'a>(Cow<'a, str>);
|
||||||
assert::<CowStr>();
|
assert::<CowStr>();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
error: duplicate serde attribute `borrow`
|
error: duplicate serde attribute `borrow`
|
||||||
--> tests/ui/borrow/duplicate_variant.rs:8:13
|
--> tests/ui/borrow/duplicate_variant.rs:9:15
|
||||||
|
|
|
|
||||||
8 | #[serde(borrow)]
|
9 | S(#[serde(borrow)] Str<'a>),
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use serde_derive::Deserialize;
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Test<'a> {
|
struct Test<'a> {
|
||||||
#[serde(borrow = "")]
|
#[serde(borrow = "")]
|
||||||
|
r: &'a str,
|
||||||
|
#[serde(borrow = " ")]
|
||||||
s: &'a str,
|
s: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,3 +3,9 @@ error: at least one lifetime must be borrowed
|
|||||||
|
|
|
|
||||||
5 | #[serde(borrow = "")]
|
5 | #[serde(borrow = "")]
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
|
error: at least one lifetime must be borrowed
|
||||||
|
--> tests/ui/borrow/empty_lifetimes.rs:7:22
|
||||||
|
|
|
||||||
|
7 | #[serde(borrow = " ")]
|
||||||
|
| ^^^^
|
||||||
|
|||||||
Reference in New Issue
Block a user