From c31c3246a8c87a3639623c30b692a57e728bb046 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 15 Dec 2020 18:43:19 +0100 Subject: [PATCH 1/4] Basic support for decl macros 2.0 --- Cargo.lock | 2 - Cargo.toml | 2 +- crates/hir/src/has_source.rs | 4 +- crates/hir/src/semantics/source_to_def.rs | 2 +- crates/hir_def/src/body/lower.rs | 5 ++- crates/hir_def/src/item_tree.rs | 20 ++++++++- crates/hir_def/src/item_tree/lower.rs | 16 ++++++- crates/hir_def/src/nameres/collector.rs | 29 +++++++++++- crates/hir_expand/src/builtin_macro.rs | 19 ++++---- crates/hir_expand/src/db.rs | 5 ++- crates/hir_expand/src/lib.rs | 7 ++- crates/syntax/src/ast.rs | 4 +- crates/syntax/src/ast/generated/nodes.rs | 36 ++++++++++++++- crates/syntax/src/ast/node_ext.rs | 54 +++++++++++++++++++++++ crates/syntax/src/display.rs | 18 ++++++-- 15 files changed, 195 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff2c33f45d18..ff1999ee0c10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1828,8 +1828,6 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "ungrammar" version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873186a460627379e7e28880a0d33b729c205634f6f021321f50b323235e62d7" [[package]] name = "unicase" diff --git a/Cargo.toml b/Cargo.toml index 59d36fbc1d19..fdf2a71a0683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,4 +26,4 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. # chalk-ir = { path = "../chalk/chalk-ir" } # chalk-recursive = { path = "../chalk/chalk-recursive" } -# ungrammar = { path = "../ungrammar" } +ungrammar = { path = "../ungrammar" } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 107ad06904d6..ecf3194c6ad8 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -110,8 +110,8 @@ impl HasSource for TypeAlias { } } impl HasSource for MacroDef { - type Ast = ast::MacroRules; - fn source(self, db: &dyn HirDatabase) -> InFile { + type Ast = ast::Macro; + fn source(self, db: &dyn HirDatabase) -> InFile { InFile { file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()), diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index a333d7aea9da..d499ae340221 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -157,7 +157,7 @@ impl SourceToDefCtx<'_, '_> { let file_id = src.file_id.original_file(self.db.upcast()); let krate = self.file_to_def(file_id)?.krate; let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); - let ast_id = Some(AstId::new(src.file_id, file_ast_id)); + let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false }) } diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index bdba4c33ea45..e4bf5603c7e8 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -772,7 +772,10 @@ impl ExprCollector<'_> { | ast::Item::Module(_) | ast::Item::MacroCall(_) => return None, ast::Item::MacroRules(def) => { - return Some(Either::Right(def)); + return Some(Either::Right(ast::Macro::from(def))); + } + ast::Item::MacroDef(def) => { + return Some(Either::Right(ast::Macro::from(def))); } }; diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 1c9babf37126..8cd0b18ccdad 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -143,6 +143,7 @@ impl ItemTree { mods, macro_calls, macro_rules, + macro_defs, exprs, vis, generics, @@ -164,6 +165,7 @@ impl ItemTree { mods.shrink_to_fit(); macro_calls.shrink_to_fit(); macro_rules.shrink_to_fit(); + macro_defs.shrink_to_fit(); exprs.shrink_to_fit(); vis.arena.shrink_to_fit(); @@ -283,6 +285,7 @@ struct ItemTreeData { mods: Arena, macro_calls: Arena, macro_rules: Arena, + macro_defs: Arena, exprs: Arena, vis: ItemVisibilities, @@ -431,6 +434,7 @@ mod_items! { Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, MacroRules in macro_rules -> ast::MacroRules, + MacroDef in macro_defs -> ast::MacroDef, } macro_rules! impl_index { @@ -640,7 +644,7 @@ pub struct MacroCall { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroRules { - /// For `macro_rules!` declarations, this is the name of the declared macro. + /// The name of the declared macro. pub name: Name, /// Has `#[macro_export]`. pub is_export: bool, @@ -651,6 +655,16 @@ pub struct MacroRules { pub ast_id: FileAstId, } +/// "Macros 2.0" macro definition. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroDef { + pub name: Name, + pub visibility: RawVisibilityId, + /// Has `#[rustc_builtin_macro]`. + pub is_builtin: bool, + pub ast_id: FileAstId, +} + // NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array // lengths, but we don't do much with them yet. #[derive(Debug, Clone, Eq, PartialEq)] @@ -680,7 +694,8 @@ impl ModItem { | ModItem::Trait(_) | ModItem::Impl(_) | ModItem::Mod(_) - | ModItem::MacroRules(_) => None, + | ModItem::MacroRules(_) + | ModItem::MacroDef(_) => None, ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), ModItem::Const(konst) => Some(AssocItem::Const(*konst)), ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), @@ -708,6 +723,7 @@ impl ModItem { ModItem::Mod(it) => tree[it.index].ast_id().upcast(), ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(), ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(), + ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(), } } } diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index b39d7fb7ac17..1dc06a211556 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -101,7 +101,8 @@ impl Ctx { | ast::Item::ExternCrate(_) | ast::Item::Use(_) | ast::Item::MacroCall(_) - | ast::Item::MacroRules(_) => {} + | ast::Item::MacroRules(_) + | ast::Item::MacroDef(_) => {} }; let attrs = Attrs::new(item, &self.hygiene); @@ -122,6 +123,7 @@ impl Ctx { ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into), ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), + ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), ast::Item::ExternBlock(ast) => { Some(ModItems(self.lower_extern_block(ast).into_iter().collect::>())) } @@ -561,6 +563,18 @@ impl Ctx { Some(id(self.data().macro_rules.alloc(res))) } + fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { + let name = m.name().map(|it| it.as_name())?; + let attrs = Attrs::new(m, &self.hygiene); + + let ast_id = self.source_ast_id_map.ast_id(m); + let visibility = self.lower_visibility(m); + + let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); + let res = MacroDef { name, is_builtin, ast_id, visibility }; + Some(id(self.data().macro_defs.alloc(res))) + } + fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec { block.extern_item_list().map_or(Vec::new(), |list| { list.extern_items() diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 85cc342c4462..c2f741060f5c 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -976,6 +976,33 @@ impl ModCollector<'_, '_> { } ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), ModItem::MacroRules(mac) => self.collect_macro_rules(&self.item_tree[mac]), + ModItem::MacroDef(id) => { + let mac = &self.item_tree[id]; + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); + + // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it + // to define builtin macros, so we support at least that part. + if mac.is_builtin { + let krate = self.def_collector.def_map.krate; + if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { + let vis = self + .def_collector + .def_map + .resolve_visibility( + self.def_collector.db, + self.module_id, + &self.item_tree[mac.visibility], + ) + .unwrap_or(Visibility::Public); + self.def_collector.update( + self.module_id, + &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))], + vis, + ImportType::Named, + ); + } + } + } ModItem::Impl(imp) => { let module = ModuleId { krate: self.def_collector.def_map.krate, @@ -1280,7 +1307,7 @@ impl ModCollector<'_, '_> { } fn collect_macro_rules(&mut self, mac: &MacroRules) { - let ast_id = InFile::new(self.file_id, mac.ast_id); + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); // Case 1: builtin macros if mac.is_builtin { diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index bd92238257c1..df82cf8e65f7 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -63,7 +63,7 @@ macro_rules! register_builtin { pub fn find_builtin_macro( ident: &name::Name, krate: CrateId, - ast_id: AstId, + ast_id: AstId, ) -> Option { let kind = find_by_name(ident)?; @@ -515,16 +515,19 @@ mod tests { fn expand_builtin_macro(ra_fixture: &str) -> String { let (db, file_id) = TestDB::with_single_file(&ra_fixture); let parsed = db.parse(file_id); - let macro_rules: Vec<_> = + let mut macro_rules: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); - let macro_calls: Vec<_> = + let mut macro_calls: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); let ast_id_map = db.ast_id_map(file_id.into()); assert_eq!(macro_rules.len(), 1, "test must contain exactly 1 `macro_rules!`"); assert_eq!(macro_calls.len(), 1, "test must contain exactly 1 macro call"); - let expander = find_by_name(¯o_rules[0].name().unwrap().as_name()).unwrap(); + let macro_rules = ast::Macro::from(macro_rules.pop().unwrap()); + let macro_call = macro_calls.pop().unwrap(); + + let expander = find_by_name(¯o_rules.name().unwrap().as_name()).unwrap(); let krate = CrateId(0); let file_id = match expander { @@ -532,7 +535,7 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(CrateId(0)), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltIn(expander), local_inner: false, }; @@ -542,7 +545,7 @@ mod tests { krate, kind: MacroCallKind::FnLike(AstId::new( file_id.into(), - ast_id_map.ast_id(¯o_calls[0]), + ast_id_map.ast_id(¯o_call), )), }; @@ -553,12 +556,12 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(krate), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, }; - let args = macro_calls[0].token_tree().unwrap(); + let args = macro_call.token_tree().unwrap(); let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; let arg_id = db.intern_eager_expansion({ diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 11b5b98c87de..4477d867f7cf 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -129,7 +129,10 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option> { match id.kind { MacroDefKind::Declarative => { - let macro_call = id.ast_id?.to_node(db); + let macro_call = match id.ast_id?.to_node(db) { + syntax::ast::Macro::MacroRules(mac) => mac, + syntax::ast::Macro::MacroDef(_) => return None, + }; let arg = macro_call.token_tree()?; let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { log::warn!("fail on macro_def to token tree: {:#?}", arg); diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index ae3086a95bd9..55f026c7be9b 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -145,7 +145,10 @@ impl HirFileId { let arg_tt = loc.kind.arg(db)?; let def = loc.def.ast_id.and_then(|id| { - let def_tt = id.to_node(db).token_tree()?; + let def_tt = match id.to_node(db) { + ast::Macro::MacroRules(mac) => mac.token_tree()?, + ast::Macro::MacroDef(_) => return None, + }; Some(InFile::new(id.file_id, def_tt)) }); @@ -228,7 +231,7 @@ pub struct MacroDefId { // (which will probably require touching this code), we can instead use // that (and also remove the hacks for resolving built-in derives). pub krate: Option, - pub ast_id: Option>, + pub ast_id: Option>, pub kind: MacroDefKind, pub local_inner: bool, diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 7844f9ed6354..70c568ea1e7f 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs @@ -19,8 +19,8 @@ pub use self::{ expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp}, generated::{nodes::*, tokens::*}, node_ext::{ - AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, - StructKind, TypeBoundKind, VisibilityKind, + AttrKind, FieldKind, Macro, NameOrNameRef, PathSegmentKind, SelfParamKind, + SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind, }, token_ext::*, traits::*, diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 0ad75214fc42..6eae323f4a24 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -286,6 +286,18 @@ impl MacroRules { pub fn token_tree(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MacroDef { + pub(crate) syntax: SyntaxNode, +} +impl ast::AttrsOwner for MacroDef {} +impl ast::NameOwner for MacroDef {} +impl ast::VisibilityOwner for MacroDef {} +impl MacroDef { + pub fn macro_token(&self) -> Option { support::token(&self.syntax, T![macro]) } + pub fn args(&self) -> Option { support::child(&self.syntax) } + pub fn body(&self) -> Option { support::child(&self.syntax) } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Module { pub(crate) syntax: SyntaxNode, } @@ -1332,6 +1344,7 @@ pub enum Item { Impl(Impl), MacroCall(MacroCall), MacroRules(MacroRules), + MacroDef(MacroDef), Module(Module), Static(Static), Struct(Struct), @@ -1689,6 +1702,17 @@ impl AstNode for MacroRules { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for MacroDef { + fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for Module { fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE } fn cast(syntax: SyntaxNode) -> Option { @@ -3086,6 +3110,9 @@ impl From for Item { impl From for Item { fn from(node: MacroRules) -> Item { Item::MacroRules(node) } } +impl From for Item { + fn from(node: MacroDef) -> Item { Item::MacroDef(node) } +} impl From for Item { fn from(node: Module) -> Item { Item::Module(node) } } @@ -3111,7 +3138,7 @@ impl AstNode for Item { fn can_cast(kind: SyntaxKind) -> bool { match kind { CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL | MACRO_CALL | MACRO_RULES - | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true, + | MACRO_DEF | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true, _ => false, } } @@ -3125,6 +3152,7 @@ impl AstNode for Item { IMPL => Item::Impl(Impl { syntax }), MACRO_CALL => Item::MacroCall(MacroCall { syntax }), MACRO_RULES => Item::MacroRules(MacroRules { syntax }), + MACRO_DEF => Item::MacroDef(MacroDef { syntax }), MODULE => Item::Module(Module { syntax }), STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), @@ -3146,6 +3174,7 @@ impl AstNode for Item { Item::Impl(it) => &it.syntax, Item::MacroCall(it) => &it.syntax, Item::MacroRules(it) => &it.syntax, + Item::MacroDef(it) => &it.syntax, Item::Module(it) => &it.syntax, Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, @@ -3615,6 +3644,11 @@ impl std::fmt::Display for MacroRules { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for MacroDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Module { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index c59a29eab301..40dec3c7f99c 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -3,6 +3,7 @@ use std::fmt; +use ast::AttrsOwner; use itertools::Itertools; use parser::SyntaxKind; @@ -31,6 +32,57 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { node.green().children().next().and_then(|it| it.into_token()).unwrap().text() } +pub enum Macro { + MacroRules(ast::MacroRules), + MacroDef(ast::MacroDef), +} + +impl From for Macro { + fn from(it: ast::MacroRules) -> Self { + Macro::MacroRules(it) + } +} + +impl From for Macro { + fn from(it: ast::MacroDef) -> Self { + Macro::MacroDef(it) + } +} + +impl AstNode for Macro { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::MACRO_RULES => Macro::MacroRules(ast::MacroRules { syntax }), + SyntaxKind::MACRO_DEF => Macro::MacroDef(ast::MacroDef { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Macro::MacroRules(it) => it.syntax(), + Macro::MacroDef(it) => it.syntax(), + } + } +} + +impl NameOwner for Macro { + fn name(&self) -> Option { + match self { + Macro::MacroRules(mac) => mac.name(), + Macro::MacroDef(mac) => mac.name(), + } + } +} + +impl AttrsOwner for Macro {} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum AttrKind { Inner, @@ -462,4 +514,6 @@ impl ast::DocCommentsOwner for ast::Const {} impl ast::DocCommentsOwner for ast::TypeAlias {} impl ast::DocCommentsOwner for ast::Impl {} impl ast::DocCommentsOwner for ast::MacroRules {} +impl ast::DocCommentsOwner for ast::MacroDef {} +impl ast::DocCommentsOwner for ast::Macro {} impl ast::DocCommentsOwner for ast::Use {} diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs index d33bde30c59b..391647fc6716 100644 --- a/crates/syntax/src/display.rs +++ b/crates/syntax/src/display.rs @@ -76,8 +76,20 @@ pub fn type_label(node: &ast::TypeAlias) -> String { label.trim().to_owned() } -pub fn macro_label(node: &ast::MacroRules) -> String { +pub fn macro_label(node: &ast::Macro) -> String { let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); - let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; - format!("{}macro_rules! {}", vis, name) + match node { + ast::Macro::MacroRules(node) => { + let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; + format!("{}macro_rules! {}", vis, name) + } + ast::Macro::MacroDef(node) => { + let mut s = String::new(); + if let Some(vis) = node.visibility() { + format_to!(s, "{} ", vis); + } + format_to!(s, "macro {}", name); + s + } + } } From b238ddd21adf9910769522a21e31c2e14f664396 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 15 Dec 2020 20:33:05 +0100 Subject: [PATCH 2/4] Make macro def krate mandatory Refactors builtin derive support to go through proper name resolution --- crates/hir/src/code_model.rs | 2 +- crates/hir/src/semantics/source_to_def.rs | 2 +- crates/hir_def/src/body/lower.rs | 2 +- crates/hir_def/src/item_scope.rs | 2 +- crates/hir_def/src/nameres/collector.rs | 18 +++------ crates/hir_def/src/nameres/tests/macros.rs | 31 +++++++++++++- crates/hir_expand/src/builtin_derive.rs | 40 ++++++++++++++----- crates/hir_expand/src/builtin_macro.rs | 8 ++-- crates/hir_expand/src/hygiene.rs | 4 +- crates/hir_expand/src/lib.rs | 8 +--- crates/hir_ty/src/tests/macros.rs | 6 +++ crates/ide/src/goto_implementation.rs | 2 + .../test_data/highlighting.html | 5 ++- crates/ide/src/syntax_highlighting/tests.rs | 3 ++ 14 files changed, 91 insertions(+), 42 deletions(-) diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 42dc35b762c0..9bfcd215a5e7 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -970,7 +970,7 @@ impl MacroDef { /// defines this macro. The reasons for this is that macros are expanded /// early, in `hir_expand`, where modules simply do not exist yet. pub fn module(self, db: &dyn HirDatabase) -> Option { - let krate = self.id.krate?; + let krate = self.id.krate; let module_id = db.crate_def_map(krate).root; Some(Module::new(Crate { id: krate }, module_id)) } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index d499ae340221..3efca5baa5cd 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -158,7 +158,7 @@ impl SourceToDefCtx<'_, '_> { let krate = self.file_to_def(file_id)?.krate; let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); - Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false }) + Some(MacroDefId { krate, ast_id, kind, local_inner: false }) } pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option { diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index e4bf5603c7e8..23e2fd7641fa 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -803,7 +803,7 @@ impl ExprCollector<'_> { } Either::Right(e) => { let mac = MacroDefId { - krate: Some(self.expander.module.krate), + krate: self.expander.module.krate, ast_id: Some(self.expander.ast_id(&e)), kind: MacroDefKind::Declarative, local_inner: false, diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index a8b3fe844a53..62ab3b2bd622 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs @@ -363,7 +363,7 @@ impl ItemInNs { ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, ModuleDefId::BuiltinType(_) => return None, }, - ItemInNs::Macros(id) => return id.krate, + ItemInNs::Macros(id) => return Some(id.krate), }) } } diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index c2f741060f5c..785895277fa4 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -309,13 +309,13 @@ impl DefCollector<'_> { let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { Some((_, expander)) => MacroDefId { ast_id: None, - krate: Some(self.def_map.krate), + krate: self.def_map.krate, kind: MacroDefKind::ProcMacro(*expander), local_inner: false, }, None => MacroDefId { ast_id: None, - krate: Some(self.def_map.krate), + krate: self.def_map.krate, kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)), local_inner: false, }, @@ -784,14 +784,6 @@ impl DefCollector<'_> { directive: &DeriveDirective, path: &ModPath, ) -> Option { - if let Some(name) = path.as_ident() { - // FIXME this should actually be handled with the normal name - // resolution; the std lib defines built-in stubs for the derives, - // but these are new-style `macro`s, which we don't support yet - if let Some(def_id) = find_builtin_derive(name) { - return Some(def_id); - } - } let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -984,7 +976,9 @@ impl ModCollector<'_, '_> { // to define builtin macros, so we support at least that part. if mac.is_builtin { let krate = self.def_collector.def_map.krate; - if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { + let macro_id = find_builtin_macro(&mac.name, krate, ast_id) + .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); + if let Some(macro_id) = macro_id { let vis = self .def_collector .def_map @@ -1326,7 +1320,7 @@ impl ModCollector<'_, '_> { // Case 2: normal `macro_rules!` macro let macro_id = MacroDefId { ast_id: Some(ast_id), - krate: Some(self.def_collector.def_map.krate), + krate: self.def_collector.def_map.krate, kind: MacroDefKind::Declarative, local_inner: mac.is_local_inner, }; diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 305fca0f9c1f..6fe2ee78a1fc 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -633,14 +633,43 @@ pub struct bar; fn expand_derive() { let map = compute_crate_def_map( " - //- /main.rs + //- /main.rs crate:main deps:core + use core::*; + #[derive(Copy, Clone)] struct Foo; + + //- /core.rs crate:core + #[rustc_builtin_macro] + pub macro Copy {} + + #[rustc_builtin_macro] + pub macro Clone {} ", ); assert_eq!(map.modules[map.root].scope.impls().len(), 2); } +#[test] +fn resolve_builtin_derive() { + check( + r#" +//- /main.rs crate:main deps:core +use core::*; + +//- /core.rs crate:core +#[rustc_builtin_macro] +pub macro Clone {} + +pub trait Clone {} +"#, + expect![[r#" + crate + Clone: t m + "#]], + ); +} + #[test] fn macro_expansion_overflow() { mark::check!(macro_expansion_overflow); diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index 988a60d56ade..ad378762a1ea 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs @@ -8,7 +8,7 @@ use syntax::{ match_ast, }; -use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind}; +use crate::{db::AstDatabase, name, quote, AstId, CrateId, LazyMacroId, MacroDefId, MacroDefKind}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -29,16 +29,15 @@ macro_rules! register_builtin { }; expander(db, id, tt) } - } - - pub fn find_builtin_derive(ident: &name::Name) -> Option { - let kind = match ident { - $( id if id == &name::name![$trait] => BuiltinDeriveExpander::$trait, )* - _ => return None, - }; - Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind), local_inner: false }) + fn find_by_name(name: &name::Name) -> Option { + match name { + $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )* + _ => None, + } + } } + }; } @@ -54,6 +53,20 @@ register_builtin! { PartialEq => partial_eq_expand } +pub fn find_builtin_derive( + ident: &name::Name, + krate: CrateId, + ast_id: AstId, +) -> Option { + let expander = BuiltinDeriveExpander::find_by_name(ident)?; + Some(MacroDefId { + krate, + ast_id: Some(ast_id), + kind: MacroDefKind::BuiltInDerive(expander), + local_inner: false, + }) +} + struct BasicAdtInfo { name: tt::Ident, type_params: usize, @@ -261,7 +274,7 @@ mod tests { use super::*; fn expand_builtin_derive(s: &str, name: Name) -> String { - let def = find_builtin_derive(&name).unwrap(); + let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); let fixture = format!( r#"//- /main.rs crate:main deps:core <|> @@ -283,7 +296,12 @@ mod tests { let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); let loc = MacroCallLoc { - def, + def: MacroDefId { + krate: CrateId(0), + ast_id: None, + kind: MacroDefKind::BuiltInDerive(expander), + local_inner: false, + }, krate: CrateId(0), kind: MacroCallKind::Attr(attr_id, name.to_string()), }; diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index df82cf8e65f7..dddbbcdac48e 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -69,13 +69,13 @@ pub fn find_builtin_macro( match kind { Either::Left(kind) => Some(MacroDefId { - krate: Some(krate), + krate, ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind), local_inner: false, }), Either::Right(kind) => Some(MacroDefId { - krate: Some(krate), + krate, ast_id: Some(ast_id), kind: MacroDefKind::BuiltInEager(kind), local_inner: false, @@ -534,7 +534,7 @@ mod tests { Either::Left(expander) => { // the first one should be a macro_rules let def = MacroDefId { - krate: Some(CrateId(0)), + krate: CrateId(0), ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltIn(expander), local_inner: false, @@ -555,7 +555,7 @@ mod tests { Either::Right(expander) => { // the first one should be a macro_rules let def = MacroDefId { - krate: Some(krate), + krate, ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 5d3fa0518f41..7ab0a5e52eb0 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -29,8 +29,8 @@ impl Hygiene { MacroCallId::LazyMacro(id) => { let loc = db.lookup_intern_macro(id); match loc.def.kind { - MacroDefKind::Declarative => (loc.def.krate, loc.def.local_inner), - MacroDefKind::BuiltIn(_) => (loc.def.krate, false), + MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner), + MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false), MacroDefKind::BuiltInDerive(_) => (None, false), MacroDefKind::BuiltInEager(_) => (None, false), MacroDefKind::ProcMacro(_) => (None, false), diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 55f026c7be9b..d486186e59d3 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -224,13 +224,7 @@ impl From for MacroCallId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroDefId { - // FIXME: krate and ast_id are currently optional because we don't have a - // definition location for built-in derives. There is one, though: the - // standard library defines them. The problem is that it uses the new - // `macro` syntax for this, which we don't support yet. As soon as we do - // (which will probably require touching this code), we can instead use - // that (and also remove the hacks for resolving built-in derives). - pub krate: Option, + pub krate: CrateId, pub ast_id: Option>, pub kind: MacroDefKind, diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index de97ec3c206d..a7656b864865 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -686,6 +686,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } "#, ); @@ -702,6 +704,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } #[derive(Clone)] pub struct S; @@ -737,6 +741,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } "#, ); diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 529004878d32..68c628d31437 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -221,6 +221,8 @@ struct Foo<|>; mod marker { trait Copy {} } +#[rustc_builtin_macro] +macro Copy {} "#, ); } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 0569cf1e5a7f..3530a5fdb4b4 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -38,6 +38,9 @@
use inner::{self as inner_mod};
 mod inner {}
 
+#[rustc_builtin_macro]
+macro Copy {}
+
 // Needed for function consuming vs normal
 pub mod marker {
     #[lang = "copy"]
@@ -119,7 +122,7 @@
     f()
 }
 
-fn foobar() -> impl Copy {}
+fn foobar() -> impl Copy {}
 
 fn foo() {
     let bar = foobar();
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 1dc018a167bb..f53d2c3ba413 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -12,6 +12,9 @@ fn test_highlighting() {
 use inner::{self as inner_mod};
 mod inner {}
 
+#[rustc_builtin_macro]
+macro Copy {}
+
 // Needed for function consuming vs normal
 pub mod marker {
     #[lang = "copy"]

From d34611633b3b2404188b9e12b08c5def589808c2 Mon Sep 17 00:00:00 2001
From: Jonas Schievink 
Date: Wed, 16 Dec 2020 12:54:34 +0100
Subject: [PATCH 3/4] Unpatch ungrammar

---
 Cargo.lock | 4 +++-
 Cargo.toml | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index ff1999ee0c10..e44b7d1d9209 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1827,7 +1827,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 
 [[package]]
 name = "ungrammar"
-version = "1.2.2"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7311ee93faac43aa9da26b043eb244092a29a3078c79af9396f63f800cc3a59a"
 
 [[package]]
 name = "unicase"
diff --git a/Cargo.toml b/Cargo.toml
index fdf2a71a0683..59d36fbc1d19 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,4 +26,4 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
 # chalk-ir = { path = "../chalk/chalk-ir" }
 # chalk-recursive = { path = "../chalk/chalk-recursive" }
 
-ungrammar = { path = "../ungrammar" }
+# ungrammar = { path = "../ungrammar" }

From dd496223f50232fe98312ee8edc89eb4b5ee3d85 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 15 Dec 2020 19:23:51 +0100
Subject: [PATCH 4/4] Node-ify lifetimes

---
 Cargo.lock                                    |  4 +-
 crates/assists/src/handlers/generate_impl.rs  |  2 +-
 crates/assists/src/handlers/generate_new.rs   |  2 +-
 .../src/handlers/introduce_named_lifetime.rs  | 21 +++----
 crates/hir/src/semantics.rs                   | 17 ++---
 crates/hir_def/src/body/lower.rs              | 24 +++----
 crates/hir_def/src/generics.rs                |  9 ++-
 crates/hir_def/src/item_tree/lower.rs         |  4 +-
 crates/hir_def/src/path/lower.rs              |  4 +-
 crates/hir_def/src/type_ref.rs                | 10 +--
 crates/hir_expand/src/name.rs                 |  5 +-
 crates/ide/src/extend_selection.rs            |  2 +-
 crates/mbe/src/mbe_expander/matcher.rs        |  2 +-
 crates/mbe/src/subtree_source.rs              |  8 ++-
 crates/mbe/src/syntax_bridge.rs               |  4 +-
 crates/parser/src/grammar.rs                  |  7 +++
 crates/parser/src/grammar/expressions/atom.rs | 16 +++--
 crates/parser/src/grammar/items/traits.rs     | 10 +--
 crates/parser/src/grammar/params.rs           | 13 ++--
 crates/parser/src/grammar/type_args.rs        |  4 +-
 crates/parser/src/grammar/type_params.rs      | 18 +++---
 crates/parser/src/grammar/types.rs            |  4 +-
 crates/parser/src/syntax_kind/generated.rs    |  5 +-
 crates/syntax/src/ast.rs                      |  2 +-
 crates/syntax/src/ast/generated/nodes.rs      | 62 +++++++++++--------
 crates/syntax/src/ast/node_ext.rs             | 12 +++-
 crates/syntax/src/parsing/lexer.rs            |  4 +-
 ... => 0057_lifetime_starts_with_a_number.rs} |  0
 ...=> 0057_lifetime_starts_with_a_number.txt} |  4 +-
 .../test_data/lexer/ok/0007_lifetimes.txt     |  8 +--
 .../parser/err/0024_many_type_parens.rast     | 22 ++++---
 .../parser/err/0027_incomplere_where_for.rast |  3 +-
 .../parser/err/0043_weird_blocks.rast         |  3 +-
 .../parser/err/0044_unexpected_for_type.rast  | 45 +++++++++-----
 .../err/0046_ambiguous_trait_object.rast      |  9 ++-
 .../inline/err/0002_misplaced_label_err.rast  |  3 +-
 .../parser/inline/ok/0003_where_pred_for.rast |  6 +-
 .../parser/inline/ok/0006_self_param.rast     |  6 +-
 .../inline/ok/0007_type_param_bounds.rast     |  3 +-
 .../parser/inline/ok/0015_continue_expr.rast  |  3 +-
 .../inline/ok/0028_impl_trait_type.rast       |  6 +-
 .../inline/ok/0033_reference_type;.rast       |  3 +-
 .../parser/inline/ok/0034_break_expr.rast     |  6 +-
 .../parser/inline/ok/0039_type_arg.rast       |  3 +-
 .../ok/0045_param_list_opt_patterns.rast      |  3 +-
 .../inline/ok/0048_path_type_with_bounds.rast |  6 +-
 .../parser/inline/ok/0056_where_clause.rast   | 18 ++++--
 .../parser/inline/ok/0065_dyn_trait_type.rast |  6 +-
 .../parser/inline/ok/0081_for_type.rast       | 15 +++--
 .../parser/inline/ok/0109_label.rast          |  9 ++-
 .../0122_generic_lifetime_type_attribute.rast |  6 +-
 .../parser/inline/ok/0161_labeled_block.rast  |  3 +-
 .../parser/ok/0018_struct_type_params.rast    | 51 ++++++++++-----
 .../parser/ok/0020_type_param_bounds.rast     | 42 ++++++++-----
 .../test_data/parser/ok/0032_where_for.rast   |  6 +-
 .../test_data/parser/ok/0033_label_break.rast | 21 ++++---
 .../test_data/parser/ok/0035_weird_exprs.rast | 19 +++---
 .../parser/ok/0051_parameter_attrs.rast       | 18 ++++--
 .../parser/ok/0067_where_for_pred.rast        | 45 +++++++++-----
 .../parser/ok/0069_multi_trait_object.rast    |  9 ++-
 xtask/Cargo.toml                              |  4 +-
 xtask/src/ast_src.rs                          |  3 +-
 xtask/src/codegen/gen_syntax.rs               |  2 +-
 63 files changed, 420 insertions(+), 274 deletions(-)
 rename crates/syntax/test_data/lexer/err/{0057_lifetime_strarts_with_a_number.rs => 0057_lifetime_starts_with_a_number.rs} (100%)
 rename crates/syntax/test_data/lexer/err/{0057_lifetime_strarts_with_a_number.txt => 0057_lifetime_starts_with_a_number.txt} (77%)

diff --git a/Cargo.lock b/Cargo.lock
index e44b7d1d9209..4476d188bc3b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1827,9 +1827,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 
 [[package]]
 name = "ungrammar"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7311ee93faac43aa9da26b043eb244092a29a3078c79af9396f63f800cc3a59a"
+checksum = "68951379f3ced25754472ca5addbf74d7dab58c9818f49290a3d8caa3ab44fb7"
 
 [[package]]
 name = "unicase"
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs
index 114974465e10..960af5ab3756 100644
--- a/crates/assists/src/handlers/generate_impl.rs
+++ b/crates/assists/src/handlers/generate_impl.rs
@@ -53,7 +53,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
             if let Some(type_params) = type_params {
                 let lifetime_params = type_params
                     .lifetime_params()
-                    .filter_map(|it| it.lifetime_token())
+                    .filter_map(|it| it.lifetime())
                     .map(|it| it.text().clone());
                 let type_params = type_params
                     .type_params()
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs
index 7db10f276863..c5fec4e0a255 100644
--- a/crates/assists/src/handlers/generate_new.rs
+++ b/crates/assists/src/handlers/generate_new.rs
@@ -99,7 +99,7 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
     if let Some(type_params) = type_params {
         let lifetime_params = type_params
             .lifetime_params()
-            .filter_map(|it| it.lifetime_token())
+            .filter_map(|it| it.lifetime())
             .map(|it| it.text().clone());
         let type_params =
             type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
diff --git a/crates/assists/src/handlers/introduce_named_lifetime.rs b/crates/assists/src/handlers/introduce_named_lifetime.rs
index 4cc8dae65026..ab8fe3ea9f6e 100644
--- a/crates/assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/assists/src/handlers/introduce_named_lifetime.rs
@@ -1,7 +1,7 @@
 use rustc_hash::FxHashSet;
 use syntax::{
     ast::{self, GenericParamsOwner, NameOwner},
-    AstNode, SyntaxKind, TextRange, TextSize,
+    AstNode, TextRange, TextSize,
 };
 
 use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -35,13 +35,12 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
 // FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
 // FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo
 pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let lifetime_token = ctx
-        .find_token_syntax_at_offset(SyntaxKind::LIFETIME)
-        .filter(|lifetime| lifetime.text() == "'_")?;
-    if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::Fn::cast) {
-        generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
-    } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::Impl::cast) {
-        generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
+    let lifetime =
+        ctx.find_node_at_offset::().filter(|lifetime| lifetime.text() == "'_")?;
+    if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) {
+        generate_fn_def_assist(acc, &fn_def, lifetime.lifetime_ident_token()?.text_range())
+    } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) {
+        generate_impl_def_assist(acc, &impl_def, lifetime.lifetime_ident_token()?.text_range())
     } else {
         None
     }
@@ -58,7 +57,7 @@ fn generate_fn_def_assist(
     let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
     let self_param =
         // use the self if it's a reference and has no explicit lifetime
-        param_list.self_param().filter(|p| p.lifetime_token().is_none() && p.amp_token().is_some());
+        param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some());
     // compute the location which implicitly has the same lifetime as the anonymous lifetime
     let loc_needing_lifetime = if let Some(self_param) = self_param {
         // if we have a self reference, use that
@@ -68,9 +67,7 @@ fn generate_fn_def_assist(
         let fn_params_without_lifetime: Vec<_> = param_list
             .params()
             .filter_map(|param| match param.ty() {
-                Some(ast::Type::RefType(ascribed_type))
-                    if ascribed_type.lifetime_token() == None =>
-                {
+                Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => {
                     Some(ascribed_type.amp_token()?.text_range().end())
                 }
                 _ => None,
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 5959ac4ca9e7..ee20746023b5 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -178,9 +178,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
     }
 
-    // FIXME: Replace the SyntaxToken with a typed ast Node/Token
-    pub fn resolve_lifetime_param(&self, lifetime_token: &SyntaxToken) -> Option {
-        self.imp.resolve_lifetime_param(lifetime_token)
+    pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option {
+        self.imp.resolve_lifetime_param(lifetime)
     }
 
     pub fn type_of_expr(&self, expr: &ast::Expr) -> Option {
@@ -402,13 +401,9 @@ impl<'db> SemanticsImpl<'db> {
             .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
     }
 
-    // FIXME: Replace the SyntaxToken with a typed ast Node/Token
-    fn resolve_lifetime_param(&self, lifetime_token: &SyntaxToken) -> Option {
-        if lifetime_token.kind() != syntax::SyntaxKind::LIFETIME {
-            return None;
-        }
-        let lifetime_text = lifetime_token.text();
-        let lifetime_param = lifetime_token.parent().ancestors().find_map(|syn| {
+    fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option {
+        let text = lifetime.text();
+        let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| {
             let gpl = match_ast! {
                 match syn {
                     ast::Fn(it) => it.generic_param_list()?,
@@ -424,7 +419,7 @@ impl<'db> SemanticsImpl<'db> {
                 }
             };
             gpl.lifetime_params()
-                .find(|tp| tp.lifetime_token().as_ref().map(|lt| lt.text()) == Some(lifetime_text))
+                .find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()) == Some(text))
         })?;
         let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param);
         ToDef::to_def(self, src)
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 23e2fd7641fa..3b3d7498744f 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -233,8 +233,7 @@ impl ExprCollector<'_> {
                         let res = self.collect_block(block);
                         match &mut self.body.exprs[res] {
                             Expr::Block { label: block_label, .. } => {
-                                *block_label =
-                                    label.lifetime_token().map(|t| Name::new_lifetime(&t))
+                                *block_label = label.lifetime().map(|t| Name::new_lifetime(&t))
                             }
                             _ => unreachable!(),
                         }
@@ -254,10 +253,7 @@ impl ExprCollector<'_> {
                 self.alloc_expr(
                     Expr::Loop {
                         body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
+                        label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)),
                     },
                     syntax_ptr,
                 )
@@ -288,7 +284,7 @@ impl ExprCollector<'_> {
                                     body: match_expr,
                                     label: e
                                         .label()
-                                        .and_then(|l| l.lifetime_token())
+                                        .and_then(|l| l.lifetime())
                                         .map(|l| Name::new_lifetime(&l)),
                                 },
                                 syntax_ptr,
@@ -301,10 +297,7 @@ impl ExprCollector<'_> {
                     Expr::While {
                         condition,
                         body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
+                        label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)),
                     },
                     syntax_ptr,
                 )
@@ -318,10 +311,7 @@ impl ExprCollector<'_> {
                         iterable,
                         pat,
                         body,
-                        label: e
-                            .label()
-                            .and_then(|l| l.lifetime_token())
-                            .map(|l| Name::new_lifetime(&l)),
+                        label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)),
                     },
                     syntax_ptr,
                 )
