Repository: dtolnay/syn
Pull request #1753 (2024-10-20 03:39:13)

Add TypeParamBound::PreciseCapture
Closes https://github.com/dtolnay/syn/issues/1677.


Patch:
diff --git a/src/classify.rs b/src/classify.rs
index c125fc21..b3a6ae96 100644
--- a/src/classify.rs
+++ b/src/classify.rs
@@ -204,7 +204,9 @@ pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool {
     ) -> ControlFlow<bool, &Type> {
         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 037da1bc..be2b6984 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 54a12dba..aa42e32c 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 cffa5d29..128e8991 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 ab794056..1e6b724a 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>(
+    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>(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>(
+    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<F>(
@@ -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 51345ead..04f23453 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<H>(&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<H>(&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 419fb593..b82f2750 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 997ef266..c7458b13 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>(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>(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>(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>(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 5b26af7e..bbc32d3e 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&lt;&hellip;&gt;' 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<CapturedParam, Token![,]>,
+        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<T>() -> impl Trait + use<T>` or `fn f<const K: T>() ->
+        /// impl Trait + use<K>`.
+        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<Self> {
             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::<Token![<]>()?;
-                loop {
-                    let lookahead = input.lookahead1();
-                    if lookahead.peek(Lifetime) {
-                        input.parse::<Lifetime>()?;
-                    } else if lookahead.peek(Ident) {
-                        input.parse::<Ident>()?;
-                    } 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::<Token![,]>()
+                        } 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::<Token![,]>()?;
-                    } 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 5bb751ab..fdda6a87 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 be514f0b..ff43ae56 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 68ac006d..510349f3 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"


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index d900ece2..d766a8e3 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -314,6 +314,27 @@ impl Debug for Lite<syn::BoundLifetimes> {
         formatter.finish()
     }
 }
+impl Debug for Lite<syn::CapturedParam> {
+    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<syn::ConstParam> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("ConstParam");
@@ -3566,6 +3587,15 @@ impl Debug for Lite<syn::PointerMutability> {
         }
     }
 }
+impl Debug for Lite<syn::PreciseCapture> {
+    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<syn::PredicateLifetime> {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let mut formatter = formatter.debug_struct("PredicateLifetime");
@@ -4388,6 +4418,13 @@ impl Debug for Lite<syn::TypeParamBound> {
                 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 e54d7cbc..ea55d945 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![,],
+                ],
+            }),
         ],
     }
     "#);