Skip to content

Commit db2eb29

Browse files
committed
Always print all traits in error messages
- Instead, if the crate feature is not enabled, we say that in the error message instead. - Implement `Serialize` skeleton. - Hook up `serde` in the CI. - Handle `#[serde(...)]` attributes without `De/Serialize`. - Add tests for all these new errors. - Add test to check for `#[derive(De/Serialize)`]` support without `#[derive_where(De/Serialize)]`.
1 parent 6e9e0d9 commit db2eb29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+418
-295
lines changed

.github/workflows/lint.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ jobs:
2828
features: --features safe
2929
- channel: nightly
3030
features: --features nightly
31+
- channel: stable
32+
features: --features serde
3133
- channel: stable
3234
features: --features zeroize
3335
- channel: stable
3436
features: --features zeroize-on-drop
3537
- channel: nightly
3638
features: --features safe,nightly
39+
- channel: stable
40+
features: --features safe,serde
3741
- channel: stable
3842
features: --features safe,zeroize
3943
- channel: stable
@@ -42,6 +46,12 @@ jobs:
4246
features: --features nightly,zeroize
4347
- channel: nightly
4448
features: --features nightly,zeroize-on-drop
49+
- channel: nightly
50+
features: --features nightly,serde
51+
- channel: stable
52+
features: --features serde,zeroize
53+
- channel: stable
54+
features: --features serde,zeroize-on-drop
4555
- channel: nightly
4656
features: --all-features
4757

.github/workflows/test.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,23 @@ jobs:
2828
features:
2929
- ""
3030
- --features safe
31+
- --features serde
3132
- --features zeroize
3233
- --features zeroize-on-drop
34+
- --features safe,serde
3335
- --features safe,zeroize
3436
- --features safe,zeroize-on-drop
37+
- --features serde,zeroize
38+
- --features serde,zeroize-on-drop
3539
include:
3640
- rust: 1.57.0
3741
msrv: true
3842
- rust: nightly
3943
features: --features nightly
4044
- rust: nightly
4145
features: --features safe,nightly
46+
- rust: nightly
47+
features: --features nightly,serde
4248
- rust: nightly
4349
features: --features nightly,zeroize
4450
- rust: nightly
@@ -83,17 +89,23 @@ jobs:
8389
features:
8490
- ""
8591
- --features safe
92+
- --features serde
8693
- --features zeroize
8794
- --features zeroize-on-drop
95+
- --features safe,serde
8896
- --features safe,zeroize
8997
- --features safe,zeroize-on-drop
98+
- --features serde,zeroize
99+
- --features serde,zeroize-on-drop
90100
include:
91101
- rust: 1.57.0
92102
msrv: true
93103
- rust: nightly
94104
features: --features nightly
95105
- rust: nightly
96106
features: --features safe,nightly
107+
- rust: nightly
108+
features: --features nightly,serde
97109
- rust: nightly
98110
features: --features nightly,zeroize
99111
- rust: nightly
@@ -127,9 +139,9 @@ jobs:
127139
matrix:
128140
rust:
129141
- version: 1.57.0
130-
features: safe,zeroize-on-drop
142+
features: safe,serde,zeroize-on-drop
131143
- version: stable
132-
features: safe,zeroize-on-drop
144+
features: safe,serde,zeroize-on-drop
133145

134146
steps:
135147
- name: Checkout

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- `no_drop` item-level option to `ZeroizeOnDrop` which does not implement
1212
`Drop` but instead only asserts that every field implements `ZeroizeOnDrop`.
1313