@@ -380,13 +370,13 @@ impl ExprCollector<'_> {
                 self.alloc_expr(path, syntax_ptr)
             }
             ast::Expr::ContinueExpr(e) => self.alloc_expr(
-                Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
+                Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
                 syntax_ptr,
             ),
             ast::Expr::BreakExpr(e) => {
                 let expr = e.expr().map(|e| self.collect_expr(e));
                 self.alloc_expr(
-                    Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
+                    Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
                     syntax_ptr,
                 )
             }
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 81912a4546f8..bc0125f0b136 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -260,9 +260,8 @@ impl GenericParams {
             self.fill_bounds(&lower_ctx, &type_param, Either::Left(type_ref));
         }
         for lifetime_param in params.lifetime_params() {
-            let name = lifetime_param
-                .lifetime_token()
-                .map_or_else(Name::missing, |tok| Name::new_lifetime(&tok));
+            let name =
+                lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<));
             let param = LifetimeParamData { name: name.clone() };
             let param_id = self.lifetimes.alloc(param);
             sm.lifetime_params.insert(param_id, lifetime_param.clone());
@@ -275,8 +274,8 @@ impl GenericParams {
         for pred in where_clause.predicates() {
             let target = if let Some(type_ref) = pred.ty() {
                 Either::Left(TypeRef::from_ast(lower_ctx, type_ref))
-            } else if let Some(lifetime_tok) = pred.lifetime_token() {
-                Either::Right(LifetimeRef::from_token(lifetime_tok))
+            } else if let Some(lifetime) = pred.lifetime() {
+                Either::Right(LifetimeRef::new(&lifetime))
             } else {
                 continue;
             };
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 1dc06a211556..dd3409762e3a 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -300,12 +300,12 @@ impl Ctx {
                             ast::SelfParamKind::Owned => self_type,
                             ast::SelfParamKind::Ref => TypeRef::Reference(
                                 Box::new(self_type),
-                                self_param.lifetime_token().map(LifetimeRef::from_token),
+                                self_param.lifetime().as_ref().map(LifetimeRef::new),
                                 Mutability::Shared,
                             ),
                             ast::SelfParamKind::MutRef => TypeRef::Reference(
                                 Box::new(self_type),
-                                self_param.lifetime_token().map(LifetimeRef::from_token),
+                                self_param.lifetime().as_ref().map(LifetimeRef::new),
                                 Mutability::Mut,
                             ),
                         }
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 609925012950..8a01e6eead0f 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -169,8 +169,8 @@ pub(super) fn lower_generic_args(
                 }
             }
             ast::GenericArg::LifetimeArg(lifetime_arg) => {
-                if let Some(lifetime) = lifetime_arg.lifetime_token() {
-                    let lifetime_ref = LifetimeRef::from_token(lifetime);
+                if let Some(lifetime) = lifetime_arg.lifetime() {
+                    let lifetime_ref = LifetimeRef::new(&lifetime);
                     args.push(GenericArg::Lifetime(lifetime_ref))
                 }
             }
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 347ceabb9b03..ae93d0d108d4 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -1,7 +1,7 @@
 //! HIR for references to types. Paths in these are not yet resolved. They can
 //! be directly created from an ast::TypeRef, without further queries.
 use hir_expand::name::Name;
-use syntax::{ast, SyntaxToken};
+use syntax::ast;
 
 use crate::{body::LowerCtx, path::Path};
 
@@ -80,8 +80,8 @@ impl LifetimeRef {
         LifetimeRef { name }
     }
 
-    pub(crate) fn from_token(token: SyntaxToken) -> Self {
-        LifetimeRef { name: Name::new_lifetime(&token) }
+    pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
+        LifetimeRef { name: Name::new_lifetime(lifetime) }
     }
 
     pub fn missing() -> LifetimeRef {
@@ -127,7 +127,7 @@ impl TypeRef {
             }
             ast::Type::RefType(inner) => {
                 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
-                let lifetime = inner.lifetime_token().map(|t| LifetimeRef::from_token(t));
+                let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(<));
                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
                 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
             }
@@ -259,7 +259,7 @@ impl TypeBound {
             }
             ast::TypeBoundKind::ForType(_) => TypeBound::Error, // FIXME ForType
             ast::TypeBoundKind::Lifetime(lifetime) => {
-                TypeBound::Lifetime(LifetimeRef::from_token(lifetime))
+                TypeBound::Lifetime(LifetimeRef::new(&lifetime))
             }
         }
     }
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 69d8e6803945..7fb4caea3682 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -37,9 +37,8 @@ impl Name {
         Name(Repr::TupleField(idx))
     }
 
