Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- `no_drop` item-level option to `ZeroizeOnDrop` which does not implement
`Drop` but instead only asserts that every field implements `ZeroizeOnDrop`.

## [1.4.0] - 2025-05-01

### Added
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,12 @@ If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`]
and can be implemented without [`Zeroize`], otherwise it only implements
[`Drop`] and requires [`Zeroize`] to be implemented.

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

```rust
#[derive_where(ZeroizeOnDrop(crate = zeroize_))]
Expand Down
2 changes: 1 addition & 1 deletion src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use self::{
default::Default,
field::FieldAttr,
incomparable::Incomparable,
item::{DeriveTrait, DeriveWhere, ItemAttr},
item::{DeriveWhere, ItemAttr},
skip::{Skip, SkipGroup},
variant::VariantAttr,
};
2 changes: 1 addition & 1 deletion src/attr/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use syn::{spanned::Spanned, Attribute, Meta, Result};

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

/// Attributes on field.
#[derive(Default)]
Expand Down
2 changes: 1 addition & 1 deletion src/attr/incomparable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use proc_macro2::Span;
use syn::{spanned::Spanned, Meta, Result};

use crate::{attr::DeriveTrait, DeriveWhere, Error};
use crate::{trait_::DeriveTrait, DeriveWhere, Error};

/// Stores if this variant should be incomparable when implementing
/// [`PartialEq`] or [`PartialOrd`].
Expand Down
182 changes: 5 additions & 177 deletions src/attr/item.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
//! [`Attribute`] parsing for items.

use std::{borrow::Cow, ops::Deref};
use std::borrow::Cow;

use proc_macro2::Span;
use syn::{
parse::{discouraged::Speculative, Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Attribute, BoundLifetimes, Data, Ident, Meta, Path, PredicateType, Result, Token, TraitBound,
TraitBoundModifier, Type, TypeParamBound, TypePath, WhereClause, WherePredicate,
Attribute, BoundLifetimes, Data, Ident, Meta, PredicateType, Result, Token, Type, TypePath,
WhereClause, WherePredicate,
};

use crate::{
util::{self, MetaListExt},
Error, Incomparable, Item, Skip, SkipGroup, Trait, TraitImpl, DERIVE_WHERE,
};
use crate::{trait_::DeriveTrait, Error, Incomparable, Item, Skip, SkipGroup, Trait, DERIVE_WHERE};

/// Attributes on item.
#[derive(Default)]
Expand Down Expand Up @@ -259,7 +256,7 @@ impl DeriveWhere {
pub fn any_skip(&self) -> bool {
self.traits
.iter()
.any(|trait_| SkipGroup::trait_supported_by_skip_all(**trait_))
.any(|trait_| SkipGroup::trait_supported_by_skip_all(***trait_))
}

/// Create [`WhereClause`] for the given parameters.
Expand Down Expand Up @@ -353,172 +350,3 @@ impl Parse for Generic {
}
}
}

/// Trait to implement.
#[derive(Eq, PartialEq)]
pub enum DeriveTrait {
/// [`Clone`].
Clone,
/// [`Copy`].
Copy,
/// [`Debug`](std::fmt::Debug).
Debug,
/// [`Default`].
Default,
/// [`Eq`].
Eq,
/// [`Hash`](std::hash::Hash).
Hash,
/// [`Ord`].
Ord,
/// [`PartialEq`].
PartialEq,
/// [`PartialOrd`].
PartialOrd,
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html).
#[cfg(feature = "zeroize")]
Zeroize {
/// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) path.
crate_: Option<Path>,
},
/// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html).
#[cfg(feature = "zeroize")]
ZeroizeOnDrop {
/// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) path.
crate_: Option<Path>,
},
}

impl Deref for DeriveTrait {
type Target = Trait;

fn deref(&self) -> &Self::Target {
use DeriveTrait::*;

match self {
Clone => &Trait::Clone,
Copy => &Trait::Copy,
Debug => &Trait::Debug,
Default => &Trait::Default,
Eq => &Trait::Eq,
Hash => &Trait::Hash,
Ord => &Trait::Ord,
PartialEq => &Trait::PartialEq,
PartialOrd => &Trait::PartialOrd,
#[cfg(feature = "zeroize")]
Zeroize { .. } => &Trait::Zeroize,
#[cfg(feature = "zeroize")]
ZeroizeOnDrop { .. } => &Trait::ZeroizeOnDrop,
}
}
}

impl PartialEq<Trait> for &DeriveTrait {
fn eq(&self, other: &Trait) -> bool {
let trait_: &Trait = self;
trait_ == other
}
}