14+
### Changed
15+
- Error messages now point to crate features instead of reporting traits as
16+
unsupported.
17+
1418
### Fixed
1519
- Stop depending on unstable APIs for `Eq` for `ZeroizeOnDrop`.
1620

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ syn = { version = "2", default-features = false, features = [
4343
pretty_assertions = "1"
4444
rustversion = "1"
4545
serde_ = { package = "serde", version = "1", default-features = false, features = ["derive"] }
46+
serde_test = "1"
4647
trybuild = { version = "1.0.18", default-features = false }
4748
zeroize_ = { package = "zeroize", version = "1.5", default-features = false }
4849

src/attr/item.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ impl ItemAttr {
9696
return Err(Error::none(span));
9797
}
9898

99+
// Check for `#[serde(...)]` attributes without `De/Serialize`.
100+
#[cfg(feature = "serde")]
101+
if !self_.derive_wheres.iter().any(|derive_where| {
102+
derive_where.contains(Trait::Deserialize) | derive_where.contains(Trait::Serialize)
103+
}) {
104+
for attr in attrs {
105+
if attr.path().is_ident("serde") {
106+
return Err(Error::serde_without_serde(attr.span()));
107+
}
108+
}
109+
}
110+
99111
// Merge `DeriveWhere`s with the same bounds.
100112
self_
101113
.derive_wheres

src/attr/skip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ impl SkipGroup {
271271
| Trait::PartialEq
272272
| Trait::PartialOrd => true,
273273
#[cfg(feature = "serde")]
274-
Trait::Deserialize => false,
274+
Trait::Deserialize | Trait::Serialize => false,
275275
#[cfg(feature = "zeroize")]
276276
Trait::Zeroize | Trait::ZeroizeOnDrop => true,
277277
}

src/error.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,24 @@ impl Error {
191191
)
192192
}
193193

194+
/// Requires crate feature `serde`.
195+
#[cfg(not(feature = "serde"))]
196+
pub fn serde_feature(span: Span) -> syn::Error {
197+
syn::Error::new(span, "requires crate feature `serde`")
198+
}
199+
200+
/// Requires crate feature `zeroize`.
201+
#[cfg(not(feature = "zeroize"))]
202+
pub fn zeroize_feature(span: Span) -> syn::Error {
203+
syn::Error::new(span, "requires crate feature `zeroize`")
204+
}
205+
206+
/// Requires crate feature `zeroize-on-drop`.
207+
#[cfg(all(feature = "zeroize", not(feature = "zeroize-on-drop")))]
208+
pub fn zeroize_on_drop_feature(span: Span) -> syn::Error {
209+
syn::Error::new(span, "requires crate feature `zeroize-on-drop`")
210+
}
211+
194212
/// Invalid delimiter in `derive_where` attribute for
195213
/// [`Trait`](crate::Trait)s.
196214
pub fn derive_where_delimiter(span: Span) -> syn::Error {
@@ -282,37 +300,35 @@ impl Error {
282300
syn::Error::new(skip_clone, "Cannot skip `Clone` while deriving `Copy`")
283301
}
284302

303+
/// Unsupported `serde(...)` without deriving `De/Serialize`.
304+
#[cfg(feature = "serde")]
305+
pub fn serde_without_serde(serde: Span) -> syn::Error {
306+
syn::Error::new(serde, "Found unused `#[serde(...)]`")
307+
}
308+
285309
/// List of available [`Trait`](crate::Trait)s.
286310
fn trait_list() -> String {
287311
[
288312
"Clone",
289313
"Copy",
290314
"Debug",
291315
"Default",
316+
"Deserialize",
292317
"Eq",
293318
"Hash",
294319
"Ord",
295320
"PartialEq",
296321
"PartialOrd",
297-
#[cfg(feature = "zeroize")]
322+
"Serialize",
298323
"Zeroize",
299-
#[cfg(feature = "zeroize")]
300324
"ZeroizeOnDrop",
301325
]
302326
.join(", ")
303327
}
304328

305329
/// List of available [`SkipGroup`](crate::SkipGroup)s.
306330
fn skip_group_list() -> String {
307-
[
308-
"Clone",
309-
"Debug",
310-
"EqHashOrd",
311-
"Hash",
312-
#[cfg(feature = "zeroize")]
313-
"Zeroize",
314-
]
315-
.join(", ")
331+
["Clone", "Debug", "EqHashOrd", "Hash", "Zeroize"].join(", ")
316332
}
317333

318334
/// Unsupported `Zeroize` option if [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) isn't implemented.

src/trait_.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub mod hash;
1212
pub mod ord;
1313
pub mod partial_eq;
1414
pub mod partial_ord;
15+
#[cfg(feature = "serde")]
16+
pub mod serialize;
1517
#[cfg(feature = "zeroize")]
1618
pub mod zeroize;
1719
#[cfg(feature = "zeroize")]
@@ -56,6 +58,9 @@ pub enum Trait {
5658
PartialEq,
5759
/// [`PartialOrd`].
5860
PartialOrd,
61+
/// [`Serialize`](https://docs.rs/serde/latest/serde/derive.Serialize.html).
62+
#[cfg(feature = "serde")]
63+
Serialize,
5964
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html).
6065
#[cfg(feature = "zeroize")]
6166
Zeroize,
@@ -78,6 +83,8 @@ macro_rules! trait_dispatch {
7883
Trait::Ord => ord::Ord::$method($($par),*),
7984
Trait::PartialEq => partial_eq::PartialEq::$method($($par),*),
8085
Trait::PartialOrd => partial_ord::PartialOrd::$method($($par),*),
86+
#[cfg(feature = "serde")]
87+
Trait::Serialize => serialize::Serialize::$method($($par),*),
8188
#[cfg(feature = "zeroize")]
8289
Trait::Zeroize => zeroize::Zeroize::$method($($par),*),
8390
#[cfg(feature = "zeroize")]
@@ -99,15 +106,25 @@ impl Trait {
99106
"Default" => Ok(Default),
100107
#[cfg(feature = "serde")]
101108
"Deserialize" => Ok(Deserialize),
109+
#[cfg(not(feature = "serde"))]
110+
"Deserialize" => Err(Error::serde_feature(path.span())),
102111
"Eq" => Ok(Eq),
103112
"Hash" => Ok(Hash),
104113
"Ord" => Ok(Ord),
105114
"PartialEq" => Ok(PartialEq),
106115
"PartialOrd" => Ok(PartialOrd),
116+
#[cfg(feature = "serde")]
117+
"Serialize" => Ok(Serialize),
118+
#[cfg(not(feature = "serde"))]
119+
"Serialize" => Err(Error::serde_feature(path.span())),
107120
#[cfg(feature = "zeroize")]
108121
"Zeroize" => Ok(Zeroize),
122+
#[cfg(not(feature = "zeroize"))]
123+
"Zeroize" => Err(Error::zeroize_feature(path.span())),
109124
#[cfg(feature = "zeroize")]
110125
"ZeroizeOnDrop" => Ok(ZeroizeOnDrop),
126+
#[cfg(not(feature = "zeroize"))]
127+
"ZeroizeOnDrop" => Err(Error::zeroize_feature(path.span())),
111128
"crate" => Err(Error::crate_(path.span())),
112129
_ => Err(Error::trait_(path.span())),
113130
}
@@ -170,6 +187,9 @@ pub enum DeriveTrait {
170187
PartialEq,
171188
/// [`PartialOrd`].
172189
PartialOrd,
190+
/// [`Serialize`](https://docs.rs/serde/latest/serde/derive.Serialize.html).
191+
#[cfg(feature = "serde")]
192+
Serialize(serialize::Serialize),
173193
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html).
174194
#[cfg(feature = "zeroize")]
175195
Zeroize(zeroize::Zeroize),
@@ -196,6 +216,8 @@ impl Deref for DeriveTrait {
196216
Ord => &ord::Ord,
197217
PartialEq => &partial_eq::PartialEq,
198218
PartialOrd => &partial_ord::PartialOrd,
219+
#[cfg(feature = "serde")]
220+
Serialize(trait_) => trait_,
199221
#[cfg(feature = "zeroize")]
200222
Zeroize(trait_) => trait_,
201223
#[cfg(feature = "zeroize")]

src/trait_/serialize.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! [`Serialize`](https://docs.rs/serde/latest/serde/derive.Serialize.html) implementation.
2+
3+
use std::{borrow::Cow, ops::Deref};
4+
5+
use proc_macro2::TokenStream;
6+
use quote::quote;
7+
use syn::{DeriveInput, Ident, ImplGenerics, Path, TypeGenerics, WhereClause};
8+
9+
use crate::{util, DeriveTrait, Trait, TraitImpl, DERIVE_WHERE};
10+
11+
/// [`TraitImpl`] for [`Serialize`](https://docs.rs/serde/latest/serde/derive.Serialize.html).
12+
#[derive(Eq, PartialEq)]
13+
pub struct Serialize {
14+
/// [`Serialize`](https://docs.rs/serde/latest/serde/derive.Serialize.html) path.
15+
pub crate_: Option<Path>,
16+
}
17+
18+
impl TraitImpl for Serialize {
19+
fn as_str() -> &'static str
20+
where
21+
Self: Sized,
22+
{
23+
"Serialize"
24+
}
25+
26+
fn default_derive_trait() -> super::DeriveTrait
27+
where
28+
Self: Sized,
29+
{
30+
DeriveTrait::Serialize(Self { crate_: None })
31+
}
32+
33+
fn path(&self) -> Path {
34+
util::path_from_root_and_strs(self.crate_(), &["Serialize"])
35+
}
36+
37+
fn impl_item(
38+
&self,
39+
crate_: Option<&Path>,
40+
full_item: &DeriveInput,
41+
_: &ImplGenerics<'_>,
42+
_: &Ident,
43+
_: &TypeGenerics<'_>,
44+
_: &Option<Cow<'_, WhereClause>>,
45+
_: TokenStream,
46+
) -> TokenStream {
47+
let derive_where = crate_
48+
.map(Cow::Borrowed)
49+
.unwrap_or_else(|| Cow::Owned(util::path_from_strs(&[DERIVE_WHERE])));
50+
let serde = self.crate_();
51+
52+
quote! {
53+
#[derive(#serde::Serialize)]
54+
#[#derive_where::derive_where_serde]
55+
#full_item
56+
}
57+
}
58+
}
59+
60+
impl Serialize {
61+
/// Returns the path to the root crate for this trait.
62+
fn crate_(&self) -> Path {
63+
if let Some(crate_) = &self.crate_ {
64+
crate_.clone()
65+
} else {
66+
util::path_from_strs(&["serde"])
67+
}
68+
}
69+
}
70+
71+
impl Deref for Serialize {
72+
type Target = Trait;
73+
74+
fn deref(&self) -> &Self::Target {
75+
&Trait::Serialize
76+
}
77+
}

src/trait_/zeroize_on_drop.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,18 @@ impl TraitImpl for ZeroizeOnDrop {
4545
for meta in list {
4646
match &meta {
4747
Meta::Path(path) => {
48-
#[cfg(feature = "zeroize-on-drop")]
4948
if path.is_ident("no_drop") {
5049
// Check for duplicate `no_drop` option.
50+
#[cfg(feature = "zeroize-on-drop")]
5151
if !no_drop {
5252
no_drop = true;
53+
continue;
5354
} else {
5455
return Err(Error::option_duplicate(path.span(), "no_drop"));
5556
}
5657

57-
continue;
58+
#[cfg(not(feature = "zeroize-on-drop"))]
59+
return Err(Error::zeroize_on_drop_feature(path.span()));
5860
}
5961

6062
return Err(Error::option_trait(path.span(), Self::as_str()));

0 commit comments

Comments
 (0)