Skip to content

Commit 440496c

Browse files
authored
Feat: Allow lifetime for bounds in non-binded generic params (#107)
1 parent 957f5c9 commit 440496c

File tree

3 files changed

+67
-12
lines changed

3 files changed

+67
-12
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- Allow lifetime `for<'a, ...>` bounds in non-bounded generic parameters.
12+
1013
### Changed
1114
- Use the `Copy` implementation for `Clone` and the `Ord` implementation for
1215
`PartialOrd` when custom bounds are present.

src/attr/item.rs

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

@@ -241,7 +241,10 @@ impl DeriveWhere {
241241
/// Returns `true` if the given generic type parameter if present.
242242
pub fn has_type_param(&self, type_param: &Ident) -> bool {
243243
self.generics.iter().any(|generic| match generic {
244-
Generic::NoBound(Type::Path(TypePath { qself: None, path })) => {
244+
Generic::NoBound(GenericNoBound {
245+
lifetimes: _,
246+
ty: Type::Path(TypePath { qself: None, path }),
247+
}) => {
245248
if let Some(ident) = path.get_ident() {
246249
ident == type_param
247250
} else {
@@ -281,9 +284,12 @@ impl DeriveWhere {
281284
.predicates
282285
.push(WherePredicate::Type(match generic {
283286
Generic::CustomBound(type_bound) => type_bound.clone(),
284-
Generic::NoBound(path) => PredicateType {
285-
lifetimes: None,
286-
bounded_ty: path.clone(),
287+
Generic::NoBound(GenericNoBound {
288+
lifetimes: bound_lifetimes,
289+
ty,
290+
}) => PredicateType {
291+
lifetimes: bound_lifetimes.clone(),
292+
bounded_ty: ty.clone(),
287293
colon_token: <Token![:]>::default(),
288294
bounds: trait_.where_bounds(item),
289295
},
@@ -293,22 +299,43 @@ impl DeriveWhere {
293299
}
294300
}
295301

296-
/// Holds a single generic [type](Type) or [type with bound](PredicateType).
302+
/// Holds the first part of a [`PredicateType`] prior to the `:`. Optionally
303+
/// contains lifetime `for` bindings.
304+
#[derive(Eq, PartialEq)]
305+
pub struct GenericNoBound {
306+
/// Any `for<'a, 'b, 'etc>` bindings for the type.
307+
lifetimes: Option<BoundLifetimes>,
308+
/// The type bound to the [`DeriveTrait`].
309+
ty: Type,
310+
}
311+
312+
impl Parse for GenericNoBound {
313+
fn parse(input: ParseStream) -> Result<Self> {
314+
Ok(Self {
315+
lifetimes: input.parse()?,
316+
ty: input.parse()?,
317+
})
318+
}
319+
}
320+
321+
/// Holds a single generic [type](GenericNoBound) with optional lifetime bounds
322+
/// or [type with bound](PredicateType).
297323
#[derive(Eq, PartialEq)]
298324
pub enum Generic {
299325
/// Generic type with custom [specified bounds](PredicateType).
300326
CustomBound(PredicateType),
301-
/// Generic [type](Type) which will be bound to the [`DeriveTrait`].
302-
NoBound(Type),
327+
/// Generic [type](GenericNoBound) which will be bound to the
328+
/// [`DeriveTrait`].
329+
NoBound(GenericNoBound),
303330
}
304331

305332
impl Parse for Generic {
306333
fn parse(input: ParseStream) -> Result<Self> {
307334
let fork = input.fork();
308335

309336
// Try to parse input as a `WherePredicate`. The problem is, both expressions
310-
// start with a Type, so starting with the `WherePredicate` is the easiest way
311-
// of differentiating them.
337+
// start with an optional lifetime for bound and then Type, so starting with the
338+
// `WherePredicate` is the easiest way of differentiating them.
312339
if let Ok(where_predicate) = WherePredicate::parse(&fork) {
313340
input.advance_to(&fork);
314341

@@ -319,8 +346,8 @@ impl Parse for Generic {
319346
Err(Error::generic(where_predicate.span()))
320347
}
321348
} else {
322-
match Type::parse(input) {
323-
Ok(type_) => Ok(Generic::NoBound(type_)),
349+
match GenericNoBound::parse(input) {
350+
Ok(no_bound) => Ok(Generic::NoBound(no_bound)),
324351
Err(error) => Err(Error::generic_syntax(error.span(), error)),
325352
}
326353
}

src/test/bound.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,31 @@ fn where_() -> Result<()> {
111111
)
112112
}
113113

114+
#[test]
115+
fn for_lifetime() -> Result<()> {
116+
test_derive(
117+
quote! {
118+
#[derive_where(Clone; for<'a> T)]
119+
struct Test<T, U>(T, std::marker::PhantomData<U>) where T: std::fmt::Debug;
120+
},
121+
quote! {
122+
#[automatically_derived]
123+
impl<T, U> ::core::clone::Clone for Test<T, U>
124+
where
125+
T: std::fmt::Debug,
126+
for<'a> T: ::core::clone::Clone
127+
{
128+
#[inline]
129+
fn clone(&self) -> Self {
130+
match self {
131+
Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)),
132+
}
133+
}
134+
}
135+
},
136+
)
137+
}
138+
114139
#[test]
115140
fn associated_type() -> Result<()> {
116141
test_derive(

0 commit comments

Comments
 (0)