impl DeriveTrait {
/// Returns fully qualified [`Path`] for this trait.
pub fn path(&self) -> Path {
use DeriveTrait::*;

match self {
Clone => util::path_from_root_and_strs(self.crate_(), &["clone", "Clone"]),
Copy => util::path_from_root_and_strs(self.crate_(), &["marker", "Copy"]),
Debug => util::path_from_root_and_strs(self.crate_(), &["fmt", "Debug"]),
Default => util::path_from_root_and_strs(self.crate_(), &["default", "Default"]),
Eq => util::path_from_root_and_strs(self.crate_(), &["cmp", "Eq"]),
Hash => util::path_from_root_and_strs(self.crate_(), &["hash", "Hash"]),
Ord => util::path_from_root_and_strs(self.crate_(), &["cmp", "Ord"]),
PartialEq => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialEq"]),
PartialOrd => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialOrd"]),
#[cfg(feature = "zeroize")]
Zeroize { .. } => util::path_from_root_and_strs(self.crate_(), &["Zeroize"]),
#[cfg(feature = "zeroize")]
ZeroizeOnDrop { .. } => util::path_from_root_and_strs(self.crate_(), &["ZeroizeOnDrop"]),
}
}

/// Returns the path to the root crate for this trait.
pub fn crate_(&self) -> Path {
use DeriveTrait::*;

match self {
Clone => util::path_from_strs(&["core"]),
Copy => util::path_from_strs(&["core"]),
Debug => util::path_from_strs(&["core"]),
Default => util::path_from_strs(&["core"]),
Eq => util::path_from_strs(&["core"]),
Hash => util::path_from_strs(&["core"]),
Ord => util::path_from_strs(&["core"]),
PartialEq => util::path_from_strs(&["core"]),
PartialOrd => util::path_from_strs(&["core"]),
#[cfg(feature = "zeroize")]
Zeroize { crate_, .. } => {
if let Some(crate_) = crate_ {
crate_.clone()
} else {
util::path_from_strs(&["zeroize"])
}
}
#[cfg(feature = "zeroize")]
ZeroizeOnDrop { crate_, .. } => {
if let Some(crate_) = crate_ {
crate_.clone()
} else {
util::path_from_strs(&["zeroize"])
}
}
}
}

/// Returns where-clause bounds for the trait in respect of the item type.
fn where_bounds(&self, data: &Item) -> Punctuated<TypeParamBound, Token![+]> {
let mut list = Punctuated::new();

list.push(TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: self.path(),
}));

// Add bounds specific to the trait.
if let Some(bound) = self.additional_where_bounds(data) {
list.push(bound)
}

list
}

/// Create [`DeriveTrait`] from [`ParseStream`].
fn from_stream(span: Span, data: &Data, input: ParseStream) -> Result<(Span, Self)> {
match Meta::parse(input) {
Ok(meta) => {
let trait_ = Trait::from_path(meta.path())?;

if let Data::Union(_) = data {
// Make sure this `Trait` supports unions.
if !trait_.supports_union() {
return Err(Error::union(span));
}
}

match &meta {
Meta::Path(path) => Ok((path.span(), trait_.default_derive_trait())),
Meta::List(list) => {
let nested = list.parse_non_empty_nested_metas()?;

// This will return an error if no options are supported.
Ok((list.span(), trait_.parse_derive_trait(meta.span(), nested)?))
}
Meta::NameValue(name_value) => Err(Error::option_syntax(name_value.span())),
}
}
Err(error) => Err(Error::trait_syntax(error.span())),
}
}
}
2 changes: 1 addition & 1 deletion src/attr/skip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::default::Default;

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

use crate::{attr::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};
use crate::{trait_::DeriveTrait, util::MetaListExt, DeriveWhere, Error, Trait};

/// Stores what [`Trait`]s to skip this field or variant for.
#[cfg_attr(test, derive(Debug))]
Expand Down
2 changes: 1 addition & 1 deletion src/attr/zeroize_fqs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use syn::{spanned::Spanned, Meta, Result};

use crate::{util::MetaListExt, DeriveWhere, Error, Trait, TraitImpl};
use crate::{util::MetaListExt, DeriveWhere, Error, Trait};

/// Stores if this field should use FQS to call [`Zeroize::zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize).
#[derive(Default)]
Expand Down
19 changes: 13 additions & 6 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
use proc_macro2::Span;
use syn::{DeriveInput, GenericParam, Generics, ImplGenerics, Result, TypeGenerics, WhereClause};

#[cfg(feature = "zeroize")]
use crate::DeriveTrait;
#[cfg(not(feature = "nightly"))]
use crate::Discriminant;
#[cfg(feature = "zeroize")]
use crate::{
trait_::{zeroize::Zeroize, zeroize_on_drop::ZeroizeOnDrop},
DeriveTrait,
};
use crate::{Data, DeriveWhere, Either, Error, Item, ItemAttr, Trait};

/// Parsed input.
Expand Down Expand Up @@ -170,7 +173,7 @@ impl<'a> Input<'a> {
}

// Any field is skipped with a corresponding `Trait`.
if item.any_skip_trait(**trait_) {
if item.any_skip_trait(***trait_) {
continue;
}

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

#[cfg(feature = "zeroize")]
{
// `Zeroize(crate = ..)` or `ZeroizeOnDrop(crate = ..)` is used.
if let DeriveTrait::Zeroize { crate_: Some(_) }
| DeriveTrait::ZeroizeOnDrop { crate_: Some(_) } = *trait_
// `Zeroize(crate = ..)`, `ZeroizeOnDrop(crate = ..)` or
// `ZeroizeOnDrop(no_drop)` is used.
if let DeriveTrait::Zeroize(Zeroize { crate_: Some(_) })
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop {
crate_: Some(_), ..
})
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop { no_drop: true, .. }) = *trait_
{
continue;
}
Expand Down
Loading