diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/tools/rust-analyzer/crates/parser/src/grammar | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/parser/src/grammar')
14 files changed, 3730 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs new file mode 100644 index 000000000..0cf6a16f8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -0,0 +1,53 @@ +use super::*; + +pub(super) fn inner_attrs(p: &mut Parser<'_>) { + while p.at(T![#]) && p.nth(1) == T![!] { + attr(p, true); + } +} + +pub(super) fn outer_attrs(p: &mut Parser<'_>) { + while p.at(T![#]) { + attr(p, false); + } +} + +fn attr(p: &mut Parser<'_>, inner: bool) { + assert!(p.at(T![#])); + + let attr = p.start(); + p.bump(T![#]); + + if inner { + p.bump(T![!]); + } + + if p.eat(T!['[']) { + meta(p); + + if !p.eat(T![']']) { + p.error("expected `]`"); + } + } else { + p.error("expected `[`"); + } + attr.complete(p, ATTR); +} + +pub(super) fn meta(p: &mut Parser<'_>) { + let meta = p.start(); + paths::use_path(p); + + match p.current() { + T![=] => { + p.bump(T![=]); + if !expressions::expr(p) { + p.error("expected expression"); + } + } + T!['('] | T!['['] | T!['{'] => items::token_tree(p), + _ => {} + } + + meta.complete(p, META); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs new file mode 100644 index 000000000..e7402104e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -0,0 +1,625 @@ +mod atom; + +use super::*; + +pub(crate) use self::atom::{block_expr, match_arm_list}; +pub(super) use self::atom::{literal, LITERAL_FIRST}; + +#[derive(PartialEq, Eq)] +pub(super) enum Semicolon { + Required, + Optional, + Forbidden, +} + +const EXPR_FIRST: TokenSet = LHS_FIRST; + +pub(super) fn expr(p: &mut Parser<'_>) -> bool { + let r = Restrictions { forbid_structs: false, prefer_stmt: false }; + expr_bp(p, None, r, 1).is_some() +} + +pub(super) fn expr_stmt( + p: &mut Parser<'_>, + m: Option<Marker>, +) -> Option<(CompletedMarker, BlockLike)> { + let r = Restrictions { forbid_structs: false, prefer_stmt: true }; + expr_bp(p, m, r, 1) +} + +fn expr_no_struct(p: &mut Parser<'_>) { + let r = Restrictions { forbid_structs: true, prefer_stmt: false }; + expr_bp(p, None, r, 1); +} + +/// Parses the expression in `let pattern = expression`. +/// It needs to be parsed with lower precedence than `&&`, so that +/// `if let true = true && false` is parsed as `if (let true = true) && (true)` +/// and not `if let true = (true && true)`. +fn expr_let(p: &mut Parser<'_>) { + let r = Restrictions { forbid_structs: true, prefer_stmt: false }; + expr_bp(p, None, r, 5); +} + +pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { + if p.eat(T![;]) { + return; + } + + let m = p.start(); + // test attr_on_expr_stmt + // fn foo() { + // #[A] foo(); + // #[B] bar!{} + // #[C] #[D] {} + // #[D] return (); + // } + attributes::outer_attrs(p); + + if p.at(T![let]) { + let_stmt(p, m, semicolon); + return; + } + + // test block_items + // fn a() { fn b() {} } + let m = match items::opt_item(p, m) { + Ok(()) => return, + Err(m) => m, + }; + + if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) { + if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) { + // test no_semi_after_block + // fn foo() { + // if true {} + // loop {} + // match () {} + // while true {} + // for _ in () {} + // {} + // {} + // macro_rules! test { + // () => {} + // } + // test!{} + // } + let m = cm.precede(p); + match semicolon { + Semicolon::Required => { + if blocklike.is_block() { + p.eat(T![;]); + } else { + p.expect(T![;]); + } + } + Semicolon::Optional => { + p.eat(T![;]); + } + Semicolon::Forbidden => (), + } + m.complete(p, EXPR_STMT); + } + } + + // test let_stmt + // fn f() { let x: i32 = 92; } + fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) { + p.bump(T![let]); + patterns::pattern(p); + if p.at(T![:]) { + // test let_stmt_ascription + // fn f() { let x: i32; } + types::ascription(p); + } + if p.eat(T![=]) { + // test let_stmt_init + // fn f() { let x = 92; } + expressions::expr(p); + } + + if p.at(T![else]) { + // test let_else + // fn f() { let Some(x) = opt else { return }; } + + let m = p.start(); + p.bump(T![else]); + block_expr(p); + m.complete(p, LET_ELSE); + } + + match with_semi { + Semicolon::Forbidden => (), + Semicolon::Optional => { + p.eat(T![;]); + } + Semicolon::Required => { + p.expect(T![;]); + } + } + m.complete(p, LET_STMT); + } +} + +pub(super) fn expr_block_contents(p: &mut Parser<'_>) { + attributes::inner_attrs(p); + + while !p.at(EOF) && !p.at(T!['}']) { + // test nocontentexpr + // fn foo(){ + // ;;;some_expr();;;;{;;;};;;;Ok(()) + // } + + // test nocontentexpr_after_item + // fn simple_function() { + // enum LocalEnum { + // One, + // Two, + // }; + // fn f() {}; + // struct S {}; + // } + stmt(p, Semicolon::Required); + } +} + +#[derive(Clone, Copy)] +struct Restrictions { + forbid_structs: bool, + prefer_stmt: bool, +} + +/// Binding powers of operators for a Pratt parser. +/// +/// See <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html> +#[rustfmt::skip] +fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) { + const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); + match p.current() { + T![|] if p.at(T![||]) => (3, T![||]), + T![|] if p.at(T![|=]) => (1, T![|=]), + T![|] => (6, T![|]), + T![>] if p.at(T![>>=]) => (1, T![>>=]), + T![>] if p.at(T![>>]) => (9, T![>>]), + T![>] if p.at(T![>=]) => (5, T![>=]), + T![>] => (5, T![>]), + T![=] if p.at(T![=>]) => NOT_AN_OP, + T![=] if p.at(T![==]) => (5, T![==]), + T![=] => (1, T![=]), + T![<] if p.at(T![<=]) => (5, T![<=]), + T![<] if p.at(T![<<=]) => (1, T![<<=]), + T![<] if p.at(T![<<]) => (9, T![<<]), + T![<] => (5, T![<]), + T![+] if p.at(T![+=]) => (1, T![+=]), + T![+] => (10, T![+]), + T![^] if p.at(T![^=]) => (1, T![^=]), + T![^] => (7, T![^]), + T![%] if p.at(T![%=]) => (1, T![%=]), + T![%] => (11, T![%]), + T![&] if p.at(T![&=]) => (1, T![&=]), + // If you update this, remember to update `expr_let()` too. + T![&] if p.at(T![&&]) => (4, T![&&]), + T![&] => (8, T![&]), + T![/] if p.at(T![/=]) => (1, T![/=]), + T![/] => (11, T![/]), + T![*] if p.at(T![*=]) => (1, T![*=]), + T![*] => (11, T![*]), + T![.] if p.at(T![..=]) => (2, T![..=]), + T![.] if p.at(T![..]) => (2, T![..]), + T![!] if p.at(T![!=]) => (5, T![!=]), + T![-] if p.at(T![-=]) => (1, T![-=]), + T![-] => (10, T![-]), + T![as] => (12, T![as]), + + _ => NOT_AN_OP + } +} + +// Parses expression with binding power of at least bp. +fn expr_bp( + p: &mut Parser<'_>, + m: Option<Marker>, + mut r: Restrictions, + bp: u8, +) -> Option<(CompletedMarker, BlockLike)> { + let m = m.unwrap_or_else(|| { + let m = p.start(); + attributes::outer_attrs(p); + m + }); + let mut lhs = match lhs(p, r) { + Some((lhs, blocklike)) => { + let lhs = lhs.extend_to(p, m); + if r.prefer_stmt && blocklike.is_block() { + // test stmt_bin_expr_ambiguity + // fn f() { + // let _ = {1} & 2; + // {1} &2; + // } + return Some((lhs, BlockLike::Block)); + } + lhs + } + None => { + m.abandon(p); + return None; + } + }; + + loop { + let is_range = p.at(T![..]) || p.at(T![..=]); + let (op_bp, op) = current_op(p); + if op_bp < bp { + break; + } + // test as_precedence + // fn f() { let _ = &1 as *const i32; } + if p.at(T![as]) { + lhs = cast_expr(p, lhs); + continue; + } + let m = lhs.precede(p); + p.bump(op); + + // test binop_resets_statementness + // fn f() { v = {1}&2; } + r = Restrictions { prefer_stmt: false, ..r }; + + if is_range { + // test postfix_range + // fn foo() { + // let x = 1..; + // match 1.. { _ => () }; + // match a.b()..S { _ => () }; + // } + let has_trailing_expression = + p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])); + if !has_trailing_expression { + // no RHS + lhs = m.complete(p, RANGE_EXPR); + break; + } + } + + expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1); + lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); + } + Some((lhs, BlockLike::NotBlock)) +} + +const LHS_FIRST: TokenSet = + atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-]])); + +fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { + let m; + let kind = match p.current() { + // test ref_expr + // fn foo() { + // // reference operator + // let _ = &1; + // let _ = &mut &f(); + // let _ = &raw; + // let _ = &raw.0; + // // raw reference operator + // let _ = &raw mut foo; + // let _ = &raw const foo; + // } + T![&] => { + m = p.start(); + p.bump(T![&]); + if p.at_contextual_kw(T![raw]) && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const])) { + p.bump_remap(T![raw]); + p.bump_any(); + } else { + p.eat(T![mut]); + } + REF_EXPR + } + // test unary_expr + // fn foo() { + // **&1; + // !!true; + // --1; + // } + T![*] | T![!] | T![-] => { + m = p.start(); + p.bump_any(); + PREFIX_EXPR + } + _ => { + // test full_range_expr + // fn foo() { xs[..]; } + for op in [T![..=], T![..]] { + if p.at(op) { + m = p.start(); + p.bump(op); + if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { + expr_bp(p, None, r, 2); + } + let cm = m.complete(p, RANGE_EXPR); + return Some((cm, BlockLike::NotBlock)); + } + } + + // test expression_after_block + // fn foo() { + // let mut p = F{x: 5}; + // {p}.x = 10; + // } + let (lhs, blocklike) = atom::atom_expr(p, r)?; + let (cm, block_like) = + postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block())); + return Some((cm, block_like)); + } + }; + // parse the interior of the unary expression + expr_bp(p, None, r, 255); + let cm = m.complete(p, kind); + Some((cm, BlockLike::NotBlock)) +} + +fn postfix_expr( + p: &mut Parser<'_>, + mut lhs: CompletedMarker, + // Calls are disallowed if the type is a block and we prefer statements because the call cannot be disambiguated from a tuple + // E.g. `while true {break}();` is parsed as + // `while true {break}; ();` + mut block_like: BlockLike, + mut allow_calls: bool, +) -> (CompletedMarker, BlockLike) { + loop { + lhs = match p.current() { + // test stmt_postfix_expr_ambiguity + // fn foo() { + // match () { + // _ => {} + // () => {} + // [] => {} + // } + // } + T!['('] if allow_calls => call_expr(p, lhs), + T!['['] if allow_calls => index_expr(p, lhs), + T![.] => match postfix_dot_expr(p, lhs) { + Ok(it) => it, + Err(it) => { + lhs = it; + break; + } + }, + T![?] => try_expr(p, lhs), + _ => break, + }; + allow_calls = true; + block_like = BlockLike::NotBlock; + } + return (lhs, block_like); + + fn postfix_dot_expr( + p: &mut Parser<'_>, + lhs: CompletedMarker, + ) -> Result<CompletedMarker, CompletedMarker> { + assert!(p.at(T![.])); + if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { + return Ok(method_call_expr(p, lhs)); + } + + // test await_expr + // fn foo() { + // x.await; + // x.0.await; + // x.0().await?.hello(); + // } + if p.nth(1) == T![await] { + let m = lhs.precede(p); + p.bump(T![.]); + p.bump(T![await]); + return Ok(m.complete(p, AWAIT_EXPR)); + } + + if p.at(T![..=]) || p.at(T![..]) { + return Err(lhs); + } + + Ok(field_expr(p, lhs)) + } +} + +// test call_expr +// fn foo() { +// let _ = f(); +// let _ = f()(1)(1, 2,); +// let _ = f(<Foo>::func()); +// f(<Foo as Trait>::func()); +// } +fn call_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T!['('])); + let m = lhs.precede(p); + arg_list(p); + m.complete(p, CALL_EXPR) +} + +// test index_expr +// fn foo() { +// x[1][2]; +// } +fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T!['['])); + let m = lhs.precede(p); + p.bump(T!['[']); + expr(p); + p.expect(T![']']); + m.complete(p, INDEX_EXPR) +} + +// test method_call_expr +// fn foo() { +// x.foo(); +// y.bar::<T>(1, 2,); +// } +fn method_call_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::]))); + let m = lhs.precede(p); + p.bump_any(); + name_ref(p); + generic_args::opt_generic_arg_list(p, true); + if p.at(T!['(']) { + arg_list(p); + } + m.complete(p, METHOD_CALL_EXPR) +} + +// test field_expr +// fn foo() { +// x.foo; +// x.0.bar; +// x.0(); +// } +fn field_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T![.])); + let m = lhs.precede(p); + p.bump(T![.]); + if p.at(IDENT) || p.at(INT_NUMBER) { + name_ref_or_index(p); + } else if p.at(FLOAT_NUMBER) { + // FIXME: How to recover and instead parse INT + T![.]? + p.bump_any(); + } else { + p.error("expected field name or number"); + } + m.complete(p, FIELD_EXPR) +} + +// test try_expr +// fn foo() { +// x?; +// } +fn try_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T![?])); + let m = lhs.precede(p); + p.bump(T![?]); + m.complete(p, TRY_EXPR) +} + +// test cast_expr +// fn foo() { +// 82 as i32; +// 81 as i8 + 1; +// 79 as i16 - 1; +// 0x36 as u8 <= 0x37; +// } +fn cast_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T![as])); + let m = lhs.precede(p); + p.bump(T![as]); + // Use type_no_bounds(), because cast expressions are not + // allowed to have bounds. + types::type_no_bounds(p); + m.complete(p, CAST_EXPR) +} + +fn arg_list(p: &mut Parser<'_>) { + assert!(p.at(T!['('])); + let m = p.start(); + p.bump(T!['(']); + while !p.at(T![')']) && !p.at(EOF) { + // test arg_with_attr + // fn main() { + // foo(#[attr] 92) + // } + if !expr(p) { + break; + } + if !p.at(T![')']) && !p.expect(T![,]) { + break; + } + } + p.eat(T![')']); + m.complete(p, ARG_LIST); +} + +// test path_expr +// fn foo() { +// let _ = a; +// let _ = a::b; +// let _ = ::a::<b>; +// let _ = format!(); +// } +fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike) { + assert!(paths::is_path_start(p)); + let m = p.start(); + paths::expr_path(p); + match p.current() { + T!['{'] if !r.forbid_structs => { + record_expr_field_list(p); + (m.complete(p, RECORD_EXPR), BlockLike::NotBlock) + } + T![!] if !p.at(T![!=]) => { + let block_like = items::macro_call_after_excl(p); + (m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_EXPR), block_like) + } + _ => (m.complete(p, PATH_EXPR), BlockLike::NotBlock), + } +} + +// test record_lit +// fn foo() { +// S {}; +// S { x, y: 32, }; +// S { x, y: 32, ..Default::default() }; +// TupleStruct { 0: 1 }; +// } +pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(EOF) && !p.at(T!['}']) { + let m = p.start(); + // test record_literal_field_with_attr + // fn main() { + // S { #[cfg(test)] field: 1 } + // } + attributes::outer_attrs(p); + + match p.current() { + IDENT | INT_NUMBER => { + // test_err record_literal_before_ellipsis_recovery + // fn main() { + // S { field ..S::default() } + // } + if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) { + name_ref_or_index(p); + p.expect(T![:]); + } + expr(p); + m.complete(p, RECORD_EXPR_FIELD); + } + T![.] if p.at(T![..]) => { + m.abandon(p); + p.bump(T![..]); + + // test destructuring_assignment_struct_rest_pattern + // fn foo() { + // S { .. } = S {}; + // } + + // We permit `.. }` on the left-hand side of a destructuring assignment. + if !p.at(T!['}']) { + expr(p); + } + } + T!['{'] => { + error_block(p, "expected a field"); + m.abandon(p); + } + _ => { + p.err_and_bump("expected identifier"); + m.abandon(p); + } + } + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, RECORD_EXPR_FIELD_LIST); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs new file mode 100644 index 000000000..99f42a266 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -0,0 +1,643 @@ +use super::*; + +// test expr_literals +// fn foo() { +// let _ = true; +// let _ = false; +// let _ = 1; +// let _ = 2.0; +// let _ = b'a'; +// let _ = 'b'; +// let _ = "c"; +// let _ = r"d"; +// let _ = b"e"; +// let _ = br"f"; +// } +pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ + T![true], + T![false], + INT_NUMBER, + FLOAT_NUMBER, + BYTE, + CHAR, + STRING, + BYTE_STRING, +]); + +pub(crate) fn literal(p: &mut Parser<'_>) -> Option<CompletedMarker> { + if !p.at_ts(LITERAL_FIRST) { + return None; + } + let m = p.start(); + p.bump_any(); + Some(m.complete(p, LITERAL)) +} + +// E.g. for after the break in `if break {}`, this should not match +pub(super) const ATOM_EXPR_FIRST: TokenSet = + LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[ + T!['('], + T!['{'], + T!['['], + T![|], + T![move], + T![box], + T![if], + T![while], + T![match], + T![unsafe], + T![return], + T![yield], + T![break], + T![continue], + T![async], + T![try], + T![const], + T![loop], + T![for], + LIFETIME_IDENT, + ])); + +const EXPR_RECOVERY_SET: TokenSet = TokenSet::new(&[T![let]]); + +pub(super) fn atom_expr( + p: &mut Parser<'_>, + r: Restrictions, +) -> Option<(CompletedMarker, BlockLike)> { + if let Some(m) = literal(p) { + return Some((m, BlockLike::NotBlock)); + } + if paths::is_path_start(p) { + return Some(path_expr(p, r)); + } + let la = p.nth(1); + let done = match p.current() { + T!['('] => tuple_expr(p), + T!['['] => array_expr(p), + T![if] => if_expr(p), + T![let] => let_expr(p), + T![_] => { + // test destructuring_assignment_wildcard_pat + // fn foo() { + // _ = 1; + // Some(_) = None; + // } + let m = p.start(); + p.bump(T![_]); + m.complete(p, UNDERSCORE_EXPR) + } + T![loop] => loop_expr(p, None), + T![box] => box_expr(p, None), + T![while] => while_expr(p, None), + T![try] => try_block_expr(p, None), + T![match] => match_expr(p), + T![return] => return_expr(p), + T![yield] => yield_expr(p), + T![continue] => continue_expr(p), + T![break] => break_expr(p, r), + + LIFETIME_IDENT if la == T![:] => { + let m = p.start(); + label(p); + match p.current() { + T![loop] => loop_expr(p, Some(m)), + T![for] => for_expr(p, Some(m)), + T![while] => while_expr(p, Some(m)), + // test labeled_block + // fn f() { 'label: {}; } + T!['{'] => { + stmt_list(p); + m.complete(p, BLOCK_EXPR) + } + _ => { + // test_err misplaced_label_err + // fn main() { + // 'loop: impl + // } + p.error("expected a loop"); + m.complete(p, ERROR); + return None; + } + } + } + // test effect_blocks + // fn f() { unsafe { } } + // fn f() { const { } } + // fn f() { async { } } + // fn f() { async move { } } + T![const] | T![unsafe] | T![async] if la == T!['{'] => { + let m = p.start(); + p.bump_any(); + stmt_list(p); + m.complete(p, BLOCK_EXPR) + } + T![async] if la == T![move] && p.nth(2) == T!['{'] => { + let m = p.start(); + p.bump(T![async]); + p.eat(T![move]); + stmt_list(p); + m.complete(p, BLOCK_EXPR) + } + T!['{'] => { + // test for_range_from + // fn foo() { + // for x in 0 .. { + // break; + // } + // } + let m = p.start(); + stmt_list(p); + m.complete(p, BLOCK_EXPR) + } + + T![static] | T![async] | T![move] | T![|] => closure_expr(p), + T![for] if la == T![<] => closure_expr(p), + T![for] => for_expr(p, None), + + _ => { + p.err_recover("expected expression", EXPR_RECOVERY_SET); + return None; + } + }; + let blocklike = match done.kind() { + IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block, + _ => BlockLike::NotBlock, + }; + Some((done, blocklike)) +} + +// test tuple_expr +// fn foo() { +// (); +// (1); +// (1,); +// } +fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T!['('])); + let m = p.start(); + p.expect(T!['(']); + + let mut saw_comma = false; + let mut saw_expr = false; + while !p.at(EOF) && !p.at(T![')']) { + saw_expr = true; + + // test tuple_attrs + // const A: (i64, i64) = (1, #[cfg(test)] 2); + if !expr(p) { + break; + } + + if !p.at(T![')']) { + saw_comma = true; + p.expect(T![,]); + } + } + p.expect(T![')']); + m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR }) +} + +// test array_expr +// fn foo() { +// []; +// [1]; +// [1, 2,]; +// [1; 2]; +// } +fn array_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T!['['])); + let m = p.start(); + + let mut n_exprs = 0u32; + let mut has_semi = false; + + p.bump(T!['[']); + while !p.at(EOF) && !p.at(T![']']) { + n_exprs += 1; + + // test array_attrs + // const A: &[i64] = &[1, #[cfg(test)] 2]; + if !expr(p) { + break; + } + + if n_exprs == 1 && p.eat(T![;]) { + has_semi = true; + continue; + } + + if has_semi || !p.at(T![']']) && !p.expect(T![,]) { + break; + } + } + p.expect(T![']']); + + m.complete(p, ARRAY_EXPR) +} + +// test lambda_expr +// fn foo() { +// || (); +// || -> i32 { 92 }; +// |x| x; +// move |x: i32,| x; +// async || {}; +// move || {}; +// async move || {}; +// static || {}; +// static move || {}; +// static async || {}; +// static async move || {}; +// for<'a> || {}; +// for<'a> move || {}; +// } +fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(match p.current() { + T![static] | T![async] | T![move] | T![|] => true, + T![for] => p.nth(1) == T![<], + _ => false, + }); + + let m = p.start(); + + if p.at(T![for]) { + types::for_binder(p); + } + + p.eat(T![static]); + p.eat(T![async]); + p.eat(T![move]); + + if !p.at(T![|]) { + p.error("expected `|`"); + return m.complete(p, CLOSURE_EXPR); + } + params::param_list_closure(p); + if opt_ret_type(p) { + // test lambda_ret_block + // fn main() { || -> i32 { 92 }(); } + block_expr(p); + } else if p.at_ts(EXPR_FIRST) { + expr(p); + } else { + p.error("expected expression"); + } + m.complete(p, CLOSURE_EXPR) +} + +// test if_expr +// fn foo() { +// if true {}; +// if true {} else {}; +// if true {} else if false {} else {}; +// if S {}; +// if { true } { } else { }; +// } +fn if_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![if])); + let m = p.start(); + p.bump(T![if]); + expr_no_struct(p); + block_expr(p); + if p.at(T![else]) { + p.bump(T![else]); + if p.at(T![if]) { + if_expr(p); + } else { + block_expr(p); + } + } + m.complete(p, IF_EXPR) +} + +// test label +// fn foo() { +// 'a: loop {} +// 'b: while true {} +// 'c: for x in () {} +// } +fn label(p: &mut Parser<'_>) { + assert!(p.at(LIFETIME_IDENT) && p.nth(1) == T![:]); + let m = p.start(); + lifetime(p); + p.bump_any(); + m.complete(p, LABEL); +} + +// test loop_expr +// fn foo() { +// loop {}; +// } +fn loop_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { + assert!(p.at(T![loop])); + let m = m.unwrap_or_else(|| p.start()); + p.bump(T![loop]); + block_expr(p); + m.complete(p, LOOP_EXPR) +} + +// test while_expr +// fn foo() { +// while true {}; +// while let Some(x) = it.next() {}; +// while { true } {}; +// } +fn while_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { + assert!(p.at(T![while])); + let m = m.unwrap_or_else(|| p.start()); + p.bump(T![while]); + expr_no_struct(p); + block_expr(p); + m.complete(p, WHILE_EXPR) +} + +// test for_expr +// fn foo() { +// for x in [] {}; +// } +fn for_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { + assert!(p.at(T![for])); + let m = m.unwrap_or_else(|| p.start()); + p.bump(T![for]); + patterns::pattern(p); + p.expect(T![in]); + expr_no_struct(p); + block_expr(p); + m.complete(p, FOR_EXPR) +} + +// test let_expr +// fn foo() { +// if let Some(_) = None && true {} +// while 1 == 5 && (let None = None) {} +// } +fn let_expr(p: &mut Parser<'_>) -> CompletedMarker { + let m = p.start(); + p.bump(T![let]); + patterns::pattern_top(p); + p.expect(T![=]); + expr_let(p); + m.complete(p, LET_EXPR) +} + +// test match_expr +// fn foo() { +// match () { }; +// match S {}; +// match { } { _ => () }; +// match { S {} } {}; +// } +fn match_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![match])); + let m = p.start(); + p.bump(T![match]); + expr_no_struct(p); + if p.at(T!['{']) { + match_arm_list(p); + } else { + p.error("expected `{`"); + } + m.complete(p, MATCH_EXPR) +} + +pub(crate) fn match_arm_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.eat(T!['{']); + + // test match_arms_inner_attribute + // fn foo() { + // match () { + // #![doc("Inner attribute")] + // #![doc("Can be")] + // #![doc("Stacked")] + // _ => (), + // } + // } + attributes::inner_attrs(p); + + while !p.at(EOF) && !p.at(T!['}']) { + if p.at(T!['{']) { + error_block(p, "expected match arm"); + continue; + } + match_arm(p); + } + p.expect(T!['}']); + m.complete(p, MATCH_ARM_LIST); +} + +// test match_arm +// fn foo() { +// match () { +// _ => (), +// _ if Test > Test{field: 0} => (), +// X | Y if Z => (), +// | X | Y if Z => (), +// | X => (), +// }; +// } +fn match_arm(p: &mut Parser<'_>) { + let m = p.start(); + // test match_arms_outer_attributes + // fn foo() { + // match () { + // #[cfg(feature = "some")] + // _ => (), + // #[cfg(feature = "other")] + // _ => (), + // #[cfg(feature = "many")] + // #[cfg(feature = "attributes")] + // #[cfg(feature = "before")] + // _ => (), + // } + // } + attributes::outer_attrs(p); + + patterns::pattern_top_r(p, TokenSet::EMPTY); + if p.at(T![if]) { + match_guard(p); + } + p.expect(T![=>]); + let blocklike = match expr_stmt(p, None) { + Some((_, blocklike)) => blocklike, + None => BlockLike::NotBlock, + }; + + // test match_arms_commas + // fn foo() { + // match () { + // _ => (), + // _ => {} + // _ => () + // } + // } + if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { + p.error("expected `,`"); + } + m.complete(p, MATCH_ARM); +} + +// test match_guard +// fn foo() { +// match () { +// _ if foo => (), +// _ if let foo = bar => (), +// } +// } +fn match_guard(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![if])); + let m = p.start(); + p.bump(T![if]); + expr(p); + m.complete(p, MATCH_GUARD) +} + +// test block +// fn a() {} +// fn b() { let _ = 1; } +// fn c() { 1; 2; } +// fn d() { 1; 2 } +pub(crate) fn block_expr(p: &mut Parser<'_>) { + if !p.at(T!['{']) { + p.error("expected a block"); + return; + } + let m = p.start(); + stmt_list(p); + m.complete(p, BLOCK_EXPR); +} + +fn stmt_list(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + expr_block_contents(p); + p.expect(T!['}']); + m.complete(p, STMT_LIST) +} + +// test return_expr +// fn foo() { +// return; +// return 92; +// } +fn return_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![return])); + let m = p.start(); + p.bump(T![return]); + if p.at_ts(EXPR_FIRST) { + expr(p); + } + m.complete(p, RETURN_EXPR) +} +// test yield_expr +// fn foo() { +// yield; +// yield 1; +// } +fn yield_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![yield])); + let m = p.start(); + p.bump(T![yield]); + if p.at_ts(EXPR_FIRST) { + expr(p); + } + m.complete(p, YIELD_EXPR) +} + +// test continue_expr +// fn foo() { +// loop { +// continue; +// continue 'l; +// } +// } +fn continue_expr(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![continue])); + let m = p.start(); + p.bump(T![continue]); + if p.at(LIFETIME_IDENT) { + lifetime(p); + } + m.complete(p, CONTINUE_EXPR) +} + +// test break_expr +// fn foo() { +// loop { +// break; +// break 'l; +// break 92; +// break 'l 92; +// } +// } +fn break_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker { + assert!(p.at(T![break])); + let m = p.start(); + p.bump(T![break]); + if p.at(LIFETIME_IDENT) { + lifetime(p); + } + // test break_ambiguity + // fn foo(){ + // if break {} + // while break {} + // for i in break {} + // match break {} + // } + if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { + expr(p); + } + m.complete(p, BREAK_EXPR) +} + +// test try_block_expr +// fn foo() { +// let _ = try {}; +// } +fn try_block_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { + assert!(p.at(T![try])); + let m = m.unwrap_or_else(|| p.start()); + // Special-case `try!` as macro. + // This is a hack until we do proper edition support + if p.nth_at(1, T![!]) { + // test try_macro_fallback + // fn foo() { try!(Ok(())); } + let macro_call = p.start(); + let path = p.start(); + let path_segment = p.start(); + let name_ref = p.start(); + p.bump_remap(IDENT); + name_ref.complete(p, NAME_REF); + path_segment.complete(p, PATH_SEGMENT); + path.complete(p, PATH); + let _block_like = items::macro_call_after_excl(p); + macro_call.complete(p, MACRO_CALL); + return m.complete(p, MACRO_EXPR); + } + + p.bump(T![try]); + if p.at(T!['{']) { + stmt_list(p); + } else { + p.error("expected a block"); + } + m.complete(p, BLOCK_EXPR) +} + +// test box_expr +// fn foo() { +// let x = box 1i32; +// let y = (box 1i32, box 2i32); +// let z = Foo(box 1i32, box 2i32); +// } +fn box_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { + assert!(p.at(T![box])); + let m = m.unwrap_or_else(|| p.start()); + p.bump(T![box]); + if p.at_ts(EXPR_FIRST) { + expr(p); + } + m.complete(p, BOX_EXPR) +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs new file mode 100644 index 000000000..c438943a0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs @@ -0,0 +1,131 @@ +use super::*; + +pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) { + let m; + if p.at(T![::]) && p.nth(2) == T![<] { + m = p.start(); + p.bump(T![::]); + p.bump(T![<]); + } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] { + m = p.start(); + p.bump(T![<]); + } else { + return; + } + + while !p.at(EOF) && !p.at(T![>]) { + generic_arg(p); + if !p.at(T![>]) && !p.expect(T![,]) { + break; + } + } + p.expect(T![>]); + m.complete(p, GENERIC_ARG_LIST); +} + +// test generic_arg +// type T = S<i32>; +fn generic_arg(p: &mut Parser<'_>) { + match p.current() { + LIFETIME_IDENT => lifetime_arg(p), + T!['{'] | T![true] | T![false] | T![-] => const_arg(p), + k if k.is_literal() => const_arg(p), + // test associated_type_bounds + // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {} + + // test macro_inside_generic_arg + // type A = Foo<syn::Token![_]>; + IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) && !p.nth_at(1, T![::]) => { + let m = p.start(); + name_ref(p); + opt_generic_arg_list(p, false); + match p.current() { + T![=] => { + p.bump_any(); + if types::TYPE_FIRST.contains(p.current()) { + // test assoc_type_eq + // type T = StreamingIterator<Item<'a> = &'a T>; + types::type_(p); + } else { + // test assoc_const_eq + // fn foo<F: Foo<N=3>>() {} + // const TEST: usize = 3; + // fn bar<F: Foo<N={TEST}>>() {} + const_arg(p); + } + m.complete(p, ASSOC_TYPE_ARG); + } + // test assoc_type_bound + // type T = StreamingIterator<Item<'a>: Clone>; + T![:] if !p.at(T![::]) => { + generic_params::bounds(p); + m.complete(p, ASSOC_TYPE_ARG); + } + _ => { + let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH); + let m = paths::type_path_for_qualifier(p, m); + m.precede(p).complete(p, PATH_TYPE).precede(p).complete(p, TYPE_ARG); + } + } + } + _ => type_arg(p), + } +} + +// test lifetime_arg +// type T = S<'static>; +fn lifetime_arg(p: &mut Parser<'_>) { + let m = p.start(); + lifetime(p); + m.complete(p, LIFETIME_ARG); +} + +pub(super) fn const_arg_expr(p: &mut Parser<'_>) { + // The tests in here are really for `const_arg`, which wraps the content + // CONST_ARG. + match p.current() { + // test const_arg_block + // type T = S<{90 + 2}>; + T!['{'] => { + expressions::block_expr(p); + } + // test const_arg_literal + // type T = S<"hello", 0xdeadbeef>; + k if k.is_literal() => { + expressions::literal(p); + } + // test const_arg_bool_literal + // type T = S<true>; + T![true] | T![false] => { + expressions::literal(p); + } + // test const_arg_negative_number + // type T = S<-92>; + T![-] => { + let lm = p.start(); + p.bump(T![-]); + expressions::literal(p); + lm.complete(p, PREFIX_EXPR); + } + _ => { + // This shouldn't be hit by `const_arg` + let lm = p.start(); + paths::use_path(p); + lm.complete(p, PATH_EXPR); + } + } +} + +// test const_arg +// type T = S<92>; +pub(super) fn const_arg(p: &mut Parser<'_>) { + let m = p.start(); + const_arg_expr(p); + m.complete(p, CONST_ARG); +} + +fn type_arg(p: &mut Parser<'_>) { + let m = p.start(); + types::type_(p); + m.complete(p, TYPE_ARG); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs new file mode 100644 index 000000000..6db28ef13 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -0,0 +1,242 @@ +use super::*; + +pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) { + if p.at(T![<]) { + generic_param_list(p); + } +} + +// test generic_param_list +// fn f<T: Clone>() {} +fn generic_param_list(p: &mut Parser<'_>) { + assert!(p.at(T![<])); + let m = p.start(); + p.bump(T![<]); + + while !p.at(EOF) && !p.at(T![>]) { + generic_param(p); + if !p.at(T![>]) && !p.expect(T![,]) { + break; + } + } + p.expect(T![>]); + m.complete(p, GENERIC_PARAM_LIST); +} + +fn generic_param(p: &mut Parser<'_>) { + let m = p.start(); + // test generic_param_attribute + // fn foo<#[lt_attr] 'a, #[t_attr] T>() {} + attributes::outer_attrs(p); + match p.current() { + LIFETIME_IDENT => lifetime_param(p, m), + IDENT => type_param(p, m), + T![const] => const_param(p, m), + _ => { + m.abandon(p); + p.err_and_bump("expected type parameter"); + } + } +} + +// test lifetime_param +// fn f<'a: 'b>() {} +fn lifetime_param(p: &mut Parser<'_>, m: Marker) { + assert!(p.at(LIFETIME_IDENT)); + lifetime(p); + if p.at(T![:]) { + lifetime_bounds(p); + } + m.complete(p, LIFETIME_PARAM); +} + +// test type_param +// fn f<T: Clone>() {} +fn type_param(p: &mut Parser<'_>, m: Marker) { + assert!(p.at(IDENT)); + name(p); + if p.at(T![:]) { + bounds(p); + } + if p.at(T![=]) { + // test type_param_default + // struct S<T = i32>; + p.bump(T![=]); + types::type_(p); + } + m.complete(p, TYPE_PARAM); +} + +// test const_param +// struct S<const N: u32>; +fn const_param(p: &mut Parser<'_>, m: Marker) { + p.bump(T![const]); + name(p); + if p.at(T![:]) { + types::ascription(p); + } else { + p.error("missing type for const parameter"); + } + + if p.at(T![=]) { + // test const_param_default_literal + // struct A<const N: i32 = -1>; + p.bump(T![=]); + + // test const_param_default_expression + // struct A<const N: i32 = { 1 }>; + + // test const_param_default_path + // struct A<const N: i32 = i32::MAX>; + generic_args::const_arg_expr(p); + } + + m.complete(p, CONST_PARAM); +} + +fn lifetime_bounds(p: &mut Parser<'_>) { + assert!(p.at(T![:])); + p.bump(T![:]); + while p.at(LIFETIME_IDENT) { + lifetime(p); + if !p.eat(T![+]) { + break; + } + } +} + +// test type_param_bounds +// struct S<T: 'a + ?Sized + (Copy) + ~const Drop>; +pub(super) fn bounds(p: &mut Parser<'_>) { + assert!(p.at(T![:])); + p.bump(T![:]); + bounds_without_colon(p); +} + +pub(super) fn bounds_without_colon(p: &mut Parser<'_>) { + let m = p.start(); + bounds_without_colon_m(p, m); +} + +pub(super) fn bounds_without_colon_m(p: &mut Parser<'_>, marker: Marker) -> CompletedMarker { + while type_bound(p) { + if !p.eat(T![+]) { + break; + } + } + marker.complete(p, TYPE_BOUND_LIST) +} + +fn type_bound(p: &mut Parser<'_>) -> bool { + let m = p.start(); + let has_paren = p.eat(T!['(']); + match p.current() { + LIFETIME_IDENT => lifetime(p), + T![for] => types::for_type(p, false), + T![?] if p.nth_at(1, T![for]) => { + // test question_for_type_trait_bound + // fn f<T>() where T: ?for<> Sized {} + p.bump_any(); + types::for_type(p, false) + } + current => { + match current { + T![?] => p.bump_any(), + T![~] => { + p.bump_any(); + p.expect(T![const]); + } + _ => (), + } + if paths::is_use_path_start(p) { + types::path_type_(p, false); + } else { + m.abandon(p); + return false; + } + } + } + if has_paren { + p.expect(T![')']); + } + m.complete(p, TYPE_BOUND); + + true +} + +// test where_clause +// fn foo() +// where +// 'a: 'b + 'c, +// T: Clone + Copy + 'static, +// Iterator::Item: 'a, +// <T as Iterator>::Item: 'a +// {} +pub(super) fn opt_where_clause(p: &mut Parser<'_>) { + if !p.at(T![where]) { + return; + } + let m = p.start(); + p.bump(T![where]); + + while is_where_predicate(p) { + where_predicate(p); + + let comma = p.eat(T![,]); + + match p.current() { + T!['{'] | T![;] | T![=] => break, + _ => (), + } + + if !comma { + p.error("expected comma"); + } + } + + m.complete(p, WHERE_CLAUSE); + + fn is_where_predicate(p: &mut Parser<'_>) -> bool { + match p.current() { + LIFETIME_IDENT => true, + T![impl] => false, + token => types::TYPE_FIRST.contains(token), + } + } +} + +fn where_predicate(p: &mut Parser<'_>) { + let m = p.start(); + match p.current() { + LIFETIME_IDENT => { + lifetime(p); + if p.at(T![:]) { + bounds(p); + } else { + p.error("expected colon"); + } + } + T![impl] => { + p.error("expected lifetime or type"); + } + _ => { + if p.at(T![for]) { + // test where_pred_for + // fn for_trait<F>() + // where + // for<'a> F: Fn(&'a str) + // { } + types::for_binder(p); + } + + types::type_(p); + + if p.at(T![:]) { + bounds(p); + } else { + p.error("expected colon"); + } + } + } + m.complete(p, WHERE_PRED); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs new file mode 100644 index 000000000..5e0951bf8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -0,0 +1,465 @@ +mod consts; +mod adt; +mod traits; +mod use_item; + +pub(crate) use self::{ + adt::{record_field_list, variant_list}, + expressions::{match_arm_list, record_expr_field_list}, + traits::assoc_item_list, + use_item::use_tree_list, +}; +use super::*; + +// test mod_contents +// fn foo() {} +// macro_rules! foo {} +// foo::bar!(); +// super::baz! {} +// struct S; +pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) { + attributes::inner_attrs(p); + while !p.at(EOF) && !(p.at(T!['}']) && stop_on_r_curly) { + item_or_macro(p, stop_on_r_curly); + } +} + +pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ + T![fn], + T![struct], + T![enum], + T![impl], + T![trait], + T![const], + T![static], + T![let], + T![mod], + T![pub], + T![crate], + T![use], + T![macro], + T![;], +]); + +pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) { + let m = p.start(); + attributes::outer_attrs(p); + + let m = match opt_item(p, m) { + Ok(()) => { + if p.at(T![;]) { + p.err_and_bump( + "expected item, found `;`\n\ + consider removing this semicolon", + ); + } + return; + } + Err(m) => m, + }; + + if paths::is_use_path_start(p) { + match macro_call(p) { + BlockLike::Block => (), + BlockLike::NotBlock => { + p.expect(T![;]); + } + } + m.complete(p, MACRO_CALL); + return; + } + + m.abandon(p); + match p.current() { + T!['{'] => error_block(p, "expected an item"), + T!['}'] if !stop_on_r_curly => { + let e = p.start(); + p.error("unmatched `}`"); + p.bump(T!['}']); + e.complete(p, ERROR); + } + EOF | T!['}'] => p.error("expected an item"), + _ => p.err_and_bump("expected an item"), + } +} + +/// Try to parse an item, completing `m` in case of success. +pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { + // test_err pub_expr + // fn foo() { pub 92; } + let has_visibility = opt_visibility(p, false); + + let m = match opt_item_without_modifiers(p, m) { + Ok(()) => return Ok(()), + Err(m) => m, + }; + + let mut has_mods = false; + let mut has_extern = false; + + // modifiers + if p.at(T![const]) && p.nth(1) != T!['{'] { + p.eat(T![const]); + has_mods = true; + } + + // test_err async_without_semicolon + // fn foo() { let _ = async {} } + if p.at(T![async]) && !matches!(p.nth(1), T!['{'] | T![move] | T![|]) { + p.eat(T![async]); + has_mods = true; + } + + // test_err unsafe_block_in_mod + // fn foo(){} unsafe { } fn bar(){} + if p.at(T![unsafe]) && p.nth(1) != T!['{'] { + p.eat(T![unsafe]); + has_mods = true; + } + + if p.at(T![extern]) { + has_extern = true; + has_mods = true; + abi(p); + } + if p.at_contextual_kw(T![auto]) && p.nth(1) == T![trait] { + p.bump_remap(T![auto]); + has_mods = true; + } + + // test default_item + // default impl T for Foo {} + if p.at_contextual_kw(T![default]) { + match p.nth(1) { + T![fn] | T![type] | T![const] | T![impl] => { + p.bump_remap(T![default]); + has_mods = true; + } + // test default_unsafe_item + // default unsafe impl T for Foo { + // default unsafe fn foo() {} + // } + T![unsafe] if matches!(p.nth(2), T![impl] | T![fn]) => { + p.bump_remap(T![default]); + p.bump(T![unsafe]); + has_mods = true; + } + // test default_async_fn + // impl T for Foo { + // default async fn foo() {} + // } + T![async] => { + let mut maybe_fn = p.nth(2); + let is_unsafe = if matches!(maybe_fn, T![unsafe]) { + // test default_async_unsafe_fn + // impl T for Foo { + // default async unsafe fn foo() {} + // } + maybe_fn = p.nth(3); + true + } else { + false + }; + + if matches!(maybe_fn, T![fn]) { + p.bump_remap(T![default]); + p.bump(T![async]); + if is_unsafe { + p.bump(T![unsafe]); + } + has_mods = true; + } + } + _ => (), + } + } + + // test existential_type + // existential type Foo: Fn() -> usize; + if p.at_contextual_kw(T![existential]) && p.nth(1) == T![type] { + p.bump_remap(T![existential]); + has_mods = true; + } + + // items + match p.current() { + T![fn] => fn_(p, m), + + T![const] if p.nth(1) != T!['{'] => consts::konst(p, m), + + T![trait] => traits::trait_(p, m), + T![impl] => traits::impl_(p, m), + + T![type] => type_alias(p, m), + + // test extern_block + // unsafe extern "C" {} + // extern {} + T!['{'] if has_extern => { + extern_item_list(p); + m.complete(p, EXTERN_BLOCK); + } + + _ if has_visibility || has_mods => { + if has_mods { + p.error("expected existential, fn, trait or impl"); + } else { + p.error("expected an item"); + } + m.complete(p, ERROR); + } + + _ => return Err(m), + } + Ok(()) +} + +fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { + let la = p.nth(1); + match p.current() { + T![extern] if la == T![crate] => extern_crate(p, m), + T![use] => use_item::use_(p, m), + T![mod] => mod_item(p, m), + + T![type] => type_alias(p, m), + T![struct] => adt::strukt(p, m), + T![enum] => adt::enum_(p, m), + IDENT if p.at_contextual_kw(T![union]) && p.nth(1) == IDENT => adt::union(p, m), + + T![macro] => macro_def(p, m), + IDENT if p.at_contextual_kw(T![macro_rules]) && p.nth(1) == BANG => macro_rules(p, m), + + T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m), + T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m), + + _ => return Err(m), + }; + Ok(()) +} + +// test extern_crate +// extern crate foo; +fn extern_crate(p: &mut Parser<'_>, m: Marker) { + p.bump(T![extern]); + p.bump(T![crate]); + + if p.at(T![self]) { + // test extern_crate_self + // extern crate self; + let m = p.start(); + p.bump(T![self]); + m.complete(p, NAME_REF); + } else { + name_ref(p); + } + + // test extern_crate_rename + // extern crate foo as bar; + opt_rename(p); + p.expect(T![;]); + m.complete(p, EXTERN_CRATE); +} + +// test mod_item +// mod a; +pub(crate) fn mod_item(p: &mut Parser<'_>, m: Marker) { + p.bump(T![mod]); + name(p); + if p.at(T!['{']) { + // test mod_item_curly + // mod b { } + item_list(p); + } else if !p.eat(T![;]) { + p.error("expected `;` or `{`"); + } + m.complete(p, MODULE); +} + +// test type_alias +// type Foo = Bar; +fn type_alias(p: &mut Parser<'_>, m: Marker) { + p.bump(T![type]); + + name(p); + + // test type_item_type_params + // type Result<T> = (); + generic_params::opt_generic_param_list(p); + + if p.at(T![:]) { + generic_params::bounds(p); + } + + // test type_item_where_clause_deprecated + // type Foo where Foo: Copy = (); + generic_params::opt_where_clause(p); + if p.eat(T![=]) { + types::type_(p); + } + + // test type_item_where_clause + // type Foo = () where Foo: Copy; + generic_params::opt_where_clause(p); + + p.expect(T![;]); + m.complete(p, TYPE_ALIAS); +} + +pub(crate) fn item_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + mod_contents(p, true); + p.expect(T!['}']); + m.complete(p, ITEM_LIST); +} + +pub(crate) fn extern_item_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + mod_contents(p, true); + p.expect(T!['}']); + m.complete(p, EXTERN_ITEM_LIST); +} + +fn macro_rules(p: &mut Parser<'_>, m: Marker) { + assert!(p.at_contextual_kw(T![macro_rules])); + p.bump_remap(T![macro_rules]); + p.expect(T![!]); + + if p.at(IDENT) { + name(p); + } + // Special-case `macro_rules! try`. + // This is a hack until we do proper edition support + + // test try_macro_rules + // macro_rules! try { () => {} } + if p.at(T![try]) { + let m = p.start(); + p.bump_remap(IDENT); + m.complete(p, NAME); + } + + match p.current() { + // test macro_rules_non_brace + // macro_rules! m ( ($i:ident) => {} ); + // macro_rules! m [ ($i:ident) => {} ]; + T!['['] | T!['('] => { + token_tree(p); + p.expect(T![;]); + } + T!['{'] => token_tree(p), + _ => p.error("expected `{`, `[`, `(`"), + } + m.complete(p, MACRO_RULES); +} + +// test macro_def +// macro m($i:ident) {} +fn macro_def(p: &mut Parser<'_>, m: Marker) { + p.expect(T![macro]); + name_r(p, ITEM_RECOVERY_SET); + if p.at(T!['{']) { + // test macro_def_curly + // macro m { ($i:ident) => {} } + token_tree(p); + } else if p.at(T!['(']) { + let m = p.start(); + token_tree(p); + match p.current() { + T!['{'] | T!['['] | T!['('] => token_tree(p), + _ => p.error("expected `{`, `[`, `(`"), + } + m.complete(p, TOKEN_TREE); + } else { + p.error("unmatched `(`"); + } + + m.complete(p, MACRO_DEF); +} + +// test fn +// fn foo() {} +fn fn_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![fn]); + + name_r(p, ITEM_RECOVERY_SET); + // test function_type_params + // fn foo<T: Clone + Copy>(){} + generic_params::opt_generic_param_list(p); + + if p.at(T!['(']) { + params::param_list_fn_def(p); + } else { + p.error("expected function arguments"); + } + // test function_ret_type + // fn foo() {} + // fn bar() -> () {} + opt_ret_type(p); + + // test function_where_clause + // fn foo<T>() where T: Copy {} + generic_params::opt_where_clause(p); + + if p.at(T![;]) { + // test fn_decl + // trait T { fn foo(); } + p.bump(T![;]); + } else { + expressions::block_expr(p); + } + m.complete(p, FN); +} + +fn macro_call(p: &mut Parser<'_>) -> BlockLike { + assert!(paths::is_use_path_start(p)); + paths::use_path(p); + macro_call_after_excl(p) +} + +pub(super) fn macro_call_after_excl(p: &mut Parser<'_>) -> BlockLike { + p.expect(T![!]); + + match p.current() { + T!['{'] => { + token_tree(p); + BlockLike::Block + } + T!['('] | T!['['] => { + token_tree(p); + BlockLike::NotBlock + } + _ => { + p.error("expected `{`, `[`, `(`"); + BlockLike::NotBlock + } + } +} + +pub(crate) fn token_tree(p: &mut Parser<'_>) { + let closing_paren_kind = match p.current() { + T!['{'] => T!['}'], + T!['('] => T![')'], + T!['['] => T![']'], + _ => unreachable!(), + }; + let m = p.start(); + p.bump_any(); + while !p.at(EOF) && !p.at(closing_paren_kind) { + match p.current() { + T!['{'] | T!['('] | T!['['] => token_tree(p), + T!['}'] => { + p.error("unmatched `}`"); + m.complete(p, TOKEN_TREE); + return; + } + T![')'] | T![']'] => p.err_and_bump("unmatched brace"), + _ => p.bump_any(), + } + } + p.expect(closing_paren_kind); + m.complete(p, TOKEN_TREE); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs new file mode 100644 index 000000000..e7d30516b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -0,0 +1,168 @@ +use super::*; + +// test struct_item +// struct S {} +pub(super) fn strukt(p: &mut Parser<'_>, m: Marker) { + p.bump(T![struct]); + struct_or_union(p, m, true); +} + +// test union_item +// struct U { i: i32, f: f32 } +pub(super) fn union(p: &mut Parser<'_>, m: Marker) { + assert!(p.at_contextual_kw(T![union])); + p.bump_remap(T![union]); + struct_or_union(p, m, false); +} + +fn struct_or_union(p: &mut Parser<'_>, m: Marker, is_struct: bool) { + name_r(p, ITEM_RECOVERY_SET); + generic_params::opt_generic_param_list(p); + match p.current() { + T![where] => { + generic_params::opt_where_clause(p); + match p.current() { + T![;] => p.bump(T![;]), + T!['{'] => record_field_list(p), + _ => { + //FIXME: special case `(` error message + p.error("expected `;` or `{`"); + } + } + } + T!['{'] => record_field_list(p), + // test unit_struct + // struct S; + T![;] if is_struct => { + p.bump(T![;]); + } + // test tuple_struct + // struct S(String, usize); + T!['('] if is_struct => { + tuple_field_list(p); + // test tuple_struct_where + // struct S<T>(T) where T: Clone; + generic_params::opt_where_clause(p); + p.expect(T![;]); + } + _ => p.error(if is_struct { "expected `;`, `{`, or `(`" } else { "expected `{`" }), + } + m.complete(p, if is_struct { STRUCT } else { UNION }); +} + +pub(super) fn enum_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![enum]); + name_r(p, ITEM_RECOVERY_SET); + generic_params::opt_generic_param_list(p); + generic_params::opt_where_clause(p); + if p.at(T!['{']) { + variant_list(p); + } else { + p.error("expected `{`"); + } + m.complete(p, ENUM); +} + +pub(crate) fn variant_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(EOF) && !p.at(T!['}']) { + if p.at(T!['{']) { + error_block(p, "expected enum variant"); + continue; + } + variant(p); + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, VARIANT_LIST); + + fn variant(p: &mut Parser<'_>) { + let m = p.start(); + attributes::outer_attrs(p); + if p.at(IDENT) { + name(p); + match p.current() { + T!['{'] => record_field_list(p), + T!['('] => tuple_field_list(p), + _ => (), + } + + // test variant_discriminant + // enum E { X(i32) = 10 } + if p.eat(T![=]) { + expressions::expr(p); + } + m.complete(p, VARIANT); + } else { + m.abandon(p); + p.err_and_bump("expected enum variant"); + } + } +} + +// test record_field_list +// struct S { a: i32, b: f32 } +pub(crate) fn record_field_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(T!['}']) && !p.at(EOF) { + if p.at(T!['{']) { + error_block(p, "expected field"); + continue; + } + record_field(p); + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, RECORD_FIELD_LIST); + + fn record_field(p: &mut Parser<'_>) { + let m = p.start(); + // test record_field_attrs + // struct S { #[attr] f: f32 } + attributes::outer_attrs(p); + opt_visibility(p, false); + if p.at(IDENT) { + name(p); + p.expect(T![:]); + types::type_(p); + m.complete(p, RECORD_FIELD); + } else { + m.abandon(p); + p.err_and_bump("expected field declaration"); + } + } +} + +fn tuple_field_list(p: &mut Parser<'_>) { + assert!(p.at(T!['('])); + let m = p.start(); + p.bump(T!['(']); + while !p.at(T![')']) && !p.at(EOF) { + let m = p.start(); + // test tuple_field_attrs + // struct S (#[attr] f32); + attributes::outer_attrs(p); + opt_visibility(p, true); + if !p.at_ts(types::TYPE_FIRST) { + p.error("expected a type"); + m.complete(p, ERROR); + break; + } + types::type_(p); + m.complete(p, TUPLE_FIELD); + + if !p.at(T![')']) { + p.expect(T![,]); + } + } + p.expect(T![')']); + m.complete(p, TUPLE_FIELD_LIST); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs new file mode 100644 index 000000000..9549ec9b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/consts.rs @@ -0,0 +1,37 @@ +use super::*; + +// test const_item +// const C: u32 = 92; +pub(super) fn konst(p: &mut Parser<'_>, m: Marker) { + p.bump(T![const]); + const_or_static(p, m, true); +} + +pub(super) fn static_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![static]); + const_or_static(p, m, false); +} + +fn const_or_static(p: &mut Parser<'_>, m: Marker, is_const: bool) { + p.eat(T![mut]); + + if is_const && p.eat(T![_]) { + // test anonymous_const + // const _: u32 = 0; + } else { + // test_err anonymous_static + // static _: i32 = 5; + name(p); + } + + if p.at(T![:]) { + types::ascription(p); + } else { + p.error("missing type for `const` or `static`"); + } + if p.eat(T![=]) { + expressions::expr(p); + } + p.expect(T![;]); + m.complete(p, if is_const { CONST } else { STATIC }); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs new file mode 100644 index 000000000..c982e2d56 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs @@ -0,0 +1,140 @@ +use super::*; + +// test trait_item +// trait T { fn new() -> Self; } +pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![trait]); + name_r(p, ITEM_RECOVERY_SET); + + // test trait_item_generic_params + // trait X<U: Debug + Display> {} + generic_params::opt_generic_param_list(p); + + if p.eat(T![=]) { + // test trait_alias + // trait Z<U> = T<U>; + generic_params::bounds_without_colon(p); + + // test trait_alias_where_clause + // trait Z<U> = T<U> where U: Copy; + // trait Z<U> = where Self: T<U>; + generic_params::opt_where_clause(p); + p.expect(T![;]); + m.complete(p, TRAIT); + return; + } + + if p.at(T![:]) { + // test trait_item_bounds + // trait T: Hash + Clone {} + generic_params::bounds(p); + } + + // test trait_item_where_clause + // trait T where Self: Copy {} + generic_params::opt_where_clause(p); + + if p.at(T!['{']) { + assoc_item_list(p); + } else { + p.error("expected `{`"); + } + m.complete(p, TRAIT); +} + +// test impl_item +// impl S {} +pub(super) fn impl_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![impl]); + if p.at(T![<]) && not_a_qualified_path(p) { + generic_params::opt_generic_param_list(p); + } + + // test impl_item_const + // impl const Send for S {} + p.eat(T![const]); + + // FIXME: never type + // impl ! {} + + // test impl_item_neg + // impl !Send for S {} + p.eat(T![!]); + impl_type(p); + if p.eat(T![for]) { + impl_type(p); + } + generic_params::opt_where_clause(p); + if p.at(T!['{']) { + assoc_item_list(p); + } else { + p.error("expected `{`"); + } + m.complete(p, IMPL); +} + +// test assoc_item_list +// impl F { +// type A = i32; +// const B: i32 = 92; +// fn foo() {} +// fn bar(&self) {} +// } +pub(crate) fn assoc_item_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + + let m = p.start(); + p.bump(T!['{']); + // test assoc_item_list_inner_attrs + // impl S { #![attr] } + attributes::inner_attrs(p); + + while !p.at(EOF) && !p.at(T!['}']) { + if p.at(T!['{']) { + error_block(p, "expected an item"); + continue; + } + item_or_macro(p, true); + } + p.expect(T!['}']); + m.complete(p, ASSOC_ITEM_LIST); +} + +// test impl_type_params +// impl<const N: u32> Bar<N> {} +fn not_a_qualified_path(p: &Parser<'_>) -> bool { + // There's an ambiguity between generic parameters and qualified paths in impls. + // If we see `<` it may start both, so we have to inspect some following tokens. + // The following combinations can only start generics, + // but not qualified paths (with one exception): + // `<` `>` - empty generic parameters + // `<` `#` - generic parameters with attributes + // `<` `const` - const generic parameters + // `<` (LIFETIME_IDENT|IDENT) `>` - single generic parameter + // `<` (LIFETIME_IDENT|IDENT) `,` - first generic parameter in a list + // `<` (LIFETIME_IDENT|IDENT) `:` - generic parameter with bounds + // `<` (LIFETIME_IDENT|IDENT) `=` - generic parameter with a default + // The only truly ambiguous case is + // `<` IDENT `>` `::` IDENT ... + // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`) + // because this is what almost always expected in practice, qualified paths in impls + // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment. + if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == T![const] { + return true; + } + (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT) + && (p.nth(2) == T![>] || p.nth(2) == T![,] || p.nth(2) == T![:] || p.nth(2) == T![=]) +} + +// test_err impl_type +// impl Type {} +// impl Trait1 for T {} +// impl impl NotType {} +// impl Trait2 for impl NotType {} +pub(crate) fn impl_type(p: &mut Parser<'_>) { + if p.at(T![impl]) { + p.error("expected trait or type"); + return; + } + types::type_(p); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/use_item.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/use_item.rs new file mode 100644 index 000000000..69880b794 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/use_item.rs @@ -0,0 +1,93 @@ +use super::*; + +// test use_item +// use std::collections; +pub(super) fn use_(p: &mut Parser<'_>, m: Marker) { + p.bump(T![use]); + use_tree(p, true); + p.expect(T![;]); + m.complete(p, USE); +} + +// test use_tree +// use outer::tree::{inner::tree}; +fn use_tree(p: &mut Parser<'_>, top_level: bool) { + let m = p.start(); + match p.current() { + // test use_tree_star + // use *; + // use std::{*}; + T![*] => p.bump(T![*]), + // test use_tree_abs_star + // use ::*; + // use std::{::*}; + T![:] if p.at(T![::]) && p.nth(2) == T![*] => { + p.bump(T![::]); + p.bump(T![*]); + } + T!['{'] => use_tree_list(p), + T![:] if p.at(T![::]) && p.nth(2) == T!['{'] => { + p.bump(T![::]); + use_tree_list(p); + } + + // test use_tree_path + // use ::std; + // use std::collections; + // + // use self::m; + // use super::m; + // use crate::m; + _ if paths::is_use_path_start(p) => { + paths::use_path(p); + match p.current() { + // test use_tree_alias + // use std as stdlib; + // use Trait as _; + T![as] => opt_rename(p), + T![:] if p.at(T![::]) => { + p.bump(T![::]); + match p.current() { + // test use_tree_path_star + // use std::*; + T![*] => p.bump(T![*]), + // test use_tree_path_use_tree + // use std::{collections}; + T!['{'] => use_tree_list(p), + _ => p.error("expected `{` or `*`"), + } + } + _ => (), + } + } + _ => { + m.abandon(p); + let msg = "expected one of `*`, `::`, `{`, `self`, `super` or an identifier"; + if top_level { + p.err_recover(msg, ITEM_RECOVERY_SET); + } else { + // if we are parsing a nested tree, we have to eat a token to + // main balanced `{}` + p.err_and_bump(msg); + } + return; + } + } + m.complete(p, USE_TREE); +} + +// test use_tree_list +// use {a, b, c}; +pub(crate) fn use_tree_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(EOF) && !p.at(T!['}']) { + use_tree(p, false); + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, USE_TREE_LIST); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs new file mode 100644 index 000000000..20e8e95f0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs @@ -0,0 +1,209 @@ +use super::*; + +// test param_list +// fn a() {} +// fn b(x: i32) {} +// fn c(x: i32, ) {} +// fn d(x: i32, y: ()) {} +pub(super) fn param_list_fn_def(p: &mut Parser<'_>) { + list_(p, Flavor::FnDef); +} + +// test param_list_opt_patterns +// fn foo<F: FnMut(&mut Foo<'a>)>(){} +pub(super) fn param_list_fn_trait(p: &mut Parser<'_>) { + list_(p, Flavor::FnTrait); +} + +pub(super) fn param_list_fn_ptr(p: &mut Parser<'_>) { + list_(p, Flavor::FnPointer); +} + +pub(super) fn param_list_closure(p: &mut Parser<'_>) { + list_(p, Flavor::Closure); +} + +#[derive(Debug, Clone, Copy)] +enum Flavor { + FnDef, // Includes trait fn params; omitted param idents are not supported + FnTrait, // Params for `Fn(...)`/`FnMut(...)`/`FnOnce(...)` annotations + FnPointer, + Closure, +} + +fn list_(p: &mut Parser<'_>, flavor: Flavor) { + use Flavor::*; + + let (bra, ket) = match flavor { + Closure => (T![|], T![|]), + FnDef | FnTrait | FnPointer => (T!['('], T![')']), + }; + + let list_marker = p.start(); + p.bump(bra); + + let mut param_marker = None; + if let FnDef = flavor { + // test self_param_outer_attr + // fn f(#[must_use] self) {} + let m = p.start(); + attributes::outer_attrs(p); + match opt_self_param(p, m) { + Ok(()) => {} + Err(m) => param_marker = Some(m), + } + } + + while !p.at(EOF) && !p.at(ket) { + // test param_outer_arg + // fn f(#[attr1] pat: Type) {} + let m = match param_marker.take() { + Some(m) => m, + None => { + let m = p.start(); + attributes::outer_attrs(p); + m + } + }; + + if !p.at_ts(PARAM_FIRST) { + p.error("expected value parameter"); + m.abandon(p); + break; + } + param(p, m, flavor); + if !p.at(ket) { + p.expect(T![,]); + } + } + + if let Some(m) = param_marker { + m.abandon(p); + } + + p.expect(ket); + list_marker.complete(p, PARAM_LIST); +} + +const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); + +fn param(p: &mut Parser<'_>, m: Marker, flavor: Flavor) { + match flavor { + // test param_list_vararg + // extern "C" { fn printf(format: *const i8, ..., _: u8) -> i32; } + Flavor::FnDef | Flavor::FnPointer if p.eat(T![...]) => {} + + // test fn_def_param + // fn foo(..., (x, y): (i32, i32)) {} + Flavor::FnDef => { + patterns::pattern(p); + if !variadic_param(p) { + if p.at(T![:]) { + types::ascription(p); + } else { + // test_err missing_fn_param_type + // fn f(x y: i32, z, t: i32) {} + p.error("missing type for function parameter"); + } + } + } + // test value_parameters_no_patterns + // type F = Box<Fn(i32, &i32, &i32, ())>; + Flavor::FnTrait => { + types::type_(p); + } + // test fn_pointer_param_ident_path + // type Foo = fn(Bar::Baz); + // type Qux = fn(baz: Bar::Baz); + + // test fn_pointer_unnamed_arg + // type Foo = fn(_: bar); + Flavor::FnPointer => { + if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) { + patterns::pattern_single(p); + if !variadic_param(p) { + if p.at(T![:]) { + types::ascription(p); + } else { + p.error("missing type for function parameter"); + } + } + } else { + types::type_(p); + } + } + // test closure_params + // fn main() { + // let foo = |bar, baz: Baz, qux: Qux::Quux| (); + // } + Flavor::Closure => { + patterns::pattern_single(p); + if p.at(T![:]) && !p.at(T![::]) { + types::ascription(p); + } + } + } + m.complete(p, PARAM); +} + +fn variadic_param(p: &mut Parser<'_>) -> bool { + if p.at(T![:]) && p.nth_at(1, T![...]) { + p.bump(T![:]); + p.bump(T![...]); + true + } else { + false + } +} + +// test self_param +// impl S { +// fn a(self) {} +// fn b(&self,) {} +// fn c(&'a self,) {} +// fn d(&'a mut self, x: i32) {} +// fn e(mut self) {} +// } +fn opt_self_param(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { + if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { + p.eat(T![mut]); + self_as_name(p); + // test arb_self_types + // impl S { + // fn a(self: &Self) {} + // fn b(mut self: Box<Self>) {} + // } + if p.at(T![:]) { + types::ascription(p); + } + } else { + let la1 = p.nth(1); + let la2 = p.nth(2); + let la3 = p.nth(3); + if !matches!( + (p.current(), la1, la2, la3), + (T![&], T![self], _, _) + | (T![&], T![mut] | LIFETIME_IDENT, T![self], _) + | (T![&], LIFETIME_IDENT, T![mut], T![self]) + ) { + return Err(m); + } + p.bump(T![&]); + if p.at(LIFETIME_IDENT) { + lifetime(p); + } + p.eat(T![mut]); + self_as_name(p); + } + m.complete(p, SELF_PARAM); + if !p.at(T![')']) { + p.expect(T![,]); + } + Ok(()) +} + +fn self_as_name(p: &mut Parser<'_>) { + let m = p.start(); + p.bump(T![self]); + m.complete(p, NAME); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs new file mode 100644 index 000000000..8de5d33a1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs @@ -0,0 +1,132 @@ +use super::*; + +pub(super) const PATH_FIRST: TokenSet = + TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self], T![:], T![<]]); + +pub(super) fn is_path_start(p: &Parser<'_>) -> bool { + is_use_path_start(p) || p.at(T![<]) || p.at(T![Self]) +} + +pub(super) fn is_use_path_start(p: &Parser<'_>) -> bool { + match p.current() { + IDENT | T![self] | T![super] | T![crate] => true, + T![:] if p.at(T![::]) => true, + _ => false, + } +} + +pub(super) fn use_path(p: &mut Parser<'_>) { + path(p, Mode::Use); +} + +pub(crate) fn type_path(p: &mut Parser<'_>) { + path(p, Mode::Type); +} + +pub(super) fn expr_path(p: &mut Parser<'_>) { + path(p, Mode::Expr); +} + +pub(crate) fn type_path_for_qualifier( + p: &mut Parser<'_>, + qual: CompletedMarker, +) -> CompletedMarker { + path_for_qualifier(p, Mode::Type, qual) +} + +#[derive(Clone, Copy, Eq, PartialEq)] +enum Mode { + Use, + Type, + Expr, +} + +fn path(p: &mut Parser<'_>, mode: Mode) { + let path = p.start(); + path_segment(p, mode, true); + let qual = path.complete(p, PATH); + path_for_qualifier(p, mode, qual); +} + +fn path_for_qualifier( + p: &mut Parser<'_>, + mode: Mode, + mut qual: CompletedMarker, +) -> CompletedMarker { + loop { + let use_tree = mode == Mode::Use && matches!(p.nth(2), T![*] | T!['{']); + if p.at(T![::]) && !use_tree { + let path = qual.precede(p); + p.bump(T![::]); + path_segment(p, mode, false); + let path = path.complete(p, PATH); + qual = path; + } else { + return qual; + } + } +} + +fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) { + let m = p.start(); + // test qual_paths + // type X = <A as B>::Output; + // fn foo() { <usize as Default>::default(); } + if first && p.eat(T![<]) { + types::type_(p); + if p.eat(T![as]) { + if is_use_path_start(p) { + types::path_type(p); + } else { + p.error("expected a trait"); + } + } + p.expect(T![>]); + } else { + let mut empty = true; + if first { + p.eat(T![::]); + empty = false; + } + match p.current() { + IDENT => { + name_ref(p); + opt_path_type_args(p, mode); + } + // test crate_path + // use crate::foo; + T![self] | T![super] | T![crate] | T![Self] => { + let m = p.start(); + p.bump_any(); + m.complete(p, NAME_REF); + } + _ => { + p.err_recover("expected identifier", items::ITEM_RECOVERY_SET); + if empty { + // test_err empty_segment + // use crate::; + m.abandon(p); + return; + } + } + }; + } + m.complete(p, PATH_SEGMENT); +} + +fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) { + match mode { + Mode::Use => {} + Mode::Type => { + // test path_fn_trait_args + // type F = Box<Fn(i32) -> ()>; + if p.at(T!['(']) { + params::param_list_fn_trait(p); + opt_ret_type(p); + } else { + generic_args::opt_generic_arg_list(p, false); + } + } + Mode::Expr => generic_args::opt_generic_arg_list(p, true), + } +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs new file mode 100644 index 000000000..4cbf10306 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -0,0 +1,440 @@ +use super::*; + +pub(super) const PATTERN_FIRST: TokenSet = + expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[ + T![box], + T![ref], + T![mut], + T!['('], + T!['['], + T![&], + T![_], + T![-], + T![.], + ])); + +pub(crate) fn pattern(p: &mut Parser<'_>) { + pattern_r(p, PAT_RECOVERY_SET); +} + +/// Parses a pattern list separated by pipes `|`. +pub(super) fn pattern_top(p: &mut Parser<'_>) { + pattern_top_r(p, PAT_RECOVERY_SET); +} + +pub(crate) fn pattern_single(p: &mut Parser<'_>) { + pattern_single_r(p, PAT_RECOVERY_SET); +} + +/// Parses a pattern list separated by pipes `|` +/// using the given `recovery_set`. +pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) { + p.eat(T![|]); + pattern_r(p, recovery_set); +} + +/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the +/// given `recovery_set`. + +// test or_pattern +// fn main() { +// match () { +// (_ | _) => (), +// &(_ | _) => (), +// (_ | _,) => (), +// [_ | _,] => (), +// } +// } +fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) { + let m = p.start(); + pattern_single_r(p, recovery_set); + + if !p.at(T![|]) { + m.abandon(p); + return; + } + while p.eat(T![|]) { + pattern_single_r(p, recovery_set); + } + m.complete(p, OR_PAT); +} + +fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { + if let Some(lhs) = atom_pat(p, recovery_set) { + // test range_pat + // fn main() { + // match 92 { + // 0 ... 100 => (), + // 101 ..= 200 => (), + // 200 .. 301 => (), + // 302 .. => (), + // } + // + // match Some(10 as u8) { + // Some(0) | None => (), + // Some(1..) => () + // } + // + // match (10 as u8, 5 as u8) { + // (0, _) => (), + // (1.., _) => () + // } + // } + + // FIXME: support half_open_range_patterns (`..=2`), + // exclusive_range_pattern (`..5`) with missing lhs + for range_op in [T![...], T![..=], T![..]] { + if p.at(range_op) { + let m = lhs.precede(p); + p.bump(range_op); + + // `0 .. =>` or `let 0 .. =` or `Some(0 .. )` + // ^ ^ ^ + if p.at(T![=]) | p.at(T![')']) | p.at(T![,]) { + // test half_open_range_pat + // fn f() { let 0 .. = 1u32; } + } else { + atom_pat(p, recovery_set); + } + m.complete(p, RANGE_PAT); + return; + } + } + } +} + +const PAT_RECOVERY_SET: TokenSet = + TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]); + +fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> { + let m = match p.current() { + T![box] => box_pat(p), + T![ref] | T![mut] => ident_pat(p, true), + T![const] => const_block_pat(p), + IDENT => match p.nth(1) { + // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro + // (T![x]). + T!['('] | T!['{'] | T![!] => path_or_macro_pat(p), + T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p), + _ => ident_pat(p, true), + }, + + // test type_path_in_pattern + // fn main() { let <_>::Foo = (); } + _ if paths::is_path_start(p) => path_or_macro_pat(p), + _ if is_literal_pat_start(p) => literal_pat(p), + + T![.] if p.at(T![..]) => rest_pat(p), + T![_] => wildcard_pat(p), + T![&] => ref_pat(p), + T!['('] => tuple_pat(p), + T!['['] => slice_pat(p), + + _ => { + p.err_recover("expected pattern", recovery_set); + return None; + } + }; + + Some(m) +} + +fn is_literal_pat_start(p: &Parser<'_>) -> bool { + p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER) + || p.at_ts(expressions::LITERAL_FIRST) +} + +// test literal_pattern +// fn main() { +// match () { +// -1 => (), +// 92 => (), +// 'c' => (), +// "hello" => (), +// } +// } +fn literal_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(is_literal_pat_start(p)); + let m = p.start(); + if p.at(T![-]) { + p.bump(T![-]); + } + expressions::literal(p); + m.complete(p, LITERAL_PAT) +} + +// test path_part +// fn foo() { +// let foo::Bar = (); +// let ::Bar = (); +// let Bar { .. } = (); +// let Bar(..) = (); +// } +fn path_or_macro_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(paths::is_path_start(p)); + let m = p.start(); + paths::expr_path(p); + let kind = match p.current() { + T!['('] => { + tuple_pat_fields(p); + TUPLE_STRUCT_PAT + } + T!['{'] => { + record_pat_field_list(p); + RECORD_PAT + } + // test marco_pat + // fn main() { + // let m!(x) = 0; + // } + T![!] => { + items::macro_call_after_excl(p); + return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT); + } + _ => PATH_PAT, + }; + m.complete(p, kind) +} + +// test tuple_pat_fields +// fn foo() { +// let S() = (); +// let S(_) = (); +// let S(_,) = (); +// let S(_, .. , x) = (); +// } +fn tuple_pat_fields(p: &mut Parser<'_>) { + assert!(p.at(T!['('])); + p.bump(T!['(']); + pat_list(p, T![')']); + p.expect(T![')']); +} + +// test record_pat_field +// fn foo() { +// let S { 0: 1 } = (); +// let S { x: 1 } = (); +// let S { #[cfg(any())] x: 1 } = (); +// } +fn record_pat_field(p: &mut Parser<'_>) { + match p.current() { + IDENT | INT_NUMBER if p.nth(1) == T![:] => { + name_ref_or_index(p); + p.bump(T![:]); + pattern(p); + } + T![box] => { + // FIXME: not all box patterns should be allowed + box_pat(p); + } + T![ref] | T![mut] | IDENT => { + ident_pat(p, false); + } + _ => { + p.err_and_bump("expected identifier"); + } + } +} + +// test record_pat_field_list +// fn foo() { +// let S {} = (); +// let S { f, ref mut g } = (); +// let S { h: _, ..} = (); +// let S { h: _, } = (); +// let S { #[cfg(any())] .. } = (); +// } +fn record_pat_field_list(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(EOF) && !p.at(T!['}']) { + let m = p.start(); + attributes::outer_attrs(p); + + match p.current() { + // A trailing `..` is *not* treated as a REST_PAT. + T![.] if p.at(T![..]) => { + p.bump(T![..]); + m.complete(p, REST_PAT); + } + T!['{'] => { + error_block(p, "expected ident"); + m.abandon(p); + } + _ => { + record_pat_field(p); + m.complete(p, RECORD_PAT_FIELD); + } + } + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, RECORD_PAT_FIELD_LIST); +} + +// test placeholder_pat +// fn main() { let _ = (); } +fn wildcard_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![_])); + let m = p.start(); + p.bump(T![_]); + m.complete(p, WILDCARD_PAT) +} + +// test dot_dot_pat +// fn main() { +// let .. = (); +// // +// // Tuples +// // +// let (a, ..) = (); +// let (a, ..,) = (); +// let Tuple(a, ..) = (); +// let Tuple(a, ..,) = (); +// let (.., ..) = (); +// let Tuple(.., ..) = (); +// let (.., a, ..) = (); +// let Tuple(.., a, ..) = (); +// // +// // Slices +// // +// let [..] = (); +// let [head, ..] = (); +// let [head, tail @ ..] = (); +// let [head, .., cons] = (); +// let [head, mid @ .., cons] = (); +// let [head, .., .., cons] = (); +// let [head, .., mid, tail @ ..] = (); +// let [head, .., mid, .., cons] = (); +// } +fn rest_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![..])); + let m = p.start(); + p.bump(T![..]); + m.complete(p, REST_PAT) +} + +// test ref_pat +// fn main() { +// let &a = (); +// let &mut b = (); +// } +fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![&])); + let m = p.start(); + p.bump(T![&]); + p.eat(T![mut]); + pattern_single(p); + m.complete(p, REF_PAT) +} + +// test tuple_pat +// fn main() { +// let (a, b, ..) = (); +// let (a,) = (); +// let (..) = (); +// let () = (); +// } +fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T!['('])); + let m = p.start(); + p.bump(T!['(']); + let mut has_comma = false; + let mut has_pat = false; + let mut has_rest = false; + while !p.at(EOF) && !p.at(T![')']) { + has_pat = true; + if !p.at_ts(PATTERN_FIRST) { + p.error("expected a pattern"); + break; + } + has_rest |= p.at(T![..]); + + pattern(p); + if !p.at(T![')']) { + has_comma = true; + p.expect(T![,]); + } + } + p.expect(T![')']); + + m.complete(p, if !has_comma && !has_rest && has_pat { PAREN_PAT } else { TUPLE_PAT }) +} + +// test slice_pat +// fn main() { +// let [a, b, ..] = []; +// } +fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T!['['])); + let m = p.start(); + p.bump(T!['[']); + pat_list(p, T![']']); + p.expect(T![']']); + m.complete(p, SLICE_PAT) +} + +fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) { + while !p.at(EOF) && !p.at(ket) { + if !p.at_ts(PATTERN_FIRST) { + p.error("expected a pattern"); + break; + } + + pattern(p); + if !p.at(ket) { + p.expect(T![,]); + } + } +} + +// test bind_pat +// fn main() { +// let a = (); +// let mut b = (); +// let ref c = (); +// let ref mut d = (); +// let e @ _ = (); +// let ref mut f @ g @ _ = (); +// } +fn ident_pat(p: &mut Parser<'_>, with_at: bool) -> CompletedMarker { + assert!(matches!(p.current(), T![ref] | T![mut] | IDENT)); + let m = p.start(); + p.eat(T![ref]); + p.eat(T![mut]); + name_r(p, PAT_RECOVERY_SET); + if with_at && p.eat(T![@]) { + pattern_single(p); + } + m.complete(p, IDENT_PAT) +} + +// test box_pat +// fn main() { +// let box i = (); +// let box Outer { box i, j: box Inner(box &x) } = (); +// let box ref mut i = (); +// } +fn box_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![box])); + let m = p.start(); + p.bump(T![box]); + pattern_single(p); + m.complete(p, BOX_PAT) +} + +// test const_block_pat +// fn main() { +// let const { 15 } = (); +// let const { foo(); bar() } = (); +// } +fn const_block_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![const])); + let m = p.start(); + p.bump(T![const]); + expressions::block_expr(p); + m.complete(p, CONST_BLOCK_PAT) +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs new file mode 100644 index 000000000..5c6e18fee --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -0,0 +1,352 @@ +use super::*; + +pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ + T!['('], + T!['['], + T![<], + T![!], + T![*], + T![&], + T![_], + T![fn], + T![unsafe], + T![extern], + T![for], + T![impl], + T![dyn], + T![Self], +])); + +const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[ + T![')'], + T![,], + // test_err struct_field_recover + // struct S { f pub g: () } + T![pub], +]); + +pub(crate) fn type_(p: &mut Parser<'_>) { + type_with_bounds_cond(p, true); +} + +pub(super) fn type_no_bounds(p: &mut Parser<'_>) { + type_with_bounds_cond(p, false); +} + +fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { + match p.current() { + T!['('] => paren_or_tuple_type(p), + T![!] => never_type(p), + T![*] => ptr_type(p), + T!['['] => array_or_slice_type(p), + T![&] => ref_type(p), + T![_] => infer_type(p), + T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p), + T![for] => for_type(p, allow_bounds), + T![impl] => impl_trait_type(p), + T![dyn] => dyn_trait_type(p), + // Some path types are not allowed to have bounds (no plus) + T![<] => path_type_(p, allow_bounds), + _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds), + _ => { + p.err_recover("expected type", TYPE_RECOVERY_SET); + } + } +} + +pub(super) fn ascription(p: &mut Parser<'_>) { + assert!(p.at(T![:])); + p.bump(T![:]); + if p.at(T![=]) { + // recover from `let x: = expr;`, `const X: = expr;` and similars + // hopefully no type starts with `=` + p.error("missing type"); + return; + } + type_(p); +} + +fn paren_or_tuple_type(p: &mut Parser<'_>) { + assert!(p.at(T!['('])); + let m = p.start(); + p.bump(T!['(']); + let mut n_types: u32 = 0; + let mut trailing_comma: bool = false; + while !p.at(EOF) && !p.at(T![')']) { + n_types += 1; + type_(p); + if p.eat(T![,]) { + trailing_comma = true; + } else { + trailing_comma = false; + break; + } + } + p.expect(T![')']); + + let kind = if n_types == 1 && !trailing_comma { + // test paren_type + // type T = (i32); + PAREN_TYPE + } else { + // test unit_type + // type T = (); + + // test singleton_tuple_type + // type T = (i32,); + TUPLE_TYPE + }; + m.complete(p, kind); +} + +// test never_type +// type Never = !; +fn never_type(p: &mut Parser<'_>) { + assert!(p.at(T![!])); + let m = p.start(); + p.bump(T![!]); + m.complete(p, NEVER_TYPE); +} + +fn ptr_type(p: &mut Parser<'_>) { + assert!(p.at(T![*])); + let m = p.start(); + p.bump(T![*]); + + match p.current() { + // test pointer_type_mut + // type M = *mut (); + // type C = *mut (); + T![mut] | T![const] => p.bump_any(), + _ => { + // test_err pointer_type_no_mutability + // type T = *(); + p.error( + "expected mut or const in raw pointer type \ + (use `*mut T` or `*const T` as appropriate)", + ); + } + }; + + type_no_bounds(p); + m.complete(p, PTR_TYPE); +} + +fn array_or_slice_type(p: &mut Parser<'_>) { + assert!(p.at(T!['['])); + let m = p.start(); + p.bump(T!['[']); + + type_(p); + let kind = match p.current() { + // test slice_type + // type T = [()]; + T![']'] => { + p.bump(T![']']); + SLICE_TYPE + } + + // test array_type + // type T = [(); 92]; + T![;] => { + p.bump(T![;]); + expressions::expr(p); + p.expect(T![']']); + ARRAY_TYPE + } + // test_err array_type_missing_semi + // type T = [() 92]; + _ => { + p.error("expected `;` or `]`"); + SLICE_TYPE + } + }; + m.complete(p, kind); +} + +// test reference_type; +// type A = &(); +// type B = &'static (); +// type C = &mut (); +fn ref_type(p: &mut Parser<'_>) { + assert!(p.at(T![&])); + let m = p.start(); + p.bump(T![&]); + if p.at(LIFETIME_IDENT) { + lifetime(p); + } + p.eat(T![mut]); + type_no_bounds(p); + m.complete(p, REF_TYPE); +} + +// test placeholder_type +// type Placeholder = _; +fn infer_type(p: &mut Parser<'_>) { + assert!(p.at(T![_])); + let m = p.start(); + p.bump(T![_]); + m.complete(p, INFER_TYPE); +} + +// test fn_pointer_type +// type A = fn(); +// type B = unsafe fn(); +// type C = unsafe extern "C" fn(); +// type D = extern "C" fn ( u8 , ... ) -> u8; +fn fn_ptr_type(p: &mut Parser<'_>) { + let m = p.start(); + p.eat(T![unsafe]); + if p.at(T![extern]) { + abi(p); + } + // test_err fn_pointer_type_missing_fn + // type F = unsafe (); + if !p.eat(T![fn]) { + m.abandon(p); + p.error("expected `fn`"); + return; + } + if p.at(T!['(']) { + params::param_list_fn_ptr(p); + } else { + p.error("expected parameters"); + } + // test fn_pointer_type_with_ret + // type F = fn() -> (); + opt_ret_type(p); + m.complete(p, FN_PTR_TYPE); +} + +pub(super) fn for_binder(p: &mut Parser<'_>) { + assert!(p.at(T![for])); + p.bump(T![for]); + if p.at(T![<]) { + generic_params::opt_generic_param_list(p); + } else { + p.error("expected `<`"); + } +} + +// test for_type +// type A = for<'a> fn() -> (); +// type B = for<'a> unsafe extern "C" fn(&'a ()) -> (); +// type Obj = for<'a> PartialEq<&'a i32>; +pub(super) fn for_type(p: &mut Parser<'_>, allow_bounds: bool) { + assert!(p.at(T![for])); + let m = p.start(); + for_binder(p); + match p.current() { + T![fn] | T![unsafe] | T![extern] => {} + // OK: legacy trait object format + _ if paths::is_use_path_start(p) => {} + _ => { + p.error("expected a function pointer or path"); + } + } + type_no_bounds(p); + let completed = m.complete(p, FOR_TYPE); + + // test no_dyn_trait_leading_for + // type A = for<'a> Test<'a> + Send; + if allow_bounds { + opt_type_bounds_as_dyn_trait_type(p, completed); + } +} + +// test impl_trait_type +// type A = impl Iterator<Item=Foo<'a>> + 'a; +fn impl_trait_type(p: &mut Parser<'_>) { + assert!(p.at(T![impl])); + let m = p.start(); + p.bump(T![impl]); + generic_params::bounds_without_colon(p); + m.complete(p, IMPL_TRAIT_TYPE); +} + +// test dyn_trait_type +// type A = dyn Iterator<Item=Foo<'a>> + 'a; +fn dyn_trait_type(p: &mut Parser<'_>) { + assert!(p.at(T![dyn])); + let m = p.start(); + p.bump(T![dyn]); + generic_params::bounds_without_colon(p); + m.complete(p, DYN_TRAIT_TYPE); +} + +// test path_type +// type A = Foo; +// type B = ::Foo; +// type C = self::Foo; +// type D = super::Foo; +pub(super) fn path_type(p: &mut Parser<'_>) { + path_type_(p, true); +} + +// test macro_call_type +// type A = foo!(); +// type B = crate::foo!(); +fn path_or_macro_type_(p: &mut Parser<'_>, allow_bounds: bool) { + assert!(paths::is_path_start(p)); + let r = p.start(); + let m = p.start(); + + paths::type_path(p); + + let kind = if p.at(T![!]) && !p.at(T![!=]) { + items::macro_call_after_excl(p); + m.complete(p, MACRO_CALL); + MACRO_TYPE + } else { + m.abandon(p); + PATH_TYPE + }; + + let path = r.complete(p, kind); + + if allow_bounds { + opt_type_bounds_as_dyn_trait_type(p, path); + } +} + +pub(super) fn path_type_(p: &mut Parser<'_>, allow_bounds: bool) { + assert!(paths::is_path_start(p)); + let m = p.start(); + paths::type_path(p); + + // test path_type_with_bounds + // fn foo() -> Box<T + 'f> {} + // fn foo() -> Box<dyn T + 'f> {} + let path = m.complete(p, PATH_TYPE); + if allow_bounds { + opt_type_bounds_as_dyn_trait_type(p, path); + } +} + +/// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE +/// with a TYPE_BOUND_LIST +fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedMarker) { + assert!(matches!( + type_marker.kind(), + SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE + )); + if !p.at(T![+]) { + return; + } + + // First create a TYPE_BOUND from the completed PATH_TYPE + let m = type_marker.precede(p).complete(p, TYPE_BOUND); + + // Next setup a marker for the TYPE_BOUND_LIST + let m = m.precede(p); + + // This gets consumed here so it gets properly set + // in the TYPE_BOUND_LIST + p.eat(T![+]); + + // Parse rest of the bounds into the TYPE_BOUND_LIST + let m = generic_params::bounds_without_colon_m(p, m); + + // Finally precede everything with DYN_TRAIT_TYPE + m.precede(p).complete(p, DYN_TRAIT_TYPE); +} |