Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs20
-rw-r--r--crates/hir-expand/src/name.rs7
-rw-r--r--crates/ide-completion/src/context.rs5
-rw-r--r--crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs4
-rw-r--r--crates/ide-db/src/syntax_helpers/node_ext.rs4
-rw-r--r--crates/ide/src/goto_definition.rs4
-rw-r--r--crates/ide/src/highlight_related.rs4
-rw-r--r--crates/ide/src/hover.rs3
-rw-r--r--crates/ide/src/hover/render.rs3
-rw-r--r--crates/ide/src/references.rs4
-rw-r--r--crates/ide/src/rename.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/macro_.rs3
-rw-r--r--crates/parser/src/lexed_str.rs13
-rw-r--r--crates/parser/src/shortcuts.rs10
-rw-r--r--crates/parser/src/syntax_kind/generated.rs145
-rw-r--r--crates/syntax-bridge/src/lib.rs3
-rw-r--r--crates/syntax-bridge/src/to_parser_input.rs10
-rw-r--r--crates/syntax/rust.ungram26
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/utils.rs4
-rw-r--r--xtask/src/codegen/grammar.rs113
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs74
23 files changed, 347 insertions, 129 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index b430e2cefb..7f76119251 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -25,7 +25,7 @@ use hir_expand::{
InFile, MacroFileId, MacroFileIdExt,
};
use intern::Symbol;
-use span::Span;
+use span::{Edition, Span};
use stdx::{format_to, format_to_acc};
use syntax::{
ast::{self, edit::IndentLevel},
@@ -257,21 +257,25 @@ fn pretty_print_macro_expansion(
(T![;] | T!['{'] | T!['}'], _) => "\n",
(_, T!['}']) => "\n",
(IDENT | LIFETIME_IDENT, IDENT | LIFETIME_IDENT) => " ",
- _ if prev_kind.is_keyword() && curr_kind.is_keyword() => " ",
- (IDENT, _) if curr_kind.is_keyword() => " ",
- (_, IDENT) if prev_kind.is_keyword() => " ",
+ _ if prev_kind.is_keyword(Edition::CURRENT)
+ && curr_kind.is_keyword(Edition::CURRENT) =>
+ {
+ " "
+ }
+ (IDENT, _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
+ (_, IDENT) if prev_kind.is_keyword(Edition::CURRENT) => " ",
(T![>], IDENT) => " ",
- (T![>], _) if curr_kind.is_keyword() => " ",
+ (T![>], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
(T![->], _) | (_, T![->]) => " ",
(T![&&], _) | (_, T![&&]) => " ",
(T![,], _) => " ",
(T![:], IDENT | T!['(']) => " ",
- (T![:], _) if curr_kind.is_keyword() => " ",
+ (T![:], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
(T![fn], T!['(']) => "",
- (T![']'], _) if curr_kind.is_keyword() => " ",
+ (T![']'], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
(T![']'], T![#]) => "\n",
(T![Self], T![::]) => "",
- _ if prev_kind.is_keyword() => " ",
+ _ if prev_kind.is_keyword(Edition::CURRENT) => " ",
_ => "",
};
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index d012d272d7..2a52aaba6a 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -104,14 +104,17 @@ impl Name {
/// Resolve a name from the text of token.
fn resolve(raw_text: &str) -> Name {
+ // FIXME: Edition
match raw_text.strip_prefix("r#") {
// When `raw_text` starts with "r#" but the name does not coincide with any
// keyword, we never need the prefix so we strip it.
- Some(text) if !is_raw_identifier(text) => Name::new_ref(text),
+ Some(text) if !is_raw_identifier(text, span::Edition::CURRENT) => Name::new_ref(text),
// Keywords (in the current edition) *can* be used as a name in earlier editions of
// Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
// escaped form.
- None if is_raw_identifier(raw_text) => Name::new_text(&format!("r#{}", raw_text)),
+ None if is_raw_identifier(raw_text, span::Edition::CURRENT) => {
+ Name::new_text(&format!("r#{}", raw_text))
+ }
_ => Name::new_text(raw_text),
}
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 952d9217c7..5b64a98a8b 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -15,7 +15,7 @@ use ide_db::{
};
use syntax::{
ast::{self, AttrKind, NameOrNameRef},
- AstNode, SmolStr,
+ AstNode, Edition, SmolStr,
SyntaxKind::{self, *},
SyntaxToken, TextRange, TextSize, T,
};
@@ -468,7 +468,8 @@ impl CompletionContext<'_> {
TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
}
IDENT | LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
- _ if kind.is_keyword() => self.original_token.text_range(),
+ // FIXME: Edition
+ _ if kind.is_keyword(Edition::CURRENT) => self.original_token.text_range(),
_ => TextRange::empty(self.position.offset),
}
}
diff --git a/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
index 97b6d4a572..6714e411e2 100644
--- a/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
@@ -1,4 +1,5 @@
//! Utilities for formatting macro expanded nodes until we get a proper formatter.
+use span::Edition;
use syntax::{
ast::make,
ted::{self, Position},
@@ -131,5 +132,6 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
}
fn is_text(k: SyntaxKind) -> bool {
- k.is_keyword() || k.is_literal() || k == IDENT || k == UNDERSCORE
+ // FIXME: Edition
+ k.is_keyword(Edition::CURRENT) || k.is_literal() || k == IDENT || k == UNDERSCORE
}
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs
index 37238cc61d..d10a55120a 100644
--- a/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -1,6 +1,7 @@
//! Various helper functions to work with SyntaxNodes.
use itertools::Itertools;
use parser::T;
+use span::Edition;
use syntax::{
ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
@@ -461,7 +462,8 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
let tokens =
input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
// seeing a keyword means the attribute is unclosed so stop parsing here
- Some(tok) if tok.kind().is_keyword() => None,
+ // FIXME: Edition
+ Some(tok) if tok.kind().is_keyword(Edition::CURRENT) => None,
// don't include the right token tree parenthesis if it exists
tok @ Some(_) if tok == r_paren => None,
// only nodes that we can find are other TokenTrees, those are unexpected in this parse though
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 8a8bc07945..b1d2b34ea4 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -17,7 +17,7 @@ use ide_db::{
};
use itertools::Itertools;
-use span::FileId;
+use span::{Edition, FileId};
use syntax::{
ast::{self, HasLoopBody},
match_ast, AstNode, AstToken,
@@ -55,7 +55,7 @@ pub(crate) fn goto_definition(
| COMMENT => 4,
// index and prefix ops
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
- kind if kind.is_keyword() => 2,
+ kind if kind.is_keyword(Edition::CURRENT) => 2,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 8fcd38b4e3..946dca85d4 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -11,7 +11,7 @@ use ide_db::{
},
FxHashMap, FxHashSet, RootDatabase,
};
-use span::EditionedFileId;
+use span::{Edition, EditionedFileId};
use syntax::{
ast::{self, HasLoopBody},
match_ast, AstNode,
@@ -65,7 +65,7 @@ pub(crate) fn highlight_related(
let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
T![->] => 4,
- kind if kind.is_keyword() => 3,
+ kind if kind.is_keyword(Edition::CURRENT) => 3,
IDENT | INT_NUMBER => 2,
T![|] => 1,
_ => 0,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 500674e32b..75f8ac2d2b 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -14,6 +14,7 @@ use ide_db::{
FileRange, FxIndexSet, RootDatabase,
};
use itertools::{multizip, Itertools};
+use span::Edition;
use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T};
use crate::{
@@ -140,7 +141,7 @@ fn hover_simple(
| T![_] => 4,
// index and prefix ops and closure pipe
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
- kind if kind.is_keyword() => 2,
+ kind if kind.is_keyword(Edition::CURRENT) => 2,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 7091b15b8a..13b7ba1e2b 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -20,6 +20,7 @@ use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
+use span::Edition;
use stdx::format_to;
use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T};
@@ -251,7 +252,7 @@ pub(super) fn keyword(
config: &HoverConfig,
token: &SyntaxToken,
) -> Option<HoverResult> {
- if !token.kind().is_keyword() || !config.documentation || !config.keywords {
+ if !token.kind().is_keyword(Edition::CURRENT) || !config.documentation || !config.keywords {
return None;
}
let parent = token.parent()?;
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 64b82b31c7..aef162bf0a 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -17,6 +17,7 @@ use ide_db::{
};
use itertools::Itertools;
use nohash_hasher::IntMap;
+use span::Edition;
use syntax::{
ast::{self, HasName},
match_ast, AstNode,
@@ -305,7 +306,8 @@ fn handle_control_flow_keywords(
FilePosition { file_id, offset }: FilePosition,
) -> Option<ReferenceSearchResult> {
let file = sema.parse_guess_edition(file_id);
- let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword())?;
+ let token =
+ file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword(Edition::CURRENT))?;
let references = match token.kind() {
T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token),
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 9581474ca7..ed0e3d89dd 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -6,6 +6,7 @@
use hir::{AsAssocItem, HirFileIdExt, InFile, Semantics};
use ide_db::{
+ base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
rename::{bail, format_err, source_edit_from_references, IdentifierKind},
source_change::SourceChangeBuilder,
@@ -162,11 +163,12 @@ pub(crate) fn will_rename_file(
let sema = Semantics::new(db);
let module = sema.file_to_module_def(file_id)?;
let def = Definition::Module(module);
- let mut change = if is_raw_identifier(new_name_stem) {
- def.rename(&sema, &SmolStr::from_iter(["r#", new_name_stem])).ok()?
- } else {
- def.rename(&sema, new_name_stem).ok()?
- };
+ let mut change =
+ if is_raw_identifier(new_name_stem, db.crate_graph()[module.krate().into()].edition) {
+ def.rename(&sema, &SmolStr::from_iter(["r#", new_name_stem])).ok()?
+ } else {
+ def.rename(&sema, new_name_stem).ok()?
+ };
change.file_system_edits.clear();
Some(change)
}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 291073f877..70fdc0a1b3 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -6,6 +6,7 @@ use ide_db::{
defs::{Definition, IdentClass, NameClass, NameRefClass},
FxHashMap, RootDatabase, SymbolKind,
};
+use span::Edition;
use stdx::hash_once;
use syntax::{
ast, match_ast, AstNode, AstToken, NodeOrToken,
@@ -41,7 +42,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O
HlTag::None.into()
}
p if p.is_punct() => punctuation(sema, token, p),
- k if k.is_keyword() => keyword(sema, token, k)?,
+ k if k.is_keyword(Edition::CURRENT) => keyword(sema, token, k)?,
_ => return None,
};
Some(highlight)
diff --git a/crates/ide/src/syntax_highlighting/macro_.rs b/crates/ide/src/syntax_highlighting/macro_.rs
index 1099d9c23b..460fc4fe14 100644
--- a/crates/ide/src/syntax_highlighting/macro_.rs
+++ b/crates/ide/src/syntax_highlighting/macro_.rs
@@ -1,4 +1,5 @@
//! Syntax highlighting for macro_rules!.
+use span::Edition;
use syntax::{SyntaxKind, SyntaxToken, TextRange, T};
use crate::{HlRange, HlTag};
@@ -117,7 +118,7 @@ fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
fn is_metavariable(token: &SyntaxToken) -> Option<TextRange> {
match token.kind() {
- kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
+ kind if kind == SyntaxKind::IDENT || kind.is_keyword(Edition::CURRENT) => {
if let Some(_dollar) = token.prev_token().filter(|t| t.kind() == T![$]) {
return Some(token.text_range());
}
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 13fc61074d..ff924830ae 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -178,19 +178,8 @@ 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)
+ SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT)
}
rustc_lexer::TokenKind::InvalidPrefix | rustc_lexer::TokenKind::InvalidIdent => {
err = "Ident contains invalid characters";
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs
index 1cf81e79b0..7adedba7c4 100644
--- a/crates/parser/src/shortcuts.rs
+++ b/crates/parser/src/shortcuts.rs
@@ -35,12 +35,10 @@ impl LexedStr<'_> {
was_joint = false
} else if kind == SyntaxKind::IDENT {
let token_text = self.text(i);
- let contextual_kw = if !edition.at_least_2018() && token_text == "dyn" {
- SyntaxKind::DYN_KW
- } else {
- SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT)
- };
- res.push_ident(contextual_kw);
+ res.push_ident(
+ SyntaxKind::from_contextual_keyword(token_text, edition)
+ .unwrap_or(SyntaxKind::IDENT),
+ )
} else {
if was_joint {
res.was_joint();
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 7bddf88740..eaacd88e3e 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -1,6 +1,7 @@
//! Generated by `cargo codegen grammar`, do not edit by hand.
#![allow(bad_style, missing_docs, unreachable_pub)]
+use crate::Edition;
#[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`."]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u16)]
@@ -64,8 +65,6 @@ pub enum SyntaxKind {
SELF_TYPE_KW,
ABSTRACT_KW,
AS_KW,
- ASYNC_KW,
- AWAIT_KW,
BECOME_KW,
BOX_KW,
BREAK_KW,
@@ -73,7 +72,6 @@ pub enum SyntaxKind {
CONTINUE_KW,
CRATE_KW,
DO_KW,
- DYN_KW,
ELSE_KW,
ENUM_KW,
EXTERN_KW,
@@ -81,7 +79,6 @@ pub enum SyntaxKind {
FINAL_KW,
FN_KW,
FOR_KW,
- GEN_KW,
IF_KW,
IMPL_KW,
IN_KW,
@@ -103,7 +100,6 @@ pub enum SyntaxKind {
SUPER_KW,
TRAIT_KW,
TRUE_KW,
- TRY_KW,
TYPE_KW,
TYPEOF_KW,
UNSAFE_KW,
@@ -114,13 +110,18 @@ pub enum SyntaxKind {
WHILE_KW,
YIELD_KW,
ASM_KW,
+ ASYNC_KW,
AUTO_KW,
+ AWAIT_KW,
BUILTIN_KW,
DEFAULT_KW,
+ DYN_KW,
FORMAT_ARGS_KW,
+ GEN_KW,
MACRO_RULES_KW,
OFFSET_OF_KW,
RAW_KW,
+ TRY_KW,
UNION_KW,
YEET_KW,
BYTE,
@@ -296,14 +297,14 @@ pub enum SyntaxKind {
}
use self::SyntaxKind::*;
impl SyntaxKind {
- pub fn is_keyword(self) -> bool {
+ #[doc = r" Checks whether this syntax kind is a strict keyword for the given edition."]
+ #[doc = r" Strict keywords are identifiers that are always considered keywords."]
+ pub fn is_strict_keyword(self, edition: Edition) -> bool {
matches!(
self,
SELF_TYPE_KW
| ABSTRACT_KW
| AS_KW
- | ASYNC_KW
- | AWAIT_KW
| BECOME_KW
| BOX_KW
| BREAK_KW
@@ -311,7 +312,6 @@ impl SyntaxKind {
| CONTINUE_KW
| CRATE_KW
| DO_KW
- | DYN_KW
| ELSE_KW
| ENUM_KW
| EXTERN_KW
@@ -319,7 +319,6 @@ impl SyntaxKind {
| FINAL_KW
| FN_KW
| FOR_KW
- | GEN_KW
| IF_KW
| IMPL_KW
| IN_KW
@@ -341,7 +340,6 @@ impl SyntaxKind {
| SUPER_KW
| TRAIT_KW
| TRUE_KW
- | TRY_KW
| TYPE_KW
| TYPEOF_KW
| UNSAFE_KW
@@ -351,17 +349,103 @@ impl SyntaxKind {
| WHERE_KW
| WHILE_KW
| YIELD_KW
- | ASM_KW
- | AUTO_KW
- | BUILTIN_KW
- | DEFAULT_KW
- | FORMAT_ARGS_KW
- | MACRO_RULES_KW
- | OFFSET_OF_KW
- | RAW_KW
- | UNION_KW
- | YEET_KW
- )
+ ) || match self {
+ ASYNC_KW if Edition::Edition2018 <= edition => true,
+ AWAIT_KW if Edition::Edition2018 <= edition => true,
+ DYN_KW if Edition::Edition2018 <= edition => true,
+ GEN_KW if Edition::Edition2024 <= edition => true,
+ TRY_KW if Edition::Edition2018 <= edition => true,
+ _ => false,
+ }
+ }
+ #[doc = r" Checks whether this syntax kind is a weak keyword for the given edition."]
+ #[doc = r" Weak keywords are identifiers that are considered keywords only in certain contexts."]
+ pub fn is_contextual_keyword(self, edition: Edition) -> bool {
+ match self {
+ ASM_KW => true,
+ AUTO_KW => true,
+ BUILTIN_KW => true,
+ DEFAULT_KW => true,
+ DYN_KW if edition < Edition::Edition2018 => true,
+ FORMAT_ARGS_KW => true,
+ MACRO_RULES_KW => true,
+ OFFSET_OF_KW => true,
+ RAW_KW => true,
+ UNION_KW => true,
+ YEET_KW => true,
+ _ => false,
+ }
+ }
+ #[doc = r" Checks whether this syntax kind is a strict or weak keyword for the given edition."]
+ pub fn is_keyword(self, edition: Edition) -> bool {
+ matches!(
+ self,
+ SELF_TYPE_KW
+ | ABSTRACT_KW
+ | AS_KW
+ | BECOME_KW
+ | BOX_KW
+ | BREAK_KW
+ | CONST_KW
+ | CONTINUE_KW
+ | CRATE_KW
+ | DO_KW
+ | ELSE_KW
+ | ENUM_KW
+ | EXTERN_KW
+ | FALSE_KW
+ | FINAL_KW
+ | FN_KW
+ | FOR_KW
+ | IF_KW
+ | IMPL_KW
+ | IN_KW
+ | LET_KW
+ | LOOP_KW
+ | MACRO_KW
+ | MATCH_KW
+ | MOD_KW
+ | MOVE_KW
+ | MUT_KW
+ | OVERRIDE_KW
+ | PRIV_KW
+ | PUB_KW
+ | REF_KW
+ | RETURN_KW
+ | SELF_KW
+ | STATIC_KW
+ | STRUCT_KW
+ | SUPER_KW
+ | TRAIT_KW
+ | TRUE_KW
+ | TYPE_KW
+ | TYPEOF_KW
+ | UNSAFE_KW
+ | UNSIZED_KW
+ | USE_KW
+ | VIRTUAL_KW
+ | WHERE_KW
+ | WHILE_KW
+ | YIELD_KW
+ ) || match self {
+ ASYNC_KW if Edition::Edition2018 <= edition => true,
+ AWAIT_KW if Edition::Edition2018 <= edition => true,
+ DYN_KW if Edition::Edition2018 <= edition => true,
+ GEN_KW if Edition::Edition2024 <= edition => true,
+ TRY_KW if Edition::Edition2018 <= edition => true,
+ ASM_KW => true,
+ AUTO_KW => true,
+ BUILTIN_KW => true,
+ DEFAULT_KW => true,
+ DYN_KW if edition < Edition::Edition2018 => true,
+ FORMAT_ARGS_KW => true,
+ MACRO_RULES_KW => true,
+ OFFSET_OF_KW => true,
+ RAW_KW => true,
+ UNION_KW => true,
+ YEET_KW => true,
+ _ => false,
+ }
}
pub fn is_punct(self) -> bool {
matches!(
@@ -434,13 +518,11 @@ impl SyntaxKind {
| STRING
)
}
- pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+ pub fn from_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
"Self" => SELF_TYPE_KW,
"abstract" => ABSTRACT_KW,
"as" => AS_KW,
- "async" => ASYNC_KW,
- "await" => AWAIT_KW,
"become" => BECOME_KW,
"box" => BOX_KW,
"break" => BREAK_KW,
@@ -448,7 +530,6 @@ impl SyntaxKind {
"continue" => CONTINUE_KW,
"crate" => CRATE_KW,
"do" => DO_KW,
- "dyn" => DYN_KW,
"else" => ELSE_KW,
"enum" => ENUM_KW,
"extern" => EXTERN_KW,
@@ -456,7 +537,6 @@ impl SyntaxKind {
"final" => FINAL_KW,
"fn" => FN_KW,
"for" => FOR_KW,
- "gen" => GEN_KW,
"if" => IF_KW,
"impl" => IMPL_KW,
"in" => IN_KW,
@@ -478,7 +558,6 @@ impl SyntaxKind {
"super" => SUPER_KW,
"trait" => TRAIT_KW,
"true" => TRUE_KW,
- "try" => TRY_KW,
"type" => TYPE_KW,
"typeof" => TYPEOF_KW,
"unsafe" => UNSAFE_KW,
@@ -488,16 +567,22 @@ impl SyntaxKind {
"where" => WHERE_KW,
"while" => WHILE_KW,
"yield" => YIELD_KW,
+ "async" if Edition::Edition2018 <= edition => ASYNC_KW,
+ "await" if Edition::Edition2018 <= edition => AWAIT_KW,
+ "dyn" if Edition::Edition2018 <= edition => DYN_KW,
+ "gen" if Edition::Edition2024 <= edition => GEN_KW,
+ "try" if Edition::Edition2018 <= edition => TRY_KW,
_ => return None,
};
Some(kw)
}
- pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
+ pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
"asm" => ASM_KW,
"auto" => AUTO_KW,
"builtin" => BUILTIN_KW,
"default" => DEFAULT_KW,
+ "dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
"macro_rules" => MACRO_RULES_KW,
"offset_of" => OFFSET_OF_KW,
@@ -544,4 +629,4 @@ impl SyntaxKind {
}
}
#[macro_export]
-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 } ; }
+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 } ; [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 } ; [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 } ; [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 } ; [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 } ; [dyn] => { $ crate :: SyntaxKind :: DYN_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 } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_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/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs
index b0afd245c5..5153db2372 100644
--- a/crates/syntax-bridge/src/lib.rs
+++ b/crates/syntax-bridge/src/lib.rs
@@ -307,7 +307,8 @@ where
tt::Ident::new(&text, conv.span_for(abs_range)).into()
}
UNDERSCORE => make_ident!(),
- k if k.is_keyword() => make_ident!(),
+ // FIXME: Edition
+ k if k.is_keyword(Edition::CURRENT) => make_ident!(),
k if k.is_literal() => {
let text = token.to_text(conv);
let span = conv.span_for(abs_range);
diff --git a/crates/syntax-bridge/src/to_parser_input.rs b/crates/syntax-bridge/src/to_parser_input.rs
index 2c54899268..14216e3093 100644
--- a/crates/syntax-bridge/src/to_parser_input.rs
+++ b/crates/syntax-bridge/src/to_parser_input.rs
@@ -64,14 +64,12 @@ pub fn to_parser_input<S: Copy + fmt::Debug>(
"_" => res.push(T![_]),
i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
_ if ident.is_raw.yes() => res.push(IDENT),
- "gen" if !edition.at_least_2024() => res.push(IDENT),
- "dyn" if !edition.at_least_2018() => res.push_ident(DYN_KW),
- "async" | "await" | "try" if !edition.at_least_2018() => res.push(IDENT),
- text => match SyntaxKind::from_keyword(text) {
+ text => match SyntaxKind::from_keyword(text, edition) {
Some(kind) => res.push(kind),
None => {
- let contextual_keyword = SyntaxKind::from_contextual_keyword(text)
- .unwrap_or(SyntaxKind::IDENT);
+ let contextual_keyword =
+ SyntaxKind::from_contextual_keyword(text, edition)
+ .unwrap_or(SyntaxKind::IDENT);
res.push_ident(contextual_keyword);
}
},
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index c23bcd6914..069be2df3a 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -9,8 +9,6 @@
// // -- comment
// Name = -- non-terminal definition
// 'ident' -- keyword or punct token (terminal)
-// '?ident' -- contextual keyword (terminal)
-// too)
// '#ident' -- generic token (terminal)
// '@ident' -- literal token (terminal)
// A B -- sequence
@@ -152,7 +150,7 @@ Item =
MacroRules =
Attr* Visibility?
- '?macro_rules' '!' Name
+ 'macro_rules' '!' Name
TokenTree
MacroDef =
@@ -188,7 +186,7 @@ UseTreeList =
Fn =
Attr* Visibility?
- '?default'? 'const'? 'async'? 'unsafe'? Abi?
+ 'default'? 'const'? 'async'? 'unsafe'? Abi?
'fn' Name GenericParamList? ParamList RetType? WhereClause?
(body:BlockExpr | ';')
@@ -220,7 +218,7 @@ RetType =
TypeAlias =
Attr* Visibility?
- '?default'?
+ 'default'?
'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause?
('=' Type)? ';'
@@ -263,7 +261,7 @@ Variant =
Union =
Attr* Visibility?
- '?union' Name GenericParamList? WhereClause?
+ 'union' Name GenericParamList? WhereClause?
RecordFieldList
// A Data Type.
@@ -276,7 +274,7 @@ Adt =
Const =
Attr* Visibility?
- '?default'?
+ 'default'?
'const' (Name | '_') ':' Type
('=' body:Expr)? ';'
@@ -287,7 +285,7 @@ Static =
Trait =
Attr* Visibility?
- 'unsafe'? '?auto'?
+ 'unsafe'? 'auto'?
'trait' Name GenericParamList?
(':' TypeBoundList?)? WhereClause? AssocItemList
@@ -306,7 +304,7 @@ AssocItem =
Impl =
Attr* Visibility?
- '?default'? 'unsafe'?
+ 'default'? 'unsafe'?
'impl' GenericParamList? ('const'? '!'? trait:Type 'for')? self_ty:Type WhereClause?
AssocItemList
@@ -387,13 +385,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)* ','?)? )?
')'
@@ -425,7 +423,7 @@ StmtList =
'}'
RefExpr =
- Attr* '&' (('?raw' 'const'?)| ('?raw'? 'mut') ) Expr
+ Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr
TryExpr =
Attr* Expr '?'
@@ -550,7 +548,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/make.rs b/crates/syntax/src/ast/make.rs
index 0228d9dd71..645575c25e 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -117,7 +117,7 @@ pub fn name_ref(name_ref: &str) -> ast::NameRef {
ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
}
fn raw_ident_esc(ident: &str) -> &'static str {
- if is_raw_identifier(ident) {
+ if is_raw_identifier(ident, Edition::CURRENT) {
"r#"
} else {
""
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
index a38f8b2b55..77d49b442e 100644
--- a/crates/syntax/src/utils.rs
+++ b/crates/syntax/src/utils.rs
@@ -2,7 +2,7 @@
use crate::SyntaxKind;
-pub fn is_raw_identifier(name: &str) -> bool {
- let is_keyword = SyntaxKind::from_keyword(name).is_some();
+pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool {
+ let is_keyword = SyntaxKind::from_keyword(name, edition).is_some();
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
}
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 0352539754..39e06f9642 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -396,24 +396,66 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
let punctuation =
grammar.punct.iter().map(|(_token, name)| format_ident!("{}", name)).collect::<Vec<_>>();
- let x = |&name| match name {
+ let fmt_kw_as_variant = |&name| match name {
"Self" => format_ident!("SELF_TYPE_KW"),
name => format_ident!("{}_KW", to_upper_snake_case(name)),
};
- let full_keywords_values = grammar.keywords;
- let full_keywords = full_keywords_values.iter().map(x);
+ let strict_keywords = grammar.keywords;
+ let strict_keywords_variants =
+ strict_keywords.iter().map(fmt_kw_as_variant).collect::<Vec<_>>();
+ let strict_keywords_tokens = strict_keywords.iter().map(|it| format_ident!("{it}"));
- let contextual_keywords_values = &grammar.contextual_keywords;
- let contextual_keywords = contextual_keywords_values.iter().map(x);
+ let edition_dependent_keywords_variants_match_arm = grammar
+ .edition_dependent_keywords
+ .iter()
+ .map(|(kw, ed)| {
+ let kw = fmt_kw_as_variant(kw);
+ quote! { #kw if #ed <= edition }
+ })
+ .collect::<Vec<_>>();
+ let edition_dependent_keywords_str_match_arm = grammar
+ .edition_dependent_keywords
+ .iter()
+ .map(|(kw, ed)| {
+ quote! { #kw if #ed <= edition }
+ })
+ .collect::<Vec<_>>();
+ let edition_dependent_keywords_variants = grammar
+ .edition_dependent_keywords
+ .iter()
+ .map(|(kw, _)| fmt_kw_as_variant(kw))
+ .collect::<Vec<_>>();
+ let edition_dependent_keywords_tokens =
+ grammar.edition_dependent_keywords.iter().map(|(it, _)| format_ident!("{it}"));
+
+ let contextual_keywords = grammar.contextual_keywords;
+ let contextual_keywords_variants =
+ contextual_keywords.iter().map(fmt_kw_as_variant).collect::<Vec<_>>();
+ let contextual_keywords_tokens = contextual_keywords.iter().map(|it| format_ident!("{it}"));
+ let contextual_keywords_str_match_arm = grammar.contextual_keywords.iter().map(|kw| {
+ match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw) {
+ Some((_, ed)) => quote! { #kw if edition < #ed },
+ None => quote! { #kw },
+ }
+ });
+ let contextual_keywords_variants_match_arm = grammar
+ .contextual_keywords
+ .iter()
+ .map(|kw_s| {
+ let kw = fmt_kw_as_variant(kw_s);
+ match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw_s) {
+ Some((_, ed)) => quote! { #kw if edition < #ed },
+ None => quote! { #kw },
+ }
+ })
+ .collect::<Vec<_>>();
- let all_keywords_values = grammar
- .keywords
+ let non_strict_keyword_variants = contextual_keywords_variants
.iter()
- .chain(grammar.contextual_keywords.iter())
- .copied()
+ .chain(edition_dependent_keywords_variants.iter())
+ .sorted()
+ .dedup()
.collect::<Vec<_>>();
- let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
- let all_keywords = all_keywords_values.iter().map(x).collect::<Vec<_>>();
let literals =
grammar.literals.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
@@ -424,6 +466,8 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
let ast = quote! {
#![allow(bad_style, missing_docs, unreachable_pub)]
+ use crate::Edition;
+
/// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u16)]
@@ -435,7 +479,8 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
#[doc(hidden)]
EOF,
#(#punctuation,)*
- #(#all_keywords,)*
+ #(#strict_keywords_variants,)*
+ #(#non_strict_keyword_variants,)*
#(#literals,)*
#(#tokens,)*
#(#nodes,)*
@@ -447,31 +492,55 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
use self::SyntaxKind::*;
impl SyntaxKind {
- pub fn is_keyword(self) -> bool {
- matches!(self, #(#all_keywords)|*)
+ /// Checks whether this syntax kind is a strict keyword for the given edition.
+ /// Strict keywords are identifiers that are always considered keywords.
+ pub fn is_strict_keyword(self, edition: Edition) -> bool {
+ matches!(self, #(#strict_keywords_variants)|*)
+ || match self {
+ #(#edition_dependent_keywords_variants_match_arm => true,)*
+ _ => false,
+ }
}
- pub fn is_punct(self) -> bool {
+ /// Checks whether this syntax kind is a weak keyword for the given edition.
+ /// Weak keywords are identifiers that are considered keywords only in certain contexts.
+ pub fn is_contextual_keyword(self, edition: Edition) -> bool {
+ match self {
+ #(#contextual_keywords_variants_match_arm => true,)*
+ _ => false,
+ }
+ }
- matches!(self, #(#punctuation)|*)
+ /// Checks whether this syntax kind is a strict or weak keyword for the given edition.
+ pub fn is_keyword(self, edition: Edition) -> bool {
+ matches!(self, #(#strict_keywords_variants)|*)
+ || match self {
+ #(#edition_dependent_keywords_variants_match_arm => true,)*
+ #(#contextual_keywords_variants_match_arm => true,)*
+ _ => false,
+ }
+ }
+ pub fn is_punct(self) -> bool {
+ matches!(self, #(#punctuation)|*)
}
pub fn is_literal(self) -> bool {
matches!(self, #(#literals)|*)
}
- pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+ pub fn from_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
- #(#full_keywords_values => #full_keywords,)*
+ #(#strict_keywords => #strict_keywords_variants,)*
+ #(#edition_dependent_keywords_str_match_arm => #edition_dependent_keywords_variants,)*
_ => return None,
};
Some(kw)
}
- pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
+ pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
- #(#contextual_keywords_values => #contextual_keywords,)*
+ #(#contextual_keywords_str_match_arm => #contextual_keywords_variants,)*
_ => return None,
};
Some(kw)
@@ -489,7 +558,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
#[macro_export]
macro_rules! T {
#([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*
- #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*
+ #([#strict_keywords_tokens] => { $crate::SyntaxKind::#strict_keywords_variants };)*
+ #([#contextual_keywords_tokens] => { $crate::SyntaxKind::#contextual_keywords_variants };)*
+ #([#edition_dependent_keywords_tokens] => { $crate::SyntaxKind::#edition_dependent_keywords_variants };)*
[lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
[int_number] => { $crate::SyntaxKind::INT_NUMBER };
[ident] => { $crate::SyntaxKind::IDENT };
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index 3444f89908..34151bd958 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -1,5 +1,7 @@
//! Defines input for code generation process.
+use quote::ToTokens;
+
use crate::codegen::grammar::to_upper_snake_case;
#[derive(Copy, Clone, Debug)]
@@ -10,6 +12,35 @@ pub(crate) struct KindsSrc {
pub(crate) literals: &'static [&'static str],
pub(crate) tokens: &'static [&'static str],
pub(crate) nodes: &'static [&'static str],
+ pub(crate) edition_dependent_keywords: &'static [(&'static str, Edition)],
+}
+
+#[allow(dead_code)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub(super) enum Edition {
+ Edition2015,
+ Edition2018,
+ Edition2021,
+ Edition2024,
+}
+
+impl ToTokens for Edition {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ match self {
+ Edition::Edition2015 => {
+ tokens.extend(quote::quote! { Edition::Edition2015 });
+ }
+ Edition::Edition2018 => {
+ tokens.extend(quote::quote! { Edition::Edition2018 });
+ }
+ Edition::Edition2021 => {
+ tokens.extend(quote::quote! { Edition::Edition2021 });
+ }
+ Edition::Edition2024 => {
+ tokens.extend(quote::quote! { Edition::Edition2024 });
+ }
+ }
+ }
}
/// The punctuations of the language.
@@ -75,17 +106,32 @@ const EOF: &str = "EOF";
const RESERVED: &[&str] = &[
"abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized",
- "virtual", "yield", "try",
+ "virtual", "yield",
+];
+// keywords that are keywords only in specific parse contexts
+#[doc(alias = "WEAK_KEYWORDS")]
+const CONTEXTUAL_KEYWORDS: &[&str] =
+ &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
+// keywords we use for special macro expansions
+const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"];
+// keywords that are keywords depending on the edition
+const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[
+ ("try", Edition::Edition2018),
+ ("dyn", Edition::Edition2018),
+ ("async", Edition::Edition2018),
+ ("await", Edition::Edition2018),
+ ("gen", Edition::Edition2024),
];
-const CONTEXTUAL_RESERVED: &[&str] = &[];
pub(crate) fn generate_kind_src(
nodes: &[AstNodeSrc],
enums: &[AstEnumSrc],
grammar: &ungrammar::Grammar,
) -> KindsSrc {
+ let mut contextual_keywords: Vec<&_> =
+ CONTEXTUAL_KEYWORDS.iter().chain(CONTEXTUAL_BUILTIN_KEYWORDS).copied().collect();
+
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()];
@@ -103,9 +149,7 @@ pub(crate) fn generate_kind_src(
("#", 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 contextual_keywords.contains(&name) => {}
_ if name.chars().all(char::is_alphabetic) => {
keywords.push(String::leak(name.to_owned()));
}
@@ -124,9 +168,14 @@ pub(crate) fn generate_kind_src(
keywords.extend(RESERVED.iter().copied());
keywords.sort();
keywords.dedup();
- contextual_keywords.extend(CONTEXTUAL_RESERVED.iter().copied());
contextual_keywords.sort();
contextual_keywords.dedup();
+ let mut edition_dependent_keywords: Vec<(&_, _)> = EDITION_DEPENDENT_KEYWORDS.to_vec();
+ edition_dependent_keywords.sort();
+ edition_dependent_keywords.dedup();
+
+ keywords.retain(|&it| !contextual_keywords.contains(&it));
+ keywords.retain(|&it| !edition_dependent_keywords.iter().any(|&(kw, _)| kw == it));
// 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
@@ -142,12 +191,21 @@ pub(crate) fn generate_kind_src(
nodes.sort();
let keywords = Vec::leak(keywords);
let contextual_keywords = Vec::leak(contextual_keywords);
+ let edition_dependent_keywords = Vec::leak(edition_dependent_keywords);
let literals = Vec::leak(literals);
literals.sort();
let tokens = Vec::leak(tokens);
tokens.sort();
- KindsSrc { punct: PUNCT, nodes, keywords, contextual_keywords, literals, tokens }
+ KindsSrc {
+ punct: PUNCT,
+ nodes,
+ keywords,
+ contextual_keywords,
+ edition_dependent_keywords,
+ literals,
+ tokens,
+ }
}
#[derive(Default, Debug)]