Store additional token information with extern type lifetimes
diff --git a/syntax/check.rs b/syntax/check.rs
index 29013cb..fcd5f9a 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -337,7 +337,7 @@
         cx.error(derive, msg);
     }
 
-    if let Some(lifetime) = ety.lifetimes.first() {
+    if let Some(lifetime) = ety.generics.lifetimes.first() {
         cx.error(lifetime, "extern type with lifetimes is not supported yet");
     }
 
@@ -445,7 +445,7 @@
         cx.error(derive, msg);
     }
 
-    if let Some(lifetime) = alias.lifetimes.first() {
+    if let Some(lifetime) = alias.generics.lifetimes.first() {
         cx.error(lifetime, "extern type with lifetimes is not supported yet");
     }
 }
diff --git a/syntax/mod.rs b/syntax/mod.rs
index c878ae2..009afee 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -74,7 +74,7 @@
     pub derives: Vec<Derive>,
     pub type_token: Token![type],
     pub name: Pair,
-    pub lifetimes: Vec<Lifetime>,
+    pub generics: Lifetimes,
     pub colon_token: Option<Token![:]>,
     pub bounds: Vec<Derive>,
     pub semi_token: Token![;],
@@ -116,7 +116,7 @@
     pub derives: Vec<Derive>,
     pub type_token: Token![type],
     pub name: Pair,
-    pub lifetimes: Vec<Lifetime>,
+    pub generics: Lifetimes,
     pub eq_token: Token![=],
     pub ty: RustType,
     pub semi_token: Token![;],
@@ -130,6 +130,12 @@
     pub negative_token: Option<Token![!]>,
 }
 
+pub struct Lifetimes {
+    pub lt_token: Option<Token![<]>,
+    pub lifetimes: Punctuated<Lifetime, Token![,]>,
+    pub gt_token: Option<Token![>]>,
+}
+
 pub struct Signature {
     pub unsafety: Option<Token![unsafe]>,
     pub fn_token: Token![fn],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index 65e07aa..bb5ac7b 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -4,8 +4,8 @@
 use crate::syntax::Atom::*;
 use crate::syntax::{
     attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind,
-    Lang, Namespace, Pair, Receiver, Ref, RustName, Signature, SliceRef, Struct, Ty1, Type,
-    TypeAlias, Var, Variant,
+    Lang, Lifetimes, Namespace, Pair, Receiver, Ref, RustName, Signature, SliceRef, Struct, Ty1,
+    Type, TypeAlias, Var, Variant,
 };
 use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
 use quote::{format_ident, quote, quote_spanned};
@@ -13,8 +13,8 @@
 use syn::punctuated::Punctuated;
 use syn::{
     Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
-    GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lifetime, Lit,
-    LitStr, Pat, PathArguments, Result, ReturnType, Token, TraitBound, TraitBoundModifier,
+    GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
+    Pat, PathArguments, Result, ReturnType, Token, TraitBound, TraitBoundModifier,
     Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypeReference,
     Variant as RustVariant,
 };
@@ -385,7 +385,11 @@
 
     let type_token = foreign_type.type_token;
     let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name);
-    let lifetimes = Vec::new();
+    let generics = Lifetimes {
+        lt_token: None,
+        lifetimes: Punctuated::new(),
+        gt_token: None,
+    };
     let colon_token = None;
     let bounds = Vec::new();
     let semi_token = foreign_type.semi_token;
@@ -399,7 +403,7 @@
         derives,
         type_token,
         name,
-        lifetimes,
+        generics,
         colon_token,
         bounds,
         semi_token,
@@ -581,9 +585,10 @@
         };
         let ident: Ident = input.parse()?;
         let generics: Generics = input.parse()?;
-        let mut lifetimes = Vec::new();
+        let mut lifetimes = Punctuated::new();
         let mut has_unsupported_generic_param = false;
-        for param in generics.params {
+        for pair in generics.params.into_pairs() {
+            let (param, punct) = pair.into_tuple();
             match param {
                 GenericParam::Lifetime(param) => {
                     if !param.bounds.is_empty() && !has_unsupported_generic_param {
@@ -591,7 +596,10 @@
                         cx.error(&param, msg);
                         has_unsupported_generic_param = true;
                     }
-                    lifetimes.push(param.lifetime);
+                    lifetimes.push_value(param.lifetime);
+                    if let Some(punct) = punct {
+                        lifetimes.push_punct(punct);
+                    }
                 }
                 GenericParam::Type(param) => {
                     if !has_unsupported_generic_param {
@@ -609,6 +617,11 @@
                 }
             }
         }
+        let lifetimes = Lifetimes {
+            lt_token: generics.lt_token,
+            lifetimes,
+            gt_token: generics.gt_token,
+        };
         let lookahead = input.lookahead1();
         if lookahead.peek(Token![=]) {
             // type Alias = crate::path::to::Type;
@@ -632,7 +645,7 @@
     attrs: Vec<Attribute>,
     type_token: Token![type],
     ident: Ident,
-    lifetimes: Vec<Lifetime>,
+    generics: Lifetimes,
     input: ParseStream,
     lang: Lang,
     namespace: &Namespace,
@@ -672,7 +685,7 @@
         derives,
         type_token,
         name,
-        lifetimes,
+        generics,
         eq_token,
         ty,
         semi_token,
@@ -684,7 +697,7 @@
     attrs: Vec<Attribute>,
     type_token: Token![type],
     ident: Ident,
-    lifetimes: Vec<Lifetime>,
+    generics: Lifetimes,
     input: ParseStream,
     lang: Lang,
     trusted: bool,
@@ -752,7 +765,7 @@
         derives,
         type_token,
         name,
-        lifetimes,
+        generics,
         colon_token,
         bounds,
         semi_token,