Skip to content

Commit 5470b4f

Browse files
authored
Implement ZeroizeOnDrop without Drop (#114)
1 parent 4980b4e commit 5470b4f

30 files changed

+801
-522
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
- `no_drop` item-level option to `ZeroizeOnDrop` which does not implement
12+
`Drop` but instead only asserts that every field implements `ZeroizeOnDrop`.
13+
814
## [1.4.0] - 2025-05-01
915

1016
### Added

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,12 @@ If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`]
238238
and can be implemented without [`Zeroize`], otherwise it only implements
239239
[`Drop`] and requires [`Zeroize`] to be implemented.
240240

241-
[`ZeroizeOnDrop`] has one option:
241+
[`ZeroizeOnDrop`] has two options:
242242
- `crate`: an item-level option which specifies a path to the [`zeroize`]
243243
crate in case of a re-export or rename.
244+
- `no_drop`: an item-level option which will not implement [`Drop`] but instead
245+
only assert that every field implements [`ZeroizeOnDrop`]. Requires the
246+
`zeroize-on-drop` feature.
244247

245248
```rust
246249
#[derive_where(ZeroizeOnDrop(crate = zeroize_))]

src/attr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use self::{
1515
default::Default,
1616
field::FieldAttr,
1717
incomparable::Incomparable,
18-
item::{DeriveTrait, DeriveWhere, ItemAttr},
18+
item::{DeriveWhere, ItemAttr},
1919
skip::{Skip, SkipGroup},
2020
variant::VariantAttr,
2121
};

src/attr/field.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use syn::{spanned::Spanned, Attribute, Meta, Result};
44

55
use crate::{util::MetaListExt, DeriveWhere, Error, Skip, DERIVE_WHERE};
66
#[cfg(feature = "zeroize")]
7-
use crate::{Trait, TraitImpl, ZeroizeFqs};
7+
use crate::{Trait, ZeroizeFqs};
88

99
/// Attributes on field.
1010
#[derive(Default)]

src/attr/incomparable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use proc_macro2::Span;
44
use syn::{spanned::Spanned, Meta, Result};
55

6-
use crate::{attr::DeriveTrait, DeriveWhere, Error};
6+
use crate::{trait_::DeriveTrait, DeriveWhere, Error};
77

88
/// Stores if this variant should be incomparable when implementing
99
/// [`PartialEq`] or [`PartialOrd`].

src/attr/item.rs

Lines changed: 5 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
//! [`Attribute`] parsing for items.
22
3-
use std::{borrow::Cow, ops::Deref};
3+
use std::borrow::Cow;
44

55
use proc_macro2::Span;
66
use syn::{
77
parse::{discouraged::Speculative, Parse, ParseStream},
88
punctuated::Punctuated,
99
spanned::Spanned,
10-
Attribute, BoundLifetimes, Data, Ident, Meta, Path, PredicateType, Result, Token, TraitBound,
11-
TraitBoundModifier, Type, TypeParamBound, TypePath, WhereClause, WherePredicate,
10+
Attribute, BoundLifetimes, Data, Ident, Meta, PredicateType, Result, Token, Type, TypePath,
11+
WhereClause, WherePredicate,
1212
};
1313

14-
use crate::{
15-
util::{self, MetaListExt},
16-
Error, Incomparable, Item, Skip, SkipGroup, Trait, TraitImpl, DERIVE_WHERE,
17-
};
14+
use crate::{trait_::DeriveTrait, Error, Incomparable, Item, Skip, SkipGroup, Trait, DERIVE_WHERE};
1815

1916
/// Attributes on item.
2017
#[derive(Default)]
@@ -259,7 +256,7 @@ impl DeriveWhere {
259256
pub fn any_skip(&self) -> bool {
260257
self.traits
261258
.iter()
262-
.any(|trait_| SkipGroup::trait_supported_by_skip_all(**trait_))
259+
.any(|trait_| SkipGroup::trait_supported_by_skip_all(***trait_))
263260
}
264261

265262
/// Create [`WhereClause`] for the given parameters.
@@ -353,172 +350,3 @@ impl Parse for Generic {
353350
}
354351
}
355352
}
356-
357-
/// Trait to implement.
358-
#[derive(Eq, PartialEq)]
359-
pub enum DeriveTrait {
360-
/// [`Clone`].
361-
Clone,
362-
/// [`Copy`].
363-
Copy,
364-
/// [`Debug`](std::fmt::Debug).
365-
Debug,
366-
/// [`Default`].
367-
Default,
368-
/// [`Eq`].
369-
Eq,
370-
/// [`Hash`](std::hash::Hash).
371-
Hash,
372-
/// [`Ord`].
373-
Ord,
374-
/// [`PartialEq`].
375-
PartialEq,
376-
/// [`PartialOrd`].
377-
PartialOrd,
378-
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html).
379-
#[cfg(feature = "zeroize")]
380-
Zeroize {
381-
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) path.
382-
crate_: Option<Path>,
383-
},
384-
/// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html).
385-
#[cfg(feature = "zeroize")]
386-
ZeroizeOnDrop {
387-
/// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) path.
388-
crate_: Option<Path>,
389-
},
390-
}
391-
392-
impl Deref for DeriveTrait {
393-
type Target = Trait;
394-
395-
fn deref(&self) -> &Self::Target {
396-
use DeriveTrait::*;
397-
398-
match self {
399-
Clone => &Trait::Clone,
400-
Copy => &Trait::Copy,
401-
Debug => &Trait::Debug,
402-
Default => &Trait::Default,
403-
Eq => &Trait::Eq,
404-
Hash => &Trait::Hash,
405-
Ord => &Trait::Ord,
406-
PartialEq => &Trait::PartialEq,
407-
PartialOrd => &Trait::PartialOrd,
408-
#[cfg(feature = "zeroize")]
409-
Zeroize { .. } => &Trait::Zeroize,
410-
#[cfg(feature = "zeroize")]
411-
ZeroizeOnDrop { .. } => &Trait::ZeroizeOnDrop,
412-
}
413-
}
414-
}
415-
416-
impl PartialEq<Trait> for &DeriveTrait {
417-
fn eq(&self, other: &Trait) -> bool {
418-
let trait_: &Trait = self;
419-
trait_ == other
420-
}
421-
}
422-
423-
impl DeriveTrait {
424-
/// Returns fully qualified [`Path`] for this trait.
425-
pub fn path(&self) -> Path {
426-
use DeriveTrait::*;
427-
428-
match self {
429-
Clone => util::path_from_root_and_strs(self.crate_(), &["clone", "Clone"]),
430-
Copy => util::path_from_root_and_strs(self.crate_(), &["marker", "Copy"]),
431-
Debug => util::path_from_root_and_strs(self.crate_(), &["fmt", "Debug"]),
432-
Default => util::path_from_root_and_strs(self.crate_(), &["default", "Default"]),
433-
Eq => util::path_from_root_and_strs(self.crate_(), &["cmp", "Eq"]),
434-
Hash => util::path_from_root_and_strs(self.crate_(), &["hash", "Hash"]),
435-
Ord => util::path_from_root_and_strs(self.crate_(), &["cmp", "Ord"]),
436-
PartialEq => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialEq"]),
437-
PartialOrd => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialOrd"]),
438-
#[cfg(feature = "zeroize")]
439-
Zeroize { .. } => util::path_from_root_and_strs(self.crate_(), &["Zeroize"]),
440-
#[cfg(feature = "zeroize")]
441-
ZeroizeOnDrop { .. } => util::path_from_root_and_strs(self.crate_(), &["ZeroizeOnDrop"]),
442-
}
443-
}
444-
445-
/// Returns the path to the root crate for this trait.
446-
pub fn crate_(&self) -> Path {
447-
use DeriveTrait::*;
448-
449-
match self {
450-
Clone => util::path_from_strs(&["core"]),
451-
Copy => util::path_from_strs(&["core"]),
452-
Debug => util::path_from_strs(&["core"]),
453-
Default => util::path_from_strs(&["core"]),
454-
Eq => util::path_from_strs(&["core"]),
455-
Hash => util::path_from_strs(&["core"]),
456-
Ord => util::path_from_strs(&["core"]),
457-
PartialEq => util::path_from_strs(&["core"]),
458-
PartialOrd => util::path_from_strs(&["core"]),
459-
#[cfg(feature = "zeroize")]
460-
Zeroize { crate_, .. } => {
461-
if let Some(crate_) = crate_ {
462-
crate_.clone()
463-
} else {
464-
util::path_from_strs(&["zeroize"])
465-
}
466-
}
467-
#[cfg(feature = "zeroize")]
468-
ZeroizeOnDrop { crate_, .. } => {
469-
if let Some(crate_) = crate_ {
470-
crate_.clone()
471-
} else {
472-
util::path_from_strs(&["zeroize"])
473-
}
474-
}
475-
}
476-
}
477-
478-
/// Returns where-clause bounds for the trait in respect of the item type.
479-
fn where_bounds(&self, data: &Item) -> Punctuated<TypeParamBound, Token![+]> {
480-
let mut list = Punctuated::new();
481-
482-
list.push(TypeParamBound::Trait(TraitBound {
483-
paren_token: None,
484-
modifier: TraitBoundModifier::None,
485-
lifetimes: None,
486-
path: self.path(),
487-
}));
488-
489-
// Add bounds specific to the trait.
490-
if let Some(bound) = self.additional_where_bounds(data) {
491-
list.push(bound)
492-
}
493-
494-
list
495-
}
496-
497-
/// Create [`DeriveTrait`] from [`ParseStream`].
498-
fn from_stream(span: Span, data: &Data, input: ParseStream) -> Result<(Span, Self)> {
499-
match Meta::parse(input) {
500-
Ok(meta) => {
501-
let trait_ = Trait::from_path(meta.path())?;
502-
503-
if let Data::Union(_) = data {
504-
// Make sure this `Trait` supports unions.
505-
if !trait_.supports_union() {
506-
return Err(Error::union(span));
507-
}
508-
}
509-
510-
match &meta {
511-
Meta::Path(path) => Ok((path.span(), trait_.default_derive_trait())),
512-
Meta::List(list) => {
513-
let nested = list.parse_non_empty_nested_metas()?;
514-
515-
// This will return an error if no options are supported.
516-
Ok((list.span(), trait_.parse_derive_trait(meta.span(), nested)?))
517-
}
518-
Meta::NameValue(name_value) => Err(Error::option_syntax(name_value.span())),
519-
}
520-
}
521-
Err(error) => Err(Error::trait_syntax(error.span())),
522-
}
523-
}
524-
}

