From 94d7d5eff9b4cc5871b4921f5c904595609e457a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 17 Oct 2024 15:57:28 -0700 Subject: [PATCH] Add TypeParamBound::PreciseCapture --- src/classify.rs | 8 ++- src/gen/clone.rs | 30 +++++++++++ src/gen/debug.rs | 39 ++++++++++++++ src/gen/eq.rs | 34 ++++++++++++ src/gen/fold.rs | 52 ++++++++++++++++++ src/gen/hash.rs | 38 +++++++++++++- src/gen/visit.rs | 42 +++++++++++++++ src/gen/visit_mut.rs | 42 +++++++++++++++ src/generics.rs | 122 +++++++++++++++++++++++++++++++++---------- src/lib.rs | 3 ++ src/ty.rs | 32 +++++++----- syn.json | 53 +++++++++++++++++++ tests/debug/gen.rs | 37 +++++++++++++ tests/test_ty.rs | 25 ++++++++- 14 files changed, 511 insertions(+), 46 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index c125fc2164..b3a6ae960e 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -204,7 +204,9 @@ pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { ) -> ControlFlow { match bounds.last().unwrap() { TypeParamBound::Trait(t) => last_type_in_path(&t.path), - TypeParamBound::Lifetime(_) | TypeParamBound::Verbatim(_) => ControlFlow::Break(false), + TypeParamBound::Lifetime(_) + | TypeParamBound::PreciseCapture(_) + | TypeParamBound::Verbatim(_) => ControlFlow::Break(false), } } } @@ -378,7 +380,9 @@ pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { Some(t) => ControlFlow::Continue(t), None => ControlFlow::Break(false), }, - TypeParamBound::Lifetime(_) => ControlFlow::Break(false), + TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => { + ControlFlow::Break(false) + } TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)), } } diff --git a/src/gen/clone.rs b/src/gen/clone.rs index 037da1bc35..be2b698422 100644 --- a/src/gen/clone.rs +++ b/src/gen/clone.rs @@ -139,6 +139,18 @@ impl Clone for crate::BoundLifetimes { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] +impl Clone for crate::CapturedParam { + fn clone(&self) -> Self { + match self { + crate::CapturedParam::Lifetime(v0) => { + crate::CapturedParam::Lifetime(v0.clone()) + } + crate::CapturedParam::Ident(v0) => crate::CapturedParam::Ident(v0.clone()), + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] impl Clone for crate::ConstParam { @@ -1676,6 +1688,18 @@ impl Clone for crate::PointerMutability { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] +impl Clone for crate::PreciseCapture { + fn clone(&self) -> Self { + crate::PreciseCapture { + use_token: self.use_token.clone(), + lt_token: self.lt_token.clone(), + params: self.params.clone(), + gt_token: self.gt_token.clone(), + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "clone-impls")))] impl Clone for crate::PredicateLifetime { @@ -2011,9 +2035,15 @@ impl Clone for crate::TypeParamBound { crate::TypeParamBound::Lifetime(v0) => { crate::TypeParamBound::Lifetime(v0.clone()) } + #[cfg(feature = "full")] + crate::TypeParamBound::PreciseCapture(v0) => { + crate::TypeParamBound::PreciseCapture(v0.clone()) + } crate::TypeParamBound::Verbatim(v0) => { crate::TypeParamBound::Verbatim(v0.clone()) } + #[cfg(not(feature = "full"))] + _ => unreachable!(), } } } diff --git a/src/gen/debug.rs b/src/gen/debug.rs index 54a12dba2a..aa42e32c60 100644 --- a/src/gen/debug.rs +++ b/src/gen/debug.rs @@ -290,6 +290,25 @@ impl Debug for crate::BoundLifetimes { formatter.finish() } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Debug for crate::CapturedParam { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("CapturedParam::")?; + match self { + crate::CapturedParam::Lifetime(v0) => { + let mut formatter = formatter.debug_tuple("Lifetime"); + formatter.field(v0); + formatter.finish() + } + crate::CapturedParam::Ident(v0) => { + let mut formatter = formatter.debug_tuple("Ident"); + formatter.field(v0); + formatter.finish() + } + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for crate::ConstParam { @@ -2425,6 +2444,18 @@ impl Debug for crate::PointerMutability { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Debug for crate::PreciseCapture { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = formatter.debug_struct("PreciseCapture"); + formatter.field("use_token", &self.use_token); + formatter.field("lt_token", &self.lt_token); + formatter.field("params", &self.params); + formatter.field("gt_token", &self.gt_token); + formatter.finish() + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Debug for crate::PredicateLifetime { @@ -2878,11 +2909,19 @@ impl Debug for crate::TypeParamBound { formatter.finish() } crate::TypeParamBound::Lifetime(v0) => v0.debug(formatter, "Lifetime"), + #[cfg(feature = "full")] + crate::TypeParamBound::PreciseCapture(v0) => { + let mut formatter = formatter.debug_tuple("PreciseCapture"); + formatter.field(v0); + formatter.finish() + } crate::TypeParamBound::Verbatim(v0) => { let mut formatter = formatter.debug_tuple("Verbatim"); formatter.field(v0); formatter.finish() } + #[cfg(not(feature = "full"))] + _ => unreachable!(), } } } diff --git a/src/gen/eq.rs b/src/gen/eq.rs index cffa5d296d..128e8991ee 100644 --- a/src/gen/eq.rs +++ b/src/gen/eq.rs @@ -160,6 +160,25 @@ impl PartialEq for crate::BoundLifetimes { self.lifetimes == other.lifetimes } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Eq for crate::CapturedParam {} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl PartialEq for crate::CapturedParam { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + crate::CapturedParam::Lifetime(self0), + crate::CapturedParam::Lifetime(other0), + ) => self0 == other0, + (crate::CapturedParam::Ident(self0), crate::CapturedParam::Ident(other0)) => { + self0 == other0 + } + _ => false, + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Eq for crate::ConstParam {} @@ -1653,6 +1672,16 @@ impl PartialEq for crate::PointerMutability { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Eq for crate::PreciseCapture {} +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl PartialEq for crate::PreciseCapture { + fn eq(&self, other: &Self) -> bool { + self.params == other.params + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Eq for crate::PredicateLifetime {} @@ -2020,6 +2049,11 @@ impl PartialEq for crate::TypeParamBound { crate::TypeParamBound::Lifetime(self0), crate::TypeParamBound::Lifetime(other0), ) => self0 == other0, + #[cfg(feature = "full")] + ( + crate::TypeParamBound::PreciseCapture(self0), + crate::TypeParamBound::PreciseCapture(other0), + ) => self0 == other0, ( crate::TypeParamBound::Verbatim(self0), crate::TypeParamBound::Verbatim(other0), diff --git a/src/gen/fold.rs b/src/gen/fold.rs index ab794056c8..1e6b724a67 100644 --- a/src/gen/fold.rs +++ b/src/gen/fold.rs @@ -91,6 +91,11 @@ pub trait Fold { ) -> crate::BoundLifetimes { fold_bound_lifetimes(self, i) } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn fold_captured_param(&mut self, i: crate::CapturedParam) -> crate::CapturedParam { + fold_captured_param(self, i) + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn fold_const_param(&mut self, i: crate::ConstParam) -> crate::ConstParam { @@ -736,6 +741,14 @@ pub trait Fold { ) -> crate::PointerMutability { fold_pointer_mutability(self, i) } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn fold_precise_capture( + &mut self, + i: crate::PreciseCapture, + ) -> crate::PreciseCapture { + fold_precise_capture(self, i) + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn fold_predicate_lifetime( @@ -1180,6 +1193,24 @@ where gt_token: node.gt_token, } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn fold_captured_param( + f: &mut F, + node: crate::CapturedParam, +) -> crate::CapturedParam +where + F: Fold + ?Sized, +{ + match node { + crate::CapturedParam::Lifetime(_binding_0) => { + crate::CapturedParam::Lifetime(f.fold_lifetime(_binding_0)) + } + crate::CapturedParam::Ident(_binding_0) => { + crate::CapturedParam::Ident(f.fold_ident(_binding_0)) + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn fold_const_param(f: &mut F, node: crate::ConstParam) -> crate::ConstParam @@ -3108,6 +3139,22 @@ where } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn fold_precise_capture( + f: &mut F, + node: crate::PreciseCapture, +) -> crate::PreciseCapture +where + F: Fold + ?Sized, +{ + crate::PreciseCapture { + use_token: node.use_token, + lt_token: node.lt_token, + params: crate::punctuated::fold(node.params, f, F::fold_captured_param), + gt_token: node.gt_token, + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn fold_predicate_lifetime( @@ -3549,6 +3596,11 @@ where crate::TypeParamBound::Lifetime(_binding_0) => { crate::TypeParamBound::Lifetime(f.fold_lifetime(_binding_0)) } + crate::TypeParamBound::PreciseCapture(_binding_0) => { + crate::TypeParamBound::PreciseCapture( + full!(f.fold_precise_capture(_binding_0)), + ) + } crate::TypeParamBound::Verbatim(_binding_0) => { crate::TypeParamBound::Verbatim(_binding_0) } diff --git a/src/gen/hash.rs b/src/gen/hash.rs index 51345eada3..04f23453a1 100644 --- a/src/gen/hash.rs +++ b/src/gen/hash.rs @@ -230,6 +230,25 @@ impl Hash for crate::BoundLifetimes { self.lifetimes.hash(state); } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Hash for crate::CapturedParam { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + match self { + crate::CapturedParam::Lifetime(v0) => { + state.write_u8(0u8); + v0.hash(state); + } + crate::CapturedParam::Ident(v0) => { + state.write_u8(1u8); + v0.hash(state); + } + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Hash for crate::ConstParam { @@ -2120,6 +2139,16 @@ impl Hash for crate::PointerMutability { } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] +impl Hash for crate::PreciseCapture { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.params.hash(state); + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(feature = "extra-traits")))] impl Hash for crate::PredicateLifetime { @@ -2568,10 +2597,17 @@ impl Hash for crate::TypeParamBound { state.write_u8(1u8); v0.hash(state); } - crate::TypeParamBound::Verbatim(v0) => { + #[cfg(feature = "full")] + crate::TypeParamBound::PreciseCapture(v0) => { state.write_u8(2u8); + v0.hash(state); + } + crate::TypeParamBound::Verbatim(v0) => { + state.write_u8(3u8); TokenStreamHelper(v0).hash(state); } + #[cfg(not(feature = "full"))] + _ => unreachable!(), } } } diff --git a/src/gen/visit.rs b/src/gen/visit.rs index 419fb5930c..b82f2750c5 100644 --- a/src/gen/visit.rs +++ b/src/gen/visit.rs @@ -89,6 +89,11 @@ pub trait Visit<'ast> { fn visit_bound_lifetimes(&mut self, i: &'ast crate::BoundLifetimes) { visit_bound_lifetimes(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_captured_param(&mut self, i: &'ast crate::CapturedParam) { + visit_captured_param(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_const_param(&mut self, i: &'ast crate::ConstParam) { @@ -695,6 +700,11 @@ pub trait Visit<'ast> { fn visit_pointer_mutability(&mut self, i: &'ast crate::PointerMutability) { visit_pointer_mutability(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_precise_capture(&mut self, i: &'ast crate::PreciseCapture) { + visit_precise_capture(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_predicate_lifetime(&mut self, i: &'ast crate::PredicateLifetime) { @@ -1179,6 +1189,21 @@ where } skip!(node.gt_token); } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_captured_param<'ast, V>(v: &mut V, node: &'ast crate::CapturedParam) +where + V: Visit<'ast> + ?Sized, +{ + match node { + crate::CapturedParam::Lifetime(_binding_0) => { + v.visit_lifetime(_binding_0); + } + crate::CapturedParam::Ident(_binding_0) => { + v.visit_ident(_binding_0); + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_const_param<'ast, V>(v: &mut V, node: &'ast crate::ConstParam) @@ -3184,6 +3209,20 @@ where } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_precise_capture<'ast, V>(v: &mut V, node: &'ast crate::PreciseCapture) +where + V: Visit<'ast> + ?Sized, +{ + skip!(node.use_token); + skip!(node.lt_token); + for el in Punctuated::pairs(&node.params) { + let it = el.value(); + v.visit_captured_param(it); + } + skip!(node.gt_token); +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_predicate_lifetime<'ast, V>(v: &mut V, node: &'ast crate::PredicateLifetime) @@ -3640,6 +3679,9 @@ where crate::TypeParamBound::Lifetime(_binding_0) => { v.visit_lifetime(_binding_0); } + crate::TypeParamBound::PreciseCapture(_binding_0) => { + full!(v.visit_precise_capture(_binding_0)); + } crate::TypeParamBound::Verbatim(_binding_0) => { skip!(_binding_0); } diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs index 997ef266bf..c7458b1312 100644 --- a/src/gen/visit_mut.rs +++ b/src/gen/visit_mut.rs @@ -90,6 +90,11 @@ pub trait VisitMut { fn visit_bound_lifetimes_mut(&mut self, i: &mut crate::BoundLifetimes) { visit_bound_lifetimes_mut(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_captured_param_mut(&mut self, i: &mut crate::CapturedParam) { + visit_captured_param_mut(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_const_param_mut(&mut self, i: &mut crate::ConstParam) { @@ -696,6 +701,11 @@ pub trait VisitMut { fn visit_pointer_mutability_mut(&mut self, i: &mut crate::PointerMutability) { visit_pointer_mutability_mut(self, i); } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + fn visit_precise_capture_mut(&mut self, i: &mut crate::PreciseCapture) { + visit_precise_capture_mut(self, i); + } #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] fn visit_predicate_lifetime_mut(&mut self, i: &mut crate::PredicateLifetime) { @@ -1180,6 +1190,21 @@ where } skip!(node.gt_token); } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_captured_param_mut(v: &mut V, node: &mut crate::CapturedParam) +where + V: VisitMut + ?Sized, +{ + match node { + crate::CapturedParam::Lifetime(_binding_0) => { + v.visit_lifetime_mut(_binding_0); + } + crate::CapturedParam::Ident(_binding_0) => { + v.visit_ident_mut(_binding_0); + } + } +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_const_param_mut(v: &mut V, node: &mut crate::ConstParam) @@ -3184,6 +3209,20 @@ where } } } +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub fn visit_precise_capture_mut(v: &mut V, node: &mut crate::PreciseCapture) +where + V: VisitMut + ?Sized, +{ + skip!(node.use_token); + skip!(node.lt_token); + for mut el in Punctuated::pairs_mut(&mut node.params) { + let it = el.value_mut(); + v.visit_captured_param_mut(it); + } + skip!(node.gt_token); +} #[cfg(any(feature = "derive", feature = "full"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "derive", feature = "full"))))] pub fn visit_predicate_lifetime_mut(v: &mut V, node: &mut crate::PredicateLifetime) @@ -3637,6 +3676,9 @@ where crate::TypeParamBound::Lifetime(_binding_0) => { v.visit_lifetime_mut(_binding_0); } + crate::TypeParamBound::PreciseCapture(_binding_0) => { + full!(v.visit_precise_capture_mut(_binding_0)); + } crate::TypeParamBound::Verbatim(_binding_0) => { skip!(_binding_0); } diff --git a/src/generics.rs b/src/generics.rs index 5b26af7e57..bbc32d3ed4 100644 --- a/src/generics.rs +++ b/src/generics.rs @@ -426,6 +426,7 @@ ast_enum_of_structs! { pub enum TypeParamBound { Trait(TraitBound), Lifetime(Lifetime), + PreciseCapture(PreciseCapture), Verbatim(TokenStream), } } @@ -453,6 +454,34 @@ ast_enum! { } } +ast_struct! { + /// Precise capturing bound: the 'use<…>' in `impl Trait + + /// use<'a, T>`. + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + pub struct PreciseCapture #full { + pub use_token: Token![use], + pub lt_token: Token![<], + pub params: Punctuated, + pub gt_token: Token![>], + } +} + +#[cfg(feature = "full")] +ast_enum! { + /// Single parameter in a precise capturing bound. + #[cfg_attr(docsrs, doc(cfg(feature = "full")))] + #[non_exhaustive] + pub enum CapturedParam { + /// A lifetime parameter in precise capturing bound: `fn f<'a>() -> impl + /// Trait + use<'a>`. + Lifetime(Lifetime), + /// A type parameter or const generic parameter in precise capturing + /// bound: `fn f() -> impl Trait + use` or `fn f() -> + /// impl Trait + use`. + Ident(Ident), + } +} + ast_struct! { /// A `where` clause in a definition: `where T: Deserialize<'de>, D: /// 'static`. @@ -516,6 +545,8 @@ pub(crate) mod parsing { PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, WherePredicate, }; + #[cfg(feature = "full")] + use crate::generics::{CapturedParam, PreciseCapture}; use crate::ident::Ident; use crate::lifetime::Lifetime; use crate::parse::{Parse, ParseStream}; @@ -753,7 +784,7 @@ pub(crate) mod parsing { impl TypeParamBound { pub(crate) fn parse_single( input: ParseStream, - allow_precise_capture: bool, + #[cfg_attr(not(feature = "full"), allow(unused_variables))] allow_precise_capture: bool, allow_tilde_const: bool, ) -> Result { if input.peek(Lifetime) { @@ -762,36 +793,47 @@ pub(crate) mod parsing { let begin = input.fork(); - if cfg!(feature = "full") && input.peek(Token![use]) { - let use_token: Token![use] = input.parse()?; - input.parse::()?; - loop { - let lookahead = input.lookahead1(); - if lookahead.peek(Lifetime) { - input.parse::()?; - } else if lookahead.peek(Ident) { - input.parse::()?; - } else if lookahead.peek(Token![>]) { - break; - } else { - return Err(lookahead.error()); + #[cfg(feature = "full")] + { + if input.peek(Token![use]) { + let use_token: Token![use] = input.parse()?; + let lt_token: Token![<] = input.parse()?; + let mut params = Punctuated::new(); + loop { + let lookahead = input.lookahead1(); + let capture = if lookahead.peek(Lifetime) { + input.parse().map(CapturedParam::Lifetime) + } else if lookahead.peek(Ident) { + input.parse().map(CapturedParam::Ident) + } else if lookahead.peek(Token![>]) { + break; + } else { + return Err(lookahead.error()); + }?; + params.push_value(capture); + let lookahead = input.lookahead1(); + let comma = if lookahead.peek(Token![,]) { + input.parse::() + } else if lookahead.peek(Token![>]) { + break; + } else { + return Err(lookahead.error()); + }?; + params.push_punct(comma); } - let lookahead = input.lookahead1(); - if lookahead.peek(Token![,]) { - input.parse::()?; - } else if lookahead.peek(Token![>]) { - break; + let gt_token: Token![>] = input.parse()?; + return if allow_precise_capture { + Ok(TypeParamBound::PreciseCapture(PreciseCapture { + use_token, + lt_token, + params, + gt_token, + })) } else { - return Err(lookahead.error()); - } + let msg = "`use<...>` precise capturing syntax is not allowed here"; + Err(error::new2(use_token.span, gt_token.span, msg)) + }; } - let gt_token: Token![>] = input.parse()?; - return if allow_precise_capture { - Ok(TypeParamBound::Verbatim(verbatim::between(&begin, input))) - } else { - let msg = "`use<...>` precise capturing syntax is not allowed here"; - Err(error::new2(use_token.span, gt_token.span, msg)) - }; } let content; @@ -1035,6 +1077,8 @@ pub(crate) mod printing { PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, Turbofish, TypeGenerics, TypeParam, WhereClause, }; + #[cfg(feature = "full")] + use crate::generics::{CapturedParam, PreciseCapture}; use crate::print::TokensOrDefault; use crate::token; use proc_macro2::TokenStream; @@ -1288,6 +1332,28 @@ pub(crate) mod printing { } } + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] + impl ToTokens for PreciseCapture { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.use_token.to_tokens(tokens); + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } + } + + #[cfg(feature = "full")] + #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] + impl ToTokens for CapturedParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + CapturedParam::Lifetime(lifetime) => lifetime.to_tokens(tokens), + CapturedParam::Ident(ident) => ident.to_tokens(tokens), + } + } + } + pub(crate) fn print_const_argument(expr: &Expr, tokens: &mut TokenStream) { match expr { Expr::Lit(expr) => expr.to_tokens(tokens), diff --git a/src/lib.rs b/src/lib.rs index 5bb751abdb..fdda6a8713 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -401,6 +401,9 @@ pub use crate::generics::{ PredicateType, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, WhereClause, WherePredicate, }; +#[cfg(feature = "full")] +#[cfg_attr(docsrs, doc(cfg(feature = "full")))] +pub use crate::generics::{CapturedParam, PreciseCapture}; #[cfg(all(any(feature = "full", feature = "derive"), feature = "printing"))] #[cfg_attr( docsrs, diff --git a/src/ty.rs b/src/ty.rs index be514f0b0a..ff43ae560e 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -290,7 +290,7 @@ pub(crate) mod parsing { TypeReference, TypeSlice, TypeTraitObject, TypeTuple, }; use crate::verbatim; - use proc_macro2::{Span, TokenTree}; + use proc_macro2::Span; #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for Type { @@ -465,6 +465,7 @@ pub(crate) mod parsing { }) } other @ (TypeParamBound::Lifetime(_) + | TypeParamBound::PreciseCapture(_) | TypeParamBound::Verbatim(_)) => other, } } @@ -866,7 +867,9 @@ pub(crate) mod parsing { TypeParamBound::Lifetime(lifetime) => { last_lifetime_span = Some(lifetime.ident.span()); } - TypeParamBound::Verbatim(_) => unreachable!(), + TypeParamBound::PreciseCapture(_) | TypeParamBound::Verbatim(_) => { + unreachable!() + } } } // Just lifetimes like `'a + 'b` is not a TraitObject. @@ -914,18 +917,21 @@ pub(crate) mod parsing { TypeParamBound::Lifetime(lifetime) => { last_nontrait_span = Some(lifetime.ident.span()); } - TypeParamBound::Verbatim(verbatim) => { - let mut tokens = verbatim.clone().into_iter(); - match tokens.next().unwrap() { - TokenTree::Ident(ident) if ident == "use" => { - last_nontrait_span = Some(tokens.last().unwrap().span()); - } - _ => { - // ~const Trait - at_least_one_trait = true; - break; - } + TypeParamBound::PreciseCapture(precise_capture) => { + #[cfg(feature = "full")] + { + last_nontrait_span = Some(precise_capture.gt_token.span); } + #[cfg(not(feature = "full"))] + { + _ = precise_capture; + unreachable!(); + } + } + TypeParamBound::Verbatim(_) => { + // ~const Trait + at_least_one_trait = true; + break; } } } diff --git a/syn.json b/syn.json index 68ac006de8..510349f333 100644 --- a/syn.json +++ b/syn.json @@ -450,6 +450,27 @@ } } }, + { + "ident": "CapturedParam", + "features": { + "any": [ + "full" + ] + }, + "variants": { + "Lifetime": [ + { + "syn": "Lifetime" + } + ], + "Ident": [ + { + "proc_macro2": "Ident" + } + ] + }, + "exhaustive": false + }, { "ident": "ConstParam", "features": { @@ -4206,6 +4227,33 @@ ] } }, + { + "ident": "PreciseCapture", + "features": { + "any": [ + "full" + ] + }, + "fields": { + "use_token": { + "token": "Use" + }, + "lt_token": { + "token": "Lt" + }, + "params": { + "punctuated": { + "element": { + "syn": "CapturedParam" + }, + "punct": "Comma" + } + }, + "gt_token": { + "token": "Gt" + } + } + }, { "ident": "PredicateLifetime", "features": { @@ -5045,6 +5093,11 @@ "syn": "Lifetime" } ], + "PreciseCapture": [ + { + "syn": "PreciseCapture" + } + ], "Verbatim": [ { "proc_macro2": "TokenStream" diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs index d900ece2b1..d766a8e300 100644 --- a/tests/debug/gen.rs +++ b/tests/debug/gen.rs @@ -314,6 +314,27 @@ impl Debug for Lite { formatter.finish() } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match &self.value { + syn::CapturedParam::Lifetime(_val) => { + formatter.write_str("CapturedParam::Lifetime")?; + formatter.write_str("(")?; + Debug::fmt(Lite(_val), formatter)?; + formatter.write_str(")")?; + Ok(()) + } + syn::CapturedParam::Ident(_val) => { + formatter.write_str("CapturedParam::Ident")?; + formatter.write_str("(")?; + Debug::fmt(Lite(_val), formatter)?; + formatter.write_str(")")?; + Ok(()) + } + _ => unreachable!(), + } + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut formatter = formatter.debug_struct("ConstParam"); @@ -3566,6 +3587,15 @@ impl Debug for Lite { } } } +impl Debug for Lite { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = formatter.debug_struct("PreciseCapture"); + if !self.value.params.is_empty() { + formatter.field("params", Lite(&self.value.params)); + } + formatter.finish() + } +} impl Debug for Lite { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut formatter = formatter.debug_struct("PredicateLifetime"); @@ -4388,6 +4418,13 @@ impl Debug for Lite { formatter.field("ident", Lite(&_val.ident)); formatter.finish() } + syn::TypeParamBound::PreciseCapture(_val) => { + formatter.write_str("TypeParamBound::PreciseCapture")?; + formatter.write_str("(")?; + Debug::fmt(Lite(_val), formatter)?; + formatter.write_str(")")?; + Ok(()) + } syn::TypeParamBound::Verbatim(_val) => { formatter.write_str("TypeParamBound::Verbatim")?; formatter.write_str("(`")?; diff --git a/tests/test_ty.rs b/tests/test_ty.rs index e54d7cbc31..ea55d9456a 100644 --- a/tests/test_ty.rs +++ b/tests/test_ty.rs @@ -415,7 +415,21 @@ fn test_impl_trait_use() { }, }), Token![+], - TypeParamBound::Verbatim(`use < '_ , 'a , A , Test >`), + TypeParamBound::PreciseCapture(PreciseCapture { + params: [ + CapturedParam::Lifetime(Lifetime { + ident: "_", + }), + Token![,], + CapturedParam::Lifetime(Lifetime { + ident: "a", + }), + Token![,], + CapturedParam::Ident("A"), + Token![,], + CapturedParam::Ident("Test"), + ], + }), ], } "#); @@ -437,7 +451,14 @@ fn test_impl_trait_use() { }, }), Token![+], - TypeParamBound::Verbatim(`use < '_ , >`), + TypeParamBound::PreciseCapture(PreciseCapture { + params: [ + CapturedParam::Lifetime(Lifetime { + ident: "_", + }), + Token![,], + ], + }), ], } "#);