aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2023-05-27 02:07:13 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-05-27 02:07:13 +0000
commitccbb8a9ba7033b1f13866747c98ef8d7ac9ae284 (patch)
tree2cec758f59725225ddbde7467dc290b84337bc35
parent8737b682b8f58348a0e339d54c999d0630f0ddb0 (diff)
parentac6ba0aaa61b9a635ecad8eae961792807975f74 (diff)
downloadserde_derive-ccbb8a9ba7033b1f13866747c98ef8d7ac9ae284.tar.gz
Upgrade serde_derive to 1.0.158 am: 0421e426a3 am: ec14c32e9a am: e4e1cb71ac am: 3bc8acdab5 am: ac6ba0aaa6
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/serde_derive/+/2498755 Change-Id: I83df271597353702e12a4f4c87ccac6e75e7b378 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml6
-rw-r--r--Cargo.toml.orig6
-rw-r--r--LICENSE-APACHE25
-rw-r--r--METADATA8
-rw-r--r--README.md10
-rw-r--r--src/bound.rs18
-rw-r--r--src/de.rs36
-rw-r--r--src/dummy.rs6
-rw-r--r--src/internals/attr.rs1280
-rw-r--r--src/internals/check.rs63
-rw-r--r--src/internals/receiver.rs17
-rw-r--r--src/internals/symbol.rs3
-rw-r--r--src/lib.rs3
15 files changed, 652 insertions, 835 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index d6b3c53..7978e02 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "ccf9c6fc072378ea8c4f15df1024e258d35d6e61"
+ "sha1": "e3058105f0b1a64018577b12ea19cd255644a17b"
},
"path_in_vcs": "serde_derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 1c23ba9..125c57a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -41,7 +41,7 @@ rust_proc_macro {
name: "libserde_derive",
crate_name: "serde_derive",
cargo_env_compat: true,
- cargo_pkg_version: "1.0.152",
+ cargo_pkg_version: "1.0.158",
srcs: ["src/lib.rs"],
edition: "2015",
features: ["default"],
@@ -59,7 +59,7 @@ rust_test_host {
name: "serde_derive_test_src_lib",
crate_name: "serde_derive",
cargo_env_compat: true,
- cargo_pkg_version: "1.0.152",
+ cargo_pkg_version: "1.0.158",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
diff --git a/Cargo.toml b/Cargo.toml
index 6258d9d..2b9e4df 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,9 +10,9 @@
# See Cargo.toml.orig for the original contents.
[package]
-rust-version = "1.31"
+rust-version = "1.56"
name = "serde_derive"
-version = "1.0.152"
+version = "1.0.158"
authors = [
"Erick Tryzelaar <erick.tryzelaar@gmail.com>",
"David Tolnay <dtolnay@gmail.com>",
@@ -53,7 +53,7 @@ version = "1.0"
version = "1.0"
[dependencies.syn]
-version = "1.0.104"
+version = "2.0.3"
[dev-dependencies.serde]
version = "1.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index e1366f1..1a3490a 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
-version = "1.0.152" # remember to update html_root_url
+version = "1.0.158" # remember to update html_root_url
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
categories = ["no-std"]
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
@@ -11,7 +11,7 @@ keywords = ["serde", "serialization", "no_std", "derive"]
license = "MIT OR Apache-2.0"
readme = "crates-io.md"
repository = "https://github.com/serde-rs/serde"
-rust-version = "1.31"
+rust-version = "1.56"
[features]
default = []
@@ -24,7 +24,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
-syn = "1.0.104"
+syn = "2.0.3"
[dev-dependencies]
serde = { version = "1.0", path = "../serde" }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index 16fe87b..1b5ec8b 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/METADATA b/METADATA
index a9fa596..cc6c728 100644
--- a/METADATA
+++ b/METADATA
@@ -11,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/serde_derive/serde_derive-1.0.152.crate"
+ value: "https://static.crates.io/crates/serde_derive/serde_derive-1.0.158.crate"
}
- version: "1.0.152"
+ version: "1.0.158"
license_type: NOTICE
last_upgrade_date {
year: 2023
- month: 2
- day: 6
+ month: 3
+ day: 20
}
}
diff --git a/README.md b/README.md
index c3f6575..d53e572 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.13+]][Rust 1.13] [![serde_derive: rustc 1.31+]][Rust 1.31]
+# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.19+]][Rust 1.19] [![serde_derive: rustc 1.56+]][Rust 1.56]
[Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/serde/ci.yml?branch=master
[actions]: https://github.com/serde-rs/serde/actions?query=branch%3Amaster
[Latest Version]: https://img.shields.io/crates/v/serde.svg
[crates.io]: https://crates.io/crates/serde
-[serde: rustc 1.13+]: https://img.shields.io/badge/serde-rustc_1.13+-lightgray.svg
-[serde_derive: rustc 1.31+]: https://img.shields.io/badge/serde_derive-rustc_1.31+-lightgray.svg
-[Rust 1.13]: https://blog.rust-lang.org/2016/11/10/Rust-1.13.html
-[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
+[serde: rustc 1.19+]: https://img.shields.io/badge/serde-rustc_1.19+-lightgray.svg
+[serde_derive: rustc 1.56+]: https://img.shields.io/badge/serde_derive-rustc_1.56+-lightgray.svg
+[Rust 1.19]: https://blog.rust-lang.org/2017/07/20/Rust-1.19.html
+[Rust 1.56]: https://blog.rust-lang.org/2021/10/21/Rust-1.56.0.html
**Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.**
diff --git a/src/bound.rs b/src/bound.rs
index 74c9506..7bdb046 100644
--- a/src/bound.rs
+++ b/src/bound.rs
@@ -200,10 +200,16 @@ pub fn with_bound(
for arg in &arguments.args {
match arg {
syn::GenericArgument::Type(arg) => self.visit_type(arg),
- syn::GenericArgument::Binding(arg) => self.visit_type(&arg.ty),
+ syn::GenericArgument::AssocType(arg) => self.visit_type(&arg.ty),
syn::GenericArgument::Lifetime(_)
- | syn::GenericArgument::Constraint(_)
- | syn::GenericArgument::Const(_) => {}
+ | syn::GenericArgument::Const(_)
+ | syn::GenericArgument::AssocConst(_)
+ | syn::GenericArgument::Constraint(_) => {}
+ #[cfg_attr(
+ all(test, exhaustive),
+ deny(non_exhaustive_omitted_patterns)
+ )]
+ _ => {}
}
}
}
@@ -226,7 +232,9 @@ pub fn with_bound(
fn visit_type_param_bound(&mut self, bound: &'ast syn::TypeParamBound) {
match bound {
syn::TypeParamBound::Trait(bound) => self.visit_path(&bound.path),
- syn::TypeParamBound::Lifetime(_) => {}
+ syn::TypeParamBound::Lifetime(_) | syn::TypeParamBound::Verbatim(_) => {}
+ #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ _ => {}
}
}
@@ -334,7 +342,7 @@ pub fn with_self_bound(
pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
let bound = syn::Lifetime::new(lifetime, Span::call_site());
- let def = syn::LifetimeDef {
+ let def = syn::LifetimeParam {
attrs: Vec::new(),
lifetime: bound.clone(),
colon_token: None,
diff --git a/src/de.rs b/src/de.rs
index a703ada..d4238f1 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -244,9 +244,9 @@ impl BorrowedLifetimes {
}
}
- fn de_lifetime_def(&self) -> Option<syn::LifetimeDef> {
+ fn de_lifetime_param(&self) -> Option<syn::LifetimeParam> {
match self {
- BorrowedLifetimes::Borrowed(bounds) => Some(syn::LifetimeDef {
+ BorrowedLifetimes::Borrowed(bounds) => Some(syn::LifetimeParam {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'de", Span::call_site()),
colon_token: None,
@@ -954,6 +954,7 @@ fn deserialize_struct(
lifetime: _serde::__private::PhantomData,
}
};
+ let need_seed = deserializer.is_none();
let dispatch = if let Some(deserializer) = deserializer {
quote! {
_serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
@@ -999,14 +1000,14 @@ fn deserialize_struct(
_ => None,
};
- let visitor_seed = if is_enum && cattrs.has_flatten() {
+ let visitor_seed = if need_seed && is_enum && cattrs.has_flatten() {
Some(quote! {
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this_type #ty_generics;
fn deserialize<__D>(self, __deserializer: __D) -> _serde::__private::Result<Self::Value, __D::Error>
where
- __D: _serde::Deserializer<'de>,
+ __D: _serde::Deserializer<#delife>,
{
_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
// all variants have `#[serde(skip_deserializing)]`.
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);
// _serde::__private::Err(__err)
_serde::__private::Result::map(
@@ -2400,7 +2401,10 @@ fn deserialize_struct_as_struct_visitor(
.collect();
let fields_stmt = {
- let field_names = field_names_idents.iter().map(|(name, _, _)| name);
+ let field_names = field_names_idents
+ .iter()
+ .flat_map(|(_, _, aliases)| aliases);
+
quote_block! {
const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
}
@@ -2535,7 +2539,7 @@ fn deserialize_map(
let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
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));
_serde::__private::Option::map(
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
@@ -2768,7 +2772,7 @@ fn deserialize_map_in_place(
let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
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));
_serde::__private::Option::map(
try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
@@ -3007,7 +3011,7 @@ struct InPlaceImplGenerics<'a>(&'a Parameters);
impl<'a> ToTokens for DeImplGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let mut generics = self.0.generics.clone();
- if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
+ if let Some(de_lifetime) = self.0.borrowed.de_lifetime_param() {
generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
.into_iter()
.chain(generics.params)
@@ -3042,7 +3046,7 @@ impl<'a> ToTokens for InPlaceImplGenerics<'a> {
.into_iter()
.chain(generics.params)
.collect();
- if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
+ if let Some(de_lifetime) = self.0.borrowed.de_lifetime_param() {
generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
.into_iter()
.chain(generics.params)
@@ -3067,8 +3071,8 @@ struct InPlaceTypeGenerics<'a>(&'a Parameters);
impl<'a> ToTokens for DeTypeGenerics<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let mut generics = self.0.generics.clone();
- if self.0.borrowed.de_lifetime_def().is_some() {
- let def = syn::LifetimeDef {
+ if self.0.borrowed.de_lifetime_param().is_some() {
+ let def = syn::LifetimeParam {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'de", Span::call_site()),
colon_token: None,
@@ -3093,8 +3097,8 @@ impl<'a> ToTokens for InPlaceTypeGenerics<'a> {
.chain(generics.params)
.collect();
- if self.0.borrowed.de_lifetime_def().is_some() {
- let def = syn::LifetimeDef {
+ if self.0.borrowed.de_lifetime_param().is_some() {
+ let def = syn::LifetimeParam {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'de", Span::call_site()),
colon_token: None,
@@ -3118,8 +3122,8 @@ impl<'a> DeTypeGenerics<'a> {
}
#[cfg(feature = "deserialize_in_place")]
-fn place_lifetime() -> syn::LifetimeDef {
- syn::LifetimeDef {
+fn place_lifetime() -> syn::LifetimeParam {
+ syn::LifetimeParam {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'place", Span::call_site()),
colon_token: None,
diff --git a/src/dummy.rs b/src/dummy.rs
index a02bd37..2be5027 100644
--- a/src/dummy.rs
+++ b/src/dummy.rs
@@ -39,10 +39,6 @@ pub fn wrap_in_const(
}
}
-#[allow(deprecated)]
fn unraw(ident: &Ident) -> String {
- // str::trim_start_matches was added in 1.30, trim_left_matches deprecated
- // 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()
+ ident.to_string().trim_start_matches("r#").to_owned()
}
diff --git a/src/internals/attr.rs b/src/internals/attr.rs
index dc53248..9875b66 100644
--- a/src/internals/attr.rs
+++ b/src/internals/attr.rs
@@ -1,16 +1,15 @@
-use internals::respan::respan;
use internals::symbol::*;
use internals::{ungroup, Ctxt};
use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::borrow::Cow;
use std::collections::BTreeSet;
+use std::iter::FromIterator;
use syn;
-use syn::parse::{self, Parse, ParseStream};
+use syn::meta::ParseNestedMeta;
+use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
-use syn::Ident;
-use syn::Meta::{List, NameValue, Path};
-use syn::NestedMeta::{Lit, Meta};
+use syn::{token, Ident, Lifetime};
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
@@ -43,8 +42,8 @@ impl<'c, T> Attr<'c, T> {
let tokens = obj.into_token_stream();
if self.value.is_some() {
- self.cx
- .error_spanned_by(tokens, format!("duplicate serde attribute `{}`", self.name));
+ let msg = format!("duplicate serde attribute `{}`", self.name);
+ self.cx.error_spanned_by(tokens, msg);
} else {
self.tokens = tokens;
self.value = Some(value);
@@ -115,16 +114,14 @@ impl<'c, T> VecAttr<'c, T> {
self.values.push(value);
}
- fn at_most_one(mut self) -> Result<Option<T>, ()> {
+ fn at_most_one(mut self) -> Option<T> {
if self.values.len() > 1 {
let dup_token = self.first_dup_tokens;
- self.cx.error_spanned_by(
- dup_token,
- format!("duplicate serde attribute `{}`", self.name),
- );
- Err(())
+ let msg = format!("duplicate serde attribute `{}`", self.name);
+ self.cx.error_spanned_by(dup_token, msg);
+ None
} else {
- Ok(self.values.pop())
+ self.values.pop()
}
}
@@ -141,12 +138,8 @@ pub struct Name {
deserialize_aliases: Vec<String>,
}
-#[allow(deprecated)]
fn unraw(ident: &Ident) -> String {
- // str::trim_start_matches was added in 1.30, trim_left_matches deprecated
- // 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()
+ ident.to_string().trim_start_matches("r#").to_owned()
}
impl Name {
@@ -309,285 +302,206 @@ impl Container {
let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING);
- for meta_item in item
- .attrs
- .iter()
- .flat_map(|attr| get_serde_meta_items(cx, attr))
- .flatten()
- {
- match &meta_item {
- // Parse `#[serde(rename = "foo")]`
- Meta(NameValue(m)) if m.path == RENAME => {
- if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
- ser_name.set(&m.path, s.value());
- de_name.set(&m.path, s.value());
- }
- }
-
- // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
- Meta(List(m)) if m.path == RENAME => {
- if let Ok((ser, de)) = get_renames(cx, &m.nested) {
- ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
- de_name.set_opt(&m.path, de.map(syn::LitStr::value));
- }
- }
+ for attr in &item.attrs {
+ if attr.path() != SERDE {
+ continue;
+ }
- // Parse `#[serde(rename_all = "foo")]`
- Meta(NameValue(m)) if m.path == RENAME_ALL => {
- if let Ok(s) = get_lit_str(cx, RENAME_ALL, &m.lit) {
- match RenameRule::from_str(&s.value()) {
- Ok(rename_rule) => {
- rename_all_ser_rule.set(&m.path, rename_rule);
- rename_all_de_rule.set(&m.path, rename_rule);
- }
- Err(err) => cx.error_spanned_by(s, err),
+ if let Err(err) = attr.parse_nested_meta(|meta| {
+ if meta.path == RENAME {
+ // #[serde(rename = "foo")]
+ // #[serde(rename(serialize = "foo", deserialize = "bar"))]
+ let (ser, de) = get_renames(cx, RENAME, &meta)?;
+ ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
+ de_name.set_opt(&meta.path, de.as_ref().map(syn::LitStr::value));
+ } else if meta.path == RENAME_ALL {
+ // #[serde(rename_all = "foo")]
+ // #[serde(rename_all(serialize = "foo", deserialize = "bar"))]
+ let one_name = meta.input.peek(Token![=]);
+ let (ser, de) = get_renames(cx, RENAME_ALL, &meta)?;
+ if let Some(ser) = ser {
+ match RenameRule::from_str(&ser.value()) {
+ Ok(rename_rule) => rename_all_ser_rule.set(&meta.path, rename_rule),
+ Err(err) => cx.error_spanned_by(ser, err),
}
}
- }
-
- // Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
- Meta(List(m)) if m.path == RENAME_ALL => {
- if let Ok((ser, de)) = get_renames(cx, &m.nested) {
- if let Some(ser) = ser {
- match RenameRule::from_str(&ser.value()) {
- Ok(rename_rule) => rename_all_ser_rule.set(&m.path, rename_rule),
- Err(err) => cx.error_spanned_by(ser, err),
- }
- }
- if let Some(de) = de {
- match RenameRule::from_str(&de.value()) {
- Ok(rename_rule) => rename_all_de_rule.set(&m.path, rename_rule),
- Err(err) => cx.error_spanned_by(de, err),
+ if let Some(de) = de {
+ match RenameRule::from_str(&de.value()) {
+ Ok(rename_rule) => rename_all_de_rule.set(&meta.path, rename_rule),
+ Err(err) => {
+ if !one_name {
+ cx.error_spanned_by(de, err);
+ }
}
}
}
- }
-
- // Parse `#[serde(transparent)]`
- Meta(Path(word)) if word == TRANSPARENT => {
- transparent.set_true(word);
- }
-
- // Parse `#[serde(deny_unknown_fields)]`
- Meta(Path(word)) if word == DENY_UNKNOWN_FIELDS => {
- deny_unknown_fields.set_true(word);
- }
-
- // Parse `#[serde(default)]`
- Meta(Path(word)) if word == DEFAULT => match &item.data {
- syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
- syn::Fields::Named(_) => {
- default.set(word, Default::Default);
- }
- syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
- fields,
- "#[serde(default)] can only be used on structs with named fields",
- ),
- },
- 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",
- ),
- },
-
- // Parse `#[serde(default = "...")]`
- Meta(NameValue(m)) if m.path == DEFAULT => {
- if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
- match &item.data {
- syn::Data::Struct(syn::DataStruct { fields, .. }) => {
- match fields {
+ } else if meta.path == TRANSPARENT {
+ // #[serde(transparent)]
+ transparent.set_true(meta.path);
+ } else if meta.path == DENY_UNKNOWN_FIELDS {
+ // #[serde(deny_unknown_fields)]
+ deny_unknown_fields.set_true(meta.path);
+ } else if meta.path == DEFAULT {
+ if meta.input.peek(Token![=]) {
+ // #[serde(default = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, DEFAULT, &meta)? {
+ match &item.data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
syn::Fields::Named(_) => {
- default.set(&m.path, Default::Path(path));
+ default.set(&meta.path, Default::Path(path));
+ }
+ 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::Fields::Unnamed(_) | syn::Fields::Unit => cx
- .error_spanned_by(
- fields,
- "#[serde(default = \"...\")] can only be used on structs with named fields",
- ),
+ },
+ 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",
- ),
+ }
+ } else {
+ // #[serde(default)]
+ match &item.data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
+ syn::Fields::Named(_) => {
+ default.set(meta.path, Default::Default);
+ }
+ 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);
+ }
}
}
- }
-
- // Parse `#[serde(bound = "T: SomeBound")]`
- Meta(NameValue(m)) if m.path == BOUND => {
- if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
- ser_bound.set(&m.path, where_predicates.clone());
- de_bound.set(&m.path, where_predicates);
- }
- }
-
- // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
- Meta(List(m)) if m.path == BOUND => {
- if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
- ser_bound.set_opt(&m.path, ser);
- de_bound.set_opt(&m.path, de);
- }
- }
-
- // Parse `#[serde(untagged)]`
- Meta(Path(word)) if word == UNTAGGED => match item.data {
- syn::Data::Enum(_) => {
- untagged.set_true(word);
- }
- syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
- cx.error_spanned_by(
- struct_token,
- "#[serde(untagged)] can only be used on enums",
- );
- }
- syn::Data::Union(syn::DataUnion { union_token, .. }) => {
- cx.error_spanned_by(
- union_token,
- "#[serde(untagged)] can only be used on enums",
- );
+ } else if meta.path == BOUND {
+ // #[serde(bound = "T: SomeBound")]
+ // #[serde(bound(serialize = "...", deserialize = "..."))]
+ let (ser, de) = get_where_predicates(cx, &meta)?;
+ ser_bound.set_opt(&meta.path, ser);
+ de_bound.set_opt(&meta.path, de);
+ } else if meta.path == UNTAGGED {
+ // #[serde(untagged)]
+ match item.data {
+ syn::Data::Enum(_) => {
+ untagged.set_true(&meta.path);
+ }
+ syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
+ let msg = "#[serde(untagged)] can only be used on enums";
+ cx.error_spanned_by(struct_token, msg);
+ }
+ syn::Data::Union(syn::DataUnion { union_token, .. }) => {
+ let msg = "#[serde(untagged)] can only be used on enums";
+ cx.error_spanned_by(union_token, msg);
+ }
}
- },
-
- // Parse `#[serde(tag = "type")]`
- Meta(NameValue(m)) if m.path == TAG => {
- if let Ok(s) = get_lit_str(cx, TAG, &m.lit) {
+ } else if meta.path == TAG {
+ // #[serde(tag = "type")]
+ if let Some(s) = get_lit_str(cx, TAG, &meta)? {
match &item.data {
syn::Data::Enum(_) => {
- internal_tag.set(&m.path, s.value());
+ internal_tag.set(&meta.path, s.value());
}
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
syn::Fields::Named(_) => {
- internal_tag.set(&m.path, s.value());
+ internal_tag.set(&meta.path, s.value());
}
syn::Fields::Unnamed(_) | syn::Fields::Unit => {
- cx.error_spanned_by(
- fields,
- "#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
- );
+ let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
+ cx.error_spanned_by(fields, msg);
}
},
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
- cx.error_spanned_by(
- union_token,
- "#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
- );
+ let msg = "#[serde(tag = \"...\")] can only be used on enums and structs with named fields";
+ cx.error_spanned_by(union_token, msg);
}
}
}
- }
-
- // Parse `#[serde(content = "c")]`
- Meta(NameValue(m)) if m.path == CONTENT => {
- if let Ok(s) = get_lit_str(cx, CONTENT, &m.lit) {
+ } else if meta.path == CONTENT {
+ // #[serde(content = "c")]
+ if let Some(s) = get_lit_str(cx, CONTENT, &meta)? {
match &item.data {
syn::Data::Enum(_) => {
- content.set(&m.path, s.value());
+ content.set(&meta.path, s.value());
}
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
- cx.error_spanned_by(
- struct_token,
- "#[serde(content = \"...\")] can only be used on enums",
- );
+ let msg = "#[serde(content = \"...\")] can only be used on enums";
+ cx.error_spanned_by(struct_token, msg);
}
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
- cx.error_spanned_by(
- union_token,
- "#[serde(content = \"...\")] can only be used on enums",
- );
+ let msg = "#[serde(content = \"...\")] can only be used on enums";
+ cx.error_spanned_by(union_token, msg);
}
}
}
- }
-
- // Parse `#[serde(from = "Type")]
- Meta(NameValue(m)) if m.path == FROM => {
- if let Ok(from_ty) = parse_lit_into_ty(cx, FROM, &m.lit) {
- type_from.set_opt(&m.path, Some(from_ty));
+ } else if meta.path == FROM {
+ // #[serde(from = "Type")]
+ if let Some(from_ty) = parse_lit_into_ty(cx, FROM, &meta)? {
+ type_from.set_opt(&meta.path, Some(from_ty));
}
- }
-
- // Parse `#[serde(try_from = "Type")]
- Meta(NameValue(m)) if m.path == TRY_FROM => {
- 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));
+ } else if meta.path == TRY_FROM {
+ // #[serde(try_from = "Type")]
+ if let Some(try_from_ty) = parse_lit_into_ty(cx, TRY_FROM, &meta)? {
+ type_try_from.set_opt(&meta.path, Some(try_from_ty));
}
- }
-
- // Parse `#[serde(into = "Type")]
- Meta(NameValue(m)) if m.path == INTO => {
- if let Ok(into_ty) = parse_lit_into_ty(cx, INTO, &m.lit) {
- type_into.set_opt(&m.path, Some(into_ty));
+ } else if meta.path == INTO {
+ // #[serde(into = "Type")]
+ if let Some(into_ty) = parse_lit_into_ty(cx, INTO, &meta)? {
+ type_into.set_opt(&meta.path, Some(into_ty));
}
- }
-
- // Parse `#[serde(remote = "...")]`
- Meta(NameValue(m)) if m.path == REMOTE => {
- if let Ok(path) = parse_lit_into_path(cx, REMOTE, &m.lit) {
+ } else if meta.path == REMOTE {
+ // #[serde(remote = "...")]
+ if let Some(path) = parse_lit_into_path(cx, REMOTE, &meta)? {
if is_primitive_path(&path, "Self") {
- remote.set(&m.path, item.ident.clone().into());
+ remote.set(&meta.path, item.ident.clone().into());
} else {
- remote.set(&m.path, path);
+ remote.set(&meta.path, path);
}
}
- }
-
- // Parse `#[serde(field_identifier)]`
- Meta(Path(word)) if word == FIELD_IDENTIFIER => {
- field_identifier.set_true(word);
- }
-
- // Parse `#[serde(variant_identifier)]`
- Meta(Path(word)) if word == VARIANT_IDENTIFIER => {
- variant_identifier.set_true(word);
- }
-
- // Parse `#[serde(crate = "foo")]`
- Meta(NameValue(m)) if m.path == CRATE => {
- if let Ok(path) = parse_lit_into_path(cx, CRATE, &m.lit) {
- serde_path.set(&m.path, path);
+ } else if meta.path == FIELD_IDENTIFIER {
+ // #[serde(field_identifier)]
+ field_identifier.set_true(&meta.path);
+ } else if meta.path == VARIANT_IDENTIFIER {
+ // #[serde(variant_identifier)]
+ variant_identifier.set_true(&meta.path);
+ } else if meta.path == CRATE {
+ // #[serde(crate = "foo")]
+ if let Some(path) = parse_lit_into_path(cx, CRATE, &meta)? {
+ serde_path.set(&meta.path, path);
}
- }
-
- // Parse `#[serde(expecting = "a message")]`
- Meta(NameValue(m)) if m.path == EXPECTING => {
- if let Ok(s) = get_lit_str(cx, EXPECTING, &m.lit) {
- expecting.set(&m.path, s.value());
+ } else if meta.path == EXPECTING {
+ // #[serde(expecting = "a message")]
+ if let Some(s) = get_lit_str(cx, EXPECTING, &meta)? {
+ expecting.set(&meta.path, s.value());
}
- }
-
- Meta(meta_item) => {
- let path = meta_item
- .path()
- .into_token_stream()
- .to_string()
- .replace(' ', "");
- cx.error_spanned_by(
- meta_item.path(),
- format!("unknown serde container attribute `{}`", path),
+ } else {
+ let path = meta.path.to_token_stream().to_string().replace(' ', "");
+ return Err(
+ meta.error(format_args!("unknown serde container attribute `{}`", path))
);
}
-
- Lit(lit) => {
- cx.error_spanned_by(lit, "unexpected literal in serde container attribute");
- }
+ Ok(())
+ }) {
+ cx.syn_error(err);
}
}
let mut is_packed = false;
for attr in &item.attrs {
- if attr.path.is_ident("repr") {
+ if attr.path() == REPR {
let _ = attr.parse_args_with(|input: ParseStream| {
while let Some(token) = input.parse()? {
if let TokenTree::Ident(ident) = token {
@@ -725,10 +639,9 @@ fn decide_tag(
syn::Fields::Named(_) | syn::Fields::Unit => {}
syn::Fields::Unnamed(fields) => {
if fields.unnamed.len() != 1 {
- cx.error_spanned_by(
- variant,
- "#[serde(tag = \"...\")] cannot be used with tuple variants",
- );
+ let msg =
+ "#[serde(tag = \"...\")] cannot be used with tuple variants";
+ cx.error_spanned_by(variant, msg);
break;
}
}
@@ -738,48 +651,28 @@ fn decide_tag(
TagType::Internal { tag }
}
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
- cx.error_spanned_by(
- untagged_tokens,
- "enum cannot be both untagged and internally tagged",
- );
- cx.error_spanned_by(
- tag_tokens,
- "enum cannot be both untagged and internally tagged",
- );
+ let msg = "enum cannot be both untagged and internally tagged";
+ cx.error_spanned_by(untagged_tokens, msg);
+ cx.error_spanned_by(tag_tokens, msg);
TagType::External // doesn't matter, will error
}
(None, None, Some((content_tokens, _))) => {
- cx.error_spanned_by(
- content_tokens,
- "#[serde(tag = \"...\", content = \"...\")] must be used together",
- );
+ let msg = "#[serde(tag = \"...\", content = \"...\")] must be used together";
+ cx.error_spanned_by(content_tokens, msg);
TagType::External
}
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
- cx.error_spanned_by(
- untagged_tokens,
- "untagged enum cannot have #[serde(content = \"...\")]",
- );
- cx.error_spanned_by(
- content_tokens,
- "untagged enum cannot have #[serde(content = \"...\")]",
- );
+ let msg = "untagged enum cannot have #[serde(content = \"...\")]";
+ cx.error_spanned_by(untagged_tokens, msg);
+ cx.error_spanned_by(content_tokens, msg);
TagType::External
}
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent { tag, content },
(Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
- cx.error_spanned_by(
- untagged_tokens,
- "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
- );
- 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 = \"...\")]",
- );
+ let msg = "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]";
+ cx.error_spanned_by(untagged_tokens, msg);
+ cx.error_spanned_by(tag_tokens, msg);
+ cx.error_spanned_by(content_tokens, msg);
TagType::External
}
}
@@ -798,44 +691,32 @@ fn decide_identifier(
) {
(_, None, None) => Identifier::No,
(_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
- cx.error_spanned_by(
- field_identifier_tokens,
- "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
- );
- cx.error_spanned_by(
- variant_identifier_tokens,
- "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
- );
+ let msg =
+ "#[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);
Identifier::No
}
(syn::Data::Enum(_), Some(_), None) => Identifier::Field,
(syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), Some(_), None) => {
- cx.error_spanned_by(
- struct_token,
- "#[serde(field_identifier)] can only be used on an enum",
- );
+ let msg = "#[serde(field_identifier)] can only be used on an enum";
+ cx.error_spanned_by(struct_token, msg);
Identifier::No
}
(syn::Data::Union(syn::DataUnion { union_token, .. }), Some(_), None) => {
- cx.error_spanned_by(
- union_token,
- "#[serde(field_identifier)] can only be used on an enum",
- );
+ let msg = "#[serde(field_identifier)] can only be used on an enum";
+ cx.error_spanned_by(union_token, msg);
Identifier::No
}
(syn::Data::Struct(syn::DataStruct { struct_token, .. }), None, Some(_)) => {
- cx.error_spanned_by(
- struct_token,
- "#[serde(variant_identifier)] can only be used on an enum",
- );
+ let msg = "#[serde(variant_identifier)] can only be used on an enum";
+ cx.error_spanned_by(struct_token, msg);
Identifier::No
}
(syn::Data::Union(syn::DataUnion { union_token, .. }), None, Some(_)) => {
- cx.error_spanned_by(
- union_token,
- "#[serde(variant_identifier)] can only be used on an enum",
- );
+ let msg = "#[serde(variant_identifier)] can only be used on an enum";
+ cx.error_spanned_by(union_token, msg);
Identifier::No
}
}
@@ -852,7 +733,12 @@ pub struct Variant {
other: bool,
serialize_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 {
@@ -871,168 +757,125 @@ impl Variant {
let mut deserialize_with = Attr::none(cx, DESERIALIZE_WITH);
let mut borrow = Attr::none(cx, BORROW);
- for meta_item in variant
- .attrs
- .iter()
- .flat_map(|attr| get_serde_meta_items(cx, attr))
- .flatten()
- {
- match &meta_item {
- // Parse `#[serde(rename = "foo")]`
- Meta(NameValue(m)) if m.path == RENAME => {
- if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
- ser_name.set(&m.path, s.value());
- de_name.set_if_none(s.value());
- de_aliases.insert(&m.path, s.value());
- }
- }
+ for attr in &variant.attrs {
+ if attr.path() != SERDE {
+ continue;
+ }
- // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
- Meta(List(m)) if m.path == RENAME => {
- if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
- ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
- for de_value in de {
- de_name.set_if_none(de_value.value());
- de_aliases.insert(&m.path, de_value.value());
- }
+ if let Err(err) = attr.parse_nested_meta(|meta| {
+ if meta.path == RENAME {
+ // #[serde(rename = "foo")]
+ // #[serde(rename(serialize = "foo", deserialize = "bar"))]
+ let (ser, de) = get_multiple_renames(cx, &meta)?;
+ ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
+ for de_value in de {
+ de_name.set_if_none(de_value.value());
+ de_aliases.insert(&meta.path, de_value.value());
}
- }
-
- // Parse `#[serde(alias = "foo")]`
- Meta(NameValue(m)) if m.path == ALIAS => {
- if let Ok(s) = get_lit_str(cx, ALIAS, &m.lit) {
- de_aliases.insert(&m.path, s.value());
+ } else if meta.path == ALIAS {
+ // #[serde(alias = "foo")]
+ if let Some(s) = get_lit_str(cx, ALIAS, &meta)? {
+ de_aliases.insert(&meta.path, s.value());
}
- }
-
- // Parse `#[serde(rename_all = "foo")]`
- Meta(NameValue(m)) if m.path == RENAME_ALL => {
- if let Ok(s) = get_lit_str(cx, RENAME_ALL, &m.lit) {
- match RenameRule::from_str(&s.value()) {
- Ok(rename_rule) => {
- rename_all_ser_rule.set(&m.path, rename_rule);
- rename_all_de_rule.set(&m.path, rename_rule);
- }
- Err(err) => cx.error_spanned_by(s, err),
+ } else if meta.path == RENAME_ALL {
+ // #[serde(rename_all = "foo")]
+ // #[serde(rename_all(serialize = "foo", deserialize = "bar"))]
+ let one_name = meta.input.peek(Token![=]);
+ let (ser, de) = get_renames(cx, RENAME_ALL, &meta)?;
+ if let Some(ser) = ser {
+ match RenameRule::from_str(&ser.value()) {
+ Ok(rename_rule) => rename_all_ser_rule.set(&meta.path, rename_rule),
+ Err(err) => cx.error_spanned_by(ser, err),
}
}
- }
-
- // Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
- Meta(List(m)) if m.path == RENAME_ALL => {
- if let Ok((ser, de)) = get_renames(cx, &m.nested) {
- if let Some(ser) = ser {
- match RenameRule::from_str(&ser.value()) {
- Ok(rename_rule) => rename_all_ser_rule.set(&m.path, rename_rule),
- Err(err) => cx.error_spanned_by(ser, err),
- }
- }
- if let Some(de) = de {
- match RenameRule::from_str(&de.value()) {
- Ok(rename_rule) => rename_all_de_rule.set(&m.path, rename_rule),
- Err(err) => cx.error_spanned_by(de, err),
+ if let Some(de) = de {
+ match RenameRule::from_str(&de.value()) {
+ Ok(rename_rule) => rename_all_de_rule.set(&meta.path, rename_rule),
+ Err(err) => {
+ if !one_name {
+ cx.error_spanned_by(de, err);
+ }
}
}
}
- }
-
- // Parse `#[serde(skip)]`
- Meta(Path(word)) if word == SKIP => {
- skip_serializing.set_true(word);
- skip_deserializing.set_true(word);
- }
-
- // Parse `#[serde(skip_deserializing)]`
- Meta(Path(word)) if word == SKIP_DESERIALIZING => {
- skip_deserializing.set_true(word);
- }
-
- // Parse `#[serde(skip_serializing)]`
- Meta(Path(word)) if word == SKIP_SERIALIZING => {
- skip_serializing.set_true(word);
- }
-
- // Parse `#[serde(other)]`
- Meta(Path(word)) if word == OTHER => {
- other.set_true(word);
- }
-
- // Parse `#[serde(bound = "T: SomeBound")]`
- Meta(NameValue(m)) if m.path == BOUND => {
- if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
- ser_bound.set(&m.path, where_predicates.clone());
- de_bound.set(&m.path, where_predicates);
- }
- }
-
- // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
- Meta(List(m)) if m.path == BOUND => {
- if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
- ser_bound.set_opt(&m.path, ser);
- de_bound.set_opt(&m.path, de);
- }
- }
-
- // Parse `#[serde(with = "...")]`
- Meta(NameValue(m)) if m.path == WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, WITH, &m.lit) {
+ } else if meta.path == SKIP {
+ // #[serde(skip)]
+ skip_serializing.set_true(&meta.path);
+ skip_deserializing.set_true(&meta.path);
+ } else if meta.path == SKIP_DESERIALIZING {
+ // #[serde(skip_deserializing)]
+ skip_deserializing.set_true(&meta.path);
+ } else if meta.path == SKIP_SERIALIZING {
+ // #[serde(skip_serializing)]
+ skip_serializing.set_true(&meta.path);
+ } else if meta.path == OTHER {
+ // #[serde(other)]
+ other.set_true(&meta.path);
+ } else if meta.path == BOUND {
+ // #[serde(bound = "T: SomeBound")]
+ // #[serde(bound(serialize = "...", deserialize = "..."))]
+ let (ser, de) = get_where_predicates(cx, &meta)?;
+ ser_bound.set_opt(&meta.path, ser);
+ de_bound.set_opt(&meta.path, de);
+ } else if meta.path == WITH {
+ // #[serde(with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, WITH, &meta)? {
let mut ser_path = path.clone();
ser_path
.path
.segments
.push(Ident::new("serialize", Span::call_site()).into());
- serialize_with.set(&m.path, ser_path);
+ serialize_with.set(&meta.path, ser_path);
let mut de_path = path;
de_path
.path
.segments
.push(Ident::new("deserialize", Span::call_site()).into());
- deserialize_with.set(&m.path, de_path);
+ deserialize_with.set(&meta.path, de_path);
}
- }
-
- // Parse `#[serde(serialize_with = "...")]`
- Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &m.lit) {
- serialize_with.set(&m.path, path);
+ } else if meta.path == SERIALIZE_WITH {
+ // #[serde(serialize_with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &meta)? {
+ serialize_with.set(&meta.path, path);
}
- }
-
- // Parse `#[serde(deserialize_with = "...")]`
- Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &m.lit) {
- deserialize_with.set(&m.path, path);
- }
- }
-
- // Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]`
- Meta(m) if m.path() == BORROW => match &variant.fields {
- syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
- borrow.set(m.path(), m.clone());
+ } else if meta.path == DESERIALIZE_WITH {
+ // #[serde(deserialize_with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &meta)? {
+ deserialize_with.set(&meta.path, path);
}
- _ => {
- cx.error_spanned_by(
- variant,
- "#[serde(borrow)] may only be used on newtype variants",
- );
+ } else if meta.path == BORROW {
+ let borrow_attribute = if meta.input.peek(Token![=]) {
+ // #[serde(borrow = "'a + 'b")]
+ let lifetimes = parse_lit_into_lifetimes(cx, &meta)?;
+ BorrowAttribute {
+ path: meta.path.clone(),
+ lifetimes: Some(lifetimes),
+ }
+ } else {
+ // #[serde(borrow)]
+ BorrowAttribute {
+ path: meta.path.clone(),
+ lifetimes: None,
+ }
+ };
+ match &variant.fields {
+ syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
+ borrow.set(&meta.path, borrow_attribute);
+ }
+ _ => {
+ let msg = "#[serde(borrow)] may only be used on newtype variants";
+ cx.error_spanned_by(variant, msg);
+ }
}
- },
-
- Meta(meta_item) => {
- let path = meta_item
- .path()
- .into_token_stream()
- .to_string()
- .replace(' ', "");
- cx.error_spanned_by(
- meta_item.path(),
- format!("unknown serde variant attribute `{}`", path),
+ } else {
+ let path = meta.path.to_token_stream().to_string().replace(' ', "");
+ return Err(
+ meta.error(format_args!("unknown serde variant attribute `{}`", path))
);
}
-
- Lit(lit) => {
- cx.error_spanned_by(lit, "unexpected literal in serde variant attribute");
- }
+ Ok(())
+ }) {
+ cx.syn_error(err);
}
}
@@ -1168,182 +1011,139 @@ impl Field {
None => index.to_string(),
};
- let variant_borrow = attrs
- .and_then(|variant| variant.borrow.as_ref())
- .map(|borrow| Meta(borrow.clone()));
-
- for meta_item in field
- .attrs
- .iter()
- .flat_map(|attr| get_serde_meta_items(cx, attr))
- .flatten()
- .chain(variant_borrow)
- {
- match &meta_item {
- // Parse `#[serde(rename = "foo")]`
- Meta(NameValue(m)) if m.path == RENAME => {
- if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
- ser_name.set(&m.path, s.value());
- de_name.set_if_none(s.value());
- de_aliases.insert(&m.path, s.value());
- }
- }
-
- // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
- Meta(List(m)) if m.path == RENAME => {
- if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
- ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
- for de_value in de {
- de_name.set_if_none(de_value.value());
- de_aliases.insert(&m.path, de_value.value());
+ if let Some(borrow_attribute) = attrs.and_then(|variant| variant.borrow.as_ref()) {
+ if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
+ 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);
}
+ }
+ }
- // Parse `#[serde(alias = "foo")]`
- Meta(NameValue(m)) if m.path == ALIAS => {
- if let Ok(s) = get_lit_str(cx, ALIAS, &m.lit) {
- de_aliases.insert(&m.path, s.value());
- }
- }
-
- // Parse `#[serde(default)]`
- Meta(Path(word)) if word == DEFAULT => {
- default.set(word, Default::Default);
- }
+ for attr in &field.attrs {
+ if attr.path() != SERDE {
+ continue;
+ }
- // Parse `#[serde(default = "...")]`
- Meta(NameValue(m)) if m.path == DEFAULT => {
- if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
- default.set(&m.path, Default::Path(path));
+ if let Err(err) = attr.parse_nested_meta(|meta| {
+ if meta.path == RENAME {
+ // #[serde(rename = "foo")]
+ // #[serde(rename(serialize = "foo", deserialize = "bar"))]
+ let (ser, de) = get_multiple_renames(cx, &meta)?;
+ ser_name.set_opt(&meta.path, ser.as_ref().map(syn::LitStr::value));
+ for de_value in de {
+ de_name.set_if_none(de_value.value());
+ de_aliases.insert(&meta.path, de_value.value());
}
- }
-
- // Parse `#[serde(skip_serializing)]`
- Meta(Path(word)) if word == SKIP_SERIALIZING => {
- skip_serializing.set_true(word);
- }
-
- // Parse `#[serde(skip_deserializing)]`
- Meta(Path(word)) if word == SKIP_DESERIALIZING => {
- skip_deserializing.set_true(word);
- }
-
- // Parse `#[serde(skip)]`
- Meta(Path(word)) if word == SKIP => {
- skip_serializing.set_true(word);
- skip_deserializing.set_true(word);
- }
-
- // Parse `#[serde(skip_serializing_if = "...")]`
- Meta(NameValue(m)) if m.path == SKIP_SERIALIZING_IF => {
- if let Ok(path) = parse_lit_into_expr_path(cx, SKIP_SERIALIZING_IF, &m.lit) {
- skip_serializing_if.set(&m.path, path);
+ } else if meta.path == ALIAS {
+ // #[serde(alias = "foo")]
+ if let Some(s) = get_lit_str(cx, ALIAS, &meta)? {
+ de_aliases.insert(&meta.path, s.value());
}
- }
-
- // Parse `#[serde(serialize_with = "...")]`
- Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &m.lit) {
- serialize_with.set(&m.path, path);
+ } else if meta.path == DEFAULT {
+ if meta.input.peek(Token![=]) {
+ // #[serde(default = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, DEFAULT, &meta)? {
+ default.set(&meta.path, Default::Path(path));
+ }
+ } else {
+ // #[serde(default)]
+ default.set(&meta.path, Default::Default);
}
- }
-
- // Parse `#[serde(deserialize_with = "...")]`
- Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &m.lit) {
- deserialize_with.set(&m.path, path);
+ } else if meta.path == SKIP_SERIALIZING {
+ // #[serde(skip_serializing)]
+ skip_serializing.set_true(&meta.path);
+ } else if meta.path == SKIP_DESERIALIZING {
+ // #[serde(skip_deserializing)]
+ skip_deserializing.set_true(&meta.path);
+ } else if meta.path == SKIP {
+ // #[serde(skip)]
+ skip_serializing.set_true(&meta.path);
+ skip_deserializing.set_true(&meta.path);
+ } else if meta.path == SKIP_SERIALIZING_IF {
+ // #[serde(skip_serializing_if = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, SKIP_SERIALIZING_IF, &meta)? {
+ skip_serializing_if.set(&meta.path, path);
}
- }
-
- // Parse `#[serde(with = "...")]`
- Meta(NameValue(m)) if m.path == WITH => {
- if let Ok(path) = parse_lit_into_expr_path(cx, WITH, &m.lit) {
+ } else if meta.path == SERIALIZE_WITH {
+ // #[serde(serialize_with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &meta)? {
+ serialize_with.set(&meta.path, path);
+ }
+ } else if meta.path == DESERIALIZE_WITH {
+ // #[serde(deserialize_with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &meta)? {
+ deserialize_with.set(&meta.path, path);
+ }
+ } else if meta.path == WITH {
+ // #[serde(with = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, WITH, &meta)? {
let mut ser_path = path.clone();
ser_path
.path
.segments
.push(Ident::new("serialize", Span::call_site()).into());
- serialize_with.set(&m.path, ser_path);
+ serialize_with.set(&meta.path, ser_path);
let mut de_path = path;
de_path
.path
.segments
.push(Ident::new("deserialize", Span::call_site()).into());
- deserialize_with.set(&m.path, de_path);
- }
- }
-
- // Parse `#[serde(bound = "T: SomeBound")]`
- Meta(NameValue(m)) if m.path == BOUND => {
- if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
- ser_bound.set(&m.path, where_predicates.clone());
- de_bound.set(&m.path, where_predicates);
- }
- }
-
- // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
- Meta(List(m)) if m.path == BOUND => {
- if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
- ser_bound.set_opt(&m.path, ser);
- de_bound.set_opt(&m.path, de);
+ deserialize_with.set(&meta.path, de_path);
}
- }
-
- // Parse `#[serde(borrow)]`
- Meta(Path(word)) if word == BORROW => {
- if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
- borrowed_lifetimes.set(word, borrowable);
- }
- }
-
- // Parse `#[serde(borrow = "'a + 'b")]`
- Meta(NameValue(m)) if m.path == BORROW => {
- if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, BORROW, &m.lit) {
+ } else if meta.path == BOUND {
+ // #[serde(bound = "T: SomeBound")]
+ // #[serde(bound(serialize = "...", deserialize = "..."))]
+ let (ser, de) = get_where_predicates(cx, &meta)?;
+ ser_bound.set_opt(&meta.path, ser);
+ de_bound.set_opt(&meta.path, de);
+ } else if meta.path == BORROW {
+ if meta.input.peek(Token![=]) {
+ // #[serde(borrow = "'a + 'b")]
+ let lifetimes = parse_lit_into_lifetimes(cx, &meta)?;
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
for lifetime in &lifetimes {
if !borrowable.contains(lifetime) {
- cx.error_spanned_by(
- field,
- format!(
- "field `{}` does not have lifetime {}",
- ident, lifetime
- ),
+ let msg = format!(
+ "field `{}` does not have lifetime {}",
+ ident, lifetime,
);
+ cx.error_spanned_by(field, msg);
}
}
- borrowed_lifetimes.set(&m.path, lifetimes);
+ borrowed_lifetimes.set(&meta.path, lifetimes);
+ }
+ } else {
+ // #[serde(borrow)]
+ if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
+ borrowed_lifetimes.set(&meta.path, borrowable);
}
}
- }
-
- // Parse `#[serde(getter = "...")]`
- Meta(NameValue(m)) if m.path == GETTER => {
- if let Ok(path) = parse_lit_into_expr_path(cx, GETTER, &m.lit) {
- getter.set(&m.path, path);
+ } else if meta.path == GETTER {
+ // #[serde(getter = "...")]
+ if let Some(path) = parse_lit_into_expr_path(cx, GETTER, &meta)? {
+ getter.set(&meta.path, path);
}
- }
-
- // Parse `#[serde(flatten)]`
- Meta(Path(word)) if word == FLATTEN => {
- flatten.set_true(word);
- }
-
- Meta(meta_item) => {
- let path = meta_item
- .path()
- .into_token_stream()
- .to_string()
- .replace(' ', "");
- cx.error_spanned_by(
- meta_item.path(),
- format!("unknown serde field attribute `{}`", path),
+ } else if meta.path == FLATTEN {
+ // #[serde(flatten)]
+ flatten.set_true(&meta.path);
+ } else {
+ let path = meta.path.to_token_stream().to_string().replace(' ', "");
+ return Err(
+ meta.error(format_args!("unknown serde field attribute `{}`", path))
);
}
-
- Lit(lit) => {
- cx.error_spanned_by(lit, "unexpected literal in serde field attribute");
- }
+ Ok(())
+ }) {
+ cx.syn_error(err);
}
}
@@ -1497,130 +1297,154 @@ impl Field {
type SerAndDe<T> = (Option<T>, Option<T>);
-fn get_ser_and_de<'a, 'b, T, F>(
- cx: &'b Ctxt,
+fn get_ser_and_de<'c, T, F, R>(
+ cx: &'c Ctxt,
attr_name: Symbol,
- metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
+ meta: &ParseNestedMeta,
f: F,
-) -> Result<(VecAttr<'b, T>, VecAttr<'b, T>), ()>
+) -> syn::Result<(VecAttr<'c, T>, VecAttr<'c, T>)>
where
- T: 'a,
- F: Fn(&Ctxt, Symbol, Symbol, &'a syn::Lit) -> Result<T, ()>,
+ T: Clone,
+ F: Fn(&Ctxt, Symbol, Symbol, &ParseNestedMeta) -> syn::Result<R>,
+ R: Into<Option<T>>,
{
let mut ser_meta = VecAttr::none(cx, attr_name);
let mut de_meta = VecAttr::none(cx, attr_name);
- for meta in metas {
- match meta {
- Meta(NameValue(meta)) if meta.path == SERIALIZE => {
- if let Ok(v) = f(cx, attr_name, SERIALIZE, &meta.lit) {
+ let lookahead = meta.input.lookahead1();
+ if lookahead.peek(Token![=]) {
+ if let Some(both) = f(cx, attr_name, attr_name, meta)?.into() {
+ ser_meta.insert(&meta.path, both.clone());
+ de_meta.insert(&meta.path, both);
+ }
+ } else if lookahead.peek(token::Paren) {
+ meta.parse_nested_meta(|meta| {
+ if meta.path == SERIALIZE {
+ if let Some(v) = f(cx, attr_name, SERIALIZE, &meta)?.into() {
ser_meta.insert(&meta.path, v);
}
- }
-
- Meta(NameValue(meta)) if meta.path == DESERIALIZE => {
- if let Ok(v) = f(cx, attr_name, DESERIALIZE, &meta.lit) {
+ } else if meta.path == DESERIALIZE {
+ if let Some(v) = f(cx, attr_name, DESERIALIZE, &meta)?.into() {
de_meta.insert(&meta.path, v);
}
+ } else {
+ return Err(meta.error(format_args!(
+ "malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
+ attr_name,
+ )));
}
-
- _ => {
- cx.error_spanned_by(
- meta,
- format!(
- "malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
- attr_name
- ),
- );
- return Err(());
- }
- }
+ Ok(())
+ })?;
+ } else {
+ return Err(lookahead.error());
}
Ok((ser_meta, de_meta))
}
-fn get_renames<'a>(
+fn get_renames(
cx: &Ctxt,
- items: &'a Punctuated<syn::NestedMeta, Token![,]>,
-) -> Result<SerAndDe<&'a syn::LitStr>, ()> {
- let (ser, de) = get_ser_and_de(cx, RENAME, items, get_lit_str2)?;
- Ok((ser.at_most_one()?, de.at_most_one()?))
+ attr_name: Symbol,
+ meta: &ParseNestedMeta,
+) -> syn::Result<SerAndDe<syn::LitStr>> {
+ let (ser, de) = get_ser_and_de(cx, attr_name, meta, get_lit_str2)?;
+ Ok((ser.at_most_one(), de.at_most_one()))
}
-fn get_multiple_renames<'a>(
+fn get_multiple_renames(
cx: &Ctxt,
- items: &'a Punctuated<syn::NestedMeta, Token![,]>,
-) -> Result<(Option<&'a syn::LitStr>, Vec<&'a syn::LitStr>), ()> {
- let (ser, de) = get_ser_and_de(cx, RENAME, items, get_lit_str2)?;
- Ok((ser.at_most_one()?, de.get()))
+ meta: &ParseNestedMeta,
+) -> syn::Result<(Option<syn::LitStr>, Vec<syn::LitStr>)> {
+ let (ser, de) = get_ser_and_de(cx, RENAME, meta, get_lit_str2)?;
+ Ok((ser.at_most_one(), de.get()))
}
fn get_where_predicates(
cx: &Ctxt,
- items: &Punctuated<syn::NestedMeta, Token![,]>,
-) -> Result<SerAndDe<Vec<syn::WherePredicate>>, ()> {
- let (ser, de) = get_ser_and_de(cx, BOUND, items, parse_lit_into_where)?;
- Ok((ser.at_most_one()?, de.at_most_one()?))
-}
-
-pub fn get_serde_meta_items(cx: &Ctxt, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
- if attr.path != SERDE {
- return Ok(Vec::new());
- }
-
- match attr.parse_meta() {
- Ok(List(meta)) => Ok(meta.nested.into_iter().collect()),
- Ok(other) => {
- cx.error_spanned_by(other, "expected #[serde(...)]");
- Err(())
- }
- Err(err) => {
- cx.syn_error(err);
- Err(())
- }
- }
+ meta: &ParseNestedMeta,
+) -> syn::Result<SerAndDe<Vec<syn::WherePredicate>>> {
+ let (ser, de) = get_ser_and_de(cx, BOUND, meta, parse_lit_into_where)?;
+ Ok((ser.at_most_one(), de.at_most_one()))
}
-fn get_lit_str<'a>(cx: &Ctxt, attr_name: Symbol, lit: &'a syn::Lit) -> Result<&'a syn::LitStr, ()> {
- get_lit_str2(cx, attr_name, attr_name, lit)
+fn get_lit_str(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ meta: &ParseNestedMeta,
+) -> syn::Result<Option<syn::LitStr>> {
+ get_lit_str2(cx, attr_name, attr_name, meta)
}
-fn get_lit_str2<'a>(
+fn get_lit_str2(
cx: &Ctxt,
attr_name: Symbol,
meta_item_name: Symbol,
- lit: &'a syn::Lit,
-) -> Result<&'a syn::LitStr, ()> {
- if let syn::Lit::Str(lit) = lit {
- Ok(lit)
+ meta: &ParseNestedMeta,
+) -> syn::Result<Option<syn::LitStr>> {
+ let expr: syn::Expr = meta.value()?.parse()?;
+ let mut value = &expr;
+ while let syn::Expr::Group(e) = value {
+ value = &e.expr;
+ }
+ if let syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Str(lit),
+ ..
+ }) = value
+ {
+ Ok(Some(lit.clone()))
} else {
cx.error_spanned_by(
- lit,
+ expr,
format!(
"expected serde {} attribute to be a string: `{} = \"...\"`",
attr_name, meta_item_name
),
);
- Err(())
+ Ok(None)
}
}
-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)?;
- parse_lit_str(string).map_err(|_| {
- cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
+fn parse_lit_into_path(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ meta: &ParseNestedMeta,
+) -> syn::Result<Option<syn::Path>> {
+ let string = match get_lit_str(cx, attr_name, meta)? {
+ Some(string) => string,
+ None => return Ok(None),
+ };
+
+ Ok(match string.parse() {
+ Ok(path) => Some(path),
+ Err(_) => {
+ cx.error_spanned_by(
+ &string,
+ format!("failed to parse path: {:?}", string.value()),
+ );
+ None
+ }
})
}
fn parse_lit_into_expr_path(
cx: &Ctxt,
attr_name: Symbol,
- lit: &syn::Lit,
-) -> Result<syn::ExprPath, ()> {
- let string = get_lit_str(cx, attr_name, lit)?;
- parse_lit_str(string).map_err(|_| {
- cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()));
+ meta: &ParseNestedMeta,
+) -> syn::Result<Option<syn::ExprPath>> {
+ let string = match get_lit_str(cx, attr_name, meta)? {
+ Some(string) => string,
+ None => return Ok(None),
+ };
+
+ Ok(match string.parse() {
+ Ok(expr) => Some(expr),
+ Err(_) => {
+ cx.error_spanned_by(
+ &string,
+ format!("failed to parse path: {:?}", string.value()),
+ );
+ None
+ }
})
}
@@ -1628,28 +1452,43 @@ fn parse_lit_into_where(
cx: &Ctxt,
attr_name: Symbol,
meta_item_name: Symbol,
- lit: &syn::Lit,
-) -> Result<Vec<syn::WherePredicate>, ()> {
- 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());
+ meta: &ParseNestedMeta,
+) -> syn::Result<Vec<syn::WherePredicate>> {
+ let string = match get_lit_str2(cx, attr_name, meta_item_name, meta)? {
+ Some(string) => string,
+ None => return Ok(Vec::new()),
+ };
- parse_lit_str::<syn::WhereClause>(&where_string)
- .map(|wh| wh.predicates.into_iter().collect())
- .map_err(|err| cx.error_spanned_by(lit, err))
+ Ok(
+ match string.parse_with(Punctuated::<syn::WherePredicate, Token![,]>::parse_terminated) {
+ Ok(predicates) => Vec::from_iter(predicates),
+ Err(err) => {
+ cx.error_spanned_by(string, err);
+ Vec::new()
+ }
+ },
+ )
}
-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)?;
+fn parse_lit_into_ty(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ meta: &ParseNestedMeta,
+) -> syn::Result<Option<syn::Type>> {
+ let string = match get_lit_str(cx, attr_name, meta)? {
+ Some(string) => string,
+ None => return Ok(None),
+ };
- parse_lit_str(string).map_err(|_| {
- cx.error_spanned_by(
- lit,
- format!("failed to parse type: {} = {:?}", attr_name, string.value()),
- );
+ Ok(match string.parse() {
+ Ok(ty) => Some(ty),
+ Err(_) => {
+ cx.error_spanned_by(
+ &string,
+ format!("failed to parse type: {} = {:?}", attr_name, string.value()),
+ );
+ None
+ }
})
}
@@ -1657,38 +1496,41 @@ fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn
// lifetimes separated by `+`.
fn parse_lit_into_lifetimes(
cx: &Ctxt,
- 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![+]>);
-
- impl Parse for BorrowedLifetimes {
- fn parse(input: ParseStream) -> parse::Result<Self> {
- Punctuated::parse_separated_nonempty(input).map(BorrowedLifetimes)
- }
- }
+ meta: &ParseNestedMeta,
+) -> syn::Result<BTreeSet<syn::Lifetime>> {
+ let string = match get_lit_str(cx, BORROW, meta)? {
+ Some(string) => string,
+ None => return Ok(BTreeSet::new()),
+ };
- if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
+ if let Ok(lifetimes) = string.parse_with(|input: ParseStream| {
let mut set = BTreeSet::new();
- for lifetime in lifetimes {
+ while !input.is_empty() {
+ let lifetime: Lifetime = input.parse()?;
if !set.insert(lifetime.clone()) {
- cx.error_spanned_by(lit, format!("duplicate borrowed lifetime `{}`", lifetime));
+ cx.error_spanned_by(
+ &string,
+ format!("duplicate borrowed lifetime `{}`", lifetime),
+ );
}
+ if input.is_empty() {
+ break;
+ }
+ input.parse::<Token![+]>()?;
+ }
+ Ok(set)
+ }) {
+ if lifetimes.is_empty() {
+ cx.error_spanned_by(string, "at least one lifetime must be borrowed");
}
- return Ok(set);
+ return Ok(lifetimes);
}
cx.error_spanned_by(
- lit,
+ &string,
format!("failed to parse borrowed lifetimes: {:?}", string.value()),
);
- Err(())
+ Ok(BTreeSet::new())
}
fn is_implicitly_borrowed(ty: &syn::Type) -> bool {
@@ -1842,10 +1684,8 @@ fn borrowable_lifetimes(
let mut lifetimes = BTreeSet::new();
collect_lifetimes(&field.ty, &mut lifetimes);
if lifetimes.is_empty() {
- cx.error_spanned_by(
- field,
- format!("field `{}` has no lifetimes to borrow", name),
- );
+ let msg = format!("field `{}` has no lifetimes to borrow", name);
+ cx.error_spanned_by(field, msg);
Err(())
} else {
Ok(lifetimes)
@@ -1886,11 +1726,10 @@ fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet<syn::Lifetime>) {
syn::GenericArgument::Type(ty) => {
collect_lifetimes(ty, out);
}
- syn::GenericArgument::Binding(binding) => {
+ syn::GenericArgument::AssocType(binding) => {
collect_lifetimes(&binding.ty, out);
}
- syn::GenericArgument::Constraint(_)
- | syn::GenericArgument::Const(_) => {}
+ _ => {}
}
}
}
@@ -1937,16 +1776,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()))
-}
diff --git a/src/internals/check.rs b/src/internals/check.rs
index eb1297a..05b4b8f 100644
--- a/src/internals/check.rs
+++ b/src/internals/check.rs
@@ -3,8 +3,8 @@ use internals::attr::{Identifier, TagType};
use internals::{ungroup, Ctxt, Derive};
use syn::{Member, Type};
-/// Cross-cutting checks that require looking at more than a single attrs
-/// object. Simpler checks should happen when parsing and building the attrs.
+// Cross-cutting checks that require looking at more than a single attrs object.
+// Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
check_remote_generic(cx, cont);
check_getter(cx, cont);
@@ -17,18 +17,18 @@ pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
check_from_and_try_from(cx, cont);
}
-/// Remote derive definition type must have either all of the generics of the
-/// remote type:
-///
-/// #[serde(remote = "Generic")]
-/// struct Generic<T> {…}
-///
-/// or none of them, i.e. defining impls for one concrete instantiation of the
-/// remote type only:
-///
-/// #[serde(remote = "Generic<T>")]
-/// struct ConcreteDef {…}
-///
+// Remote derive definition type must have either all of the generics of the
+// remote type:
+//
+// #[serde(remote = "Generic")]
+// struct Generic<T> {…}
+//
+// or none of them, i.e. defining impls for one concrete instantiation of the
+// remote type only:
+//
+// #[serde(remote = "Generic<T>")]
+// struct ConcreteDef {…}
+//
fn check_remote_generic(cx: &Ctxt, cont: &Container) {
if let Some(remote) = cont.attrs.remote() {
let local_has_generic = !cont.generics.params.is_empty();
@@ -39,8 +39,8 @@ fn check_remote_generic(cx: &Ctxt, cont: &Container) {
}
}
-/// Getters are only allowed inside structs (not enums) with the `remote`
-/// attribute.
+// Getters are only allowed inside structs (not enums) with the `remote`
+// attribute.
fn check_getter(cx: &Ctxt, cont: &Container) {
match cont.data {
Data::Enum(_) => {
@@ -62,7 +62,7 @@ fn check_getter(cx: &Ctxt, cont: &Container) {
}
}
-/// Flattening has some restrictions we can test.
+// Flattening has some restrictions we can test.
fn check_flatten(cx: &Ctxt, cont: &Container) {
match &cont.data {
Data::Enum(variants) => {
@@ -101,12 +101,12 @@ fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
}
}
-/// The `other` attribute must be used at most once and it must be the last
-/// variant of an enum.
-///
-/// Inside a `variant_identifier` all variants must be unit variants. Inside a
-/// `field_identifier` all but possibly one variant must be unit variants. The
-/// last variant may be a newtype variant which is an implicit "other" case.
+// The `other` attribute must be used at most once and it must be the last
+// variant of an enum.
+//
+// Inside a `variant_identifier` all variants must be unit variants. Inside a
+// `field_identifier` all but possibly one variant must be unit variants. The
+// last variant may be a newtype variant which is an implicit "other" case.
fn check_identifier(cx: &Ctxt, cont: &Container) {
let variants = match &cont.data {
Data::Enum(variants) => variants,
@@ -189,8 +189,8 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
}
}
-/// Skip-(de)serializing attributes are not allowed on variants marked
-/// (de)serialize_with.
+// Skip-(de)serializing attributes are not allowed on variants marked
+// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
let variants = match &cont.data {
Data::Enum(variants) => variants,
@@ -264,10 +264,9 @@ fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
}
}
-/// The tag of an internally-tagged struct variant must not be
-/// the same as either one of its fields, as this would result in
-/// duplicate keys in the serialized output and/or ambiguity in
-/// the to-be-deserialized input.
+// The tag of an internally-tagged struct variant must not be the same as either
+// one of its fields, as this would result in duplicate keys in the serialized
+// output and/or ambiguity in the to-be-deserialized input.
fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
let variants = match &cont.data {
Data::Enum(variants) => variants,
@@ -313,8 +312,8 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
}
}
-/// In the case of adjacently-tagged enums, the type and the
-/// contents tag must differ, for the same reason.
+// In the case of adjacently-tagged enums, the type and the contents tag must
+// differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
let (type_tag, content_tag) = match cont.attrs.tag() {
TagType::Adjacent { tag, content } => (tag, content),
@@ -332,7 +331,7 @@ fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
}
}
-/// Enums and unit structs cannot be transparent.
+// Enums and unit structs cannot be transparent.
fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
if !cont.attrs.transparent() {
return;
diff --git a/src/internals/receiver.rs b/src/internals/receiver.rs
index b08c670..5dc01db 100644
--- a/src/internals/receiver.rs
+++ b/src/internals/receiver.rs
@@ -179,10 +179,13 @@ impl ReplaceReceiver<'_> {
for arg in &mut arguments.args {
match arg {
GenericArgument::Type(arg) => self.visit_type_mut(arg),
- GenericArgument::Binding(arg) => self.visit_type_mut(&mut arg.ty),
+ GenericArgument::AssocType(arg) => self.visit_type_mut(&mut arg.ty),
GenericArgument::Lifetime(_)
- | GenericArgument::Constraint(_)
- | GenericArgument::Const(_) => {}
+ | GenericArgument::Const(_)
+ | GenericArgument::AssocConst(_)
+ | GenericArgument::Constraint(_) => {}
+ #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ _ => {}
}
}
}
@@ -205,7 +208,9 @@ impl ReplaceReceiver<'_> {
fn visit_type_param_bound_mut(&mut self, bound: &mut TypeParamBound) {
match bound {
TypeParamBound::Trait(bound) => self.visit_path_mut(&mut bound.path),
- TypeParamBound::Lifetime(_) => {}
+ TypeParamBound::Lifetime(_) | TypeParamBound::Verbatim(_) => {}
+ #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ _ => {}
}
}
@@ -229,7 +234,9 @@ impl ReplaceReceiver<'_> {
self.visit_type_param_bound_mut(bound);
}
}
- WherePredicate::Lifetime(_) | WherePredicate::Eq(_) => {}
+ WherePredicate::Lifetime(_) => {}
+ #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ _ => {}
}
}
}
diff --git a/src/internals/symbol.rs b/src/internals/symbol.rs
index 1fedd27..9606edb 100644
--- a/src/internals/symbol.rs
+++ b/src/internals/symbol.rs
@@ -13,6 +13,7 @@ pub const DEFAULT: Symbol = Symbol("default");
pub const DENY_UNKNOWN_FIELDS: Symbol = Symbol("deny_unknown_fields");
pub const DESERIALIZE: Symbol = Symbol("deserialize");
pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
+pub const EXPECTING: Symbol = Symbol("expecting");
pub const FIELD_IDENTIFIER: Symbol = Symbol("field_identifier");
pub const FLATTEN: Symbol = Symbol("flatten");
pub const FROM: Symbol = Symbol("from");
@@ -22,6 +23,7 @@ pub const OTHER: Symbol = Symbol("other");
pub const REMOTE: Symbol = Symbol("remote");
pub const RENAME: Symbol = Symbol("rename");
pub const RENAME_ALL: Symbol = Symbol("rename_all");
+pub const REPR: Symbol = Symbol("repr");
pub const SERDE: Symbol = Symbol("serde");
pub const SERIALIZE: Symbol = Symbol("serialize");
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 VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
pub const WITH: Symbol = Symbol("with");
-pub const EXPECTING: Symbol = Symbol("expecting");
impl PartialEq<Symbol> for Ident {
fn eq(&self, word: &Symbol) -> bool {
diff --git a/src/lib.rs b/src/lib.rs
index 4100789..eda4acb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,7 +13,7 @@
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
-#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.152")]
+#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.158")]
#![allow(unknown_lints, bare_trait_objects)]
// Ignored clippy lints
#![allow(
@@ -43,6 +43,7 @@
clippy::enum_glob_use,
clippy::indexing_slicing,
clippy::items_after_statements,
+ clippy::let_underscore_untyped,
clippy::manual_assert,
clippy::map_err_ignore,
clippy::match_same_arms,