From 5f874ca6fa4ea9a3a5a972deb4f3a26ec4e8dcd6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Jul 2024 20:20:00 +0200 Subject: [PATCH] Parse contextual dyn keyword properly in edition 2015 --- crates/mbe/src/to_parser_input.rs | 5 +- crates/parser/src/grammar/paths.rs | 2 + crates/parser/src/grammar/types.rs | 21 ++++ crates/parser/src/parser.rs | 8 +- crates/parser/src/shortcuts.rs | 11 +- crates/parser/src/tests.rs | 2 +- crates/parser/src/tests/prefix_entries.rs | 2 +- crates/parser/test_data/generated/runner.rs | 7 ++ .../parser/inline/ok/dyn_trait_type_weak.rast | 113 ++++++++++++++++++ .../parser/inline/ok/dyn_trait_type_weak.rs | 4 + 10 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast create mode 100644 crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs diff --git a/crates/mbe/src/to_parser_input.rs b/crates/mbe/src/to_parser_input.rs index 66db52536229..c35b28527a09 100644 --- a/crates/mbe/src/to_parser_input.rs +++ b/crates/mbe/src/to_parser_input.rs @@ -65,9 +65,8 @@ pub(crate) fn to_parser_input( 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), - "async" | "await" | "dyn" | "try" if !edition.at_least_2018() => { - 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) { Some(kind) => res.push(kind), None => { diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index 01b8f9e91871..86e19fbe5f1e 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -2,6 +2,8 @@ use super::*; pub(super) const PATH_FIRST: TokenSet = TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self], T![:], T![<]]); +pub(super) const WEAK_DYN_PATH_FIRST: TokenSet = + TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self]]); pub(super) fn is_path_start(p: &Parser<'_>) -> bool { is_use_path_start(p) || p.at(T![<]) || p.at(T![Self]) diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index f95425824a8d..9a1c6dfdf7e4 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::paths::WEAK_DYN_PATH_FIRST; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -49,6 +51,13 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { T![dyn] => dyn_trait_type(p), // Some path types are not allowed to have bounds (no plus) T![<] => path_type_bounds(p, allow_bounds), + T![ident] + if !p.edition().at_least_2018() + && p.at_contextual_kw(T![dyn]) + && WEAK_DYN_PATH_FIRST.contains(p.nth(1)) => + { + dyn_trait_type_weak(p) + } _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds), LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p), _ => { @@ -279,6 +288,18 @@ fn dyn_trait_type(p: &mut Parser<'_>) { m.complete(p, DYN_TRAIT_TYPE); } +// test dyn_trait_type_weak 2015 +// type A = dyn Iterator> + 'a; +// type A = &dyn Iterator> + 'a; +// type A = dyn::Path; +fn dyn_trait_type_weak(p: &mut Parser<'_>) { + assert!(p.at_contextual_kw(T![dyn])); + let m = p.start(); + p.bump_remap(T![dyn]); + generic_params::bounds_without_colon(p); + m.complete(p, DYN_TRAIT_TYPE); +} + // test bare_dyn_types_with_leading_lifetime // type A = 'static + Trait; // type B = S<'static + Trait>; diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 5b901f911dcc..40e3c11a1d96 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -27,14 +27,14 @@ pub(crate) struct Parser<'t> { pos: usize, events: Vec, steps: Cell, - _edition: Edition, + edition: Edition, } static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000); impl<'t> Parser<'t> { pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), _edition: edition } + Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), edition: edition } } pub(crate) fn finish(self) -> Vec { @@ -277,6 +277,10 @@ impl<'t> Parser<'t> { fn push_event(&mut self, event: Event) { self.events.push(event); } + + pub(crate) fn edition(&self) -> Edition { + self.edition + } } /// See [`Parser::start`]. diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs index 7f49cc087aec..1cf81e79b03c 100644 --- a/crates/parser/src/shortcuts.rs +++ b/crates/parser/src/shortcuts.rs @@ -12,7 +12,7 @@ use std::mem; use crate::{ - LexedStr, Step, + Edition, LexedStr, Step, SyntaxKind::{self, *}, }; @@ -25,7 +25,7 @@ pub enum StrStep<'a> { } impl LexedStr<'_> { - pub fn to_input(&self) -> crate::Input { + pub fn to_input(&self, edition: Edition) -> crate::Input { let _p = tracing::info_span!("LexedStr::to_input").entered(); let mut res = crate::Input::default(); let mut was_joint = false; @@ -35,8 +35,11 @@ impl LexedStr<'_> { was_joint = false } else if kind == SyntaxKind::IDENT { let token_text = self.text(i); - let contextual_kw = - SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT); + 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); } else { if was_joint { diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs index d35d2d3b0362..e7bccb6685c9 100644 --- a/crates/parser/src/tests.rs +++ b/crates/parser/src/tests.rs @@ -70,7 +70,7 @@ fn parse_err() { fn parse(entry: TopEntryPoint, text: &str, edition: Edition) -> (String, bool) { let lexed = LexedStr::new(edition, text); - let input = lexed.to_input(); + let input = lexed.to_input(edition); let output = entry.parse(&input, edition); let mut buf = String::new(); diff --git a/crates/parser/src/tests/prefix_entries.rs b/crates/parser/src/tests/prefix_entries.rs index 4d4ab345d962..e2268eed60ab 100644 --- a/crates/parser/src/tests/prefix_entries.rs +++ b/crates/parser/src/tests/prefix_entries.rs @@ -83,7 +83,7 @@ fn meta_item() { #[track_caller] fn check(entry: PrefixEntryPoint, input: &str, prefix: &str) { let lexed = LexedStr::new(Edition::CURRENT, input); - let input = lexed.to_input(); + let input = lexed.to_input(Edition::CURRENT); let mut n_tokens = 0; for step in entry.parse(&input, Edition::CURRENT).iter() { diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index d0e6b3f6c92c..a935fe4ee30e 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -195,6 +195,13 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/dyn_trait_type.rs"); } #[test] + fn dyn_trait_type_weak() { + run_and_expect_no_errors_with_edition( + "test_data/parser/inline/ok/dyn_trait_type_weak.rs", + crate::Edition::Edition2015, + ); + } + #[test] fn effect_blocks() { run_and_expect_no_errors("test_data/parser/inline/ok/effect_blocks.rs"); } #[test] fn exclusive_range_pat() { diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast new file mode 100644 index 000000000000..00ca92402cd4 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast @@ -0,0 +1,113 @@ +SOURCE_FILE + TYPE_ALIAS + COMMENT "// 2015" + WHITESPACE "\n" + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + DYN_TRAIT_TYPE + DYN_KW "dyn" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + EQ "=" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" + R_ANGLE ">" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'a" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + REF_TYPE + AMP "&" + DYN_TRAIT_TYPE + DYN_KW "dyn" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Iterator" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "Item" + EQ "=" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" + R_ANGLE ">" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'a" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "dyn" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Path" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs new file mode 100644 index 000000000000..c4941e65bf54 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs @@ -0,0 +1,4 @@ +// 2015 +type A = dyn Iterator> + 'a; +type A = &dyn Iterator> + 'a; +type A = dyn::Path;