Repository: rust-lang/rust-analyzer
Pull request #17617 (2024-07-17 08:05:12)

Derive kinds information from ungrammar file
This reduces the need to touch more files when adding a new grammar rule


Patch:
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index b6d43af2eb..e2a5f35313 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -301,7 +301,10 @@ impl ExprCollector<'_> {
                         result_expr_id
                     })
                 }
-                None => self.collect_block(e),
+                // FIXME
+                Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => {
+                    self.collect_block(e)
+                }
             },
             ast::Expr::LoopExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index e3290f5343..c4884b8d99 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -728,6 +728,8 @@ fn include_expand(
         }
     };
     match parse_to_token_tree(
+        // FIXME
+        Edition::CURRENT,
         SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
         SyntaxContextId::ROOT,
         &db.file_text(file_id),
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 7bf8af4cab..ff62344012 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -5,10 +5,11 @@ use std::fmt;
 use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
 use intern::sym;
 use once_cell::unsync::Lazy;
+use rustc_index::IndexVec;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
     usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
-    Captures, IndexVec, PatCx, PrivateUninhabitedField,
+    Captures, PatCx, PrivateUninhabitedField,
 };
 use smallvec::{smallvec, SmallVec};
 use stdx::never;
@@ -53,7 +54,7 @@ impl EnumVariantContiguousIndex {
     }
 }
 
-impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex {
+impl rustc_index::Idx for EnumVariantContiguousIndex {
     fn new(idx: usize) -> Self {
         EnumVariantContiguousIndex(idx)
     }
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index cb34f8d81a..0ef71a3866 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -3,7 +3,7 @@ use syntax::{
     ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName},
     ted, NodeOrToken,
     SyntaxKind::{BLOCK_EXPR, BREAK_EXPR, COMMENT, LOOP_EXPR, MATCH_GUARD, PATH_EXPR, RETURN_EXPR},
-    SyntaxNode,
+    SyntaxNode, T,
 };
 
 use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
@@ -26,8 +26,8 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
 // ```
 pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let node = if ctx.has_empty_selection() {
-        if let Some(expr_stmt) = ctx.find_node_at_offset::<ast::ExprStmt>() {
-            expr_stmt.syntax().clone()
+        if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
+            t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone()
         } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() {
             expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone()
         } else {
diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs
index f2a097afc8..457e8baaca 100644
--- a/crates/ide-assists/src/utils/suggest_name.rs
+++ b/crates/ide-assists/src/utils/suggest_name.rs
@@ -184,7 +184,7 @@ fn normalize(name: &str) -> Option<String> {
 
 fn is_valid_name(name: &str) -> bool {
     matches!(
-        ide_db::syntax_helpers::LexedStr::single_token(name),
+        ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT, name),
         Some((syntax::SyntaxKind::IDENT, _error))
     )
 }
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 484c65c2b0..7f9e08cb8a 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -25,7 +25,7 @@ use std::fmt;
 use base_db::{AnchoredPathBuf, FileId, FileRange};
 use either::Either;
 use hir::{FieldSource, HirFileIdExt, InFile, ModuleSource, Semantics};
-use span::SyntaxContextId;
+use span::{Edition, SyntaxContextId};
 use stdx::{never, TupleExt};
 use syntax::{
     ast::{self, HasName},
@@ -227,7 +227,8 @@ fn rename_mod(
     module: hir::Module,
     new_name: &str,
 ) -> Result<SourceChange> {
-    if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
+    if IdentifierKind::classify(module.krate().edition(sema.db), new_name)? != IdentifierKind::Ident
+    {
         bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
     }
 
@@ -313,7 +314,12 @@ fn rename_reference(
     def: Definition,
     new_name: &str,
 ) -> Result<SourceChange> {
-    let ident_kind = IdentifierKind::classify(new_name)?;
+    let ident_kind = IdentifierKind::classify(
+        def.krate(sema.db)
+            .ok_or_else(|| RenameError("definition has no krate?".into()))?
+            .edition(sema.db),
+        new_name,
+    )?;
 
     if matches!(
         def,
@@ -605,8 +611,8 @@ pub enum IdentifierKind {
 }
 
 impl IdentifierKind {
-    pub fn classify(new_name: &str) -> Result<IdentifierKind> {
-        match parser::LexedStr::single_token(new_name) {
+    pub fn classify(edition: Edition, new_name: &str) -> Result<IdentifierKind> {
+        match parser::LexedStr::single_token(edition, new_name) {
             Some(res) => match res {
                 (SyntaxKind::IDENT, _) => {
                     if let Some(inner) = new_name.strip_prefix("r#") {
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs
index 4f706e26af..c301e10034 100644
--- a/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -277,6 +277,8 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
                     });
                 }
                 Some(ast::BlockModifier::Unsafe(_)) => (),
+                Some(ast::BlockModifier::Gen(_)) => (),
+                Some(ast::BlockModifier::AsyncGen(_)) => (),
                 None => (),
             }
             if let Some(stmt_list) = b.stmt_list() {
diff --git a/crates/ide-ssr/src/parsing.rs b/crates/ide-ssr/src/parsing.rs
index 2f91271c46..e752ee3d77 100644
--- a/crates/ide-ssr/src/parsing.rs
+++ b/crates/ide-ssr/src/parsing.rs
@@ -255,7 +255,7 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {
 }
 
 fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> {
-    let lexed = parser::LexedStr::new(source);
+    let lexed = parser::LexedStr::new(parser::Edition::CURRENT, source);
     if let Some((_, first_error)) = lexed.errors().next() {
         bail!("Failed to parse pattern: {}", first_error);
     }
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index e5f8632871..2527b92665 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -13,6 +13,7 @@ use ide_db::{
     RootDatabase,
 };
 use itertools::Itertools;
+use span::Edition;
 use stdx::{always, never};
 use syntax::{
     ast, utils::is_raw_identifier, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange, TextSize,
@@ -99,7 +100,7 @@ pub(crate) fn rename(
             // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
             // properly find "direct" usages/references.
             .map(|(.., def)| {
-                match IdentifierKind::classify(new_name)? {
+                match IdentifierKind::classify(Edition::CURRENT, new_name)? {
                     IdentifierKind::Ident => (),
                     IdentifierKind::Lifetime => {
                         bail!("Cannot alias reference to a lifetime identifier")
@@ -391,7 +392,7 @@ fn rename_self_to_param(
         return Ok(SourceChange::default());
     }
 
-    let identifier_kind = IdentifierKind::classify(new_name)?;
+    let identifier_kind = IdentifierKind::classify(Edition::CURRENT, new_name)?;
 
     let InFile { file_id, value: self_param } =
         sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 7ff8963108..58edd9900a 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -169,6 +169,7 @@ where
 /// Convert a string to a `TokenTree`. The spans of the subtree will be anchored to the provided
 /// anchor with the given context.
 pub fn parse_to_token_tree<Ctx>(
+    edition: Edition,
     anchor: SpanAnchor,
     ctx: Ctx,
     text: &str,
@@ -177,7 +178,7 @@ where
     SpanData<Ctx>: Copy + fmt::Debug,
     Ctx: Copy,
 {
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     if lexed.errors().next().is_some() {
         return None;
     }
@@ -187,11 +188,15 @@ where
 }
 
 /// Convert a string to a `TokenTree`. The passed span will be used for all spans of the produced subtree.
-pub fn parse_to_token_tree_static_span<S>(span: S, text: &str) -> Option<tt::Subtree<S>>
+pub fn parse_to_token_tree_static_span<S>(
+    edition: Edition,
+    span: S,
+    text: &str,
+) -> Option<tt::Subtree<S>>
 where
     S: Copy + fmt::Debug,
 {
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     if lexed.errors().next().is_some() {
         return None;
     }
diff --git a/crates/parser/src/edition.rs b/crates/parser/src/edition.rs
index 26178544f9..831a482059 100644
--- a/crates/parser/src/edition.rs
+++ b/crates/parser/src/edition.rs
@@ -12,8 +12,17 @@ pub enum Edition {
 }
 
 impl Edition {
+    /// The current latest stable edition, note this is usually not the right choice in code.
     pub const CURRENT: Edition = Edition::Edition2021;
     pub const DEFAULT: Edition = Edition::Edition2015;
+
+    pub fn at_least_2024(self) -> bool {
+        self >= Edition::Edition2024
+    }
+
+    pub fn at_least_2018(self) -> bool {
+        self >= Edition::Edition2018
+    }
 }
 
 #[derive(Debug)]
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 2930190cb3..7ae1e5f82e 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -165,42 +165,6 @@ pub(crate) mod entry {
             }
             m.complete(p, ERROR);
         }
-
-        pub(crate) fn eager_macro_input(p: &mut Parser<'_>) {
-            let m = p.start();
-
-            let closing_paren_kind = match p.current() {
-                T!['{'] => T!['}'],
-                T!['('] => T![')'],
-                T!['['] => T![']'],
-                _ => {
-                    p.error("expected `{`, `[`, `(`");
-                    while !p.at(EOF) {
-                        p.bump_any();
-                    }
-                    m.complete(p, ERROR);
-                    return;
-                }
-            };
-            p.bump_any();
-            while !p.at(EOF) && !p.at(closing_paren_kind) {
-                if expressions::expr(p).is_none() {
-                    break;
-                }
-                if !p.at(EOF) && !p.at(closing_paren_kind) {
-                    p.expect(T![,]);
-                }
-            }
-            p.expect(closing_paren_kind);
-            if p.at(EOF) {
-                m.complete(p, MACRO_EAGER_INPUT);
-                return;
-            }
-            while !p.at(EOF) {
-                p.bump_any();
-            }
-            m.complete(p, ERROR);
-        }
     }
 }
 
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 54ed5f0ba2..54c874d06c 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -51,6 +51,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
         T![const],
         T![continue],
         T![do],
+        T![gen],
         T![for],
         T![if],
         T![let],
@@ -138,15 +139,37 @@ pub(super) fn atom_expr(
         // fn f() { const { } }
         // fn f() { async { } }
         // fn f() { async move { } }
-        T![const] | T![unsafe] | T![async] if la == T!['{'] => {
+        T![const] | T![unsafe] | T![async] | T![gen] if la == T!['{'] => {
             let m = p.start();
             p.bump_any();
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
         }
-        T![async] if la == T![move] && p.nth(2) == T!['{'] => {
+        // test_err gen_blocks
+        // pub fn main() {
+        //     gen { yield ""; };
+        //     async gen { yield ""; };
+        //     gen move { yield ""; };
+        //     async gen move { yield ""; };
+        // }
+        T![async] if la == T![gen] && p.nth(2) == T!['{'] => {
+            let m = p.start();
+            p.bump(T![async]);
+            p.eat(T![gen]);
+            stmt_list(p);
+            m.complete(p, BLOCK_EXPR)
+        }
+        T![async] | T![gen] if la == T![move] && p.nth(2) == T!['{'] => {
+            let m = p.start();
+            p.bump_any();
+            p.bump(T![move]);
+            stmt_list(p);
+            m.complete(p, BLOCK_EXPR)
+        }
+        T![async] if la == T![gen] && p.nth(2) == T![move] && p.nth(3) == T!['{'] => {
             let m = p.start();
             p.bump(T![async]);
+            p.bump(T![gen]);
             p.bump(T![move]);
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
@@ -355,6 +378,7 @@ fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker {
     p.eat(T![const]);
     p.eat(T![static]);
     p.eat(T![async]);
+    p.eat(T![gen]);
     p.eat(T![move]);
 
     if !p.at(T![|]) {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 99bbf47654..d8468ba3cb 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -112,11 +112,22 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
 
     // test_err async_without_semicolon
     // fn foo() { let _ = async {} }
-    if p.at(T![async]) && !matches!(p.nth(1), T!['{'] | T![move] | T![|]) {
+    if p.at(T![async])
+        && (!matches!(p.nth(1), T!['{'] | T![gen] | T![move] | T![|])
+            || matches!((p.nth(1), p.nth(2)), (T![gen], T![fn])))
+    {
         p.eat(T![async]);
         has_mods = true;
     }
 
+    // test_err gen_fn
+    // gen fn gen_fn() {}
+    // async gen fn async_gen_fn() {}
+    if p.at(T![gen]) && p.nth(1) == T![fn] {
+        p.eat(T![gen]);
+        has_mods = true;
+    }
+
     // test_err unsafe_block_in_mod
     // fn foo(){} unsafe { } fn bar(){}
     if p.at(T![unsafe]) && p.nth(1) != T!['{'] {
@@ -173,13 +184,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
         }
     }
 
-    // test existential_type
-    // existential type Foo: Fn() -> usize;
-    if p.at_contextual_kw(T![existential]) && p.nth(1) == T![type] {
-        p.bump_remap(T![existential]);
-        has_mods = true;
-    }
-
     // items
     match p.current() {
         T![fn] => fn_(p, m),
@@ -201,7 +205,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
 
         _ if has_visibility || has_mods => {
             if has_mods {
-                p.error("expected existential, fn, trait or impl");
+                p.error("expected fn, trait or impl");
             } else {
                 p.error("expected an item");
             }
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 52b24b7372..13fc61074d 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -13,6 +13,7 @@ use std::ops;
 use rustc_lexer::unescape::{EscapeError, Mode};
 
 use crate::{
+    Edition,
     SyntaxKind::{self, *},
     T,
 };
@@ -30,9 +31,9 @@ struct LexError {
 }
 
 impl<'a> LexedStr<'a> {
-    pub fn new(text: &'a str) -> LexedStr<'a> {
+    pub fn new(edition: Edition, text: &'a str) -> LexedStr<'a> {
         let _p = tracing::info_span!("LexedStr::new").entered();
-        let mut conv = Converter::new(text);
+        let mut conv = Converter::new(edition, text);
         if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
             conv.res.push(SHEBANG, conv.offset);
             conv.offset = shebang_len;
@@ -47,7 +48,7 @@ impl<'a> LexedStr<'a> {
         conv.finalize_with_eof()
     }
 
-    pub fn single_token(text: &'a str) -> Option<(SyntaxKind, Option<String>)> {
+    pub fn single_token(edition: Edition, text: &'a str) -> Option<(SyntaxKind, Option<String>)> {
         if text.is_empty() {
             return None;
         }
@@ -57,7 +58,7 @@ impl<'a> LexedStr<'a> {
             return None;
         }
 
-        let mut conv = Converter::new(text);
+        let mut conv = Converter::new(edition, text);
         conv.extend_token(&token.kind, text);
         match &*conv.res.kind {
             [kind] => Some((*kind, conv.res.error.pop().map(|it| it.msg))),
@@ -129,13 +130,15 @@ impl<'a> LexedStr<'a> {
 struct Converter<'a> {
     res: LexedStr<'a>,
     offset: usize,
+    edition: Edition,
 }
 
 impl<'a> Converter<'a> {
-    fn new(text: &'a str) -> Self {
+    fn new(edition: Edition, text: &'a str) -> Self {
         Self {
             res: LexedStr { text, kind: Vec::new(), start: Vec::new(), error: Vec::new() },
             offset: 0,
+            edition,
         }
     }
 
@@ -175,6 +178,17 @@ impl<'a> Converter<'a> {
                 rustc_lexer::TokenKind::Whitespace => WHITESPACE,
 
                 rustc_lexer::TokenKind::Ident if token_text == "_" => UNDERSCORE,
+                rustc_lexer::TokenKind::Ident
+                    if ["async", "await", "dyn", "try"].contains(&token_text)
+                        && !self.edition.at_least_2018() =>
+                {
+                    IDENT
+                }
+                rustc_lexer::TokenKind::Ident
+                    if token_text == "gen" && !self.edition.at_least_2024() =>
+                {
+                    IDENT
+                }
                 rustc_lexer::TokenKind::Ident => {
                     SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
                 }
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 738ed239a7..679492066a 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -82,8 +82,6 @@ pub enum TopEntryPoint {
     /// Edge case -- macros generally don't expand to attributes, with the
     /// exception of `cfg_attr` which does!
     MetaItem,
-    /// Edge case 2 -- eager macros expand their input to a delimited list of comma separated expressions
-    MacroEagerInput,
 }
 
 impl TopEntryPoint {
@@ -97,7 +95,6 @@ impl TopEntryPoint {
             TopEntryPoint::Type => grammar::entry::top::type_,
             TopEntryPoint::Expr => grammar::entry::top::expr,
             TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
-            TopEntryPoint::MacroEagerInput => grammar::entry::top::eager_macro_input,
         };
         let mut p = parser::Parser::new(input, edition);
         entry_point(&mut p);
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index ad3398453b..7bddf88740 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -9,6 +9,7 @@ pub enum SyntaxKind {
     TOMBSTONE,
     #[doc(hidden)]
     EOF,
+    DOLLAR,
     SEMICOLON,
     COMMA,
     L_PAREN,
@@ -23,7 +24,6 @@ pub enum SyntaxKind {
     POUND,
     TILDE,
     QUESTION,
-    DOLLAR,
     AMP,
     PIPE,
     PLUS,
@@ -61,6 +61,7 @@ pub enum SyntaxKind {
     SHR,
     SHLEQ,
     SHREQ,
+    SELF_TYPE_KW,
     ABSTRACT_KW,
     AS_KW,
     ASYNC_KW,
@@ -80,6 +81,7 @@ pub enum SyntaxKind {
     FINAL_KW,
     FN_KW,
     FOR_KW,
+    GEN_KW,
     IF_KW,
     IMPL_KW,
     IN_KW,
@@ -96,7 +98,6 @@ pub enum SyntaxKind {
     REF_KW,
     RETURN_KW,
     SELF_KW,
-    SELF_TYPE_KW,
     STATIC_KW,
     STRUCT_KW,
     SUPER_KW,
@@ -112,173 +113,184 @@ pub enum SyntaxKind {
     WHERE_KW,
     WHILE_KW,
     YIELD_KW,
+    ASM_KW,
     AUTO_KW,
     BUILTIN_KW,
     DEFAULT_KW,
-    EXISTENTIAL_KW,
-    UNION_KW,
-    RAW_KW,
+    FORMAT_ARGS_KW,
     MACRO_RULES_KW,
-    YEET_KW,
     OFFSET_OF_KW,
-    ASM_KW,
-    FORMAT_ARGS_KW,
-    INT_NUMBER,
-    FLOAT_NUMBER,
-    CHAR,
+    RAW_KW,
+    UNION_KW,
+    YEET_KW,
     BYTE,
-    STRING,
     BYTE_STRING,
+    CHAR,
     C_STRING,
+    FLOAT_NUMBER,
+    INT_NUMBER,
+    RAW_BYTE_STRING,
+    RAW_C_STRING,
+    RAW_STRING,
+    STRING,
+    COMMENT,
     ERROR,
     IDENT,
-    WHITESPACE,
     LIFETIME_IDENT,
-    COMMENT,
+    NEWLINE,
     SHEBANG,
-    SOURCE_FILE,
-    STRUCT,
-    UNION,
+    WHITESPACE,
+    ABI,
+    ADT,
+    ARG_LIST,
+    ARRAY_EXPR,
+    ARRAY_TYPE,
+    ASM_EXPR,
+    ASSOC_ITEM,
+    ASSOC_ITEM_LIST,
+    ASSOC_TYPE_ARG,
+    ATTR,
+    AWAIT_EXPR,
+    BECOME_EXPR,
+    BIN_EXPR,
+    BLOCK_EXPR,
+    BOX_PAT,
+    BREAK_EXPR,
+    CALL_EXPR,
+    CAST_EXPR,
+    CLOSURE_BINDER,
+    CLOSURE_EXPR,
+    CONST,
+    CONST_ARG,
+    CONST_BLOCK_PAT,
+    CONST_PARAM,
+    CONTINUE_EXPR,
+    DYN_TRAIT_TYPE,
     ENUM,
-    FN,
-    RET_TYPE,
+    EXPR,
+    EXPR_STMT,
+    EXTERN_BLOCK,
     EXTERN_CRATE,
-    MODULE,
-    USE,
-    STATIC,
-    CONST,
-    TRAIT,
-    TRAIT_ALIAS,
-    IMPL,
-    TYPE_ALIAS,
-    MACRO_CALL,
-    MACRO_RULES,
-    MACRO_ARM,
-    TOKEN_TREE,
-    MACRO_DEF,
-    PAREN_TYPE,
-    TUPLE_TYPE,
-    MACRO_TYPE,
-    NEVER_TYPE,
-    PATH_TYPE,
-    PTR_TYPE,
-    ARRAY_TYPE,
-    SLICE_TYPE,
-    REF_TYPE,
-    INFER_TYPE,
+    EXTERN_ITEM,
+    EXTERN_ITEM_LIST,
+    FIELD_EXPR,
+    FIELD_LIST,
+    FN,
     FN_PTR_TYPE,
+    FORMAT_ARGS_ARG,
+    FORMAT_ARGS_EXPR,
+    FOR_EXPR,
     FOR_TYPE,
-    IMPL_TRAIT_TYPE,
-    DYN_TRAIT_TYPE,
-    OR_PAT,
-    PAREN_PAT,
-    REF_PAT,
-    BOX_PAT,
+    GENERIC_ARG,
+    GENERIC_ARG_LIST,
+    GENERIC_PARAM,
+    GENERIC_PARAM_LIST,
     IDENT_PAT,
-    WILDCARD_PAT,
-    REST_PAT,
-    PATH_PAT,
-    RECORD_PAT,
-    RECORD_PAT_FIELD_LIST,
-    RECORD_PAT_FIELD,
-    TUPLE_STRUCT_PAT,
-    TUPLE_PAT,
-    SLICE_PAT,
-    RANGE_PAT,
-    LITERAL_PAT,
-    MACRO_PAT,
-    CONST_BLOCK_PAT,
-    TUPLE_EXPR,
-    ARRAY_EXPR,
-    PAREN_EXPR,
-    PATH_EXPR,
-    CLOSURE_EXPR,
     IF_EXPR,
-    WHILE_EXPR,
-    LOOP_EXPR,
-    FOR_EXPR,
-    CONTINUE_EXPR,
-    BREAK_EXPR,
+    IMPL,
+    IMPL_TRAIT_TYPE,
+    INDEX_EXPR,
+    INFER_TYPE,
+    ITEM,
+    ITEM_LIST,
     LABEL,
-    BLOCK_EXPR,
-    STMT_LIST,
-    RETURN_EXPR,
-    BECOME_EXPR,
-    YIELD_EXPR,
-    YEET_EXPR,
+    LET_ELSE,
     LET_EXPR,
-    UNDERSCORE_EXPR,
+    LET_STMT,
+    LIFETIME,
+    LIFETIME_ARG,
+    LIFETIME_PARAM,
+    LITERAL,
+    LITERAL_PAT,
+    LOOP_EXPR,
+    MACRO_CALL,
+    MACRO_DEF,
     MACRO_EXPR,
-    MATCH_EXPR,
-    MATCH_ARM_LIST,
+    MACRO_ITEMS,
+    MACRO_PAT,
+    MACRO_RULES,
+    MACRO_STMTS,
+    MACRO_TYPE,
     MATCH_ARM,
+    MATCH_ARM_LIST,
+    MATCH_EXPR,
     MATCH_GUARD,
-    RECORD_EXPR,
-    RECORD_EXPR_FIELD_LIST,
-    RECORD_EXPR_FIELD,
-    OFFSET_OF_EXPR,
-    ASM_EXPR,
-    FORMAT_ARGS_EXPR,
-    FORMAT_ARGS_ARG,
-    CALL_EXPR,
-    INDEX_EXPR,
+    META,
     METHOD_CALL_EXPR,
-    FIELD_EXPR,
-    AWAIT_EXPR,
-    TRY_EXPR,
-    CAST_EXPR,
-    REF_EXPR,
+    MODULE,
+    NAME,
+    NAME_REF,
+    NEVER_TYPE,
+    OFFSET_OF_EXPR,
+    OR_PAT,
+    PARAM,
+    PARAM_LIST,
+    PAREN_EXPR,
+    PAREN_PAT,
+    PAREN_TYPE,
+    PAT,
+    PATH,
+    PATH_EXPR,
+    PATH_PAT,
+    PATH_SEGMENT,
+    PATH_TYPE,
     PREFIX_EXPR,
+    PTR_TYPE,
     RANGE_EXPR,
-    BIN_EXPR,
-    EXTERN_BLOCK,
-    EXTERN_ITEM_LIST,
-    VARIANT,
-    RECORD_FIELD_LIST,
+    RANGE_PAT,
+    RECORD_EXPR,
+    RECORD_EXPR_FIELD,
+    RECORD_EXPR_FIELD_LIST,
     RECORD_FIELD,
-    TUPLE_FIELD_LIST,
+    RECORD_FIELD_LIST,
+    RECORD_PAT,
+    RECORD_PAT_FIELD,
+    RECORD_PAT_FIELD_LIST,
+    REF_EXPR,
+    REF_PAT,
+    REF_TYPE,
+    RENAME,
+    REST_PAT,
+    RETURN_EXPR,
+    RET_TYPE,
+    SELF_PARAM,
+    SLICE_PAT,
+    SLICE_TYPE,
+    SOURCE_FILE,
+    STATIC,
+    STMT,
+    STMT_LIST,
+    STRUCT,
+    TOKEN_TREE,
+    TRAIT,
+    TRAIT_ALIAS,
+    TRY_EXPR,
+    TUPLE_EXPR,
     TUPLE_FIELD,
-    VARIANT_LIST,
-    ITEM_LIST,
-    ASSOC_ITEM_LIST,
-    ATTR,
-    META,
+    TUPLE_FIELD_LIST,
+    TUPLE_PAT,
+    TUPLE_STRUCT_PAT,
+    TUPLE_TYPE,
+    TYPE,
+    TYPE_ALIAS,
+    TYPE_ARG,
+    TYPE_BOUND,
+    TYPE_BOUND_LIST,
+    TYPE_PARAM,
+    UNDERSCORE_EXPR,
+    UNION,
+    USE,
     USE_TREE,
     USE_TREE_LIST,
-    PATH,
-    PATH_SEGMENT,
-    LITERAL,
-    RENAME,
+    VARIANT,
+    VARIANT_LIST,
     VISIBILITY,
     WHERE_CLAUSE,
     WHERE_PRED,
-    ABI,
-    NAME,
-    NAME_REF,
-    LET_STMT,
-    LET_ELSE,
-    EXPR_STMT,
-    GENERIC_PARAM_LIST,
-    GENERIC_PARAM,
-    LIFETIME_PARAM,
-    TYPE_PARAM,
-    RETURN_TYPE_ARG,
-    CONST_PARAM,
-    GENERIC_ARG_LIST,
-    LIFETIME,
-    LIFETIME_ARG,
-    TYPE_ARG,
-    ASSOC_TYPE_ARG,
-    CONST_ARG,
-    PARAM_LIST,
-    PARAM,
-    SELF_PARAM,
-    ARG_LIST,
-    TYPE_BOUND,
-    TYPE_BOUND_LIST,
-    MACRO_ITEMS,
-    MACRO_STMTS,
-    MACRO_EAGER_INPUT,
+    WHILE_EXPR,
+    WILDCARD_PAT,
+    YEET_EXPR,
+    YIELD_EXPR,
     #[doc(hidden)]
     __LAST,
 }
@@ -287,7 +299,8 @@ impl SyntaxKind {
     pub fn is_keyword(self) -> bool {
         matches!(
             self,
-            ABSTRACT_KW
+            SELF_TYPE_KW
+                | ABSTRACT_KW
                 | AS_KW
                 | ASYNC_KW
                 | AWAIT_KW
@@ -306,6 +319,7 @@ impl SyntaxKind {
                 | FINAL_KW
                 | FN_KW
                 | FOR_KW
+                | GEN_KW
                 | IF_KW
                 | IMPL_KW
                 | IN_KW
@@ -322,7 +336,6 @@ impl SyntaxKind {
                 | REF_KW
                 | RETURN_KW
                 | SELF_KW
-                | SELF_TYPE_KW
                 | STATIC_KW
                 | STRUCT_KW
                 | SUPER_KW
@@ -338,23 +351,23 @@ impl SyntaxKind {
                 | WHERE_KW
                 | WHILE_KW
                 | YIELD_KW
+                | ASM_KW
                 | AUTO_KW
                 | BUILTIN_KW
                 | DEFAULT_KW
-                | EXISTENTIAL_KW
-                | UNION_KW
-                | RAW_KW
+                | FORMAT_ARGS_KW
                 | MACRO_RULES_KW
-                | YEET_KW
                 | OFFSET_OF_KW
-                | ASM_KW
-                | FORMAT_ARGS_KW
+                | RAW_KW
+                | UNION_KW
+                | YEET_KW
         )
     }
     pub fn is_punct(self) -> bool {
         matches!(
             self,
-            SEMICOLON
+            DOLLAR
+                | SEMICOLON
                 | COMMA
                 | L_PAREN
                 | R_PAREN
@@ -368,7 +381,6 @@ impl SyntaxKind {
                 | POUND
                 | TILDE
                 | QUESTION
-                | DOLLAR
                 | AMP
                 | PIPE
                 | PLUS
@@ -409,10 +421,22 @@ impl SyntaxKind {
         )
     }
     pub fn is_literal(self) -> bool {
-        matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING)
+        matches!(
+            self,
+            BYTE | BYTE_STRING
+                | CHAR
+                | C_STRING
+                | FLOAT_NUMBER
+                | INT_NUMBER
+                | RAW_BYTE_STRING
+                | RAW_C_STRING
+                | RAW_STRING
+                | STRING
+        )
     }
     pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
         let kw = match ident {
+            "Self" => SELF_TYPE_KW,
             "abstract" => ABSTRACT_KW,
             "as" => AS_KW,
             "async" => ASYNC_KW,
@@ -432,6 +456,7 @@ impl SyntaxKind {
             "final" => FINAL_KW,
             "fn" => FN_KW,
             "for" => FOR_KW,
+            "gen" => GEN_KW,
             "if" => IF_KW,
             "impl" => IMPL_KW,
             "in" => IN_KW,
@@ -448,7 +473,6 @@ impl SyntaxKind {
             "ref" => REF_KW,
             "return" => RETURN_KW,
             "self" => SELF_KW,
-            "Self" => SELF_TYPE_KW,
             "static" => STATIC_KW,
             "struct" => STRUCT_KW,
             "super" => SUPER_KW,
@@ -470,23 +494,23 @@ impl SyntaxKind {
     }
     pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
         let kw = match ident {
+            "asm" => ASM_KW,
             "auto" => AUTO_KW,
             "builtin" => BUILTIN_KW,
             "default" => DEFAULT_KW,
-            "existential" => EXISTENTIAL_KW,
-            "union" => UNION_KW,
-            "raw" => RAW_KW,
+            "format_args" => FORMAT_ARGS_KW,
             "macro_rules" => MACRO_RULES_KW,
-            "yeet" => YEET_KW,
             "offset_of" => OFFSET_OF_KW,
-            "asm" => ASM_KW,
-            "format_args" => FORMAT_ARGS_KW,
+            "raw" => RAW_KW,
+            "union" => UNION_KW,
+            "yeet" => YEET_KW,
             _ => return None,
         };
         Some(kw)
     }
     pub fn from_char(c: char) -> Option<SyntaxKind> {
         let tok = match c {
+            '$' => DOLLAR,
             ';' => SEMICOLON,
             ',' => COMMA,
             '(' => L_PAREN,
@@ -501,7 +525,6 @@ impl SyntaxKind {
             '#' => POUND,
             '~' => TILDE,
             '?' => QUESTION,
-            '$' => DOLLAR,
             '&' => AMP,
             '|' => PIPE,
             '+' => PLUS,
@@ -521,4 +544,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 } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_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 } ; [do] => { $ crate :: SyntaxKind :: DO_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 } ; [final] => { $ crate :: SyntaxKind :: FINAL_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 } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_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 } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_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 } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ 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 :: 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 } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_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 } ; [do] => { $ crate :: SyntaxKind :: DO_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 } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_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 } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_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 } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/proc-macro-srv/src/server_impl/token_stream.rs b/crates/proc-macro-srv/src/server_impl/token_stream.rs
index b1a448427c..c382a5b241 100644
--- a/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -127,7 +127,8 @@ pub(super) mod token_stream {
     impl<S: Copy + fmt::Debug> TokenStream<S> {
         pub(crate) fn from_str(src: &str, call_site: S) -> Result<TokenStream<S>, String> {
             let subtree =
-                mbe::parse_to_token_tree_static_span(call_site, src).ok_or("lexing error")?;
+                mbe::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src)
+                    .ok_or("lexing error")?;
 
             Ok(TokenStream::with_subtree(subtree))
         }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cd8e7e339b..4c46fad4e5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -108,7 +108,7 @@ config_data! {
         /// targets and features, with the following base command line:
         ///
         /// ```bash
-        /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+        /// cargo check --quiet --workspace --message-format=json --all-targets
         /// ```
         /// .
         cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
diff --git a/crates/rust-analyzer/src/version.rs b/crates/rust-analyzer/src/version.rs
index 2ff967416c..1e829299e6 100644
--- a/crates/rust-analyzer/src/version.rs
+++ b/crates/rust-analyzer/src/version.rs
@@ -15,7 +15,7 @@ pub struct VersionInfo {
     pub version: &'static str,
     /// The release channel we were built for (stable/beta/nightly/dev).
     ///
-    /// `None` if not built via bootstrap.
+    /// `None` if not built via rustbuild.
     pub release_channel: Option<&'static str>,
     /// Information about the Git repository we may have been built from.
     ///
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 8c772b9c7a..000ac9c8f6 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -8,7 +8,11 @@
 //
 //   //          -- comment
 //   Name =      -- non-terminal definition
-//   'ident'     -- token (terminal)
+//   'ident'     -- keyword or punct token (terminal)
+//   '?ident'    -- contextual keyword (terminal)
+//   too)
+//   '#ident'    -- generic token (terminal)
+//   '@ident'    -- literal token (terminal)
 //   A B         -- sequence
 //   A | B       -- alternation
 //   A*          -- zero or more repetition
@@ -17,17 +21,17 @@
 //   label:A     -- suggested name for field of AST node
 
 //*************************//
-// Names, Paths and Macros //
+//         Paths           //
 //*************************//
 
 Name =
-  'ident' | 'self'
+  '#ident' | 'self'
 
 NameRef =
-  'ident' | 'int_number' | 'self' | 'super' | 'crate' | 'Self'
+  '#ident' | '@int_number' | 'self' | 'super' | 'crate' | 'Self'
 
 Lifetime =
-  'lifetime_ident'
+  '#lifetime_ident'
 
 Path =
   (qualifier:Path '::')? segment:PathSegment
@@ -38,6 +42,11 @@ PathSegment =
 | NameRef ParamList RetType?
 | '<' Type ('as' PathType)? '>'
 
+
+//*************************//
+//        Generics         //
+//*************************//
+
 GenericArgList =
   '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>'
 
@@ -61,6 +70,36 @@ LifetimeArg =
 ConstArg =
   Expr
 
+GenericParamList =
+  '<' (GenericParam (',' GenericParam)* ','?)? '>'
+
+GenericParam =
+  ConstParam
+| LifetimeParam
+| TypeParam
+
+TypeParam =
+  Attr* Name (':' TypeBoundList?)?
+  ('=' default_type:Type)?
+
+ConstParam =
+  Attr* 'const' Name ':' Type
+  ('=' default_val:ConstArg)?
+
+LifetimeParam =
+  Attr* Lifetime (':' TypeBoundList?)?
+
+WhereClause =
+  'where' predicates:(WherePred (',' WherePred)* ','?)
+
+WherePred =
+  ('for' GenericParamList)?  (Lifetime | Type) ':' TypeBoundList?
+
+
+//*************************//
+//          Macro          //
+//*************************//
+
 MacroCall =
   Attr* Path '!' TokenTree ';'?
 
@@ -72,22 +111,23 @@ TokenTree =
 MacroItems =
   Item*
 
-MacroEagerInput =
-  '(' (Expr (',' Expr)* ','?)? ')'
-| '{' (Expr (',' Expr)* ','?)? '}'
-| '[' (Expr (',' Expr)* ','?)? ']'
-
-
 MacroStmts =
   statements:Stmt*
   Expr?
 
+Attr =
+  '#' '!'? '[' Meta ']'
+
+Meta =
+  'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
+| Path ('=' Expr | TokenTree)?
+
 //*************************//
 //          Items          //
 //*************************//
 
 SourceFile =
-  'shebang'?
+  '#shebang'?
   Attr*
   Item*
 
@@ -112,7 +152,7 @@ Item =
 
 MacroRules =
   Attr* Visibility?
-  'macro_rules' '!' Name
+  '?macro_rules' '!' Name
   TokenTree
 
 MacroDef =
@@ -148,12 +188,12 @@ UseTreeList =
 
 Fn =
  Attr* Visibility?
- 'default'? 'const'? 'async'? 'unsafe'? Abi?
+ '?default'? 'const'? 'async'? 'unsafe'? Abi?
  'fn' Name GenericParamList? ParamList RetType? WhereClause?
  (body:BlockExpr | ';')
 
 Abi =
-  'extern' 'string'?
+  'extern' '@string'?
 
 ParamList =
   '('(
@@ -180,7 +220,7 @@ RetType =
 
 TypeAlias =
   Attr* Visibility?
-  'default'?
+  '?default'?
   'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause?
   ('=' Type)? ';'
 
@@ -223,7 +263,7 @@ Variant =
 
 Union =
   Attr* Visibility?
-  'union' Name GenericParamList? WhereClause?
+  '?union' Name GenericParamList? WhereClause?
   RecordFieldList
 
 // A Data Type.
@@ -236,7 +276,7 @@ Adt =
 
 Const =
   Attr* Visibility?
-  'default'?
+  '?default'?
   'const' (Name | '_') ':' Type
   ('=' body:Expr)? ';'
 
@@ -247,7 +287,7 @@ Static =
 
 Trait =
   Attr* Visibility?
-  'unsafe'? 'auto'?
+  'unsafe'? '?auto'?
   'trait' Name GenericParamList?
   (':' TypeBoundList?)? WhereClause? AssocItemList
 
@@ -266,7 +306,7 @@ AssocItem =
 
 Impl =
   Attr* Visibility?
-  'default'? 'unsafe'?
+  '?default'? 'unsafe'?
   'impl' GenericParamList? ('const'? '!'? trait:Type 'for')? self_ty:Type WhereClause?
   AssocItemList
 
@@ -282,41 +322,9 @@ ExternItem =
 | Static
 | TypeAlias
 
-GenericParamList =
-  '<' (GenericParam (',' GenericParam)* ','?)? '>'
-
-GenericParam =
-  ConstParam
-| LifetimeParam
-| TypeParam
-
-TypeParam =
-  Attr* Name (':' TypeBoundList?)?
-  ('=' default_type:Type)?
-
-ConstParam =
-  Attr* 'const' Name ':' Type
-  ('=' default_val:ConstArg)?
-
-LifetimeParam =
-  Attr* Lifetime (':' TypeBoundList?)?
-
-WhereClause =
-  'where' predicates:(WherePred (',' WherePred)* ','?)
-
-WherePred =
-  ('for' GenericParamList)?  (Lifetime | Type) ':' TypeBoundList?
-
 Visibility =
   'pub' ('(' 'in'? Path ')')?
 
-Attr =
-  '#' '!'? '[' Meta ']'
-
-Meta =
-  'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
-| Path ('=' Expr | TokenTree)?
-
 
 //****************************//
 // Statements and Expressions //
@@ -379,13 +387,13 @@ Expr =
 | UnderscoreExpr
 
 OffsetOfExpr =
-  Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+  Attr* '?builtin' '#' '?offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
 
 AsmExpr =
-  Attr* 'builtin' '#' 'asm' '(' Expr ')'
+  Attr* '?builtin' '#' '?asm' '(' Expr ')'
 
 FormatArgsExpr =
-  Attr* 'builtin' '#' 'format_args' '('
+  Attr* '?builtin' '#' '?format_args' '('
   template:Expr
   (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
   ')'
@@ -398,11 +406,12 @@ MacroExpr =
 
 Literal =
   Attr* value:(
-    'int_number' | 'float_number'
-  | 'string' | 'raw_string'
-  | 'byte_string' | 'raw_byte_string'
+    '@int_number' | '@float_number'
+  | '@string' | '@raw_string'
+  | '@byte_string' | '@raw_byte_string'
+  | '@c_string' | '@raw_c_string'
+  | '@char' | '@byte'
   | 'true' | 'false'
-  | 'char' | 'byte'
   )
 
 PathExpr =
@@ -416,13 +425,13 @@ StmtList =
   '}'
 
 RefExpr =
-  Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr
+  Attr* '&' (('?raw' 'const'?)| ('?raw'? 'mut') ) Expr
 
 TryExpr =
   Attr* Expr '?'
 
 BlockExpr =
-  Attr* Label? ('try' | 'unsafe' | 'async' | 'const') StmtList
+  Attr* Label? ('try' | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
 
 PrefixExpr =
   Attr* op:('-' | '!' | '*') Expr
@@ -482,9 +491,12 @@ FieldExpr =
   Attr* Expr '.' NameRef
 
 ClosureExpr =
-  Attr* ('for' GenericParamList)? 'const'? 'static'? 'async'? 'move'?  ParamList RetType?
+  Attr* ClosureBinder? 'const'? 'static'? 'async'? 'gen'? 'move'?  ParamList RetType?
   body:Expr
 
+ClosureBinder =
+  'for' GenericParamList
+
 IfExpr =
   Attr* 'if' condition:Expr then_branch:BlockExpr
   ('else' else_branch:(IfExpr | BlockExpr))?
@@ -538,7 +550,7 @@ YieldExpr =
   Attr* 'yield' Expr?
 
 YeetExpr =
-  Attr* 'do' 'yeet' Expr?
+  Attr* 'do' '?yeet' Expr?
 
 LetExpr =
   Attr* 'let' Pat '=' Expr
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index b0ee9dfd50..6ed205e285 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -352,13 +352,22 @@ pub enum BlockModifier {
     Unsafe(SyntaxToken),
     Try(SyntaxToken),
     Const(SyntaxToken),
+    AsyncGen(SyntaxToken),
+    Gen(SyntaxToken),
     Label(ast::Label),
 }
 
 impl ast::BlockExpr {
     pub fn modifier(&self) -> Option<BlockModifier> {
-        self.async_token()
-            .map(BlockModifier::Async)
+        self.gen_token()
+            .map(|v| {
+                if self.async_token().is_some() {
+                    BlockModifier::AsyncGen(v)
+                } else {
+                    BlockModifier::Gen(v)
+                }
+            })
+            .or_else(|| self.async_token().map(BlockModifier::Async))
             .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
             .or_else(|| self.try_token().map(BlockModifier::Try))
             .or_else(|| self.const_token().map(BlockModifier::Const))
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 0373e7c552..ceecffba5d 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -14,6 +14,8 @@ pub struct Abi {
 impl Abi {
     #[inline]
     pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) }
+    #[inline]
+    pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -182,6 +184,10 @@ impl BlockExpr {
     #[inline]
     pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
     #[inline]
+    pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
+    #[inline]
+    pub fn move_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![move]) }
+    #[inline]
     pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
     #[inline]
     pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
@@ -237,6 +243,17 @@ impl CastExpr {
     pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ClosureBinder {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ClosureBinder {
+    #[inline]
+    pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
+    #[inline]
+    pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct ClosureExpr {
     pub(crate) syntax: SyntaxNode,
@@ -244,7 +261,7 @@ pub struct ClosureExpr {
 impl ast::HasAttrs for ClosureExpr {}
 impl ClosureExpr {
     #[inline]
-    pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
+    pub fn closure_binder(&self) -> Option<ClosureBinder> { support::child(&self.syntax) }
     #[inline]
     pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) }
     #[inline]
@@ -254,7 +271,7 @@ impl ClosureExpr {
     #[inline]
     pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
     #[inline]
-    pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
+    pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
     #[inline]
     pub fn move_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![move]) }
     #[inline]
@@ -833,27 +850,6 @@ impl MacroDef {
     pub fn macro_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![macro]) }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct MacroEagerInput {
-    pub(crate) syntax: SyntaxNode,
-}
-impl MacroEagerInput {
-    #[inline]
-    pub fn exprs(&self) -> AstChildren<Expr> { support::children(&self.syntax) }
-    #[inline]
-    pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
-    #[inline]
-    pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
-    #[inline]
-    pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
-    #[inline]
-    pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
-    #[inline]
-    pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
-    #[inline]
-    pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MacroExpr {
     pub(crate) syntax: SyntaxNode,
@@ -1050,6 +1046,10 @@ impl NameRef {
     #[inline]
     pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
     #[inline]
+    pub fn int_number_token(&self) -> Option<SyntaxToken> {
+        support::token(&self.syntax, T![int_number])
+    }
+    #[inline]
     pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
     #[inline]
     pub fn super_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![super]) }
@@ -2461,6 +2461,20 @@ impl AstNode for CastExpr {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for ClosureBinder {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for ClosureExpr {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR }
@@ -3021,20 +3035,6 @@ impl AstNode for MacroDef {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
-impl AstNode for MacroEagerInput {
-    #[inline]
-    fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EAGER_INPUT }
-    #[inline]
-    fn cast(syntax: SyntaxNode) -> Option<Self> {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
-    }
-    #[inline]
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
 impl AstNode for MacroExpr {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR }
@@ -5541,6 +5541,11 @@ impl std::fmt::Display for CastExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for ClosureBinder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for ClosureExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
@@ -5741,11 +5746,6 @@ impl std::fmt::Display for MacroDef {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
-impl std::fmt::Display for MacroEagerInput {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        std::fmt::Display::fmt(self.syntax(), f)
-    }
-}
 impl std::fmt::Display for MacroExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index abff16b6f5..6d21ca1747 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -211,115 +211,6 @@ impl SourceFile {
     }
 }
 
-impl ast::TokenTree {
-    pub fn reparse_as_comma_separated_expr(
-        self,
-        edition: parser::Edition,
-    ) -> Parse<ast::MacroEagerInput> {
-        let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
-
-        let mut parser_input = parser::Input::default();
-        let mut was_joint = false;
-        for t in tokens {
-            let kind = t.kind();
-            if kind.is_trivia() {
-                was_joint = false
-            } else if kind == SyntaxKind::IDENT {
-                let token_text = t.text();
-                let contextual_kw =
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
-                parser_input.push_ident(contextual_kw);
-            } else {
-                if was_joint {
-                    parser_input.was_joint();
-                }
-                parser_input.push(kind);
-                // Tag the token as joint if it is float with a fractional part
-                // we use this jointness to inform the parser about what token split
-                // event to emit when we encounter a float literal in a field access
-                if kind == SyntaxKind::FLOAT_NUMBER {
-                    if !t.text().ends_with('.') {
-                        parser_input.was_joint();
-                    } else {
-                        was_joint = false;
-                    }
-                } else {
-                    was_joint = true;
-                }
-            }
-        }
-
-        let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input, edition);
-
-        let mut tokens =
-            self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
-        let mut text = String::new();
-        let mut pos = TextSize::from(0);
-        let mut builder = SyntaxTreeBuilder::default();
-        for event in parser_output.iter() {
-            match event {
-                parser::Step::Token { kind, n_input_tokens } => {
-                    let mut token = tokens.next().unwrap();
-                    while token.kind().is_trivia() {
-                        let text = token.text();
-                        pos += TextSize::from(text.len() as u32);
-                        builder.token(token.kind(), text);
-
-                        token = tokens.next().unwrap();
-                    }
-                    text.push_str(token.text());
-                    for _ in 1..n_input_tokens {
-                        let token = tokens.next().unwrap();
-                        text.push_str(token.text());
-                    }
-
-                    pos += TextSize::from(text.len() as u32);
-                    builder.token(kind, &text);
-                    text.clear();
-                }
-                parser::Step::FloatSplit { ends_in_dot: has_pseudo_dot } => {
-                    let token = tokens.next().unwrap();
-                    let text = token.text();
-
-                    match text.split_once('.') {
-                        Some((left, right)) => {
-                            assert!(!left.is_empty());
-                            builder.start_node(SyntaxKind::NAME_REF);
-                            builder.token(SyntaxKind::INT_NUMBER, left);
-                            builder.finish_node();
-
-                            // here we move the exit up, the original exit has been deleted in process
-                            builder.finish_node();
-
-                            builder.token(SyntaxKind::DOT, ".");
-
-                            if has_pseudo_dot {
-                                assert!(right.is_empty(), "{left}.{right}");
-                            } else {
-                                assert!(!right.is_empty(), "{left}.{right}");
-                                builder.start_node(SyntaxKind::NAME_REF);
-                                builder.token(SyntaxKind::INT_NUMBER, right);
-                                builder.finish_node();
-
-                                // the parser creates an unbalanced start node, we are required to close it here
-                                builder.finish_node();
-                            }
-                        }
-                        None => unreachable!(),
-                    }
-                    pos += TextSize::from(text.len() as u32);
-                }
-                parser::Step::Enter { kind } => builder.start_node(kind),
-                parser::Step::Exit => builder.finish_node(),
-                parser::Step::Error { msg } => builder.error(msg.to_owned(), pos),
-            }
-        }
-
-        let (green, errors) = builder.finish_raw();
-        Parse::new(green, errors)
-    }
-}
-
 /// Matches a `SyntaxNode` against an `ast` type.
 ///
 /// # Example:
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 4bf2a03279..e52daa42f1 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -11,7 +11,7 @@ pub(crate) use crate::parsing::reparsing::incremental_reparse;
 
 pub(crate) fn parse_text(text: &str, edition: parser::Edition) -> (GreenNode, Vec<SyntaxError>) {
     let _p = tracing::info_span!("parse_text").entered();
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     let parser_input = lexed.to_input();
     let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input, edition);
     let (node, errors, _eof) = build_tree(lexed, parser_output);
@@ -24,7 +24,7 @@ pub(crate) fn parse_text_at(
     edition: parser::Edition,
 ) -> (GreenNode, Vec<SyntaxError>) {
     let _p = tracing::info_span!("parse_text_at").entered();
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     let parser_input = lexed.to_input();
     let parser_output = entry.parse(&parser_input, edition);
     let (node, errors, _eof) = build_tree(lexed, parser_output);
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 354b89fd49..1e1a02f445 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -6,7 +6,7 @@
 //!   - otherwise, we search for the nearest `{}` block which contains the edit
 //!     and try to parse only this block.
 
-use parser::Reparser;
+use parser::{Edition, Reparser};
 use text_edit::Indel;
 
 use crate::{
@@ -51,7 +51,8 @@ fn reparse_token(
             }
 
             let mut new_text = get_text_after_edit(prev_token.clone().into(), edit);
-            let (new_token_kind, new_err) = parser::LexedStr::single_token(&new_text)?;
+            let (new_token_kind, new_err) =
+                parser::LexedStr::single_token(Edition::CURRENT, &new_text)?;
 
             if new_token_kind != prev_token_kind
                 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
@@ -64,7 +65,8 @@ fn reparse_token(
             // `b` no longer remains an identifier, but becomes a part of byte string literal
             if let Some(next_char) = root.text().char_at(prev_token.text_range().end()) {
                 new_text.push(next_char);
-                let token_with_next_char = parser::LexedStr::single_token(&new_text);
+                let token_with_next_char =
+                    parser::LexedStr::single_token(Edition::CURRENT, &new_text);
                 if let Some((_kind, _error)) = token_with_next_char {
                     return None;
                 }
@@ -91,7 +93,7 @@ fn reparse_block(
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
     let text = get_text_after_edit(node.clone().into(), edit);
 
-    let lexed = parser::LexedStr::new(text.as_str());
+    let lexed = parser::LexedStr::new(Edition::CURRENT, text.as_str());
     let parser_input = lexed.to_input();
     if !is_balanced(&lexed) {
         return None;
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 6fd81d0528..bbfae1839b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -82,7 +82,7 @@ By default, a cargo invocation will be constructed for the configured
 targets and features, with the following base command line:
 
 ```bash
-cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+cargo check --quiet --workspace --message-format=json --all-targets
 ```
 .
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 941b5ff4eb..9ddfcaa708 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -718,7 +718,7 @@
                 "title": "cargo",
                 "properties": {
                     "rust-analyzer.cargo.buildScripts.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets\n```\n.",
                         "default": null,
                         "type": [
                             "null",
diff --git a/rust-version b/rust-version
index 18e32e5c65..424c93a752 100644
--- a/rust-version
+++ b/rust-version
@@ -1,1 +1,1 @@
-a91f7d72f12efcc00ecf71591f066c534d45ddf7
+bcf1f6db4594ae6132378b179a30cdb3599a863d
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index acaa65129d..2491952f52 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -163,8 +163,9 @@ fn add_preamble(cg: CodegenType, mut text: String) -> String {
 /// case, updates the file and then fails the test.
 #[allow(clippy::print_stderr)]
 fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: bool) {
+    let contents = normalize_newlines(contents);
     if let Ok(old_contents) = fs::read_to_string(file) {
-        if normalize_newlines(&old_contents) == normalize_newlines(contents) {
+        if normalize_newlines(&old_contents) == contents {
             // File is already up to date.
             return;
         }
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 45fa2d37c8..44f12ba4ad 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -17,15 +17,22 @@ use quote::{format_ident, quote};
 use ungrammar::{Grammar, Rule};
 
 use crate::{
-    codegen::{add_preamble, ensure_file_contents, reformat},
+    codegen::{add_preamble, ensure_file_contents, grammar::ast_src::generate_kind_src, reformat},
     project_root,
 };
 
 mod ast_src;
-use self::ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC};
+use self::ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc};
 
 pub(crate) fn generate(check: bool) {
-    let syntax_kinds = generate_syntax_kinds(KINDS_SRC);
+    let grammar = fs::read_to_string(project_root().join("crates/syntax/rust.ungram"))
+        .unwrap()
+        .parse()
+        .unwrap();
+    let ast = lower(&grammar);
+    let kinds_src = generate_kind_src(&ast.nodes, &ast.enums, &grammar);
+
+    let syntax_kinds = generate_syntax_kinds(kinds_src);
     let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs");
     ensure_file_contents(
         crate::flags::CodegenType::Grammar,
@@ -34,12 +41,6 @@ pub(crate) fn generate(check: bool) {
         check,
     );
 
-    let grammar = fs::read_to_string(project_root().join("crates/syntax/rust.ungram"))
-        .unwrap()
-        .parse()
-        .unwrap();
-    let ast = lower(&grammar);
-
     let ast_tokens = generate_tokens(&ast);
     let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs");
     ensure_file_contents(
@@ -49,7 +50,7 @@ pub(crate) fn generate(check: bool) {
         check,
     );
 
-    let ast_nodes = generate_nodes(KINDS_SRC, &ast);
+    let ast_nodes = generate_nodes(kinds_src, &ast);
     let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs");
     ensure_file_contents(
         crate::flags::CodegenType::Grammar,
@@ -96,7 +97,7 @@ fn generate_tokens(grammar: &AstSrc) -> String {
     .replace("#[derive", "\n#[derive")
 }
 
-fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String {
+fn generate_nodes(kinds: KindsSrc, grammar: &AstSrc) -> String {
     let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
         .nodes
         .iter()
@@ -117,7 +118,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String {
                 });
 
             let methods = node.fields.iter().map(|field| {
-                let method_name = field.method_name();
+                let method_name = format_ident!("{}", field.method_name());
                 let ty = field.ty();
 
                 if field.is_many() {
@@ -366,7 +367,7 @@ fn write_doc_comment(contents: &[String], dest: &mut String) {
     }
 }
 
-fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
+fn generate_syntax_kinds(grammar: KindsSrc) -> String {
     let (single_byte_tokens_values, single_byte_tokens): (Vec<_>, Vec<_>) = grammar
         .punct
         .iter()
@@ -481,7 +482,9 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
             #([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*
             #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*
             [lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
+            [int_number] => { $crate::SyntaxKind::INT_NUMBER };
             [ident] => { $crate::SyntaxKind::IDENT };
+            [string] => { $crate::SyntaxKind::STRING };
             [shebang] => { $crate::SyntaxKind::SHEBANG };
         }
     };
@@ -550,7 +553,7 @@ impl Field {
             _ => None,
         }
     }
-    fn method_name(&self) -> proc_macro2::Ident {
+    fn method_name(&self) -> String {
         match self {
             Field::Token(name) => {
                 let name = match name.as_str() {
@@ -585,13 +588,13 @@ impl Field {
                     "~" => "tilde",
                     _ => name,
                 };
-                format_ident!("{}_token", name)
+                format!("{name}_token",)
             }
             Field::Node { name, .. } => {
                 if name == "type" {
-                    format_ident!("ty")
+                    String::from("ty")
                 } else {
-                    format_ident!("{}", name)
+                    name.to_owned()
                 }
             }
         }
@@ -604,6 +607,15 @@ impl Field {
     }
 }
 
+fn clean_token_name(name: &str) -> String {
+    let cleaned = name.trim_start_matches(['@', '#', '?']);
+    if cleaned.is_empty() {
+        name.to_owned()
+    } else {
+        cleaned.to_owned()
+    }
+}
+
 fn lower(grammar: &Grammar) -> AstSrc {
     let mut res = AstSrc {
         tokens:
@@ -683,14 +695,12 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
         }
         Rule::Token(token) => {
             assert!(label.is_none());
-            let mut name = grammar[*token].name.clone();
-            if name != "int_number" && name != "string" {
-                if "[]{}()".contains(&name) {
-                    name = format!("'{name}'");
-                }
-                let field = Field::Token(name);
-                acc.push(field);
+            let mut name = clean_token_name(&grammar[*token].name);
+            if "[]{}()".contains(&name) {
+                name = format!("'{name}'");
             }
+            let field = Field::Token(name);
+            acc.push(field);
         }
         Rule::Rep(inner) => {
             if let Rule::Node(node) = &**inner {
@@ -863,7 +873,7 @@ fn extract_struct_traits(ast: &mut AstSrc) {
 fn extract_struct_trait(node: &mut AstNodeSrc, trait_name: &str, methods: &[&str]) {
     let mut to_remove = Vec::new();
     for (i, field) in node.fields.iter().enumerate() {
-        let method_name = field.method_name().to_string();
+        let method_name = field.method_name();
         if methods.iter().any(|&it| it == method_name) {
             to_remove.push(i);
         }
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index c246ee9950..3444f89908 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -1,241 +1,154 @@
 //! Defines input for code generation process.
 
-pub(crate) struct KindsSrc<'a> {
-    pub(crate) punct: &'a [(&'a str, &'a str)],
-    pub(crate) keywords: &'a [&'a str],
-    pub(crate) contextual_keywords: &'a [&'a str],
-    pub(crate) literals: &'a [&'a str],
-    pub(crate) tokens: &'a [&'a str],
-    pub(crate) nodes: &'a [&'a str],
+use crate::codegen::grammar::to_upper_snake_case;
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct KindsSrc {
+    pub(crate) punct: &'static [(&'static str, &'static str)],
+    pub(crate) keywords: &'static [&'static str],
+    pub(crate) contextual_keywords: &'static [&'static str],
+    pub(crate) literals: &'static [&'static str],
+    pub(crate) tokens: &'static [&'static str],
+    pub(crate) nodes: &'static [&'static str],
 }
 
-pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
-    punct: &[
-        (";", "SEMICOLON"),
-        (",", "COMMA"),
-        ("(", "L_PAREN"),
-        (")", "R_PAREN"),
-        ("{", "L_CURLY"),
-        ("}", "R_CURLY"),
-        ("[", "L_BRACK"),
-        ("]", "R_BRACK"),
-        ("<", "L_ANGLE"),
-        (">", "R_ANGLE"),
-        ("@", "AT"),
-        ("#", "POUND"),
-        ("~", "TILDE"),
-        ("?", "QUESTION"),
-        ("$", "DOLLAR"),
-        ("&", "AMP"),
-        ("|", "PIPE"),
-        ("+", "PLUS"),
-        ("*", "STAR"),
-        ("/", "SLASH"),
-        ("^", "CARET"),
-        ("%", "PERCENT"),
-        ("_", "UNDERSCORE"),
-        (".", "DOT"),
-        ("..", "DOT2"),
-        ("...", "DOT3"),
-        ("..=", "DOT2EQ"),
-        (":", "COLON"),
-        ("::", "COLON2"),
-        ("=", "EQ"),
-        ("==", "EQ2"),
-        ("=>", "FAT_ARROW"),
-        ("!", "BANG"),
-        ("!=", "NEQ"),
-        ("-", "MINUS"),
-        ("->", "THIN_ARROW"),
-        ("<=", "LTEQ"),
-        (">=", "GTEQ"),
-        ("+=", "PLUSEQ"),
-        ("-=", "MINUSEQ"),
-        ("|=", "PIPEEQ"),
-        ("&=", "AMPEQ"),
-        ("^=", "CARETEQ"),
-        ("/=", "SLASHEQ"),
-        ("*=", "STAREQ"),
-        ("%=", "PERCENTEQ"),
-        ("&&", "AMP2"),
-        ("||", "PIPE2"),
-        ("<<", "SHL"),
-        (">>", "SHR"),
-        ("<<=", "SHLEQ"),
-        (">>=", "SHREQ"),
-    ],
-    keywords: &[
-        "abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
-        "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
-        "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
-        "return", "self", "Self", "static", "struct", "super", "trait", "true", "try", "type",
-        "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
-    ],
-    contextual_keywords: &[
-        "auto",
-        "builtin",
-        "default",
-        "existential",
-        "union",
-        "raw",
-        "macro_rules",
-        "yeet",
-        "offset_of",
-        "asm",
-        "format_args",
-    ],
-    literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"],
-    tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
-    nodes: &[
-        "SOURCE_FILE",
-        "STRUCT",
-        "UNION",
-        "ENUM",
-        "FN",
-        "RET_TYPE",
-        "EXTERN_CRATE",
-        "MODULE",
-        "USE",
-        "STATIC",
-        "CONST",
-        "TRAIT",
-        "TRAIT_ALIAS",
-        "IMPL",
-        "TYPE_ALIAS",
-        "MACRO_CALL",
-        "MACRO_RULES",
-        "MACRO_ARM",
-        "TOKEN_TREE",
-        "MACRO_DEF",
-        "PAREN_TYPE",
-        "TUPLE_TYPE",
-        "MACRO_TYPE",
-        "NEVER_TYPE",
-        "PATH_TYPE",
-        "PTR_TYPE",
-        "ARRAY_TYPE",
-        "SLICE_TYPE",
-        "REF_TYPE",
-        "INFER_TYPE",
-        "FN_PTR_TYPE",
-        "FOR_TYPE",
-        "IMPL_TRAIT_TYPE",
-        "DYN_TRAIT_TYPE",
-        "OR_PAT",
-        "PAREN_PAT",
-        "REF_PAT",
-        "BOX_PAT",
-        "IDENT_PAT",
-        "WILDCARD_PAT",
-        "REST_PAT",
-        "PATH_PAT",
-        "RECORD_PAT",
-        "RECORD_PAT_FIELD_LIST",
-        "RECORD_PAT_FIELD",
-        "TUPLE_STRUCT_PAT",
-        "TUPLE_PAT",
-        "SLICE_PAT",
-        "RANGE_PAT",
-        "LITERAL_PAT",
-        "MACRO_PAT",
-        "CONST_BLOCK_PAT",
-        // atoms
-        "TUPLE_EXPR",
-        "ARRAY_EXPR",
-        "PAREN_EXPR",
-        "PATH_EXPR",
-        "CLOSURE_EXPR",
-        "IF_EXPR",
-        "WHILE_EXPR",
-        "LOOP_EXPR",
-        "FOR_EXPR",
-        "CONTINUE_EXPR",
-        "BREAK_EXPR",
-        "LABEL",
-        "BLOCK_EXPR",
-        "STMT_LIST",
-        "RETURN_EXPR",
-        "BECOME_EXPR",
-        "YIELD_EXPR",
-        "YEET_EXPR",
-        "LET_EXPR",
-        "UNDERSCORE_EXPR",
-        "MACRO_EXPR",
-        "MATCH_EXPR",
-        "MATCH_ARM_LIST",
-        "MATCH_ARM",
-        "MATCH_GUARD",
-        "RECORD_EXPR",
-        "RECORD_EXPR_FIELD_LIST",
-        "RECORD_EXPR_FIELD",
-        "OFFSET_OF_EXPR",
-        "ASM_EXPR",
-        "FORMAT_ARGS_EXPR",
-        "FORMAT_ARGS_ARG",
-        // postfix
-        "CALL_EXPR",
-        "INDEX_EXPR",
-        "METHOD_CALL_EXPR",
-        "FIELD_EXPR",
-        "AWAIT_EXPR",
-        "TRY_EXPR",
-        "CAST_EXPR",
-        // unary
-        "REF_EXPR",
-        "PREFIX_EXPR",
-        "RANGE_EXPR", // just weird
-        "BIN_EXPR",
-        "EXTERN_BLOCK",
-        "EXTERN_ITEM_LIST",
-        "VARIANT",
-        "RECORD_FIELD_LIST",
-        "RECORD_FIELD",
-        "TUPLE_FIELD_LIST",
-        "TUPLE_FIELD",
-        "VARIANT_LIST",
-        "ITEM_LIST",
-        "ASSOC_ITEM_LIST",
-        "ATTR",
-        "META",
-        "USE_TREE",
-        "USE_TREE_LIST",
-        "PATH",
-        "PATH_SEGMENT",
-        "LITERAL",
-        "RENAME",
-        "VISIBILITY",
-        "WHERE_CLAUSE",
-        "WHERE_PRED",
-        "ABI",
-        "NAME",
-        "NAME_REF",
-        "LET_STMT",
-        "LET_ELSE",
-        "EXPR_STMT",
-        "GENERIC_PARAM_LIST",
-        "GENERIC_PARAM",
-        "LIFETIME_PARAM",
-        "TYPE_PARAM",
-        "RETURN_TYPE_ARG",
-        "CONST_PARAM",
-        "GENERIC_ARG_LIST",
-        "LIFETIME",
-        "LIFETIME_ARG",
-        "TYPE_ARG",
-        "ASSOC_TYPE_ARG",
-        "CONST_ARG",
-        "PARAM_LIST",
-        "PARAM",
-        "SELF_PARAM",
-        "ARG_LIST",
-        "TYPE_BOUND",
-        "TYPE_BOUND_LIST",
-        // macro related
-        "MACRO_ITEMS",
-        "MACRO_STMTS",
-        "MACRO_EAGER_INPUT",
-    ],
-};
+/// The punctuations of the language.
+const PUNCT: &[(&str, &str)] = &[
+    // KEEP THE DOLLAR AT THE TOP ITS SPECIAL
+    ("$", "DOLLAR"),
+    (";", "SEMICOLON"),
+    (",", "COMMA"),
+    ("(", "L_PAREN"),
+    (")", "R_PAREN"),
+    ("{", "L_CURLY"),
+    ("}", "R_CURLY"),
+    ("[", "L_BRACK"),
+    ("]", "R_BRACK"),
+    ("<", "L_ANGLE"),
+    (">", "R_ANGLE"),
+    ("@", "AT"),
+    ("#", "POUND"),
+    ("~", "TILDE"),
+    ("?", "QUESTION"),
+    ("&", "AMP"),
+    ("|", "PIPE"),
+    ("+", "PLUS"),
+    ("*", "STAR"),
+    ("/", "SLASH"),
+    ("^", "CARET"),
+    ("%", "PERCENT"),
+    ("_", "UNDERSCORE"),
+    (".", "DOT"),
+    ("..", "DOT2"),
+    ("...", "DOT3"),
+    ("..=", "DOT2EQ"),
+    (":", "COLON"),
+    ("::", "COLON2"),
+    ("=", "EQ"),
+    ("==", "EQ2"),
+    ("=>", "FAT_ARROW"),
+    ("!", "BANG"),
+    ("!=", "NEQ"),
+    ("-", "MINUS"),
+    ("->", "THIN_ARROW"),
+    ("<=", "LTEQ"),
+    (">=", "GTEQ"),
+    ("+=", "PLUSEQ"),
+    ("-=", "MINUSEQ"),
+    ("|=", "PIPEEQ"),
+    ("&=", "AMPEQ"),
+    ("^=", "CARETEQ"),
+    ("/=", "SLASHEQ"),
+    ("*=", "STAREQ"),
+    ("%=", "PERCENTEQ"),
+    ("&&", "AMP2"),
+    ("||", "PIPE2"),
+    ("<<", "SHL"),
+    (">>", "SHR"),
+    ("<<=", "SHLEQ"),
+    (">>=", "SHREQ"),
+];
+const TOKENS: &[&str] = &["ERROR", "WHITESPACE", "NEWLINE", "COMMENT"];
+// &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],;
+
+const EOF: &str = "EOF";
+
+const RESERVED: &[&str] = &[
+    "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized",
+    "virtual", "yield", "try",
+];
+const CONTEXTUAL_RESERVED: &[&str] = &[];
+
+pub(crate) fn generate_kind_src(
+    nodes: &[AstNodeSrc],
+    enums: &[AstEnumSrc],
+    grammar: &ungrammar::Grammar,
+) -> KindsSrc {
+    let mut keywords: Vec<&_> = Vec::new();
+    let mut contextual_keywords: Vec<&_> = Vec::new();
+    let mut tokens: Vec<&_> = TOKENS.to_vec();
+    let mut literals: Vec<&_> = Vec::new();
+    let mut used_puncts = vec![false; PUNCT.len()];
+    // Mark $ as used
+    used_puncts[0] = true;
+    grammar.tokens().for_each(|token| {
+        let name = &*grammar[token].name;
+        if name == EOF {
+            return;
+        }
+        match name.split_at(1) {
+            ("@", lit) if !lit.is_empty() => {
+                literals.push(String::leak(to_upper_snake_case(lit)));
+            }
+            ("#", token) if !token.is_empty() => {
+                tokens.push(String::leak(to_upper_snake_case(token)));
+            }
+            ("?", kw) if !kw.is_empty() => {
+                contextual_keywords.push(String::leak(kw.to_owned()));
+            }
+            _ if name.chars().all(char::is_alphabetic) => {
+                keywords.push(String::leak(name.to_owned()));
+            }
+            _ => {
+                let idx = PUNCT
+                    .iter()
+                    .position(|(punct, _)| punct == &name)
+                    .unwrap_or_else(|| panic!("Grammar references unknown punctuation {name:?}"));
+                used_puncts[idx] = true;
+            }
+        }
+    });
+    PUNCT.iter().zip(used_puncts).filter(|(_, used)| !used).for_each(|((punct, _), _)| {
+        panic!("Punctuation {punct:?} is not used in grammar");
+    });
+    keywords.extend(RESERVED.iter().copied());
+    keywords.sort();
+    keywords.dedup();
+    contextual_keywords.extend(CONTEXTUAL_RESERVED.iter().copied());
+    contextual_keywords.sort();
+    contextual_keywords.dedup();
+
+    // we leak things here for simplicity, that way we don't have to deal with lifetimes
+    // The execution is a one shot job so thats fine
+    let nodes = nodes
+        .iter()
+        .map(|it| &it.name)
+        .chain(enums.iter().map(|it| &it.name))
+        .map(|it| to_upper_snake_case(it))
+        .map(String::leak)
+        .map(|it| &*it)
+        .collect();
+    let nodes = Vec::leak(nodes);
+    nodes.sort();
+    let keywords = Vec::leak(keywords);
+    let contextual_keywords = Vec::leak(contextual_keywords);
+    let literals = Vec::leak(literals);
+    literals.sort();
+    let tokens = Vec::leak(tokens);
+    tokens.sort();
+
+    KindsSrc { punct: PUNCT, nodes, keywords, contextual_keywords, literals, tokens }
+}
 
 #[derive(Default, Debug)]
 pub(crate) struct AstSrc {


Test patch:
diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs
index a38689791c..b837387be1 100644
--- a/crates/parser/src/tests.rs
+++ b/crates/parser/src/tests.rs
@@ -9,7 +9,7 @@ use std::{
 
 use expect_test::expect_file;
 
-use crate::{LexedStr, TopEntryPoint};
+use crate::{Edition, LexedStr, TopEntryPoint};
 
 #[test]
 fn lex_ok() {
@@ -30,7 +30,7 @@ fn lex_err() {
 }
 
 fn lex(text: &str) -> String {
-    let lexed = LexedStr::new(text);
+    let lexed = LexedStr::new(Edition::CURRENT, text);
 
     let mut res = String::new();
     for i in 0..lexed.len() {
@@ -85,9 +85,9 @@ fn parse_inline_err() {
 }
 
 fn parse(entry: TopEntryPoint, text: &str) -> (String, bool) {
-    let lexed = LexedStr::new(text);
+    let lexed = LexedStr::new(Edition::CURRENT, text);
     let input = lexed.to_input();
-    let output = entry.parse(&input, crate::Edition::CURRENT);
+    let output = entry.parse(&input, Edition::CURRENT);
 
     let mut buf = String::new();
     let mut errors = Vec::new();
diff --git a/crates/parser/src/tests/prefix_entries.rs b/crates/parser/src/tests/prefix_entries.rs
index f92b39edb7..4d4ab345d9 100644
--- a/crates/parser/src/tests/prefix_entries.rs
+++ b/crates/parser/src/tests/prefix_entries.rs
@@ -1,4 +1,4 @@
-use crate::{LexedStr, PrefixEntryPoint, Step};
+use crate::{Edition, LexedStr, PrefixEntryPoint, Step};
 
 #[test]
 fn vis() {
@@ -82,11 +82,11 @@ fn meta_item() {
 
 #[track_caller]
 fn check(entry: PrefixEntryPoint, input: &str, prefix: &str) {
-    let lexed = LexedStr::new(input);
+    let lexed = LexedStr::new(Edition::CURRENT, input);
     let input = lexed.to_input();
 
     let mut n_tokens = 0;
-    for step in entry.parse(&input, crate::Edition::CURRENT).iter() {
+    for step in entry.parse(&input, Edition::CURRENT).iter() {
         match step {
             Step::Token { n_input_tokens, .. } => n_tokens += n_input_tokens as usize,
             Step::FloatSplit { .. } => n_tokens += 1,
diff --git a/crates/parser/test_data/parser/err/0011_extern_struct.rast b/crates/parser/test_data/parser/err/0011_extern_struct.rast
index bd5ec4b7c2..249bfeeeee 100644
--- a/crates/parser/test_data/parser/err/0011_extern_struct.rast
+++ b/crates/parser/test_data/parser/err/0011_extern_struct.rast
@@ -10,4 +10,4 @@ SOURCE_FILE
       IDENT "Foo"
     SEMICOLON ";"
   WHITESPACE "\n"
-error 6: expected existential, fn, trait or impl
+error 6: expected fn, trait or impl
diff --git a/crates/parser/test_data/parser/err/0042_weird_blocks.rast b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
index 1cdc6e6e71..d6d2e75cca 100644
--- a/crates/parser/test_data/parser/err/0042_weird_blocks.rast
+++ b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
@@ -69,7 +69,7 @@ SOURCE_FILE
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
-error 24: expected existential, fn, trait or impl
-error 41: expected existential, fn, trait or impl
+error 24: expected fn, trait or impl
+error 41: expected fn, trait or impl
 error 56: expected a block
 error 75: expected a loop or block
diff --git a/crates/parser/test_data/parser/err/0044_item_modifiers.rast b/crates/parser/test_data/parser/err/0044_item_modifiers.rast
index 96e471a69a..76464bf7cc 100644
--- a/crates/parser/test_data/parser/err/0044_item_modifiers.rast
+++ b/crates/parser/test_data/parser/err/0044_item_modifiers.rast
@@ -39,7 +39,7 @@ SOURCE_FILE
     L_CURLY "{"
     R_CURLY "}"
   WHITESPACE "\n"
-error 6: expected existential, fn, trait or impl
+error 6: expected fn, trait or impl
 error 38: expected a name
 error 40: missing type for `const` or `static`
 error 40: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast b/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
index 4b2a740362..a56d692335 100644
--- a/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
+++ b/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
@@ -11,5 +11,5 @@ SOURCE_FILE
       WHITESPACE " "
       STRING "\"C\""
   WHITESPACE "\n"
-error 10: expected existential, fn, trait or impl
-error 21: expected existential, fn, trait or impl
+error 10: expected fn, trait or impl
+error 21: expected fn, trait or impl
diff --git a/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rast b/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rast
new file mode 100644
index 0000000000..08a85891ed
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rast
@@ -0,0 +1,139 @@
+SOURCE_FILE
+  FN
+    VISIBILITY
+      PUB_KW "pub"
+    WHITESPACE " "
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "main"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "gen"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              ERROR
+                YIELD_KW "yield"
+              WHITESPACE " "
+              ERROR
+                STRING "\"\""
+              ERROR
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        ERROR
+          ASYNC_KW "async"
+        WHITESPACE " "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "gen"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              ERROR
+                YIELD_KW "yield"
+              WHITESPACE " "
+              ERROR
+                STRING "\"\""
+              ERROR
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          PATH_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "gen"
+        WHITESPACE " "
+        EXPR_STMT
+          CLOSURE_EXPR
+            MOVE_KW "move"
+        WHITESPACE " "
+        EXPR_STMT
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        ERROR
+          ASYNC_KW "async"
+        WHITESPACE " "
+        EXPR_STMT
+          PATH_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "gen"
+        WHITESPACE " "
+        EXPR_STMT
+          CLOSURE_EXPR
+            MOVE_KW "move"
+        WHITESPACE " "
+        EXPR_STMT
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 26: expected identifier
+error 31: expected COMMA
+error 32: expected identifier
+error 34: expected COMMA
+error 34: expected identifier
+error 48: expected fn, trait or impl
+error 55: expected identifier
+error 60: expected COMMA
+error 61: expected identifier
+error 63: expected COMMA
+error 63: expected identifier
+error 75: expected SEMICOLON
+error 80: expected `|`
+error 80: expected SEMICOLON
+error 105: expected fn, trait or impl
+error 109: expected SEMICOLON
+error 114: expected `|`
+error 114: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rs b/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rs
new file mode 100644
index 0000000000..f7687331d6
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0035_gen_blocks.rs
@@ -0,0 +1,6 @@
+pub fn main() {
+    gen { yield ""; };
+    async gen { yield ""; };
+    gen move { yield ""; };
+    async gen move { yield ""; };
+}
diff --git a/crates/parser/test_data/parser/inline/err/0036_gen_fn.rast b/crates/parser/test_data/parser/inline/err/0036_gen_fn.rast
new file mode 100644
index 0000000000..9609ece77d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0036_gen_fn.rast
@@ -0,0 +1,51 @@
+SOURCE_FILE
+  MACRO_CALL
+    PATH
+      PATH_SEGMENT
+        NAME_REF
+          IDENT "gen"
+  WHITESPACE " "
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "gen_fn"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
+  ERROR
+    ASYNC_KW "async"
+  WHITESPACE " "
+  MACRO_CALL
+    PATH
+      PATH_SEGMENT
+        NAME_REF
+          IDENT "gen"
+  WHITESPACE " "
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "async_gen_fn"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 3: expected BANG
+error 3: expected `{`, `[`, `(`
+error 3: expected SEMICOLON
+error 24: expected fn, trait or impl
+error 28: expected BANG
+error 28: expected `{`, `[`, `(`
+error 28: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/inline/err/0036_gen_fn.rs b/crates/parser/test_data/parser/inline/err/0036_gen_fn.rs
new file mode 100644
index 0000000000..80882e0a40
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0036_gen_fn.rs
@@ -0,0 +1,2 @@
+gen fn gen_fn() {}
+async gen fn async_gen_fn() {}
diff --git a/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast b/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast
deleted file mode 100644
index b73780261b..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast
+++ /dev/null
@@ -1,31 +0,0 @@
-SOURCE_FILE
-  TYPE_ALIAS
-    EXISTENTIAL_KW "existential"
-    WHITESPACE " "
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "Foo"
-    COLON ":"
-    WHITESPACE " "
-    TYPE_BOUND_LIST
-      TYPE_BOUND
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "Fn"
-              PARAM_LIST
-                L_PAREN "("
-                R_PAREN ")"
-              WHITESPACE " "
-              RET_TYPE
-                THIN_ARROW "->"
-                WHITESPACE " "
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "usize"
-    SEMICOLON ";"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs b/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs
deleted file mode 100644
index 23baf7145c..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-existential type Foo: Fn() -> usize;
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index 03b1117a5b..1080e8c993 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -9,7 +9,7 @@ use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv};
 
 fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream<TokenId> {
     crate::server_impl::TokenStream::with_subtree(
-        mbe::parse_to_token_tree_static_span(call_site, src).unwrap(),
+        mbe::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src).unwrap(),
     )
 }
 
@@ -19,7 +19,7 @@ fn parse_string_spanned(
     src: &str,
 ) -> crate::server_impl::TokenStream<Span> {
     crate::server_impl::TokenStream::with_subtree(
-        mbe::parse_to_token_tree(anchor, call_site, src).unwrap(),
+        mbe::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(),
     )
 }