diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 4967e35d1..4806a6e5f 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1,5 +1,6 @@ use crate::fragment::{Expr, Fragment, Match, Stmts}; use crate::internals::ast::{Container, Data, Field, Style, Variant}; +use crate::internals::deprecated::allow_deprecated; use crate::internals::name::Name; use crate::internals::{attr, replace_receiver, ungroup, Ctxt, Derive}; use crate::{bound, dummy, pretend, this}; @@ -22,6 +23,8 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result syn::Result(__deserializer: __D) -> #serde::__private::Result<#remote #ty_generics, __D::Error> where @@ -49,6 +53,7 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result for #ident #ty_generics #where_clause { fn deserialize<__D>(__deserializer: __D) -> #serde::__private::Result where diff --git a/serde_derive/src/internals/deprecated.rs b/serde_derive/src/internals/deprecated.rs new file mode 100644 index 000000000..f3fed95a9 --- /dev/null +++ b/serde_derive/src/internals/deprecated.rs @@ -0,0 +1,58 @@ +use proc_macro2::TokenStream; +use quote::quote; + +pub fn allow_deprecated(input: &syn::DeriveInput) -> syn::Result { + if should_allow_deprecated(input)? { + Ok(quote! { #[allow(deprecated)]}) + } else { + Ok(TokenStream::default()) + } +} + +/// Determine if an `#[allow(deprecated)]` should be added to the derived impl. +/// +/// This should happen if the derive input or a variant of the enum (if derive input is an enum) +/// has on of: +/// - `#[deprecated]` +/// - `#[allow(deprecated)]` +fn should_allow_deprecated(input: &syn::DeriveInput) -> syn::Result { + if contains_deprecated_attrs(&input.attrs)? { + return Ok(true); + } + if let syn::Data::Enum(data_enum) = &input.data { + for variant in &data_enum.variants { + if contains_deprecated_attrs(&variant.attrs)? { + return Ok(true); + } + } + } + Ok(false) +} + +/// Check whether a set of attributes contains one of: +/// - `#[deprecated]` +/// - `#[allow(deprecated)]` +fn contains_deprecated_attrs(attrs: &[syn::Attribute]) -> syn::Result { + for attr in attrs { + if let syn::Meta::Path(path) = &attr.meta { + if path.is_ident("deprecated") { + return Ok(true); + } + } + if let syn::Meta::List(meta_list) = &attr.meta { + if meta_list.path.is_ident("allow") { + let mut deprecated_allowed = false; + meta_list.parse_nested_meta(|meta| { + if meta.path.is_ident("deprecated") { + deprecated_allowed = true; + } + Ok(()) + })?; + if deprecated_allowed { + return Ok(true); + } + } + } + } + Ok(false) +} diff --git a/serde_derive/src/internals/mod.rs b/serde_derive/src/internals/mod.rs index cd1e81052..d4e993449 100644 --- a/serde_derive/src/internals/mod.rs +++ b/serde_derive/src/internals/mod.rs @@ -1,5 +1,6 @@ pub mod ast; pub mod attr; +pub mod deprecated; pub mod name; mod case; diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 46be736c4..664c80ec9 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -1,5 +1,6 @@ use crate::fragment::{Fragment, Match, Stmts}; use crate::internals::ast::{Container, Data, Field, Style, Variant}; +use crate::internals::deprecated::allow_deprecated; use crate::internals::name::Name; use crate::internals::{attr, replace_receiver, Ctxt, Derive}; use crate::{bound, dummy, pretend, this}; @@ -18,6 +19,7 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result syn::Result(__self: &#remote #ty_generics, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> where @@ -43,6 +46,7 @@ pub fn expand_derive_serialize(input: &mut syn::DeriveInput) -> syn::Result(&self, __serializer: __S) -> #serde::__private::Result<__S::Ok, __S::Error> where diff --git a/test_suite/tests/deprecated.rs b/test_suite/tests/deprecated.rs new file mode 100644 index 000000000..5fee73b21 --- /dev/null +++ b/test_suite/tests/deprecated.rs @@ -0,0 +1,34 @@ +#![deny(deprecated)] +#![allow(dead_code)] + +use serde_derive::{Deserialize, Serialize}; + +/// deprecated enum +#[derive(Serialize, Deserialize)] +#[deprecated] +enum E1 { + A, + B, +} + +/// deprecated struct +#[derive(Serialize, Deserialize)] +#[deprecated] +struct S1 { + a: bool, +} + +/// deprecated enum variant +#[derive(Serialize, Deserialize)] +enum E2 { + A, + #[deprecated] + B, +} + +/// deprecated struct field +#[derive(Serialize, Deserialize)] +struct S2 { + #[deprecated] + a: bool, +} diff --git a/test_suite/tests/ui/deprecated/deprecated_de_with.rs b/test_suite/tests/ui/deprecated/deprecated_de_with.rs new file mode 100644 index 000000000..83466525f --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_de_with.rs @@ -0,0 +1,20 @@ +#![deny(deprecated)] + +use serde::Deserializer; +use serde_derive::Deserialize; + +#[derive(Deserialize)] +pub struct Struct { + #[serde(deserialize_with = "deprecated_with")] + pub field: i32, +} + +#[deprecated] +fn deprecated_with<'de, D>(_deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + unimplemented!() +} + +fn main() {} diff --git a/test_suite/tests/ui/deprecated/deprecated_de_with.stderr b/test_suite/tests/ui/deprecated/deprecated_de_with.stderr new file mode 100644 index 000000000..6e4ede677 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_de_with.stderr @@ -0,0 +1,11 @@ +error: use of deprecated function `deprecated_with` + --> tests/ui/deprecated/deprecated_de_with.rs:8:32 + | +8 | #[serde(deserialize_with = "deprecated_with")] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/deprecated/deprecated_de_with.rs:1:9 + | +1 | #![deny(deprecated)] + | ^^^^^^^^^^ diff --git a/test_suite/tests/ui/deprecated/deprecated_ser_with.rs b/test_suite/tests/ui/deprecated/deprecated_ser_with.rs new file mode 100644 index 000000000..72f55a36d --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_ser_with.rs @@ -0,0 +1,20 @@ +#![deny(deprecated)] + +use serde::Serializer; +use serde_derive::Serialize; + +#[derive(Serialize)] +pub struct Struct { + #[serde(serialize_with = "deprecated_with")] + pub field: i32, +} + +#[deprecated] +fn deprecated_with(_field: &i32, _serializer: S) -> Result +where + S: Serializer, +{ + unimplemented!() +} + +fn main() {} diff --git a/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr b/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr new file mode 100644 index 000000000..574c71168 --- /dev/null +++ b/test_suite/tests/ui/deprecated/deprecated_ser_with.stderr @@ -0,0 +1,11 @@ +error: use of deprecated function `deprecated_with` + --> tests/ui/deprecated/deprecated_ser_with.rs:8:30 + | +8 | #[serde(serialize_with = "deprecated_with")] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/deprecated/deprecated_ser_with.rs:1:9 + | +1 | #![deny(deprecated)] + | ^^^^^^^^^^