Repository: rust-lang/rust-analyzer (2026-04-14T16:17:11Z)

Issue:
Syntax error when using 'type const'
<!--
Troubleshooting guide: https://rust-analyzer.github.io/book/troubleshooting.html
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->

**rust-analyzer version**: : 0.3.2862-standalone (7b6e1249b7 2026-04-12)

**rustc version**: rustc 1.97.0-nightly (17584a181 2026-04-13)

**editor or extension**: Zed (0.231.2 cc335b70f85a17974a4c61f852dbebff8c4b1db7)
(I checked it in VSCode as well, and the same error occurred.)

**code snippet to reproduce**:
```rust
#![feature(min_generic_const_args)]
#![allow(incomplete_features, unused)]

type const FOO: i32 = 2i32;

fn main() {}
```

The code is compilable, but 8 syntax errors occur.

<img width="1273" height="410" alt="Image" src="https://github.com/user-attachments/assets/3fd0db77-de65-4e09-b40b-11cb9dd46f92" />


Discussion:
Supporting this is really not hard: you have to add `'type'?` in rust.ungram under `Const`, parse them in `opt_item()` in https://github.com/rust-lang/rust-analyzer/blob/f53911152a931648119710cbadb3244603343dfd/crates/parser/src/grammar/items.rs, with a test like you see there, then run `cargo xtask codegen` to generate the tests and the nodes, and finally `UPDATE_EXPECT=1` to update the test output.



Patch:
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 6c46fac29076..c5c6e04dd49a 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -235,6 +235,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res
         T![trait] => traits::trait_(p, m),
         T![impl] => traits::impl_(p, m),
 
+        T![type] if p.nth(1) == T![const] => consts::konst(p, m),
         T![type] => type_alias(p, m),
 
         // test extern_block
@@ -266,6 +267,9 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
         T![use] => use_item::use_(p, m),
         T![mod] => mod_item(p, m),
 
+        // test type_const
+        // type const FOO: i32 = 2;
+        T![type] if la == T![const] => consts::konst(p, m),
         T![type] => type_alias(p, m),
         T![struct] => adt::strukt(p, m),
         T![enum] => adt::enum_(p, m),
diff --git a/crates/parser/src/grammar/items/consts.rs b/crates/parser/src/grammar/items/consts.rs
index e6a8aca5861a..cc5bb73bdcab 100644
--- a/crates/parser/src/grammar/items/consts.rs
+++ b/crates/parser/src/grammar/items/consts.rs
@@ -3,6 +3,7 @@ use super::*;
 // test const_item
 // const C: u32 = 92;
 pub(super) fn konst(p: &mut Parser<'_>, m: Marker) {
+    p.eat(T![type]);
     p.bump(T![const]);
     const_or_static(p, m, true);
 }
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index caf92aca8774..768cf2013d65 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -328,6 +328,7 @@ VariantDef =
 Const =
   Attr* Visibility?
   'default'?
+  'type'?
   'const' (Name | '_') GenericParamList? ':' Type
   ('=' body:Expr)?
   WhereClause? ';'
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 69fd703c0398..9a2bba9ebf0d 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -484,6 +484,8 @@ impl Const {
     pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
     #[inline]
     pub fn default_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![default]) }
+    #[inline]
+    pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![type]) }
 }
 pub struct ConstArg {
     pub(crate) syntax: SyntaxNode,


Test patch:
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 10b1f85d3911..6dfb78b12878 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -688,6 +688,8 @@ mod ok {
     #[test]
     fn type_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/type_alias.rs"); }
     #[test]
+    fn type_const() { run_and_expect_no_errors("test_data/parser/inline/ok/type_const.rs"); }
+    #[test]
     fn type_item_type_params() {
         run_and_expect_no_errors("test_data/parser/inline/ok/type_item_type_params.rs");
     }
diff --git a/crates/parser/test_data/parser/inline/ok/type_const.rast b/crates/parser/test_data/parser/inline/ok/type_const.rast
new file mode 100644
index 000000000000..9ceae9e44b3a
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/type_const.rast
@@ -0,0 +1,22 @@
+SOURCE_FILE
+  CONST
+    TYPE_KW "type"
+    WHITESPACE " "
+    CONST_KW "const"
+    WHITESPACE " "
+    NAME
+      IDENT "FOO"
+    COLON ":"
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          NAME_REF
+            IDENT "i32"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    LITERAL
+      INT_NUMBER "2"
+    SEMICOLON ";"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/type_const.rs b/crates/parser/test_data/parser/inline/ok/type_const.rs
new file mode 100644
index 000000000000..8e2c4259227a
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/type_const.rs
@@ -0,0 +1,1 @@
+type const FOO: i32 = 2;