From f90bdfc13d6005fba0b8747a58dac8d11ce73b78 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 15 Aug 2024 14:50:57 +0200 Subject: [PATCH] internal: Properly check the edition for edition dependent syntax kinds --- .../hir-def/src/macro_expansion_tests/mod.rs | 20 ++- crates/hir-expand/src/name.rs | 7 +- crates/ide-completion/src/context.rs | 5 +- .../insert_whitespace_into_node.rs | 4 +- crates/ide-db/src/syntax_helpers/node_ext.rs | 4 +- crates/ide/src/goto_definition.rs | 4 +- crates/ide/src/highlight_related.rs | 4 +- crates/ide/src/hover.rs | 3 +- crates/ide/src/hover/render.rs | 3 +- crates/ide/src/references.rs | 4 +- crates/ide/src/rename.rs | 12 +- .../ide/src/syntax_highlighting/highlight.rs | 3 +- crates/ide/src/syntax_highlighting/macro_.rs | 3 +- crates/parser/src/lexed_str.rs | 13 +- crates/parser/src/shortcuts.rs | 10 +- crates/parser/src/syntax_kind/generated.rs | 145 ++++++++++++++---- crates/syntax-bridge/src/lib.rs | 3 +- crates/syntax-bridge/src/to_parser_input.rs | 10 +- crates/syntax/rust.ungram | 26 ++-- crates/syntax/src/ast/make.rs | 2 +- crates/syntax/src/utils.rs | 4 +- xtask/src/codegen/grammar.rs | 115 +++++++++++--- xtask/src/codegen/grammar/ast_src.rs | 74 ++++++++- 23 files changed, 348 insertions(+), 130 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 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 { - 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 { 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 { 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 { + pub fn from_keyword(ident: &str, edition: Edition) -> Option { 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 { + pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option { 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( "_" => 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::>(); - 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::>(); + 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 all_keywords_values = grammar - .keywords + let edition_dependent_keywords_variants_match_arm = grammar + .edition_dependent_keywords .iter() - .chain(grammar.contextual_keywords.iter()) - .copied() + .map(|(kw, ed)| { + let kw = fmt_kw_as_variant(kw); + quote! { #kw if #ed <= edition } + }) + .collect::>(); + let edition_dependent_keywords_str_match_arm = grammar + .edition_dependent_keywords + .iter() + .map(|(kw, ed)| { + quote! { #kw if #ed <= edition } + }) + .collect::>(); + let edition_dependent_keywords_variants = grammar + .edition_dependent_keywords + .iter() + .map(|(kw, _)| fmt_kw_as_variant(kw)) + .collect::>(); + 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::>(); + 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::>(); + + let non_strict_keyword_variants = contextual_keywords_variants + .iter() + .chain(edition_dependent_keywords_variants.iter()) + .sorted() + .dedup() .collect::>(); - let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw)); - let all_keywords = all_keywords_values.iter().map(x).collect::>(); let literals = grammar.literals.iter().map(|name| format_ident!("{}", name)).collect::>(); @@ -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, + } + } + + /// 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, + } + } + + /// 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 { + pub fn from_keyword(ident: &str, edition: Edition) -> Option { 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 { + pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option { 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)]