Skip to content

Commit 34c7eea

Browse files
committed
Initial framework
1 parent 1933e5a commit 34c7eea

File tree

13 files changed

+218
-56
lines changed

13 files changed

+218
-56
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ proc-macro = true
2323
[features]
2424
nightly = []
2525
safe = []
26+
serde = []
2627
zeroize = []
2728
zeroize-on-drop = ["zeroize"]
2829

@@ -41,6 +42,7 @@ syn = { version = "2", default-features = false, features = [
4142
[dev-dependencies]
4243
pretty_assertions = "1"
4344
rustversion = "1"
45+
serde = { version = "1", default-features = false, features = ["derive"] }
4446
trybuild = { version = "1.0.18", default-features = false }
4547
zeroize_ = { version = "1.5", package = "zeroize", default-features = false }
4648

src/attr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! [`Attribute`](syn::Attribute) parsing for items, variants and fields.
22
3+
mod crate_;
34
mod default;
45
mod field;
56
mod incomparable;
@@ -12,6 +13,7 @@ mod zeroize_fqs;
1213
#[cfg(feature = "zeroize")]
1314
pub use self::zeroize_fqs::ZeroizeFqs;
1415
pub use self::{
16+
crate_::parse_crate,
1517
default::Default,
1618
field::FieldAttr,
1719
incomparable::Incomparable,

src/attr/crate_.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! Parsing implementation for `#[derive_where(crate = ...)]`.
2+
3+
use proc_macro2::Span;
4+
use syn::{spanned::Spanned, Expr, ExprLit, ExprPath, Lit, Meta, Path, Result};
5+
6+
use crate::{util, Error, DERIVE_WHERE};
7+
8+
/// Parses `#[derive_where(crate = ...)]`.
9+
pub fn parse_crate(meta: Meta) -> Result<(Path, Span)> {
10+
if let Meta::NameValue(name_value) = meta {
11+
let path = match &name_value.value {
12+
Expr::Lit(ExprLit {
13+
lit: Lit::Str(lit_str),
14+
..
15+
}) => match lit_str.parse::<Path>() {
16+
Ok(path) => path,
17+
Err(error) => return Err(Error::path(lit_str.span(), error)),
18+
},
19+
Expr::Path(ExprPath { path, .. }) => path.clone(),
20+
_ => return Err(Error::option_syntax(name_value.value.span())),
21+
};
22+
23+
if path == util::path_from_strs(&[DERIVE_WHERE]) {
24+
Err(Error::path_unnecessary(
25+
path.span(),
26+
&format!("::{}", DERIVE_WHERE),
27+
))
28+
} else {
29+
Ok((path, name_value.span()))
30+
}
31+
} else {
32+
Err(Error::option_syntax(meta.span()))
33+
}
34+
}

src/attr/item.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@ use syn::{
77
parse::{discouraged::Speculative, Parse, ParseStream},
88
punctuated::Punctuated,
99
spanned::Spanned,
10-
Attribute, BoundLifetimes, Data, Ident, Meta, PredicateType, Result, Token, Type, TypePath,
11-
WhereClause, WherePredicate,
10+
Attribute, BoundLifetimes, Data, Ident, Meta, Path, PredicateType, Result, Token, Type,
11+
TypePath, WhereClause, WherePredicate,
1212
};
1313

1414
use crate::{trait_::DeriveTrait, Error, Incomparable, Item, Skip, SkipGroup, Trait, DERIVE_WHERE};
1515

1616
/// Attributes on item.
1717
#[derive(Default)]
1818
pub struct ItemAttr {
19+
/// Path to `derive_where` if set by `#[derive_where(crate = ...)]`.
20+
pub crate_: Option<Path>,
1921
/// [`Trait`]s to skip all fields for.
2022
pub skip_inner: Skip,
2123
/// Comparing item will yield `false` for [`PartialEq`] and [`None`] for
@@ -59,8 +61,9 @@ impl ItemAttr {
5961
// Needs to be parsed after all traits are known.
6062
incomparables.push(meta)
6163
} else if meta.path().is_ident("crate") {
62-
// Do nothing, we checked this before
63-
// already.
64+
let (path, _) = super::parse_crate(meta)
65+
.expect("failed to parse previously parsed attribute");
66+
self_.crate_ = Some(path);
6467
}
6568
// The list can have one item but still not be the `skip_inner`
6669
// attribute, continue with parsing `DeriveWhere`.

src/attr/skip.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ impl SkipGroup {
270270
| Trait::Ord
271271
| Trait::PartialEq
272272
| Trait::PartialOrd => true,
273+
#[cfg(feature = "serde")]
274+
Trait::Deserialize => false,
273275
#[cfg(feature = "zeroize")]
274276
Trait::Zeroize | Trait::ZeroizeOnDrop => true,
275277
}

src/input.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Parses [`DeriveInput`] into something more useful.
22
33
use proc_macro2::Span;
4-
use syn::{DeriveInput, GenericParam, Generics, ImplGenerics, Result, TypeGenerics, WhereClause};
4+
use syn::{
5+
DeriveInput, GenericParam, Generics, ImplGenerics, Path, Result, TypeGenerics, WhereClause,
6+
};
57

68
#[cfg(not(feature = "nightly"))]
79
use crate::Discriminant;
@@ -14,6 +16,8 @@ use crate::{Data, DeriveWhere, Either, Error, Item, ItemAttr, Trait};
1416

1517
/// Parsed input.
1618
pub struct Input<'a> {
19+
/// Path to `derive_where` if set by `#[derive_where(crate = ...)]`.
20+
pub crate_: Option<Path>,
1721
/// `derive_where` attributes on the item.
1822
pub derive_wheres: Vec<DeriveWhere>,
1923
/// Generics necessary to define for an `impl`.
@@ -36,6 +40,7 @@ impl<'a> Input<'a> {
3640
) -> Result<Self> {
3741
// Parse `Attribute`s on item.
3842
let ItemAttr {
43+
crate_,
3944
skip_inner,
4045
derive_wheres,
4146
incomparable,
@@ -208,6 +213,7 @@ impl<'a> Input<'a> {
208213
let generics = SplitGenerics::new(generics);
209214

210215
Ok(Self {
216+
crate_,
211217
derive_wheres,
212218
generics,
213219
item,

src/lib.rs

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,8 @@ use input::SplitGenerics;
405405
use proc_macro2::TokenStream;
406406
use quote::{quote, quote_spanned, ToTokens};
407407
use syn::{
408-
spanned::Spanned, Attribute, DataEnum, DataStruct, DataUnion, DeriveInput, Expr, ExprLit,
409-
ExprPath, Fields, FieldsNamed, FieldsUnnamed, Lit, Meta, Path, Result, Variant,
408+
spanned::Spanned, Attribute, DataEnum, DataStruct, DataUnion, DeriveInput, Fields, FieldsNamed,
409+
FieldsUnnamed, Meta, Path, Result, Variant,
410410
};
411411
use util::MetaListExt;
412412

@@ -500,39 +500,11 @@ fn derive_where_internal(mut item: DeriveInput) -> Result<TokenStream> {
500500
let meta = nested.into_iter().next().expect("unexpected empty list");
501501

502502
if meta.path().is_ident("crate") {
503-
if let Meta::NameValue(name_value) = meta {
504-
let path = match &name_value.value {
505-
Expr::Lit(ExprLit {
506-
lit: Lit::Str(lit_str),
507-
..
508-
}) => match lit_str.parse::<Path>() {
509-
Ok(path) => path,
510-
Err(error) => {
511-
return Err(Error::path(lit_str.span(), error))
512-
}
513-
},
514-
Expr::Path(ExprPath { path, .. }) => path.clone(),
515-
_ => return Err(Error::option_syntax(name_value.value.span())),
516-
};
517-
518-
if path == util::path_from_strs(&[DERIVE_WHERE]) {
519-
return Err(Error::path_unnecessary(
520-
path.span(),
521-
&format!("::{}", DERIVE_WHERE),
522-
));
523-
}
524-
525-
match crate_ {
526-
Some(_) => {
527-
return Err(Error::option_duplicate(
528-
name_value.span(),
529-
"crate",
530-
))
531-
}
532-
None => crate_ = Some(path),
533-
}
534-
} else {
535-
return Err(Error::option_syntax(meta.span()));
503+
let (path, span) = attr::parse_crate(meta)?;
504+
505+
match crate_ {
506+
Some(_) => return Err(Error::option_duplicate(span, "crate")),
507+
None => crate_ = Some(path),
536508
}
537509
}
538510
}
@@ -573,7 +545,7 @@ fn derive_where_internal(mut item: DeriveInput) -> Result<TokenStream> {
573545
#[cfg_attr(feature = "nightly", allow_internal_unstable(core_intrinsics))]
574546
pub fn derive_where_actual(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
575547
let input = TokenStream::from(input);
576-
let item = match syn::parse2::<DeriveInput>(input) {
548+
let full_item = match syn::parse2::<DeriveInput>(input) {
577549
Ok(item) => item,
578550
Err(error) => {
579551
return error.into_compile_error().into();
@@ -583,24 +555,34 @@ pub fn derive_where_actual(input: proc_macro::TokenStream) -> proc_macro::TokenS
583555
let span = {
584556
let clean_item = DeriveInput {
585557
attrs: Vec::new(),
586-
vis: item.vis.clone(),
587-
ident: item.ident.clone(),
588-
generics: item.generics.clone(),
589-
data: item.data.clone(),
558+
vis: full_item.vis.clone(),
559+
ident: full_item.ident.clone(),
560+
generics: full_item.generics.clone(),
561+
data: full_item.data.clone(),
590562
};
591563

592564
clean_item.span()
593565
};
594566

595-
match Input::from_input(span, &item) {
567+
match Input::from_input(span, &full_item) {
596568
Ok(Input {
569+
crate_,
597570
derive_wheres,
598571
generics,
599572
item,
600573
}) => derive_wheres
601574
.iter()
602575
.flat_map(|derive_where| iter::repeat(derive_where).zip(&derive_where.traits))
603-
.map(|(derive_where, trait_)| generate_impl(derive_where, trait_, &item, &generics))
576+
.map(|(derive_where, trait_)| {
577+
generate_impl(
578+
crate_.as_ref(),
579+
&full_item,
580+
derive_where,
581+
trait_,
582+
&item,
583+
&generics,
584+
)
585+
})
604586
.collect::<TokenStream>()
605587
.into(),
606588
Err(error) => error.into_compile_error().into(),
@@ -627,6 +609,8 @@ pub fn derive_where_visited(
627609

628610
/// Generate implementation for a [`Trait`].
629611
fn generate_impl(
612+
crate_: Option<&Path>,
613+
full_item: &DeriveInput,
630614
derive_where: &DeriveWhere,
631615
trait_: &DeriveTrait,
632616
item: &Item,
@@ -643,7 +627,7 @@ fn generate_impl(
643627
let body = generate_body(derive_where, trait_, item, generics);
644628

645629
let ident = item.ident();
646-
let mut output = trait_.impl_item(imp, ident, ty, &where_clause, body);
630+
let mut output = trait_.impl_item(crate_, full_item, imp, ident, ty, &where_clause, body);
647631

648632
if let Some((path, body)) = trait_.additional_impl() {
649633
output.extend(quote! {
@@ -734,3 +718,14 @@ fn input_without_derive_where_attributes(mut input: DeriveInput) -> DeriveInput
734718

735719
input
736720
}
721+
722+
/// This is used by the Serde implementation to remove the duplicate item.
723+
#[cfg(feature = "serde")]
724+
#[doc(hidden)]
725+
#[proc_macro_attribute]
726+
pub fn derive_where_serde(
727+
_: proc_macro::TokenStream,
728+
_: proc_macro::TokenStream,
729+
) -> proc_macro::TokenStream {
730+
proc_macro::TokenStream::new()
731+
}

src/test/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,28 @@ fn compiles(input: TokenStream) -> Result<()> {
3737
fn derive_where_internal(input: TokenStream) -> Result<TokenStream> {
3838
// Save `Span` before we consume `input` when parsing it.
3939
let span = input.span();
40-
let item = syn::parse2::<DeriveInput>(input).expect("derive on unparsable item");
40+
let full_item = syn::parse2::<DeriveInput>(input).expect("derive on unparsable item");
4141

4242
let Input {
43+
crate_,
4344
derive_wheres,
4445
generics,
4546
item,
4647
..
47-
} = Input::from_input(span, &item)?;
48+
} = Input::from_input(span, &full_item)?;
4849

4950
Ok(derive_wheres
5051
.iter()
5152
.flat_map(|derive_where| iter::repeat(derive_where).zip(&derive_where.traits))
52-
.map(|(derive_where, trait_)| generate_impl(derive_where, trait_, &item, &generics))
53+
.map(|(derive_where, trait_)| {
54+
generate_impl(
55+
crate_.as_ref(),
56+
&full_item,
57+
derive_where,
58+
trait_,
59+
&item,
60+
&generics,
61+
)
62+
})
5363
.collect())
5464
}

src/trait_.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ mod common_ord;
55
pub mod copy;
66
pub mod debug;
77
pub mod default;
8+
#[cfg(feature = "serde")]
9+
pub mod deserialize;
810
pub mod eq;
911
pub mod hash;
1012
pub mod ord;
@@ -23,8 +25,8 @@ use syn::{
2325
parse::{Parse, ParseStream},
2426
punctuated::Punctuated,
2527
spanned::Spanned,
26-
Ident, ImplGenerics, Meta, Path, Result, Token, TraitBound, TraitBoundModifier, TypeGenerics,
27-
TypeParamBound, WhereClause,
28+
DeriveInput, Ident, ImplGenerics, Meta, Path, Result, Token, TraitBound, TraitBoundModifier,
29+
TypeGenerics, TypeParamBound, WhereClause,
2830
};
2931

3032
use crate::{util::MetaListExt, Data, DeriveWhere, Error, Item, SplitGenerics};
@@ -41,6 +43,9 @@ pub enum Trait {
4143
Debug,
4244
/// [`Default`].
4345
Default,
46+
/// [`Deserialize`](https://docs.rs/serde/latest/serde/derive.Deserialize.html).
47+
#[cfg(feature = "serde")]
48+
Deserialize,
4449
/// [`Eq`].
4550
Eq,
4651
/// [`Hash`](std::hash::Hash).
@@ -66,6 +71,8 @@ macro_rules! trait_dispatch {
6671
Trait::Copy => copy::Copy::$method($($par),*),
6772
Trait::Debug => debug::Debug::$method($($par),*),
6873
Trait::Default => default::Default::$method($($par),*),
74+
#[cfg(feature = "serde")]
75+
Trait::Deserialize => deserialize::Deserialize::$method($($par),*),
6976
Trait::Eq => eq::Eq::$method($($par),*),
7077
Trait::Hash => hash::Hash::$method($($par),*),
7178
Trait::Ord => ord::Ord::$method($($par),*),
@@ -90,6 +97,8 @@ impl Trait {
9097
"Copy" => Ok(Copy),
9198
"Debug" => Ok(Debug),
9299
"Default" => Ok(Default),
100+
#[cfg(feature = "serde")]
101+
"Deserialize" => Ok(Deserialize),
93102
"Eq" => Ok(Eq),
94103
"Hash" => Ok(Hash),
95104
"Ord" => Ok(Ord),
@@ -148,6 +157,9 @@ pub enum DeriveTrait {
148157
Debug,
149158
/// [`Default`].
150159
Default,
160+
/// [`Deserialize`](https://docs.rs/serde/latest/serde/derive.Deserialize.html).
161+
#[cfg(feature = "serde")]
162+
Deserialize(deserialize::Deserialize),
151163
/// [`Eq`].
152164
Eq,
153165
/// [`Hash`](std::hash::Hash).
@@ -177,6 +189,8 @@ impl Deref for DeriveTrait {
177189
Copy => &copy::Copy,
178190
Debug => &debug::Debug,
179191
Default => &default::Default,
192+
#[cfg(feature = "serde")]
193+
Deserialize(trait_) => trait_,
180194
Eq => &eq::Eq,
181195
Hash => &hash::Hash,
182196
Ord => &ord::Ord,
@@ -294,8 +308,11 @@ pub trait TraitImpl: Deref<Target = Trait> {
294308

295309
/// Trait to implement. Only used by [`Eq`] and
296310
/// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html).
311+
#[allow(clippy::too_many_arguments)]
297312
fn impl_item(
298313
&self,
314+
_crate_: Option<&Path>,
315+
_full_item: &DeriveInput,
299316
imp: &ImplGenerics<'_>,
300317
ident: &Ident,
301318
ty: &TypeGenerics<'_>,

0 commit comments

Comments
 (0)