Unnamed repository; edit this file 'description' to name the repository.
Parse contextual dyn keyword properly in edition 2015
Lukas Wirth 2024-07-20
parent 9fd6c69 · commit f4199f7
-rw-r--r--crates/mbe/src/to_parser_input.rs5
-rw-r--r--crates/parser/src/grammar/paths.rs2
-rw-r--r--crates/parser/src/grammar/types.rs21
-rw-r--r--crates/parser/src/parser.rs8
-rw-r--r--crates/parser/src/shortcuts.rs11
-rw-r--r--crates/parser/src/tests.rs2
-rw-r--r--crates/parser/src/tests/prefix_entries.rs2
-rw-r--r--crates/parser/test_data/generated/runner.rs7
-rw-r--r--crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast113
-rw-r--r--crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs4
10 files changed, 164 insertions, 11 deletions
diff --git a/crates/mbe/src/to_parser_input.rs b/crates/mbe/src/to_parser_input.rs
index 66db525362..c35b28527a 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<S: Copy + fmt::Debug>(
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 01b8f9e918..86e19fbe5f 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 f95425824a..9a1c6dfdf7 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<Item=Foo<'a>> + 'a;
+// type A = &dyn Iterator<Item=Foo<'a>> + '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 5b901f911d..40e3c11a1d 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<Event>,
steps: Cell<u32>,
- _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<Event> {
@@ -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 7f49cc087a..1cf81e79b0 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 d35d2d3b03..e7bccb6685 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 4d4ab345d9..e2268eed60 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 d0e6b3f6c9..a935fe4ee3 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 0000000000..00ca92402c
--- /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 0000000000..c4941e65bf
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
@@ -0,0 +1,4 @@
+// 2015
+type A = dyn Iterator<Item=Foo<'a>> + 'a;
+type A = &dyn Iterator<Item=Foo<'a>> + 'a;
+type A = dyn::Path;