-    pub fn new_lifetime(lt: &syntax::SyntaxToken) -> Name {
-        assert_eq!(lt.kind(), syntax::SyntaxKind::LIFETIME);
-        Name(Repr::Text(lt.text().clone()))
+    pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
+        Self::new_text(lt.text().clone())
     }
 
     /// Shortcut to create inline plain text name
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 0971f7701536..6f3022dfdfe9 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -237,7 +237,7 @@ fn pick_best(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken {
     fn priority(n: &SyntaxToken) -> usize {
         match n.kind() {
             WHITESPACE => 0,
-            IDENT | T![self] | T![super] | T![crate] | LIFETIME => 2,
+            IDENT | T![self] | T![super] | T![crate] | LIFETIME_IDENT => 2,
             _ => 1,
         }
     }
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 93ee77908619..7aeef7be5c2e 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -295,7 +295,7 @@ impl<'a> TtIter<'a> {
 
         impl<'a> TreeSink for OffsetTokenSink<'a> {
             fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
-                if kind == SyntaxKind::LIFETIME {
+                if kind == SyntaxKind::LIFETIME_IDENT {
                     n_tokens = 2;
                 }
                 for _ in 0..n_tokens {
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index ccc56c4796c3..d10d4b70e3d2 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -84,7 +84,11 @@ impl<'a> SubtreeTokenSource<'a> {
                 }
 
                 if let Some((curr, text)) = is_lifetime(cursor) {
-                    cached.push(Some(TtToken { kind: LIFETIME, is_joint_to_next: false, text }));
+                    cached.push(Some(TtToken {
+                        kind: LIFETIME_IDENT,
+                        is_joint_to_next: false,
+                        text,
+                    }));
                     self.cached_cursor.set(curr);
                     continue;
                 }
@@ -172,7 +176,7 @@ fn convert_ident(ident: &tt::Ident) -> TtToken {
     let kind = match ident.text.as_ref() {
         "true" => T![true],
         "false" => T![false],
-        i if i.starts_with('\'') => LIFETIME,
+        i if i.starts_with('\'') => LIFETIME_IDENT,
         _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
     };
 
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index d987b2500442..265c0d63d85c 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -380,7 +380,7 @@ trait TokenConvertor {
                 IDENT => make_leaf!(Ident),
                 k if k.is_keyword() => make_leaf!(Ident),
                 k if k.is_literal() => make_leaf!(Literal),
-                LIFETIME => {
+                LIFETIME_IDENT => {
                     let char_unit = TextSize::of('\'');
                     let r = TextRange::at(range.start(), char_unit);
                     let apostrophe = tt::Leaf::from(tt::Punct {
@@ -620,7 +620,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
             self.cursor = self.cursor.bump_subtree();
             return;
         }
-        if kind == LIFETIME {
+        if kind == LIFETIME_IDENT {
             n_tokens = 2;
         }
 
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 116b991a8202..23039eba4958 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -283,6 +283,13 @@ fn name_ref_or_index(p: &mut Parser) {
     m.complete(p, NAME_REF);
 }
 
+fn lifetime(p: &mut Parser) {
+    assert!(p.at(LIFETIME_IDENT));
+    let m = p.start();
+    p.bump(LIFETIME_IDENT);
+    m.complete(p, LIFETIME);
+}
+
 fn error_block(p: &mut Parser, message: &str) {
     assert!(p.at(T!['{']));
     let m = p.start();
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 31f42f16185d..18b63feb7b61 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -48,7 +48,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
         T![try],
         T![loop],
         T![for],
-        LIFETIME,
+        LIFETIME_IDENT,
     ]));
 
 const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[LET_KW, R_DOLLAR]);
@@ -75,7 +75,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
         T![for] => for_expr(p, None),
         T![while] => while_expr(p, None),
         T![try] => try_block_expr(p, None),
-        LIFETIME if la == T![:] => {
+        LIFETIME_IDENT if la == T![:] => {
             let m = p.start();
             label(p);
             match p.current() {
@@ -275,9 +275,9 @@ fn if_expr(p: &mut Parser) -> CompletedMarker {
 //     'c: for x in () {}
 // }
 fn label(p: &mut Parser) {
-    assert!(p.at(LIFETIME) && p.nth(1) == T![:]);
+    assert!(p.at(LIFETIME_IDENT) && p.nth(1) == T![:]);
     let m = p.start();
-    p.bump(LIFETIME);
+    lifetime(p);
     p.bump_any();
     m.complete(p, LABEL);
 }
@@ -501,7 +501,9 @@ fn continue_expr(p: &mut Parser) -> CompletedMarker {
     assert!(p.at(T![continue]));
     let m = p.start();
     p.bump(T![continue]);
-    p.eat(LIFETIME);
+    if p.at(LIFETIME_IDENT) {
+        lifetime(p);
+    }
     m.complete(p, CONTINUE_EXPR)
 }
 
@@ -518,7 +520,9 @@ fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
     assert!(p.at(T![break]));
     let m = p.start();
     p.bump(T![break]);
-    p.eat(LIFETIME);
+    if p.at(LIFETIME_IDENT) {
+        lifetime(p);
+    }
     // test break_ambiguity
     // fn foo(){
     //     if break {}
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index 8394020dafae..ab9a12b4deff 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -98,10 +98,10 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool {
     //     `<` `>` - empty generic parameters
     //     `<` `#` - generic parameters with attributes
     //     `<` `const` - const generic parameters
-    //     `<` (LIFETIME|IDENT) `>` - single generic parameter
-    //     `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
-    //     `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
-    //     `<` (LIFETIME|IDENT) `=` - generic parameter with a default
+    //     `<` (LIFETIME_IDENT|IDENT) `>` - single generic parameter
+    //     `<` (LIFETIME_IDENT|IDENT) `,` - first generic parameter in a list
+    //     `<` (LIFETIME_IDENT|IDENT) `:` - generic parameter with bounds
+    //     `<` (LIFETIME_IDENT|IDENT) `=` - generic parameter with a default
     // The only truly ambiguous case is
     //     `<` IDENT `>` `::` IDENT ...
     // we disambiguate it in favor of generics (`impl ::absolute::Path { ... }`)
@@ -113,7 +113,7 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool {
     if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == CONST_KW {
         return true;
     }
-    (p.nth(1) == LIFETIME || p.nth(1) == IDENT)
+    (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT)
         && (p.nth(2) == T![>] || p.nth(2) == T![,] || p.nth(2) == T![:] || p.nth(2) == T![=])
 }
 
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index a665ffc133c9..3ee4e4fca1b3 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -169,15 +169,20 @@ fn opt_self_param(p: &mut Parser) {
         let la1 = p.nth(1);
         let la2 = p.nth(2);
         let la3 = p.nth(3);
-        let n_toks = match (p.current(), la1, la2, la3) {
+        let mut n_toks = match (p.current(), la1, la2, la3) {
             (T![&], T![self], _, _) => 2,
             (T![&], T![mut], T![self], _) => 3,
-            (T![&], LIFETIME, T![self], _) => 3,
-            (T![&], LIFETIME, T![mut], T![self]) => 4,
+            (T![&], LIFETIME_IDENT, T![self], _) => 3,
+            (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4,
             _ => return,
         };
         m = p.start();
-        for _ in 0..n_toks {
+        p.bump_any();
+        if p.at(LIFETIME_IDENT) {
+            lifetime(p);
+            n_toks -= 1;
+        }
+        for _ in 1..n_toks {
             p.bump_any();
         }
     }
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs
index f2d34a749955..a013c49b9a0c 100644
--- a/crates/parser/src/grammar/type_args.rs
+++ b/crates/parser/src/grammar/type_args.rs
@@ -30,8 +30,8 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
 fn generic_arg(p: &mut Parser) {
     let m = p.start();
     match p.current() {
-        LIFETIME => {
-            p.bump(LIFETIME);
+        LIFETIME_IDENT => {
+            lifetime(p);
             m.complete(p, LIFETIME_ARG);
         }
         // test associated_type_bounds
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
index bc7d8d7244a3..9c3f7c28a2ad 100644
--- a/crates/parser/src/grammar/type_params.rs
+++ b/crates/parser/src/grammar/type_params.rs
@@ -23,7 +23,7 @@ fn generic_param_list(p: &mut Parser) {
         attributes::outer_attrs(p);
 
         match p.current() {
-            LIFETIME => lifetime_param(p, m),
+            LIFETIME_IDENT => lifetime_param(p, m),
             IDENT => type_param(p, m),
             CONST_KW => const_param(p, m),
             _ => {
@@ -40,8 +40,8 @@ fn generic_param_list(p: &mut Parser) {
 }
 
 fn lifetime_param(p: &mut Parser, m: Marker) {
-    assert!(p.at(LIFETIME));
-    p.bump(LIFETIME);
+    assert!(p.at(LIFETIME_IDENT));
+    lifetime(p);
     if p.at(T![:]) {
         lifetime_bounds(p);
     }
@@ -84,8 +84,8 @@ pub(super) fn bounds(p: &mut Parser) {
 fn lifetime_bounds(p: &mut Parser) {
     assert!(p.at(T![:]));
     p.bump(T![:]);
-    while p.at(LIFETIME) {
-        p.bump(LIFETIME);
+    while p.at(LIFETIME_IDENT) {
+        lifetime(p);
         if !p.eat(T![+]) {
             break;
         }
@@ -112,7 +112,7 @@ fn type_bound(p: &mut Parser) -> bool {
     let has_paren = p.eat(T!['(']);
     p.eat(T![?]);
     match p.current() {
-        LIFETIME => p.bump(LIFETIME),
+        LIFETIME_IDENT => lifetime(p),
         T![for] => types::for_type(p),
         _ if paths::is_use_path_start(p) => types::path_type_(p, false),
         _ => {
@@ -162,7 +162,7 @@ pub(super) fn opt_where_clause(p: &mut Parser) {
 
 fn is_where_predicate(p: &mut Parser) -> bool {
     match p.current() {
-        LIFETIME => true,
+        LIFETIME_IDENT => true,
         T![impl] => false,
         token => types::TYPE_FIRST.contains(token),
     }
@@ -175,8 +175,8 @@ fn is_where_clause_end(p: &mut Parser) -> bool {
 fn where_predicate(p: &mut Parser) {
     let m = p.start();
     match p.current() {
-        LIFETIME => {
-            p.bump(LIFETIME);
+        LIFETIME_IDENT => {
+            lifetime(p);
             if p.at(T![:]) {
                 bounds(p);
             } else {
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 1ea130ac599c..36a15eace822 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -167,7 +167,9 @@ fn ref_type(p: &mut Parser) {
     assert!(p.at(T![&]));
     let m = p.start();
     p.bump(T![&]);
-    p.eat(LIFETIME);
+    if p.at(LIFETIME_IDENT) {
+        lifetime(p);
+    }
     p.eat(T![mut]);
     type_no_bounds(p);
     m.complete(p, REF_TYPE);
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 5d6ec17a4cc7..980aa59794f8 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -116,7 +116,7 @@ pub enum SyntaxKind {
     ERROR,
     IDENT,
     WHITESPACE,
-    LIFETIME,
+    LIFETIME_IDENT,
     COMMENT,
     SHEBANG,
     L_DOLLAR,
@@ -237,6 +237,7 @@ pub enum SyntaxKind {
     TYPE_PARAM,
     CONST_PARAM,
     GENERIC_ARG_LIST,
+    LIFETIME,
     LIFETIME_ARG,
     TYPE_ARG,
     ASSOC_TYPE_ARG,
@@ -364,4 +365,4 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime] => { $ crate :: SyntaxKind :: LIFETIME } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 70c568ea1e7f..83de067d97de 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -311,7 +311,7 @@ where
     let pred = predicates.next().unwrap();
     let mut bounds = pred.type_bound_list().unwrap().bounds();
 
-    assert_eq!("'a", pred.lifetime_token().unwrap().text());
+    assert_eq!("'a", pred.lifetime().unwrap().lifetime_ident_token().unwrap().text());
 
     assert_bound("'b", bounds.next());
     assert_bound("'c", bounds.next());
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 6eae323f4a24..1588ba93ea27 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -20,6 +20,15 @@ impl NameRef {
     pub fn ident_token(&self) -> Option { support::token(&self.syntax, T![ident]) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Lifetime {
+    pub(crate) syntax: SyntaxNode,
+}
+impl Lifetime {
+    pub fn lifetime_ident_token(&self) -> Option {
+        support::token(&self.syntax, T![lifetime_ident])
+    }
+}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Path {
     pub(crate) syntax: SyntaxNode,
 }
@@ -105,9 +114,7 @@ pub struct LifetimeArg {
     pub(crate) syntax: SyntaxNode,
 }
 impl LifetimeArg {
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct ConstArg {
@@ -487,9 +494,7 @@ pub struct SelfParam {
 impl ast::AttrsOwner for SelfParam {}
 impl SelfParam {
     pub fn amp_token(&self) -> Option { support::token(&self.syntax, T![&]) }
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     pub fn mut_token(&self) -> Option { support::token(&self.syntax, T![mut]) }
     pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) }
     pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) }
@@ -605,9 +610,7 @@ pub struct LifetimeParam {
 impl ast::AttrsOwner for LifetimeParam {}
 impl ast::TypeBoundsOwner for LifetimeParam {}
 impl LifetimeParam {
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TypeParam {
@@ -628,9 +631,7 @@ impl ast::TypeBoundsOwner for WherePred {}
 impl WherePred {
     pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) }
     pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) }
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     pub fn ty(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -706,9 +707,7 @@ pub struct BreakExpr {
 impl ast::AttrsOwner for BreakExpr {}
 impl BreakExpr {
     pub fn break_token(&self) -> Option { support::token(&self.syntax, T![break]) }
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     pub fn expr(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -752,9 +751,7 @@ impl ContinueExpr {
     pub fn continue_token(&self) -> Option {
         support::token(&self.syntax, T![continue])
     }
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct EffectExpr {
@@ -937,9 +934,8 @@ pub struct Label {
     pub(crate) syntax: SyntaxNode,
 }
 impl Label {
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
+    pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct RecordExprFieldList {
@@ -1100,9 +1096,7 @@ pub struct RefType {
 }
 impl RefType {
     pub fn amp_token(&self) -> Option { support::token(&self.syntax, T![&]) }
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     pub fn mut_token(&self) -> Option { support::token(&self.syntax, T![mut]) }
     pub fn ty(&self) -> Option { support::child(&self.syntax) }
 }
@@ -1129,9 +1123,7 @@ pub struct TypeBound {
     pub(crate) syntax: SyntaxNode,
 }
 impl TypeBound {
-    pub fn lifetime_token(&self) -> Option {
-        support::token(&self.syntax, T![lifetime])
-    }
+    pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     pub fn question_mark_token(&self) -> Option { support::token(&self.syntax, T![?]) }
     pub fn ty(&self) -> Option { support::child(&self.syntax) }
 }
@@ -1438,6 +1430,17 @@ impl AstNode for NameRef {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for Lifetime {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME }
+    fn cast(syntax: SyntaxNode) -> Option {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for Path {
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH }
     fn cast(syntax: SyntaxNode) -> Option {
@@ -3524,6 +3527,11 @@ impl std::fmt::Display for NameRef {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for Lifetime {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for Path {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 40dec3c7f99c..c45cb514a0f5 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -12,6 +12,12 @@ use crate::{
     SmolStr, SyntaxElement, SyntaxToken, T,
 };
 
+impl ast::Lifetime {
+    pub fn text(&self) -> &SmolStr {
+        text_of_first_token(self.syntax())
+    }
+}
+
 impl ast::Name {
     pub fn text(&self) -> &SmolStr {
         text_of_first_token(self.syntax())
@@ -393,7 +399,7 @@ pub enum TypeBoundKind {
     /// for<'a> ...
     ForType(ast::ForType),
     /// 'a
-    Lifetime(SyntaxToken),
+    Lifetime(ast::Lifetime),
 }
 
 impl ast::TypeBound {
@@ -402,7 +408,7 @@ impl ast::TypeBound {
             TypeBoundKind::PathType(path_type)
         } else if let Some(for_type) = support::children(self.syntax()).next() {
             TypeBoundKind::ForType(for_type)
-        } else if let Some(lifetime) = self.lifetime_token() {
+        } else if let Some(lifetime) = self.lifetime() {
             TypeBoundKind::Lifetime(lifetime)
         } else {
             unreachable!()
@@ -440,7 +446,7 @@ impl ast::LifetimeParam {
             .children_with_tokens()
             .filter_map(|it| it.into_token())
             .skip_while(|x| x.kind() != T![:])
-            .filter(|it| it.kind() == T![lifetime])
+            .filter(|it| it.kind() == T![lifetime_ident])
     }
 }
 
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index 8afd7e53bc5e..0cbba73c5cba 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -146,9 +146,9 @@ fn rustc_token_kind_to_syntax_kind(
             rustc_lexer::TokenKind::RawIdent => IDENT,
             rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(&kind),
 
-            rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME,
+            rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT,
             rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => {
-                return (LIFETIME, Some("Lifetime name cannot start with a number"))
+                return (LIFETIME_IDENT, Some("Lifetime name cannot start with a number"))
             }
 
             rustc_lexer::TokenKind::Semi => T![;],
diff --git a/crates/syntax/test_data/lexer/err/0057_lifetime_strarts_with_a_number.rs b/crates/syntax/test_data/lexer/err/0057_lifetime_starts_with_a_number.rs
similarity index 100%
rename from crates/syntax/test_data/lexer/err/0057_lifetime_strarts_with_a_number.rs
rename to crates/syntax/test_data/lexer/err/0057_lifetime_starts_with_a_number.rs
diff --git a/crates/syntax/test_data/lexer/err/0057_lifetime_strarts_with_a_number.txt b/crates/syntax/test_data/lexer/err/0057_lifetime_starts_with_a_number.txt
similarity index 77%
rename from crates/syntax/test_data/lexer/err/0057_lifetime_strarts_with_a_number.txt
rename to crates/syntax/test_data/lexer/err/0057_lifetime_starts_with_a_number.txt
index e138bcebcdc5..11e0ae14a917 100644
--- a/crates/syntax/test_data/lexer/err/0057_lifetime_strarts_with_a_number.txt
+++ b/crates/syntax/test_data/lexer/err/0057_lifetime_starts_with_a_number.txt
@@ -1,6 +1,6 @@
-LIFETIME 2 "\'1"
+LIFETIME_IDENT 2 "\'1"
 WHITESPACE 1 "\n"
-LIFETIME 10 "\'1lifetime"
+LIFETIME_IDENT 10 "\'1lifetime"
 WHITESPACE 1 "\n"
 > error0..2 token("\'1") msg(Lifetime name cannot start with a number)
 > error3..13 token("\'1lifetime") msg(Lifetime name cannot start with a number)
diff --git a/crates/syntax/test_data/lexer/ok/0007_lifetimes.txt b/crates/syntax/test_data/lexer/ok/0007_lifetimes.txt
index 005c29100a06..4d6625c3a414 100644
--- a/crates/syntax/test_data/lexer/ok/0007_lifetimes.txt
+++ b/crates/syntax/test_data/lexer/ok/0007_lifetimes.txt
@@ -1,8 +1,8 @@
-LIFETIME 2 "\'a"
+LIFETIME_IDENT 2 "\'a"
 WHITESPACE 1 " "
-LIFETIME 4 "\'foo"
+LIFETIME_IDENT 4 "\'foo"
 WHITESPACE 1 " "
-LIFETIME 12 "\'foo_bar_baz"
+LIFETIME_IDENT 12 "\'foo_bar_baz"
 WHITESPACE 1 " "
-LIFETIME 2 "\'_"
+LIFETIME_IDENT 2 "\'_"
 WHITESPACE 1 "\n"
diff --git a/crates/syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
index e3be6b22e11a..4c4ddf5ecab8 100644
--- a/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
@@ -42,7 +42,8 @@ SOURCE_FILE@0..240
               GENERIC_PARAM_LIST@32..36
                 L_ANGLE@32..33 "<"
                 LIFETIME_PARAM@33..35
-                  LIFETIME@33..35 "\'a"
+                  LIFETIME@33..35
+                    LIFETIME_IDENT@33..35 "\'a"
                 R_ANGLE@35..36 ">"
               WHITESPACE@36..37 " "
               PATH_TYPE@37..46
@@ -53,7 +54,8 @@ SOURCE_FILE@0..240
                     GENERIC_ARG_LIST@42..46
                       L_ANGLE@42..43 "<"
                       LIFETIME_ARG@43..45
-                        LIFETIME@43..45 "\'a"
+                        LIFETIME@43..45
+                          LIFETIME_IDENT@43..45 "\'a"
                       R_ANGLE@45..46 ">"
             R_PAREN@46..47 ")"
       R_ANGLE@47..48 ">"
@@ -125,7 +127,8 @@ SOURCE_FILE@0..240
                 GENERIC_PARAM_LIST@106..110
                   L_ANGLE@106..107 "<"
                   LIFETIME_PARAM@107..109
-                    LIFETIME@107..109 "\'a"
+                    LIFETIME@107..109
+                      LIFETIME_IDENT@107..109 "\'a"
                   R_ANGLE@109..110 ">"
                 WHITESPACE@110..111 " "
                 PATH_TYPE@111..120
@@ -136,7 +139,8 @@ SOURCE_FILE@0..240
                       GENERIC_ARG_LIST@116..120
                         L_ANGLE@116..117 "<"
                         LIFETIME_ARG@117..119
-                          LIFETIME@117..119 "\'a"
+                          LIFETIME@117..119
+                            LIFETIME_IDENT@117..119 "\'a"
                         R_ANGLE@119..120 ">"
               R_PAREN@120..121 ")"
       EXPR_STMT@121..123
@@ -187,7 +191,7 @@ SOURCE_FILE@0..240
                 PATH_SEGMENT@154..158
                   L_ANGLE@154..155 "<"
                   ERROR@155..157
-                    LIFETIME@155..157 "\'a"
+                    LIFETIME_IDENT@155..157 "\'a"
                   R_ANGLE@157..158 ">"
             WHITESPACE@158..159 " "
             BIN_EXPR@159..180
@@ -201,7 +205,7 @@ SOURCE_FILE@0..240
                             IDENT@159..164 "Trait"
                     L_ANGLE@164..165 "<"
                     ERROR@165..167
-                      LIFETIME@165..167 "\'a"
+                      LIFETIME_IDENT@165..167 "\'a"
                   R_ANGLE@167..168 ">"
                   ERROR@168..169
                     R_PAREN@168..169 ")"
@@ -245,7 +249,8 @@ SOURCE_FILE@0..240
                             GENERIC_PARAM_LIST@200..204
                               L_ANGLE@200..201 "<"
                               LIFETIME_PARAM@201..203
-                                LIFETIME@201..203 "\'a"
+                                LIFETIME@201..203
+                                  LIFETIME_IDENT@201..203 "\'a"
                               R_ANGLE@203..204 ">"
                             WHITESPACE@204..205 " "
                             PATH_TYPE@205..214
@@ -256,7 +261,8 @@ SOURCE_FILE@0..240
                                   GENERIC_ARG_LIST@210..214
                                     L_ANGLE@210..211 "<"
                                     LIFETIME_ARG@211..213
-                                      LIFETIME@211..213 "\'a"
+                                      LIFETIME@211..213
+                                        LIFETIME_IDENT@211..213 "\'a"
                                     R_ANGLE@213..214 ">"
                           R_PAREN@214..215 ")"
             WHITESPACE@215..216 " "
diff --git a/crates/syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/syntax/test_data/parser/err/0027_incomplere_where_for.rast
index a8e42e6eaaec..c5215d6b195f 100644
--- a/crates/syntax/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/crates/syntax/test_data/parser/err/0027_incomplere_where_for.rast
@@ -16,7 +16,8 @@ SOURCE_FILE@0..30
         GENERIC_PARAM_LIST@22..26
           L_ANGLE@22..23 "<"
           LIFETIME_PARAM@23..25
-            LIFETIME@23..25 "\'a"
+            LIFETIME@23..25
+              LIFETIME_IDENT@23..25 "\'a"
           R_ANGLE@25..26 ">"
     WHITESPACE@26..27 "\n"
     BLOCK_EXPR@27..29
diff --git a/crates/syntax/test_data/parser/err/0043_weird_blocks.rast b/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
index df29017e7f23..e73bd1aea9e2 100644
--- a/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
+++ b/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
@@ -54,7 +54,8 @@ SOURCE_FILE@0..83
         EXPR_STMT@68..75
           ERROR@68..75
             LABEL@68..75
-              LIFETIME@68..74 "\'label"
+              LIFETIME@68..74
+                LIFETIME_IDENT@68..74 "\'label"
               COLON@74..75 ":"
         WHITESPACE@75..76 " "
         LITERAL@76..78
diff --git a/crates/syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/syntax/test_data/parser/err/0044_unexpected_for_type.rast
index 71aa864943f8..cc54185e5fc1 100644
--- a/crates/syntax/test_data/parser/err/0044_unexpected_for_type.rast
+++ b/crates/syntax/test_data/parser/err/0044_unexpected_for_type.rast
@@ -12,12 +12,14 @@ SOURCE_FILE@0..239
       GENERIC_PARAM_LIST@17..21
         L_ANGLE@17..18 "<"
         LIFETIME_PARAM@18..20
-          LIFETIME@18..20 "\'a"
+          LIFETIME@18..20
+            LIFETIME_IDENT@18..20 "\'a"
         R_ANGLE@20..21 ">"
       WHITESPACE@21..22 " "
       REF_TYPE@22..29
         AMP@22..23 "&"
-        LIFETIME@23..25 "\'a"
+        LIFETIME@23..25
+          LIFETIME_IDENT@23..25 "\'a"
         WHITESPACE@25..26 " "
         PATH_TYPE@26..29
           PATH@26..29
@@ -39,14 +41,16 @@ SOURCE_FILE@0..239
       GENERIC_PARAM_LIST@48..52
         L_ANGLE@48..49 "<"
         LIFETIME_PARAM@49..51
-          LIFETIME@49..51 "\'a"
+          LIFETIME@49..51
+            LIFETIME_IDENT@49..51 "\'a"
         R_ANGLE@51..52 ">"
       WHITESPACE@52..53 " "
       TUPLE_TYPE@53..63
         L_PAREN@53..54 "("
         REF_TYPE@54..61
           AMP@54..55 "&"
-          LIFETIME@55..57 "\'a"
+          LIFETIME@55..57
+            LIFETIME_IDENT@55..57 "\'a"
           WHITESPACE@57..58 " "
           PATH_TYPE@58..61
             PATH@58..61
@@ -70,7 +74,8 @@ SOURCE_FILE@0..239
       GENERIC_PARAM_LIST@84..88
         L_ANGLE@84..85 "<"
         LIFETIME_PARAM@85..87
-          LIFETIME@85..87 "\'a"
+          LIFETIME@85..87
+            LIFETIME_IDENT@85..87 "\'a"
         R_ANGLE@87..88 ">"
       WHITESPACE@88..89 " "
       SLICE_TYPE@89..94
@@ -96,7 +101,8 @@ SOURCE_FILE@0..239
       GENERIC_PARAM_LIST@115..119
         L_ANGLE@115..116 "<"
         LIFETIME_PARAM@116..118
-          LIFETIME@116..118 "\'a"
+          LIFETIME@116..118
+            LIFETIME_IDENT@116..118 "\'a"
         R_ANGLE@118..119 ">"
       WHITESPACE@119..120 " "
       FOR_TYPE@120..148
@@ -104,7 +110,8 @@ SOURCE_FILE@0..239
         GENERIC_PARAM_LIST@123..127
           L_ANGLE@123..124 "<"
           LIFETIME_PARAM@124..126
-            LIFETIME@124..126 "\'b"
+            LIFETIME@124..126
+              LIFETIME_IDENT@124..126 "\'b"
           R_ANGLE@126..127 ">"
         WHITESPACE@127..128 " "
         FN_PTR_TYPE@128..148
@@ -114,7 +121,8 @@ SOURCE_FILE@0..239
             PARAM@131..138
               REF_TYPE@131..138
                 AMP@131..132 "&"
-                LIFETIME@132..134 "\'a"
+                LIFETIME@132..134
+                  LIFETIME_IDENT@132..134 "\'a"
                 WHITESPACE@134..135 " "
                 PATH_TYPE@135..138
                   PATH@135..138
@@ -126,7 +134,8 @@ SOURCE_FILE@0..239
             PARAM@140..147
               REF_TYPE@140..147
                 AMP@140..141 "&"
-                LIFETIME@141..143 "\'b"
+                LIFETIME@141..143
+                  LIFETIME_IDENT@141..143 "\'b"
                 WHITESPACE@143..144 " "
                 PATH_TYPE@144..147
                   PATH@144..147
@@ -159,7 +168,8 @@ SOURCE_FILE@0..239
         GENERIC_PARAM_LIST@183..187
           L_ANGLE@183..184 "<"
           LIFETIME_PARAM@184..186
-            LIFETIME@184..186 "\'a"
+            LIFETIME@184..186
+              LIFETIME_IDENT@184..186 "\'a"
           R_ANGLE@186..187 ">"
         WHITESPACE@187..188 " "
         FOR_TYPE@188..227
@@ -167,7 +177,8 @@ SOURCE_FILE@0..239
           GENERIC_PARAM_LIST@191..195
             L_ANGLE@191..192 "<"
             LIFETIME_PARAM@192..194
-              LIFETIME@192..194 "\'b"
+              LIFETIME@192..194
+                LIFETIME_IDENT@192..194 "\'b"
             R_ANGLE@194..195 ">"
           WHITESPACE@195..196 " "
           FOR_TYPE@196..227
@@ -175,7 +186,8 @@ SOURCE_FILE@0..239
             GENERIC_PARAM_LIST@199..203
               L_ANGLE@199..200 "<"
               LIFETIME_PARAM@200..202
-                LIFETIME@200..202 "\'c"
+                LIFETIME@200..202
+                  LIFETIME_IDENT@200..202 "\'c"
               R_ANGLE@202..203 ">"
             WHITESPACE@203..204 " "
             FN_PTR_TYPE@204..227
@@ -185,7 +197,8 @@ SOURCE_FILE@0..239
                 PARAM@207..212
                   REF_TYPE@207..212
                     AMP@207..208 "&"
-                    LIFETIME@208..210 "\'a"
+                    LIFETIME@208..210
+                      LIFETIME_IDENT@208..210 "\'a"
                     WHITESPACE@210..211 " "
                     PATH_TYPE@211..212
                       PATH@211..212
@@ -197,7 +210,8 @@ SOURCE_FILE@0..239
                 PARAM@214..219
                   REF_TYPE@214..219
                     AMP@214..215 "&"
-                    LIFETIME@215..217 "\'b"
+                    LIFETIME@215..217
+                      LIFETIME_IDENT@215..217 "\'b"
                     WHITESPACE@217..218 " "
                     PATH_TYPE@218..219
                       PATH@218..219
@@ -209,7 +223,8 @@ SOURCE_FILE@0..239
                 PARAM@221..226
                   REF_TYPE@221..226
                     AMP@221..222 "&"
-                    LIFETIME@222..224 "\'c"
+                    LIFETIME@222..224
+                      LIFETIME_IDENT@222..224 "\'c"
                     WHITESPACE@224..225 " "
                     PATH_TYPE@225..226
                       PATH@225..226
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
index 592741cdb1b5..7049f4734c02 100644
--- a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
+++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
@@ -7,14 +7,16 @@ SOURCE_FILE@0..187
     GENERIC_PARAM_LIST@8..12
       L_ANGLE@8..9 "<"
       LIFETIME_PARAM@9..11
-        LIFETIME@9..11 "\'a"
+        LIFETIME@9..11
+          LIFETIME_IDENT@9..11 "\'a"
       R_ANGLE@11..12 ">"
     WHITESPACE@12..13 " "
     EQ@13..14 "="
     WHITESPACE@14..15 " "
     REF_TYPE@15..34
       AMP@15..16 "&"
-      LIFETIME@16..18 "\'a"
+      LIFETIME@16..18
+        LIFETIME_IDENT@16..18 "\'a"
       WHITESPACE@18..19 " "
       DYN_TRAIT_TYPE@19..34
         DYN_KW@19..22 "dyn"
@@ -101,7 +103,8 @@ SOURCE_FILE@0..187
             PLUS@99..100 "+"
             WHITESPACE@100..101 " "
             TYPE_BOUND@101..108
-              LIFETIME@101..108 "\'static"
+              LIFETIME@101..108
+                LIFETIME_IDENT@101..108 "\'static"
     SEMICOLON@108..109 ";"
   WHITESPACE@109..110 "\n"
   FN@110..186
diff --git a/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast b/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
index a4271fc87ab2..0adf2cd5a0e2 100644
--- a/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
+++ b/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
@@ -14,7 +14,8 @@ SOURCE_FILE@0..30
       EXPR_STMT@16..22
         ERROR@16..22
           LABEL@16..22
-            LIFETIME@16..21 "\'loop"
+            LIFETIME@16..21
+              LIFETIME_IDENT@16..21 "\'loop"
             COLON@21..22 ":"
       WHITESPACE@22..23 " "
       IMPL@23..27
diff --git a/crates/syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
index 62da7b887343..6cdfd058b4bc 100644
--- a/crates/syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
@@ -22,7 +22,8 @@ SOURCE_FILE@0..54
         GENERIC_PARAM_LIST@30..34
           L_ANGLE@30..31 "<"
           LIFETIME_PARAM@31..33
-            LIFETIME@31..33 "\'a"
+            LIFETIME@31..33
+              LIFETIME_IDENT@31..33 "\'a"
           R_ANGLE@33..34 ">"
         WHITESPACE@34..35 " "
         PATH_TYPE@35..36
@@ -44,7 +45,8 @@ SOURCE_FILE@0..54
                     PARAM@41..48
                       REF_TYPE@41..48
                         AMP@41..42 "&"
-                        LIFETIME@42..44 "\'a"
+                        LIFETIME@42..44
+                          LIFETIME_IDENT@42..44 "\'a"
                         WHITESPACE@44..45 " "
                         PATH_TYPE@45..48
                           PATH@45..48
diff --git a/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast b/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
index d24ad74232e6..8048f5fadd43 100644
--- a/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0006_self_param.rast
@@ -52,7 +52,8 @@ SOURCE_FILE@0..128
           L_PAREN@55..56 "("
           SELF_PARAM@56..64
             AMP@56..57 "&"
-            LIFETIME@57..59 "\'a"
+            LIFETIME@57..59
+              LIFETIME_IDENT@57..59 "\'a"
             WHITESPACE@59..60 " "
             SELF_KW@60..64 "self"
           COMMA@64..65 ","
@@ -71,7 +72,8 @@ SOURCE_FILE@0..128
           L_PAREN@78..79 "("
           SELF_PARAM@79..91
             AMP@79..80 "&"
-            LIFETIME@80..82 "\'a"
+            LIFETIME@80..82
+              LIFETIME_IDENT@80..82 "\'a"
             WHITESPACE@82..83 " "
             MUT_KW@83..86 "mut"
             WHITESPACE@86..87 " "
diff --git a/crates/syntax/test_data/parser/inline/ok/0007_type_param_bounds.rast b/crates/syntax/test_data/parser/inline/ok/0007_type_param_bounds.rast
index e95688f56a58..075b438d2f7d 100644
--- a/crates/syntax/test_data/parser/inline/ok/0007_type_param_bounds.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0007_type_param_bounds.rast
@@ -13,7 +13,8 @@ SOURCE_FILE@0..35
         WHITESPACE@11..12 " "
         TYPE_BOUND_LIST@12..32
           TYPE_BOUND@12..14
-            LIFETIME@12..14 "\'a"
+            LIFETIME@12..14
+              LIFETIME_IDENT@12..14 "\'a"
           WHITESPACE@14..15 " "
           PLUS@15..16 "+"
           WHITESPACE@16..17 " "
diff --git a/crates/syntax/test_data/parser/inline/ok/0015_continue_expr.rast b/crates/syntax/test_data/parser/inline/ok/0015_continue_expr.rast
index 104e153ce69d..b9e92b57a5f7 100644
--- a/crates/syntax/test_data/parser/inline/ok/0015_continue_expr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0015_continue_expr.rast
@@ -26,7 +26,8 @@ SOURCE_FILE@0..69
             CONTINUE_EXPR@48..59
               CONTINUE_KW@48..56 "continue"
               WHITESPACE@56..57 " "
-              LIFETIME@57..59 "\'l"
+              LIFETIME@57..59
+                LIFETIME_IDENT@57..59 "\'l"
             SEMICOLON@59..60 ";"
           WHITESPACE@60..65 "\n    "
           R_CURLY@65..66 "}"
diff --git a/crates/syntax/test_data/parser/inline/ok/0028_impl_trait_type.rast b/crates/syntax/test_data/parser/inline/ok/0028_impl_trait_type.rast
index 32b2959bdfc6..dad4362b7424 100644
--- a/crates/syntax/test_data/parser/inline/ok/0028_impl_trait_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0028_impl_trait_type.rast
@@ -31,13 +31,15 @@ SOURCE_FILE@0..43
                           GENERIC_ARG_LIST@31..35
                             L_ANGLE@31..32 "<"
                             LIFETIME_ARG@32..34
-                              LIFETIME@32..34 "\'a"
+                              LIFETIME@32..34
+                                LIFETIME_IDENT@32..34 "\'a"
                             R_ANGLE@34..35 ">"
                   R_ANGLE@35..36 ">"
         WHITESPACE@36..37 " "
         PLUS@37..38 "+"
         WHITESPACE@38..39 " "
         TYPE_BOUND@39..41
-          LIFETIME@39..41 "\'a"
+          LIFETIME@39..41
+            LIFETIME_IDENT@39..41 "\'a"
     SEMICOLON@41..42 ";"
   WHITESPACE@42..43 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0033_reference_type;.rast b/crates/syntax/test_data/parser/inline/ok/0033_reference_type;.rast
index 974df9f9aa13..ac0299268810 100644
--- a/crates/syntax/test_data/parser/inline/ok/0033_reference_type;.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0033_reference_type;.rast
@@ -24,7 +24,8 @@ SOURCE_FILE@0..54
     WHITESPACE@22..23 " "
     REF_TYPE@23..34
       AMP@23..24 "&"
-      LIFETIME@24..31 "\'static"
+      LIFETIME@24..31
+        LIFETIME_IDENT@24..31 "\'static"
       WHITESPACE@31..32 " "
       TUPLE_TYPE@32..34
         L_PAREN@32..33 "("
diff --git a/crates/syntax/test_data/parser/inline/ok/0034_break_expr.rast b/crates/syntax/test_data/parser/inline/ok/0034_break_expr.rast
index f905def6ff00..828013d458f5 100644
--- a/crates/syntax/test_data/parser/inline/ok/0034_break_expr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0034_break_expr.rast
@@ -26,7 +26,8 @@ SOURCE_FILE@0..102
             BREAK_EXPR@45..53
               BREAK_KW@45..50 "break"
               WHITESPACE@50..51 " "
-              LIFETIME@51..53 "\'l"
+              LIFETIME@51..53
+                LIFETIME_IDENT@51..53 "\'l"
             SEMICOLON@53..54 ";"
           WHITESPACE@54..63 "\n        "
           EXPR_STMT@63..72
@@ -41,7 +42,8 @@ SOURCE_FILE@0..102
             BREAK_EXPR@81..92
               BREAK_KW@81..86 "break"
               WHITESPACE@86..87 " "
-              LIFETIME@87..89 "\'l"
+              LIFETIME@87..89
+                LIFETIME_IDENT@87..89 "\'l"
               WHITESPACE@89..90 " "
               LITERAL@90..92
                 INT_NUMBER@90..92 "92"
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
index 69e98b9d6c02..51e881a8e28b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
@@ -15,7 +15,8 @@ SOURCE_FILE@0..46
           GENERIC_ARG_LIST@10..44
             L_ANGLE@10..11 "<"
             LIFETIME_ARG@11..18
-              LIFETIME@11..18 "\'static"
+              LIFETIME@11..18
+                LIFETIME_IDENT@11..18 "\'static"
             COMMA@18..19 ","
             WHITESPACE@19..20 " "
             TYPE_ARG@20..23
diff --git a/crates/syntax/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast b/crates/syntax/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast
index 6baea6e3ca2a..b6f5a5689e13 100644
--- a/crates/syntax/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast
@@ -33,7 +33,8 @@ SOURCE_FILE@0..35
                               GENERIC_ARG_LIST@24..28
                                 L_ANGLE@24..25 "<"
                                 LIFETIME_ARG@25..27
-                                  LIFETIME@25..27 "\'a"
+                                  LIFETIME@25..27
+                                    LIFETIME_IDENT@25..27 "\'a"
                                 R_ANGLE@27..28 ">"
                     R_PAREN@28..29 ")"
       R_ANGLE@29..30 ">"
diff --git a/crates/syntax/test_data/parser/inline/ok/0048_path_type_with_bounds.rast b/crates/syntax/test_data/parser/inline/ok/0048_path_type_with_bounds.rast
index 4d8404e7c09f..7df6e190aa94 100644
--- a/crates/syntax/test_data/parser/inline/ok/0048_path_type_with_bounds.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0048_path_type_with_bounds.rast
@@ -31,7 +31,8 @@ SOURCE_FILE@0..58
                     PLUS@18..19 "+"
                     WHITESPACE@19..20 " "
                     TYPE_BOUND@20..22
-                      LIFETIME@20..22 "\'f"
+                      LIFETIME@20..22
+                        LIFETIME_IDENT@20..22 "\'f"
               R_ANGLE@22..23 ">"
     WHITESPACE@23..24 " "
     BLOCK_EXPR@24..26
@@ -72,7 +73,8 @@ SOURCE_FILE@0..58
                     PLUS@49..50 "+"
                     WHITESPACE@50..51 " "
                     TYPE_BOUND@51..53
-                      LIFETIME@51..53 "\'f"
+                      LIFETIME@51..53
+                        LIFETIME_IDENT@51..53 "\'f"
               R_ANGLE@53..54 ">"
     WHITESPACE@54..55 " "
     BLOCK_EXPR@55..57
diff --git a/crates/syntax/test_data/parser/inline/ok/0056_where_clause.rast b/crates/syntax/test_data/parser/inline/ok/0056_where_clause.rast
index 28129c50c618..61dea413d95c 100644
--- a/crates/syntax/test_data/parser/inline/ok/0056_where_clause.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0056_where_clause.rast
@@ -12,17 +12,20 @@ SOURCE_FILE@0..116
       WHERE_KW@9..14 "where"
       WHITESPACE@14..18 "\n   "
       WHERE_PRED@18..29
-        LIFETIME@18..20 "\'a"
+        LIFETIME@18..20
+          LIFETIME_IDENT@18..20 "\'a"
         COLON@20..21 ":"
         WHITESPACE@21..22 " "
         TYPE_BOUND_LIST@22..29
           TYPE_BOUND@22..24
-            LIFETIME@22..24 "\'b"
+            LIFETIME@22..24
+              LIFETIME_IDENT@22..24 "\'b"
           WHITESPACE@24..25 " "
           PLUS@25..26 "+"
           WHITESPACE@26..27 " "
           TYPE_BOUND@27..29
-            LIFETIME@27..29 "\'c"
+            LIFETIME@27..29
+              LIFETIME_IDENT@27..29 "\'c"
       COMMA@29..30 ","
       WHITESPACE@30..34 "\n   "
       WHERE_PRED@34..59
@@ -53,7 +56,8 @@ SOURCE_FILE@0..116
           PLUS@50..51 "+"
           WHITESPACE@51..52 " "
           TYPE_BOUND@52..59
-            LIFETIME@52..59 "\'static"
+            LIFETIME@52..59
+              LIFETIME_IDENT@52..59 "\'static"
       COMMA@59..60 ","
       WHITESPACE@60..64 "\n   "
       WHERE_PRED@64..82
@@ -71,7 +75,8 @@ SOURCE_FILE@0..116
         WHITESPACE@79..80 " "
         TYPE_BOUND_LIST@80..82
           TYPE_BOUND@80..82
-            LIFETIME@80..82 "\'a"
+            LIFETIME@80..82
+              LIFETIME_IDENT@80..82 "\'a"
       COMMA@82..83 ","
       WHITESPACE@83..87 "\n   "
       WHERE_PRED@87..112
@@ -102,7 +107,8 @@ SOURCE_FILE@0..116
         WHITESPACE@109..110 " "
         TYPE_BOUND_LIST@110..112
           TYPE_BOUND@110..112
-            LIFETIME@110..112 "\'a"
+            LIFETIME@110..112
+              LIFETIME_IDENT@110..112 "\'a"
     WHITESPACE@112..113 "\n"
     BLOCK_EXPR@113..115
       L_CURLY@113..114 "{"
diff --git a/crates/syntax/test_data/parser/inline/ok/0065_dyn_trait_type.rast b/crates/syntax/test_data/parser/inline/ok/0065_dyn_trait_type.rast
index 3a7fcfe2487e..49d26cef43ea 100644
--- a/crates/syntax/test_data/parser/inline/ok/0065_dyn_trait_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0065_dyn_trait_type.rast
@@ -31,13 +31,15 @@ SOURCE_FILE@0..42
                           GENERIC_ARG_LIST@30..34
                             L_ANGLE@30..31 "<"
                             LIFETIME_ARG@31..33
-                              LIFETIME@31..33 "\'a"
+                              LIFETIME@31..33
+                                LIFETIME_IDENT@31..33 "\'a"
                             R_ANGLE@33..34 ">"
                   R_ANGLE@34..35 ">"
         WHITESPACE@35..36 " "
         PLUS@36..37 "+"
         WHITESPACE@37..38 " "
         TYPE_BOUND@38..40
-          LIFETIME@38..40 "\'a"
+          LIFETIME@38..40
+            LIFETIME_IDENT@38..40 "\'a"
     SEMICOLON@40..41 ";"
   WHITESPACE@41..42 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/syntax/test_data/parser/inline/ok/0081_for_type.rast
index f319d5141d50..8c909b5af113 100644
--- a/crates/syntax/test_data/parser/inline/ok/0081_for_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0081_for_type.rast
@@ -12,7 +12,8 @@ SOURCE_FILE@0..121
       GENERIC_PARAM_LIST@12..16
         L_ANGLE@12..13 "<"
         LIFETIME_PARAM@13..15
-          LIFETIME@13..15 "\'a"
+          LIFETIME@13..15
+            LIFETIME_IDENT@13..15 "\'a"
         R_ANGLE@15..16 ">"
       WHITESPACE@16..17 " "
       FN_PTR_TYPE@17..27
@@ -42,7 +43,8 @@ SOURCE_FILE@0..121
       GENERIC_PARAM_LIST@41..45
         L_ANGLE@41..42 "<"
         LIFETIME_PARAM@42..44
-          LIFETIME@42..44 "\'a"
+          LIFETIME@42..44
+            LIFETIME_IDENT@42..44 "\'a"
         R_ANGLE@44..45 ">"
       WHITESPACE@45..46 " "
       FN_PTR_TYPE@46..80
@@ -59,7 +61,8 @@ SOURCE_FILE@0..121
           PARAM@67..73
             REF_TYPE@67..73
               AMP@67..68 "&"
-              LIFETIME@68..70 "\'a"
+              LIFETIME@68..70
+                LIFETIME_IDENT@68..70 "\'a"
               WHITESPACE@70..71 " "
               TUPLE_TYPE@71..73
                 L_PAREN@71..72 "("
@@ -87,7 +90,8 @@ SOURCE_FILE@0..121
       GENERIC_PARAM_LIST@96..100
         L_ANGLE@96..97 "<"
         LIFETIME_PARAM@97..99
-          LIFETIME@97..99 "\'a"
+          LIFETIME@97..99
+            LIFETIME_IDENT@97..99 "\'a"
         R_ANGLE@99..100 ">"
       WHITESPACE@100..101 " "
       PATH_TYPE@101..119
@@ -100,7 +104,8 @@ SOURCE_FILE@0..121
               TYPE_ARG@111..118
                 REF_TYPE@111..118
                   AMP@111..112 "&"
-                  LIFETIME@112..114 "\'a"
+                  LIFETIME@112..114
+                    LIFETIME_IDENT@112..114 "\'a"
                   WHITESPACE@114..115 " "
                   PATH_TYPE@115..118
                     PATH@115..118
diff --git a/crates/syntax/test_data/parser/inline/ok/0109_label.rast b/crates/syntax/test_data/parser/inline/ok/0109_label.rast
index c9588025ce51..860dfe6088e1 100644
--- a/crates/syntax/test_data/parser/inline/ok/0109_label.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0109_label.rast
@@ -14,7 +14,8 @@ SOURCE_FILE@0..74
       EXPR_STMT@15..26
         LOOP_EXPR@15..26
           LABEL@15..18
-            LIFETIME@15..17 "\'a"
+            LIFETIME@15..17
+              LIFETIME_IDENT@15..17 "\'a"
             COLON@17..18 ":"
           WHITESPACE@18..19 " "
           LOOP_KW@19..23 "loop"
@@ -26,7 +27,8 @@ SOURCE_FILE@0..74
       EXPR_STMT@31..48
         WHILE_EXPR@31..48
           LABEL@31..34
-            LIFETIME@31..33 "\'b"
+            LIFETIME@31..33
+              LIFETIME_IDENT@31..33 "\'b"
             COLON@33..34 ":"
           WHITESPACE@34..35 " "
           WHILE_KW@35..40 "while"
@@ -41,7 +43,8 @@ SOURCE_FILE@0..74
       WHITESPACE@48..53 "\n    "
       FOR_EXPR@53..71
         LABEL@53..56
-          LIFETIME@53..55 "\'c"
+          LIFETIME@53..55
+            LIFETIME_IDENT@53..55 "\'c"
           COLON@55..56 ":"
         WHITESPACE@56..57 " "
         FOR_KW@57..60 "for"
diff --git a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
index 570b95205268..616aa984ed2a 100644
--- a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
@@ -20,7 +20,8 @@ SOURCE_FILE@0..64
             R_PAREN@24..25 ")"
           R_BRACK@25..26 "]"
         WHITESPACE@26..27 " "
-        LIFETIME@27..29 "\'a"
+        LIFETIME@27..29
+          LIFETIME_IDENT@27..29 "\'a"
       COMMA@29..30 ","
       WHITESPACE@30..31 " "
       TYPE_PARAM@31..48
@@ -49,7 +50,8 @@ SOURCE_FILE@0..64
         WHITESPACE@52..53 " "
         REF_TYPE@53..58
           AMP@53..54 "&"
-          LIFETIME@54..56 "\'a"
+          LIFETIME@54..56
+            LIFETIME_IDENT@54..56 "\'a"
           WHITESPACE@56..57 " "
           PATH_TYPE@57..58
             PATH@57..58
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_labeled_block.rast b/crates/syntax/test_data/parser/inline/ok/0161_labeled_block.rast
index 9e9a5f9c5b5a..c2dea1cc14bb 100644
--- a/crates/syntax/test_data/parser/inline/ok/0161_labeled_block.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0161_labeled_block.rast
@@ -14,7 +14,8 @@ SOURCE_FILE@0..23
       EXPR_STMT@9..20
         EFFECT_EXPR@9..19
           LABEL@9..16
-            LIFETIME@9..15 "\'label"
+            LIFETIME@9..15
+              LIFETIME_IDENT@9..15 "\'label"
             COLON@15..16 ":"
           WHITESPACE@16..17 " "
           BLOCK_EXPR@17..19
diff --git a/crates/syntax/test_data/parser/ok/0018_struct_type_params.rast b/crates/syntax/test_data/parser/ok/0018_struct_type_params.rast
index 630aa0708bb8..83e17757bdba 100644
--- a/crates/syntax/test_data/parser/ok/0018_struct_type_params.rast
+++ b/crates/syntax/test_data/parser/ok/0018_struct_type_params.rast
@@ -80,7 +80,8 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@80..84
       L_ANGLE@80..81 "<"
       LIFETIME_PARAM@81..83
-        LIFETIME@81..83 "\'a"
+        LIFETIME@81..83
+          LIFETIME_IDENT@81..83 "\'a"
       R_ANGLE@83..84 ">"
     SEMICOLON@84..85 ";"
   WHITESPACE@85..86 "\n"
@@ -92,7 +93,8 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@95..100
       L_ANGLE@95..96 "<"
       LIFETIME_PARAM@96..99
-        LIFETIME@96..98 "\'a"
+        LIFETIME@96..98
+          LIFETIME_IDENT@96..98 "\'a"
         COLON@98..99 ":"
       R_ANGLE@99..100 ">"
     SEMICOLON@100..101 ";"
@@ -105,10 +107,12 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@111..119
       L_ANGLE@111..112 "<"
       LIFETIME_PARAM@112..118
-        LIFETIME@112..114 "\'a"
+        LIFETIME@112..114
+          LIFETIME_IDENT@112..114 "\'a"
         COLON@114..115 ":"
         WHITESPACE@115..116 " "
-        LIFETIME@116..118 "\'b"
+        LIFETIME@116..118
+          LIFETIME_IDENT@116..118 "\'b"
       R_ANGLE@118..119 ">"
     SEMICOLON@119..120 ";"
   WHITESPACE@120..121 "\n"
@@ -120,10 +124,12 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@130..141
       L_ANGLE@130..131 "<"
       LIFETIME_PARAM@131..139
-        LIFETIME@131..133 "\'a"
+        LIFETIME@131..133
+          LIFETIME_IDENT@131..133 "\'a"
         COLON@133..134 ":"
         WHITESPACE@134..135 " "
-        LIFETIME@135..137 "\'b"
+        LIFETIME@135..137
+          LIFETIME_IDENT@135..137 "\'b"
         WHITESPACE@137..138 " "
         PLUS@138..139 "+"
       WHITESPACE@139..140 " "
@@ -138,14 +144,17 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@152..165
       L_ANGLE@152..153 "<"
       LIFETIME_PARAM@153..164
-        LIFETIME@153..155 "\'a"
+        LIFETIME@153..155
+          LIFETIME_IDENT@153..155 "\'a"
         COLON@155..156 ":"
         WHITESPACE@156..157 " "
-        LIFETIME@157..159 "\'b"
+        LIFETIME@157..159
+          LIFETIME_IDENT@157..159 "\'b"
         WHITESPACE@159..160 " "
         PLUS@160..161 "+"
         WHITESPACE@161..162 " "
-        LIFETIME@162..164 "\'c"
+        LIFETIME@162..164
+          LIFETIME_IDENT@162..164 "\'c"
       R_ANGLE@164..165 ">"
     SEMICOLON@165..166 ";"
   WHITESPACE@166..167 "\n"
@@ -157,7 +166,8 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@177..182
       L_ANGLE@177..178 "<"
       LIFETIME_PARAM@178..180
-        LIFETIME@178..180 "\'a"
+        LIFETIME@178..180
+          LIFETIME_IDENT@178..180 "\'a"
       COMMA@180..181 ","
       R_ANGLE@181..182 ">"
     SEMICOLON@182..183 ";"
@@ -170,11 +180,13 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@194..202
       L_ANGLE@194..195 "<"
       LIFETIME_PARAM@195..197
-        LIFETIME@195..197 "\'a"
+        LIFETIME@195..197
+          LIFETIME_IDENT@195..197 "\'a"
       COMMA@197..198 ","
       WHITESPACE@198..199 " "
       LIFETIME_PARAM@199..201
-        LIFETIME@199..201 "\'b"
+        LIFETIME@199..201
+          LIFETIME_IDENT@199..201 "\'b"
       R_ANGLE@201..202 ">"
     SEMICOLON@202..203 ";"
   WHITESPACE@203..204 "\n"
@@ -186,18 +198,22 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@214..232
       L_ANGLE@214..215 "<"
       LIFETIME_PARAM@215..222
-        LIFETIME@215..217 "\'a"
+        LIFETIME@215..217
+          LIFETIME_IDENT@215..217 "\'a"
         COLON@217..218 ":"
         WHITESPACE@218..219 " "
-        LIFETIME@219..221 "\'b"
+        LIFETIME@219..221
+          LIFETIME_IDENT@219..221 "\'b"
         PLUS@221..222 "+"
       COMMA@222..223 ","
       WHITESPACE@223..224 " "
       LIFETIME_PARAM@224..230
-        LIFETIME@224..226 "\'b"
+        LIFETIME@224..226
+          LIFETIME_IDENT@224..226 "\'b"
         COLON@226..227 ":"
         WHITESPACE@227..228 " "
-        LIFETIME@228..230 "\'c"
+        LIFETIME@228..230
+          LIFETIME_IDENT@228..230 "\'c"
       COMMA@230..231 ","
       R_ANGLE@231..232 ">"
     SEMICOLON@232..233 ";"
@@ -241,7 +257,8 @@ SOURCE_FILE@0..290
     GENERIC_PARAM_LIST@278..288
       L_ANGLE@278..279 "<"
       LIFETIME_PARAM@279..281
-        LIFETIME@279..281 "\'a"
+        LIFETIME@279..281
+          LIFETIME_IDENT@279..281 "\'a"
       COMMA@281..282 ","
       WHITESPACE@282..283 " "
       TYPE_PARAM@283..284
diff --git a/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast b/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
index 9bdc50e1e790..0612a71dee5a 100644
--- a/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
+++ b/crates/syntax/test_data/parser/ok/0020_type_param_bounds.rast
@@ -41,7 +41,8 @@ SOURCE_FILE@0..250
         WHITESPACE@38..39 " "
         TYPE_BOUND_LIST@39..41
           TYPE_BOUND@39..41
-            LIFETIME@39..41 "\'a"
+            LIFETIME@39..41
+              LIFETIME_IDENT@39..41 "\'a"
       R_ANGLE@41..42 ">"
     SEMICOLON@42..43 ";"
   WHITESPACE@43..44 "\n"
@@ -59,7 +60,8 @@ SOURCE_FILE@0..250
         WHITESPACE@55..56 " "
         TYPE_BOUND_LIST@56..60
           TYPE_BOUND@56..58
-            LIFETIME@56..58 "\'a"
+            LIFETIME@56..58
+              LIFETIME_IDENT@56..58 "\'a"
           WHITESPACE@58..59 " "
           PLUS@59..60 "+"
       WHITESPACE@60..61 " "
@@ -80,12 +82,14 @@ SOURCE_FILE@0..250
         WHITESPACE@75..76 " "
         TYPE_BOUND_LIST@76..83
           TYPE_BOUND@76..78
-            LIFETIME@76..78 "\'a"
+            LIFETIME@76..78
+              LIFETIME_IDENT@76..78 "\'a"
           WHITESPACE@78..79 " "
           PLUS@79..80 "+"
           WHITESPACE@80..81 " "
           TYPE_BOUND@81..83
-            LIFETIME@81..83 "\'d"
+            LIFETIME@81..83
+              LIFETIME_IDENT@81..83 "\'d"
       WHITESPACE@83..84 " "
       R_ANGLE@84..85 ">"
     SEMICOLON@85..86 ";"
@@ -104,12 +108,14 @@ SOURCE_FILE@0..250
         WHITESPACE@98..99 " "
         TYPE_BOUND_LIST@99..114
           TYPE_BOUND@99..101
-            LIFETIME@99..101 "\'a"
+            LIFETIME@99..101
+              LIFETIME_IDENT@99..101 "\'a"
           WHITESPACE@101..102 " "
           PLUS@102..103 "+"
           WHITESPACE@103..104 " "
           TYPE_BOUND@104..106
-            LIFETIME@104..106 "\'d"
+            LIFETIME@104..106
+              LIFETIME_IDENT@104..106 "\'d"
           WHITESPACE@106..107 " "
           PLUS@107..108 "+"
           WHITESPACE@108..109 " "
@@ -190,7 +196,8 @@ SOURCE_FILE@0..250
           PLUS@174..175 "+"
           WHITESPACE@175..176 " "
           TYPE_BOUND@176..178
-            LIFETIME@176..178 "\'a"
+            LIFETIME@176..178
+              LIFETIME_IDENT@176..178 "\'a"
       R_ANGLE@178..179 ">"
     SEMICOLON@179..180 ";"
   WHITESPACE@180..181 "\n"
@@ -225,21 +232,26 @@ SOURCE_FILE@0..250
     GENERIC_PARAM_LIST@208..249
       L_ANGLE@208..209 "<"
       LIFETIME_PARAM@209..215
-        LIFETIME@209..211 "\'a"
+        LIFETIME@209..211
+          LIFETIME_IDENT@209..211 "\'a"
         COLON@211..212 ":"
         WHITESPACE@212..213 " "
-        LIFETIME@213..215 "\'d"
+        LIFETIME@213..215
+          LIFETIME_IDENT@213..215 "\'d"
       COMMA@215..216 ","
       WHITESPACE@216..217 " "
       LIFETIME_PARAM@217..228
-        LIFETIME@217..219 "\'d"
+        LIFETIME@217..219
+          LIFETIME_IDENT@217..219 "\'d"
         COLON@219..220 ":"
         WHITESPACE@220..221 " "
-        LIFETIME@221..223 "\'a"
+        LIFETIME@221..223
+          LIFETIME_IDENT@221..223 "\'a"
         WHITESPACE@223..224 " "
         PLUS@224..225 "+"
         WHITESPACE@225..226 " "
-        LIFETIME@226..228 "\'b"
+        LIFETIME@226..228
+          LIFETIME_IDENT@226..228 "\'b"
       COMMA@228..229 ","
       WHITESPACE@229..230 " "
       TYPE_PARAM@230..248
@@ -249,12 +261,14 @@ SOURCE_FILE@0..250
         WHITESPACE@232..233 " "
         TYPE_BOUND_LIST@233..248
           TYPE_BOUND@233..235
-            LIFETIME@233..235 "\'a"
+            LIFETIME@233..235
+              LIFETIME_IDENT@233..235 "\'a"
           WHITESPACE@235..236 " "
           PLUS@236..237 "+"
           WHITESPACE@237..238 " "
           TYPE_BOUND@238..240
-            LIFETIME@238..240 "\'d"
+            LIFETIME@238..240
+              LIFETIME_IDENT@238..240 "\'d"
           WHITESPACE@240..241 " "
           PLUS@241..242 "+"
           WHITESPACE@242..243 " "
diff --git a/crates/syntax/test_data/parser/ok/0032_where_for.rast b/crates/syntax/test_data/parser/ok/0032_where_for.rast
index d59548f2107c..0cb2eca3384f 100644
--- a/crates/syntax/test_data/parser/ok/0032_where_for.rast
+++ b/crates/syntax/test_data/parser/ok/0032_where_for.rast
@@ -41,7 +41,8 @@ SOURCE_FILE@0..116
               GENERIC_PARAM_LIST@59..64
                 L_ANGLE@59..60 "<"
                 LIFETIME_PARAM@60..63
-                  LIFETIME@60..63 "\'de"
+                  LIFETIME@60..63
+                    LIFETIME_IDENT@60..63 "\'de"
                 R_ANGLE@63..64 ">"
               WHITESPACE@64..65 " "
               PATH_TYPE@65..81
@@ -52,7 +53,8 @@ SOURCE_FILE@0..116
                     GENERIC_ARG_LIST@76..81
                       L_ANGLE@76..77 "<"
                       LIFETIME_ARG@77..80
-                        LIFETIME@77..80 "\'de"
+                        LIFETIME@77..80
+                          LIFETIME_IDENT@77..80 "\'de"
                       R_ANGLE@80..81 ">"
           WHITESPACE@81..82 " "
           PLUS@82..83 "+"
diff --git a/crates/syntax/test_data/parser/ok/0033_label_break.rast b/crates/syntax/test_data/parser/ok/0033_label_break.rast
index 88800ca7a3ff..487e073baf83 100644
--- a/crates/syntax/test_data/parser/ok/0033_label_break.rast
+++ b/crates/syntax/test_data/parser/ok/0033_label_break.rast
@@ -16,7 +16,8 @@ SOURCE_FILE@0..506
       EXPR_STMT@50..66
         EFFECT_EXPR@50..66
           LABEL@50..63
-            LIFETIME@50..62 "\'empty_block"
+            LIFETIME@50..62
+              LIFETIME_IDENT@50..62 "\'empty_block"
             COLON@62..63 ":"
           WHITESPACE@63..64 " "
           BLOCK_EXPR@64..66
@@ -26,7 +27,8 @@ SOURCE_FILE@0..506
       EXPR_STMT@72..295
         EFFECT_EXPR@72..295
           LABEL@72..79
-            LIFETIME@72..78 "\'block"
+            LIFETIME@72..78
+              LIFETIME_IDENT@72..78 "\'block"
             COLON@78..79 ":"
           WHITESPACE@79..80 " "
           BLOCK_EXPR@80..295
@@ -66,7 +68,8 @@ SOURCE_FILE@0..506
                     BREAK_EXPR@147..159
                       BREAK_KW@147..152 "break"
                       WHITESPACE@152..153 " "
-                      LIFETIME@153..159 "\'block"
+                      LIFETIME@153..159
+                        LIFETIME_IDENT@153..159 "\'block"
                     SEMICOLON@159..160 ";"
                   WHITESPACE@160..169 "\n        "
                   R_CURLY@169..170 "}"
@@ -105,7 +108,8 @@ SOURCE_FILE@0..506
                     BREAK_EXPR@241..253
                       BREAK_KW@241..246 "break"
                       WHITESPACE@246..247 " "
-                      LIFETIME@247..253 "\'block"
+                      LIFETIME@247..253
+                        LIFETIME_IDENT@247..253 "\'block"
                     SEMICOLON@253..254 ";"
                   WHITESPACE@254..263 "\n        "
                   R_CURLY@263..264 "}"
@@ -135,7 +139,8 @@ SOURCE_FILE@0..506
         WHITESPACE@313..314 " "
         EFFECT_EXPR@314..502
           LABEL@314..321
-            LIFETIME@314..320 "\'block"
+            LIFETIME@314..320
+              LIFETIME_IDENT@314..320 "\'block"
             COLON@320..321 ":"
           WHITESPACE@321..322 " "
           BLOCK_EXPR@322..502
@@ -165,7 +170,8 @@ SOURCE_FILE@0..506
                     BREAK_EXPR@378..392
                       BREAK_KW@378..383 "break"
                       WHITESPACE@383..384 " "
-                      LIFETIME@384..390 "\'block"
+                      LIFETIME@384..390
+                        LIFETIME_IDENT@384..390 "\'block"
                       WHITESPACE@390..391 " "
                       LITERAL@391..392
                         INT_NUMBER@391..392 "1"
@@ -197,7 +203,8 @@ SOURCE_FILE@0..506
                     BREAK_EXPR@461..475
                       BREAK_KW@461..466 "break"
                       WHITESPACE@466..467 " "
-                      LIFETIME@467..473 "\'block"
+                      LIFETIME@467..473
+                        LIFETIME_IDENT@467..473 "\'block"
                       WHITESPACE@473..474 " "
                       LITERAL@474..475
                         INT_NUMBER@474..475 "2"
diff --git a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
index 7c61b5006de1..46b192dc1918 100644
--- a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
+++ b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
@@ -1373,14 +1373,14 @@ SOURCE_FILE@0..3813
                         WHITESPACE@2376..2377 " "
                         IDENT@2377..2379 "u8"
                         L_ANGLE@2379..2380 "<"
-                        LIFETIME@2380..2383 "\'u8"
+                        LIFETIME_IDENT@2380..2383 "\'u8"
                         COLON@2383..2384 ":"
                         WHITESPACE@2384..2385 " "
-                        LIFETIME@2385..2388 "\'u8"
+                        LIFETIME_IDENT@2385..2388 "\'u8"
                         WHITESPACE@2388..2389 " "
                         PLUS@2389..2390 "+"
                         WHITESPACE@2390..2391 " "
-                        LIFETIME@2391..2394 "\'u8"
+                        LIFETIME_IDENT@2391..2394 "\'u8"
                         R_ANGLE@2394..2395 ">"
                         TOKEN_TREE@2395..2408
                           L_PAREN@2395..2396 "("
@@ -1388,7 +1388,7 @@ SOURCE_FILE@0..3813
                           COLON@2398..2399 ":"
                           WHITESPACE@2399..2400 " "
                           AMP@2400..2401 "&"
-                          LIFETIME@2401..2404 "\'u8"
+                          LIFETIME_IDENT@2401..2404 "\'u8"
                           WHITESPACE@2404..2405 " "
                           IDENT@2405..2407 "u8"
                           R_PAREN@2407..2408 ")"
@@ -1397,7 +1397,7 @@ SOURCE_FILE@0..3813
                         R_ANGLE@2410..2411 ">"
                         WHITESPACE@2411..2412 " "
                         AMP@2412..2413 "&"
-                        LIFETIME@2413..2416 "\'u8"
+                        LIFETIME_IDENT@2413..2416 "\'u8"
                         WHITESPACE@2416..2417 " "
                         IDENT@2417..2419 "u8"
                         WHITESPACE@2419..2420 " "
@@ -1568,7 +1568,8 @@ SOURCE_FILE@0..3813
         GENERIC_PARAM_LIST@2862..2870
           L_ANGLE@2862..2863 "<"
           LIFETIME_PARAM@2863..2869
-            LIFETIME@2863..2869 "\'union"
+            LIFETIME@2863..2869
+              LIFETIME_IDENT@2863..2869 "\'union"
           R_ANGLE@2869..2870 ">"
         WHITESPACE@2870..2871 " "
         RECORD_FIELD_LIST@2871..2904
@@ -1581,7 +1582,8 @@ SOURCE_FILE@0..3813
             WHITESPACE@2879..2880 " "
             REF_TYPE@2880..2901
               AMP@2880..2881 "&"
-              LIFETIME@2881..2887 "\'union"
+              LIFETIME@2881..2887
+                LIFETIME_IDENT@2881..2887 "\'union"
               WHITESPACE@2887..2888 " "
               PATH_TYPE@2888..2901
                 PATH@2888..2901
@@ -1591,7 +1593,8 @@ SOURCE_FILE@0..3813
                     GENERIC_ARG_LIST@2893..2901
                       L_ANGLE@2893..2894 "<"
                       LIFETIME_ARG@2894..2900
-                        LIFETIME@2894..2900 "\'union"
+                        LIFETIME@2894..2900
+                          LIFETIME_IDENT@2894..2900 "\'union"
                       R_ANGLE@2900..2901 ">"
           COMMA@2901..2902 ","
           WHITESPACE@2902..2903 " "
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index d4f05f279240..8974f9e40e92 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -175,7 +175,8 @@ SOURCE_FILE@0..519
                               GENERIC_ARG_LIST@162..166
                                 L_ANGLE@162..163 "<"
                                 LIFETIME_ARG@163..165
-                                  LIFETIME@163..165 "\'a"
+                                  LIFETIME@163..165
+                                    LIFETIME_IDENT@163..165 "\'a"
                                 R_ANGLE@165..166 ">"
                     R_PAREN@166..167 ")"
       R_ANGLE@167..168 ">"
@@ -344,7 +345,8 @@ SOURCE_FILE@0..519
         GENERIC_PARAM_LIST@345..349
           L_ANGLE@345..346 "<"
           LIFETIME_PARAM@346..348
-            LIFETIME@346..348 "\'a"
+            LIFETIME@346..348
+              LIFETIME_IDENT@346..348 "\'a"
           R_ANGLE@348..349 ">"
         PARAM_LIST@349..368
           L_PAREN@349..350 "("
@@ -376,7 +378,8 @@ SOURCE_FILE@0..519
         GENERIC_PARAM_LIST@382..386
           L_ANGLE@382..383 "<"
           LIFETIME_PARAM@383..385
-            LIFETIME@383..385 "\'a"
+            LIFETIME@383..385
+              LIFETIME_IDENT@383..385 "\'a"
           R_ANGLE@385..386 ">"
         PARAM_LIST@386..404
           L_PAREN@386..387 "("
@@ -391,7 +394,8 @@ SOURCE_FILE@0..519
           WHITESPACE@394..395 " "
           SELF_PARAM@395..403
             AMP@395..396 "&"
-            LIFETIME@396..398 "\'a"
+            LIFETIME@396..398
+              LIFETIME_IDENT@396..398 "\'a"
             WHITESPACE@398..399 " "
             SELF_KW@399..403 "self"
           R_PAREN@403..404 ")"
@@ -408,7 +412,8 @@ SOURCE_FILE@0..519
         GENERIC_PARAM_LIST@418..422
           L_ANGLE@418..419 "<"
           LIFETIME_PARAM@419..421
-            LIFETIME@419..421 "\'a"
+            LIFETIME@419..421
+              LIFETIME_IDENT@419..421 "\'a"
           R_ANGLE@421..422 ">"
         PARAM_LIST@422..444
           L_PAREN@422..423 "("
@@ -423,7 +428,8 @@ SOURCE_FILE@0..519
           WHITESPACE@430..431 " "
           SELF_PARAM@431..443
             AMP@431..432 "&"
-            LIFETIME@432..434 "\'a"
+            LIFETIME@432..434
+              LIFETIME_IDENT@432..434 "\'a"
             WHITESPACE@434..435 " "
             MUT_KW@435..438 "mut"
             WHITESPACE@438..439 " "
diff --git a/crates/syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/syntax/test_data/parser/ok/0067_where_for_pred.rast
index 8f8639a375bc..325e9e655fdc 100644
--- a/crates/syntax/test_data/parser/ok/0067_where_for_pred.rast
+++ b/crates/syntax/test_data/parser/ok/0067_where_for_pred.rast
@@ -22,7 +22,8 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@31..35
           L_ANGLE@31..32 "<"
           LIFETIME_PARAM@32..34
-            LIFETIME@32..34 "\'a"
+            LIFETIME@32..34
+              LIFETIME_IDENT@32..34 "\'a"
           R_ANGLE@34..35 ">"
         WHITESPACE@35..36 " "
         PATH_TYPE@36..37
@@ -44,7 +45,8 @@ SOURCE_FILE@0..374
                     PARAM@42..49
                       REF_TYPE@42..49
                         AMP@42..43 "&"
-                        LIFETIME@43..45 "\'a"
+                        LIFETIME@43..45
+                          LIFETIME_IDENT@43..45 "\'a"
                         WHITESPACE@45..46 " "
                         PATH_TYPE@46..49
                           PATH@46..49
@@ -82,12 +84,14 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@85..89
           L_ANGLE@85..86 "<"
           LIFETIME_PARAM@86..88
-            LIFETIME@86..88 "\'a"
+            LIFETIME@86..88
+              LIFETIME_IDENT@86..88 "\'a"
           R_ANGLE@88..89 ">"
         WHITESPACE@89..90 " "
         REF_TYPE@90..95
           AMP@90..91 "&"
-          LIFETIME@91..93 "\'a"
+          LIFETIME@91..93
+            LIFETIME_IDENT@91..93 "\'a"
           WHITESPACE@93..94 " "
           PATH_TYPE@94..95
             PATH@94..95
@@ -133,14 +137,16 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@140..144
           L_ANGLE@140..141 "<"
           LIFETIME_PARAM@141..143
-            LIFETIME@141..143 "\'a"
+            LIFETIME@141..143
+              LIFETIME_IDENT@141..143 "\'a"
           R_ANGLE@143..144 ">"
         WHITESPACE@144..145 " "
         PAREN_TYPE@145..152
           L_PAREN@145..146 "("
           REF_TYPE@146..151
             AMP@146..147 "&"
-            LIFETIME@147..149 "\'a"
+            LIFETIME@147..149
+              LIFETIME_IDENT@147..149 "\'a"
             WHITESPACE@149..150 " "
             PATH_TYPE@150..151
               PATH@150..151
@@ -162,7 +168,8 @@ SOURCE_FILE@0..374
                     PARAM@157..164
                       REF_TYPE@157..164
                         AMP@157..158 "&"
-                        LIFETIME@158..160 "\'a"
+                        LIFETIME@158..160
+                          LIFETIME_IDENT@158..160 "\'a"
                         WHITESPACE@160..161 " "
                         PATH_TYPE@161..164
                           PATH@161..164
@@ -200,14 +207,16 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@202..206
           L_ANGLE@202..203 "<"
           LIFETIME_PARAM@203..205
-            LIFETIME@203..205 "\'a"
+            LIFETIME@203..205
+              LIFETIME_IDENT@203..205 "\'a"
           R_ANGLE@205..206 ">"
         WHITESPACE@206..207 " "
         SLICE_TYPE@207..214
           L_BRACK@207..208 "["
           REF_TYPE@208..213
             AMP@208..209 "&"
-            LIFETIME@209..211 "\'a"
+            LIFETIME@209..211
+              LIFETIME_IDENT@209..211 "\'a"
             WHITESPACE@211..212 " "
             PATH_TYPE@212..213
               PATH@212..213
@@ -267,7 +276,8 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@261..265
           L_ANGLE@261..262 "<"
           LIFETIME_PARAM@262..264
-            LIFETIME@262..264 "\'a"
+            LIFETIME@262..264
+              LIFETIME_IDENT@262..264 "\'a"
           R_ANGLE@264..265 ">"
         WHITESPACE@265..266 " "
         PATH_TYPE@266..285
@@ -277,7 +287,8 @@ SOURCE_FILE@0..374
                 L_ANGLE@266..267 "<"
                 REF_TYPE@267..272
                   AMP@267..268 "&"
-                  LIFETIME@268..270 "\'a"
+                  LIFETIME@268..270
+                    LIFETIME_IDENT@268..270 "\'a"
                   WHITESPACE@270..271 " "
                   PATH_TYPE@271..272
                     PATH@271..272
@@ -336,7 +347,8 @@ SOURCE_FILE@0..374
         GENERIC_PARAM_LIST@333..337
           L_ANGLE@333..334 "<"
           LIFETIME_PARAM@334..336
-            LIFETIME@334..336 "\'a"
+            LIFETIME@334..336
+              LIFETIME_IDENT@334..336 "\'a"
           R_ANGLE@336..337 ">"
         WHITESPACE@337..338 " "
         FOR_TYPE@338..362
@@ -344,7 +356,8 @@ SOURCE_FILE@0..374
           GENERIC_PARAM_LIST@341..345
             L_ANGLE@341..342 "<"
             LIFETIME_PARAM@342..344
-              LIFETIME@342..344 "\'b"
+              LIFETIME@342..344
+                LIFETIME_IDENT@342..344 "\'b"
             R_ANGLE@344..345 ">"
           WHITESPACE@345..346 " "
           FN_PTR_TYPE@346..362
@@ -354,7 +367,8 @@ SOURCE_FILE@0..374
               PARAM@349..354
                 REF_TYPE@349..354
                   AMP@349..350 "&"
-                  LIFETIME@350..352 "\'a"
+                  LIFETIME@350..352
+                    LIFETIME_IDENT@350..352 "\'a"
                   WHITESPACE@352..353 " "
                   PATH_TYPE@353..354
                     PATH@353..354
@@ -366,7 +380,8 @@ SOURCE_FILE@0..374
               PARAM@356..361
                 REF_TYPE@356..361
                   AMP@356..357 "&"
-                  LIFETIME@357..359 "\'b"
+                  LIFETIME@357..359
+                    LIFETIME_IDENT@357..359 "\'b"
                   WHITESPACE@359..360 " "
                   PATH_TYPE@360..361
                     PATH@360..361
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
index 0cd868a83376..8d3e187ae982 100644
--- a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
+++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
@@ -7,14 +7,16 @@ SOURCE_FILE@0..195
     GENERIC_PARAM_LIST@8..12
       L_ANGLE@8..9 "<"
       LIFETIME_PARAM@9..11
-        LIFETIME@9..11 "\'a"
+        LIFETIME@9..11
+          LIFETIME_IDENT@9..11 "\'a"
       R_ANGLE@11..12 ">"
     WHITESPACE@12..13 " "
     EQ@13..14 "="
     WHITESPACE@14..15 " "
     REF_TYPE@15..36
       AMP@15..16 "&"
-      LIFETIME@16..18 "\'a"
+      LIFETIME@16..18
+        LIFETIME_IDENT@16..18 "\'a"
       WHITESPACE@18..19 " "
       PAREN_TYPE@19..36
         L_PAREN@19..20 "("
@@ -109,7 +111,8 @@ SOURCE_FILE@0..195
               PLUS@104..105 "+"
               WHITESPACE@105..106 " "
               TYPE_BOUND@106..113
-                LIFETIME@106..113 "\'static"
+                LIFETIME@106..113
+                  LIFETIME_IDENT@106..113 "\'static"
           R_PAREN@113..114 ")"
     SEMICOLON@114..115 ";"
   WHITESPACE@115..116 "\n"
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 2ef956485597..78a0b54ba66d 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -15,8 +15,8 @@ flate2 = "1.0"
 pico-args = "0.3.1"
 proc-macro2 = "1.0.8"
 quote = "1.0.2"
-ungrammar = "1.1.3"
+ungrammar = "1.4"
 walkdir = "2.3.1"
-write-json =  "0.1.0"
+write-json = "0.1.0"
 xshell = "0.1"
 # Avoid adding more dependencies to this crate
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 72a4c10f5491..a69ced5ccf4c 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -76,7 +76,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "ERROR",
         "IDENT",
         "WHITESPACE",
-        "LIFETIME",
+        "LIFETIME_IDENT",
         "COMMENT",
         "SHEBANG",
         "L_DOLLAR",
@@ -202,6 +202,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "TYPE_PARAM",
         "CONST_PARAM",
         "GENERIC_ARG_LIST",
+        "LIFETIME",
         "LIFETIME_ARG",
         "TYPE_ARG",
         "ASSOC_TYPE_ARG",
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 44460effa83f..eb524d85a9fb 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -380,7 +380,7 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result {
         macro_rules! T {
             #([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*
             #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*
-            [lifetime] => { $crate::SyntaxKind::LIFETIME };
+            [lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
             [ident] => { $crate::SyntaxKind::IDENT };
             [shebang] => { $crate::SyntaxKind::SHEBANG };
         }