Skip to content

Commit e9563a8

Browse files
committed
Allow lifetime for bounds in non-binded generic params
1 parent ad5422d commit e9563a8

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

src/attr/item.rs

Lines changed: 23 additions & 10 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+
Type::Path(TypePath { qself: None, path }),
247+
)) => {
245248
if let Some(ident) = path.get_ident() {
246249
ident == type_param
247250
} else {
@@ -281,8 +284,8 @@ 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,
287+
Generic::NoBound(GenericNoBound(lifetimes, path)) => PredicateType {
288+
lifetimes: lifetimes.clone(),
286289
bounded_ty: path.clone(),
287290
colon_token: <Token![:]>::default(),
288291
bounds: trait_.where_bounds(item),
@@ -293,21 +296,31 @@ impl DeriveWhere {
293296
}
294297
}
295298

296-
/// Holds a single generic [type](Type) or [type with bound](PredicateType).
299+
/// Holds the first part of a [`PredicateType`] prior to the `:`. Optionally contains lifetime `for`
300+
/// bindings.
301+
#[derive(Eq, PartialEq)]
302+
pub struct GenericNoBound(Option<BoundLifetimes>, Type);
303+
impl Parse for GenericNoBound {
304+
fn parse(input: ParseStream) -> Result<Self> {
305+
Ok(Self(input.parse()?, input.parse()?))
306+
}
307+
}
308+
309+
/// Holds a single generic [type](GenericNoBound) with optional lifetime bounds or [type with bound](PredicateType).
297310
#[derive(Eq, PartialEq)]
298311
pub enum Generic {
299312
/// Generic type with custom [specified bounds](PredicateType).
300313
CustomBound(PredicateType),
301-
/// Generic [type](Type) which will be bound to the [`DeriveTrait`].
302-
NoBound(Type),
314+
/// Generic [type](GenericNoBound) which will be bound to the [`DeriveTrait`].
315+
NoBound(GenericNoBound),
303316
}
304317

305318
impl Parse for Generic {
306319
fn parse(input: ParseStream) -> Result<Self> {
307320
let fork = input.fork();
308321

309322
// 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
323+
// start with an optional lifetime for bound and then Type, so starting with the `WherePredicate` is the easiest way
311324
// of differentiating them.
312325
if let Ok(where_predicate) = WherePredicate::parse(&fork) {
313326
input.advance_to(&fork);
@@ -319,8 +332,8 @@ impl Parse for Generic {
319332
Err(Error::generic(where_predicate.span()))
320333
}
321334
} else {
322-
match Type::parse(input) {
323-
Ok(type_) => Ok(Generic::NoBound(type_)),
335+
match GenericNoBound::parse(input) {
336+
Ok(no_bound) => Ok(Generic::NoBound(no_bound)),
324337
Err(error) => Err(Error::generic_syntax(error.span(), error)),
325338
}
326339
}

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)