src/attr/skip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::default::Default;
44

55
use syn::{spanned::Spanned, Meta, Path, Result};
66

7-
use crate::{attr::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};
7+
use crate::{trait_::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};
88

99
/// Stores what [`Trait`]s to skip this field or variant for.
1010
#[cfg_attr(test, derive(Debug))]

src/attr/zeroize_fqs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use syn::{spanned::Spanned, Meta, Result};
44

5-
use crate::{util::MetaListExt, DeriveWhere, Error, Trait, TraitImpl};
5+
use crate::{util::MetaListExt, DeriveWhere, Error, Trait};
66

77
/// Stores if this field should use FQS to call [`Zeroize::zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize).
88
#[derive(Default)]

src/input.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
use proc_macro2::Span;
44
use syn::{DeriveInput, GenericParam, Generics, ImplGenerics, Result, TypeGenerics, WhereClause};
55

6-
#[cfg(feature = "zeroize")]
7-
use crate::DeriveTrait;
86
#[cfg(not(feature = "nightly"))]
97
use crate::Discriminant;
8+
#[cfg(feature = "zeroize")]
9+
use crate::{
10+
trait_::{zeroize::Zeroize, zeroize_on_drop::ZeroizeOnDrop},
11+
DeriveTrait,
12+
};
1013
use crate::{Data, DeriveWhere, Either, Error, Item, ItemAttr, Trait};
1114

1215
/// Parsed input.
@@ -170,7 +173,7 @@ impl<'a> Input<'a> {
170173
}
171174

172175
// Any field is skipped with a corresponding `Trait`.
173-
if item.any_skip_trait(**trait_) {
176+
if item.any_skip_trait(***trait_) {
174177
continue;
175178
}
176179

@@ -181,9 +184,13 @@ impl<'a> Input<'a> {
181184

182185
#[cfg(feature = "zeroize")]
183186
{
184-
// `Zeroize(crate = ..)` or `ZeroizeOnDrop(crate = ..)` is used.
185-
if let DeriveTrait::Zeroize { crate_: Some(_) }
186-
| DeriveTrait::ZeroizeOnDrop { crate_: Some(_) } = *trait_
187+
// `Zeroize(crate = ..)`, `ZeroizeOnDrop(crate = ..)` or
188+
// `ZeroizeOnDrop(no_drop)` is used.
189+
if let DeriveTrait::Zeroize(Zeroize { crate_: Some(_) })
190+
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop {
191+
crate_: Some(_), ..
192+
})
193+
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop { no_drop: true, .. }) = *trait_
187194
{
188195
continue;
189196
}

0 commit comments

Comments
 (0)