From 50a02eb3593591a02677e1b56f24d7ff0459b9d0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 12 Aug 2020 17:06:49 +0200 Subject: Rename ra_parser -> parser --- crates/parser/src/grammar/attributes.rs | 48 ++ crates/parser/src/grammar/expressions.rs | 651 ++++++++++++++++++++++++++ crates/parser/src/grammar/expressions/atom.rs | 611 ++++++++++++++++++++++++ crates/parser/src/grammar/items.rs | 432 +++++++++++++++++ crates/parser/src/grammar/items/adt.rs | 178 +++++++ crates/parser/src/grammar/items/consts.rs | 33 ++ crates/parser/src/grammar/items/traits.rs | 153 ++++++ crates/parser/src/grammar/items/use_item.rs | 132 ++++++ crates/parser/src/grammar/params.rs | 188 ++++++++ crates/parser/src/grammar/paths.rs | 115 +++++ crates/parser/src/grammar/patterns.rs | 379 +++++++++++++++ crates/parser/src/grammar/type_args.rs | 63 +++ crates/parser/src/grammar/type_params.rs | 209 +++++++++ crates/parser/src/grammar/types.rs | 324 +++++++++++++ 14 files changed, 3516 insertions(+) create mode 100644 crates/parser/src/grammar/attributes.rs create mode 100644 crates/parser/src/grammar/expressions.rs create mode 100644 crates/parser/src/grammar/expressions/atom.rs create mode 100644 crates/parser/src/grammar/items.rs create mode 100644 crates/parser/src/grammar/items/adt.rs create mode 100644 crates/parser/src/grammar/items/consts.rs create mode 100644 crates/parser/src/grammar/items/traits.rs create mode 100644 crates/parser/src/grammar/items/use_item.rs create mode 100644 crates/parser/src/grammar/params.rs create mode 100644 crates/parser/src/grammar/paths.rs create mode 100644 crates/parser/src/grammar/patterns.rs create mode 100644 crates/parser/src/grammar/type_args.rs create mode 100644 crates/parser/src/grammar/type_params.rs create mode 100644 crates/parser/src/grammar/types.rs (limited to 'crates/parser/src/grammar') diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs new file mode 100644 index 000000000..f3158ade3 --- /dev/null +++ b/crates/parser/src/grammar/attributes.rs @@ -0,0 +1,48 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn inner_attributes(p: &mut Parser) { + while p.at(T![#]) && p.nth(1) == T![!] { + attribute(p, true) + } +} + +pub(super) fn outer_attributes(p: &mut Parser) { + while p.at(T![#]) { + attribute(p, false) + } +} + +fn attribute(p: &mut Parser, inner: bool) { + let attr = p.start(); + assert!(p.at(T![#])); + p.bump(T![#]); + + if inner { + assert!(p.at(T![!])); + p.bump(T![!]); + } + + if p.eat(T!['[']) { + paths::use_path(p); + + match p.current() { + T![=] => { + p.bump(T![=]); + if expressions::literal(p).is_none() { + p.error("expected literal"); + } + } + T!['('] | T!['['] | T!['{'] => items::token_tree(p), + _ => {} + } + + if !p.eat(T![']']) { + p.error("expected `]`"); + } + } else { + p.error("expected `[`"); + } + attr.complete(p, ATTR); +} diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs new file mode 100644 index 000000000..3291e3f14 --- /dev/null +++ b/crates/parser/src/grammar/expressions.rs @@ -0,0 +1,651 @@ +//! FIXME: write short doc here + +mod atom; + +pub(crate) use self::atom::{block_expr, match_arm_list}; +pub(super) use self::atom::{literal, LITERAL_FIRST}; +use super::*; + +pub(super) enum StmtWithSemi { + Yes, + No, + Optional, +} + +const EXPR_FIRST: TokenSet = LHS_FIRST; + +pub(super) fn expr(p: &mut Parser) -> (Option, BlockLike) { + let r = Restrictions { forbid_structs: false, prefer_stmt: false }; + expr_bp(p, r, 1) +} + +pub(super) fn expr_with_attrs(p: &mut Parser) -> bool { + let m = p.start(); + let has_attrs = p.at(T![#]); + attributes::outer_attributes(p); + + let (cm, _block_like) = expr(p); + let success = cm.is_some(); + + match (has_attrs, cm) { + (true, Some(cm)) => { + let kind = cm.kind(); + cm.undo_completion(p).abandon(p); + m.complete(p, kind); + } + _ => m.abandon(p), + } + + success +} + +pub(super) fn expr_stmt(p: &mut Parser) -> (Option, BlockLike) { + let r = Restrictions { forbid_structs: false, prefer_stmt: true }; + expr_bp(p, r, 1) +} + +fn expr_no_struct(p: &mut Parser) { + let r = Restrictions { forbid_structs: true, prefer_stmt: false }; + expr_bp(p, r, 1); +} + +fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { + let forbid = matches!(kind, BIN_EXPR | RANGE_EXPR); + !forbid +} + +pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { + let m = p.start(); + // test attr_on_expr_stmt + // fn foo() { + // #[A] foo(); + // #[B] bar!{} + // #[C] #[D] {} + // #[D] return (); + // } + let has_attrs = p.at(T![#]); + attributes::outer_attributes(p); + + if p.at(T![let]) { + let_stmt(p, m, with_semi); + return; + } + + // test block_items + // fn a() { fn b() {} } + let m = match items::maybe_item(p, m) { + Ok(()) => return, + Err(m) => m, + }; + + let (cm, blocklike) = expr_stmt(p); + let kind = cm.as_ref().map(|cm| cm.kind()).unwrap_or(ERROR); + + if has_attrs && !is_expr_stmt_attr_allowed(kind) { + // test_err attr_on_expr_not_allowed + // fn foo() { + // #[A] 1 + 2; + // #[B] if true {}; + // } + p.error(format!("attributes are not allowed on {:?}", kind)); + } + + if p.at(T!['}']) { + // test attr_on_last_expr_in_block + // fn foo() { + // { #[A] bar!()? } + // #[B] &() + // } + if let Some(cm) = cm { + cm.undo_completion(p).abandon(p); + m.complete(p, kind); + } else { + m.abandon(p); + } + } else { + // test no_semi_after_block + // fn foo() { + // if true {} + // loop {} + // match () {} + // while true {} + // for _ in () {} + // {} + // {} + // macro_rules! test { + // () => {} + // } + // test!{} + // } + + match with_semi { + StmtWithSemi::Yes => { + if blocklike.is_block() { + p.eat(T![;]); + } else { + p.expect(T![;]); + } + } + StmtWithSemi::No => {} + StmtWithSemi::Optional => { + if p.at(T![;]) { + p.eat(T![;]); + } + } + } + + m.complete(p, EXPR_STMT); + } + + // test let_stmt + // fn foo() { + // let a; + // let b: i32; + // let c = 92; + // let d: i32 = 92; + // let e: !; + // let _: ! = {}; + // let f = #[attr]||{}; + // } + fn let_stmt(p: &mut Parser, m: Marker, with_semi: StmtWithSemi) { + assert!(p.at(T![let])); + p.bump(T![let]); + patterns::pattern(p); + if p.at(T![:]) { + types::ascription(p); + } + if p.eat(T![=]) { + expressions::expr_with_attrs(p); + } + + match with_semi { + StmtWithSemi::Yes => { + p.expect(T![;]); + } + StmtWithSemi::No => {} + StmtWithSemi::Optional => { + if p.at(T![;]) { + p.eat(T![;]); + } + } + } + m.complete(p, LET_STMT); + } +} + +pub(super) fn expr_block_contents(p: &mut Parser) { + // This is checked by a validator + attributes::inner_attributes(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 {}; + // } + + if p.at(T![;]) { + p.bump(T![;]); + continue; + } + + stmt(p, StmtWithSemi::Yes) + } +} + +#[derive(Clone, Copy)] +struct Restrictions { + forbid_structs: bool, + prefer_stmt: bool, +} + +/// Binding powers of operators for a Pratt parser. +/// +/// See https://www.oilshell.org/blog/2016/11/03.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![&=]), + 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, mut r: Restrictions, bp: u8) -> (Option, BlockLike) { + let mut lhs = match lhs(p, r) { + Some((lhs, blocklike)) => { + // test stmt_bin_expr_ambiguity + // fn foo() { + // let _ = {1} & 2; + // {1} &2; + // } + if r.prefer_stmt && blocklike.is_block() { + return (Some(lhs), BlockLike::Block); + } + lhs + } + None => return (None, BlockLike::NotBlock), + }; + + 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 foo() { + // 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 foo() { + // 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, 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(token_set![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(IDENT) + && p.at_contextual_kw("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![..]].iter() { + 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, r, 2); + } + return Some((m.complete(p, RANGE_EXPR), 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)?; + return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block()))); + } + }; + // parse the interior of the unary expression + expr_bp(p, r, 255); + Some((m.complete(p, kind), 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 { + 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(::func()); +// f(::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::(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); + type_args::opt_type_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(); +// } + +// test_err bad_tuple_index_expr +// fn foo() { +// x.0.; +// x.1i32; +// x.0x01; +// } +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_with_attrs(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::; +// 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_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), 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_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_attributes(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![..]); + 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/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs new file mode 100644 index 000000000..0b01d3bc6 --- /dev/null +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -0,0 +1,611 @@ +//! FIXME: write short doc here + +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 = token_set![ + TRUE_KW, + FALSE_KW, + INT_NUMBER, + FLOAT_NUMBER, + BYTE, + CHAR, + STRING, + RAW_STRING, + BYTE_STRING, + RAW_BYTE_STRING +]; + +pub(crate) fn literal(p: &mut Parser) -> Option { + 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(token_set![ + T!['('], + T!['{'], + T!['['], + L_DOLLAR, + T![|], + T![move], + T![box], + T![if], + T![while], + T![match], + T![unsafe], + T![return], + T![break], + T![continue], + T![async], + T![try], + T![loop], + T![for], + LIFETIME, + ]); + +const EXPR_RECOVERY_SET: TokenSet = token_set![LET_KW, R_DOLLAR]; + +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), + L_DOLLAR => meta_var_expr(p), + T![|] => lambda_expr(p), + T![move] if la == T![|] => lambda_expr(p), + T![async] if la == T![|] || (la == T![move] && p.nth(2) == T![|]) => lambda_expr(p), + T![if] => if_expr(p), + + T![loop] => loop_expr(p, None), + T![box] => box_expr(p, None), + T![for] => for_expr(p, None), + T![while] => while_expr(p, None), + T![try] => try_block_expr(p, None), + LIFETIME 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!['{'] => { + block_expr(p); + m.complete(p, EFFECT_EXPR) + } + _ => { + // test_err misplaced_label_err + // fn main() { + // 'loop: impl + // } + p.error("expected a loop"); + m.complete(p, ERROR); + return None; + } + } + } + T![async] if la == T!['{'] || (la == T![move] && p.nth(2) == T!['{']) => { + let m = p.start(); + p.bump(T![async]); + p.eat(T![move]); + block_expr(p); + m.complete(p, EFFECT_EXPR) + } + T![match] => match_expr(p), + // test unsafe_block + // fn f() { unsafe { } } + T![unsafe] if la == T!['{'] => { + let m = p.start(); + p.bump(T![unsafe]); + block_expr(p); + m.complete(p, EFFECT_EXPR) + } + T!['{'] => { + // test for_range_from + // fn foo() { + // for x in 0 .. { + // break; + // } + // } + block_expr_unchecked(p) + } + T![return] => return_expr(p), + T![continue] => continue_expr(p), + T![break] => break_expr(p, r), + _ => { + 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 | EFFECT_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; + if !p.at_ts(EXPR_FIRST) { + p.error("expected expression"); + break; + } + expr(p); + 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_with_attrs(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 || {}; +// } +fn lambda_expr(p: &mut Parser) -> CompletedMarker { + assert!( + p.at(T![|]) + || (p.at(T![move]) && p.nth(1) == T![|]) + || (p.at(T![async]) && p.nth(1) == T![|]) + || (p.at(T![async]) && p.nth(1) == T![move] && p.nth(2) == T![|]) + ); + let m = p.start(); + p.eat(T![async]); + p.eat(T![move]); + params::param_list_closure(p); + if opt_fn_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]); + cond(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) && p.nth(1) == T![:]); + let m = p.start(); + p.bump(LIFETIME); + p.bump_any(); + m.complete(p, LABEL); +} + +// test loop_expr +// fn foo() { +// loop {}; +// } +fn loop_expr(p: &mut Parser, m: Option) -> 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) -> CompletedMarker { + assert!(p.at(T![while])); + let m = m.unwrap_or_else(|| p.start()); + p.bump(T![while]); + cond(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) -> 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 cond +// fn foo() { if let Some(_) = None {} } +// fn bar() { +// if let Some(_) | Some(_) = None {} +// if let | Some(_) = None {} +// while let Some(_) | Some(_) = None {} +// while let | Some(_) = None {} +// } +fn cond(p: &mut Parser) { + let m = p.start(); + if p.eat(T![let]) { + patterns::pattern_top(p); + p.expect(T![=]); + } + expr_no_struct(p); + m.complete(p, CONDITION); +} + +// 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_attributes(p); + + while !p.at(EOF) && !p.at(T!['}']) { + if p.at(T!['{']) { + error_block(p, "expected match arm"); + continue; + } + + // test match_arms_commas + // fn foo() { + // match () { + // _ => (), + // _ => {} + // _ => () + // } + // } + if match_arm(p).is_block() { + p.eat(T![,]); + } else if !p.at(T!['}']) { + p.expect(T![,]); + } + } + 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) -> BlockLike { + 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_attributes(p); + + patterns::pattern_top_r(p, TokenSet::EMPTY); + if p.at(T![if]) { + match_guard(p); + } + p.expect(T![=>]); + let blocklike = expr_stmt(p).1; + m.complete(p, MATCH_ARM); + blocklike +} + +// test match_guard +// fn foo() { +// match () { +// _ if foo => (), +// } +// } +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; + } + block_expr_unchecked(p); +} + +fn block_expr_unchecked(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, BLOCK_EXPR) +} + +// 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 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]); + p.eat(LIFETIME); + 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]); + p.eat(LIFETIME); + // 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) -> 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 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); + return m.complete(p, MACRO_CALL); + } + + p.bump(T![try]); + block_expr(p); + m.complete(p, EFFECT_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) -> 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) +} + +/// Expression from `$var` macro expansion, wrapped in dollars +fn meta_var_expr(p: &mut Parser) -> CompletedMarker { + assert!(p.at(L_DOLLAR)); + let m = p.start(); + p.bump(L_DOLLAR); + let (completed, _is_block) = + expr_bp(p, Restrictions { forbid_structs: false, prefer_stmt: false }, 1); + + match (completed, p.current()) { + (Some(it), R_DOLLAR) => { + p.bump(R_DOLLAR); + m.abandon(p); + it + } + _ => { + while !p.at(R_DOLLAR) { + p.bump_any() + } + p.bump(R_DOLLAR); + m.complete(p, ERROR) + } + } +} diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs new file mode 100644 index 000000000..d091b0fbb --- /dev/null +++ b/crates/parser/src/grammar/items.rs @@ -0,0 +1,432 @@ +//! FIXME: write short doc here + +mod consts; +mod adt; +mod traits; +mod use_item; + +pub(crate) use self::{ + adt::{enum_variant_list, record_field_def_list}, + expressions::{match_arm_list, record_field_list}, + traits::{impl_item_list, trait_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_attributes(p); + while !(stop_on_r_curly && p.at(T!['}']) || p.at(EOF)) { + item_or_macro(p, stop_on_r_curly) + } +} + +pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ + FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, + CRATE_KW, USE_KW, MACRO_KW +]; + +pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) { + let m = p.start(); + attributes::outer_attributes(p); + let m = match maybe_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); + } else { + m.abandon(p); + if p.at(T!['{']) { + error_block(p, "expected an item"); + } else if p.at(T!['}']) && !stop_on_r_curly { + let e = p.start(); + p.error("unmatched `}`"); + p.bump(T!['}']); + e.complete(p, ERROR); + } else if !p.at(EOF) && !p.at(T!['}']) { + p.err_and_bump("expected an item"); + } else { + p.error("expected an item"); + } + } +} + +pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { + // test_err pub_expr + // fn foo() { pub 92; } + let has_visibility = opt_visibility(p); + + let m = match items_without_modifiers(p, m) { + Ok(()) => return Ok(()), + Err(m) => m, + }; + + let mut has_mods = false; + + // modifiers + has_mods |= p.eat(T![const]); + + // test_err async_without_semicolon + // fn foo() { let _ = async {} } + if p.at(T![async]) && p.nth(1) != T!['{'] && p.nth(1) != T![move] && p.nth(1) != 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_mods = true; + abi(p); + } + if p.at(IDENT) && p.at_contextual_kw("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(IDENT) && p.at_contextual_kw("default") { + match p.nth(1) { + T![fn] | T![type] | T![const] | T![impl] => { + p.bump_remap(T![default]); + has_mods = true; + } + T![unsafe] => { + // test default_unsafe_item + // default unsafe impl T for Foo { + // default unsafe fn foo() {} + // } + if matches!(p.nth(2), T![impl] | T![fn]) { + p.bump_remap(T![default]); + p.bump(T![unsafe]); + has_mods = true; + } + } + _ => (), + } + } + + // test existential_type + // existential type Foo: Fn() -> usize; + if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] { + p.bump_remap(T![existential]); + has_mods = true; + } + + // items + match p.current() { + // test fn + // fn foo() {} + T![fn] => { + fn_def(p); + m.complete(p, FN); + } + + // test trait + // trait T {} + T![trait] => { + traits::trait_def(p); + m.complete(p, TRAIT); + } + + T![const] => { + consts::const_def(p, m); + } + + // test impl + // impl T for S {} + T![impl] => { + traits::impl_def(p); + m.complete(p, IMPL); + } + + T![type] => { + type_def(p, m); + } + _ => { + if !has_visibility && !has_mods { + return Err(m); + } else { + if has_mods { + p.error("expected existential, fn, trait or impl"); + } else { + p.error("expected an item"); + } + m.complete(p, ERROR); + } + } + } + Ok(()) +} + +fn items_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> { + let la = p.nth(1); + match p.current() { + // test extern_crate + // extern crate foo; + T![extern] if la == T![crate] => extern_crate_item(p, m), + T![type] => { + type_def(p, m); + } + T![mod] => mod_item(p, m), + T![struct] => { + // test struct_items + // struct Foo; + // struct Foo {} + // struct Foo(); + // struct Foo(String, usize); + // struct Foo { + // a: i32, + // b: f32, + // } + adt::struct_def(p, m); + } + // test pub_macro_def + // pub macro m($:ident) {} + T![macro] => { + macro_def(p, m); + } + IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => { + // test union_items + // union Foo {} + // union Foo { + // a: i32, + // b: f32, + // } + adt::union_def(p, m); + } + T![enum] => adt::enum_def(p, m), + T![use] => use_item::use_item(p, m), + T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::const_def(p, m), + T![static] => consts::static_def(p, m), + // test extern_block + // extern {} + T![extern] + if la == T!['{'] || ((la == STRING || la == RAW_STRING) && p.nth(2) == T!['{']) => + { + abi(p); + extern_item_list(p); + m.complete(p, EXTERN_BLOCK); + } + _ => return Err(m), + }; + Ok(()) +} + +fn extern_crate_item(p: &mut Parser, m: Marker) { + assert!(p.at(T![extern])); + p.bump(T![extern]); + assert!(p.at(T![crate])); + p.bump(T![crate]); + + if p.at(T![self]) { + p.bump(T![self]); + } else { + name_ref(p); + } + + opt_alias(p); + p.expect(T![;]); + m.complete(p, EXTERN_CRATE); +} + +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 fn_def(p: &mut Parser) { + assert!(p.at(T![fn])); + p.bump(T![fn]); + + name_r(p, ITEM_RECOVERY_SET); + // test function_type_params + // fn foo(){} + type_params::opt_type_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_fn_ret_type(p); + + // test function_where_clause + // fn foo() where T: Copy {} + type_params::opt_where_clause(p); + + // test fn_decl + // trait T { fn foo(); } + if p.at(T![;]) { + p.bump(T![;]); + } else { + expressions::block_expr(p) + } +} + +// test type_item +// type Foo = Bar; +fn type_def(p: &mut Parser, m: Marker) { + assert!(p.at(T![type])); + p.bump(T![type]); + + name(p); + + // test type_item_type_params + // type Result = (); + type_params::opt_type_param_list(p); + + if p.at(T![:]) { + type_params::bounds(p); + } + + // test type_item_where_clause + // type Foo where Foo: Copy = (); + type_params::opt_where_clause(p); + if p.eat(T![=]) { + types::type_(p); + } + p.expect(T![;]); + m.complete(p, TYPE_ALIAS); +} + +pub(crate) fn mod_item(p: &mut Parser, m: Marker) { + assert!(p.at(T![mod])); + p.bump(T![mod]); + + name(p); + if p.at(T!['{']) { + mod_item_list(p); + } else if !p.eat(T![;]) { + p.error("expected `;` or `{`"); + } + m.complete(p, MODULE); +} + +pub(crate) fn mod_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); +} + +// test macro_def +// macro m { ($i:ident) => {} } +// 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!['{']) { + token_tree(p); + } else if !p.at(T!['(']) { + p.error("unmatched `(`"); + } else { + let m = p.start(); + token_tree(p); + match p.current() { + T!['{'] | T!['['] | T!['('] => token_tree(p), + _ => p.error("expected `{`, `[`, `(`"), + } + m.complete(p, TOKEN_TREE); + } + + m.complete(p, MACRO_DEF); +} + +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![!]); + 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() { + 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/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs new file mode 100644 index 000000000..addfb59d4 --- /dev/null +++ b/crates/parser/src/grammar/items/adt.rs @@ -0,0 +1,178 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn struct_def(p: &mut Parser, m: Marker) { + assert!(p.at(T![struct])); + p.bump(T![struct]); + struct_or_union(p, m, T![struct], STRUCT); +} + +pub(super) fn union_def(p: &mut Parser, m: Marker) { + assert!(p.at_contextual_kw("union")); + p.bump_remap(T![union]); + struct_or_union(p, m, T![union], UNION); +} + +fn struct_or_union(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) { + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + match p.current() { + T![where] => { + type_params::opt_where_clause(p); + match p.current() { + T![;] => { + p.bump(T![;]); + } + T!['{'] => record_field_def_list(p), + _ => { + //FIXME: special case `(` error message + p.error("expected `;` or `{`"); + } + } + } + T![;] if kw == T![struct] => { + p.bump(T![;]); + } + T!['{'] => record_field_def_list(p), + T!['('] if kw == T![struct] => { + tuple_field_def_list(p); + // test tuple_struct_where + // struct Test(T) where T: Clone; + // struct Test(T); + type_params::opt_where_clause(p); + p.expect(T![;]); + } + _ if kw == T![struct] => { + p.error("expected `;`, `{`, or `(`"); + } + _ => { + p.error("expected `{`"); + } + } + m.complete(p, def); +} + +pub(super) fn enum_def(p: &mut Parser, m: Marker) { + assert!(p.at(T![enum])); + p.bump(T![enum]); + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + type_params::opt_where_clause(p); + if p.at(T!['{']) { + enum_variant_list(p); + } else { + p.error("expected `{`") + } + m.complete(p, ENUM); +} + +pub(crate) fn enum_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; + } + let var = p.start(); + attributes::outer_attributes(p); + if p.at(IDENT) { + name(p); + match p.current() { + T!['{'] => record_field_def_list(p), + T!['('] => tuple_field_def_list(p), + _ => (), + } + + // test variant_discriminant + // enum E { X(i32) = 10 } + if p.eat(T![=]) { + expressions::expr(p); + } + var.complete(p, VARIANT); + } else { + var.abandon(p); + p.err_and_bump("expected enum variant"); + } + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, VARIANT_LIST); +} + +pub(crate) fn record_field_def_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_def(p); + if !p.at(T!['}']) { + p.expect(T![,]); + } + } + p.expect(T!['}']); + m.complete(p, RECORD_FIELD_LIST); + + fn record_field_def(p: &mut Parser) { + let m = p.start(); + // test record_field_attrs + // struct S { + // #[serde(with = "url_serde")] + // pub uri: Uri, + // } + attributes::outer_attributes(p); + opt_visibility(p); + 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_def_list(p: &mut Parser) { + assert!(p.at(T!['('])); + let m = p.start(); + if !p.expect(T!['(']) { + return; + } + while !p.at(T![')']) && !p.at(EOF) { + let m = p.start(); + // test tuple_field_attrs + // struct S ( + // #[serde(with = "url_serde")] + // pub Uri, + // ); + // + // enum S { + // Uri(#[serde(with = "url_serde")] Uri), + // } + attributes::outer_attributes(p); + opt_visibility(p); + 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/crates/parser/src/grammar/items/consts.rs b/crates/parser/src/grammar/items/consts.rs new file mode 100644 index 000000000..35ad766dc --- /dev/null +++ b/crates/parser/src/grammar/items/consts.rs @@ -0,0 +1,33 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn static_def(p: &mut Parser, m: Marker) { + const_or_static(p, m, T![static], STATIC) +} + +pub(super) fn const_def(p: &mut Parser, m: Marker) { + const_or_static(p, m, T![const], CONST) +} + +fn const_or_static(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) { + assert!(p.at(kw)); + p.bump(kw); + p.eat(T![mut]); // FIXME: validator to forbid const mut + + // Allow `_` in place of an identifier in a `const`. + let is_const_underscore = kw == T![const] && p.eat(T![_]); + if !is_const_underscore { + name(p); + } + + // test_err static_underscore + // static _: i32 = 5; + + types::ascription(p); + if p.eat(T![=]) { + expressions::expr(p); + } + p.expect(T![;]); + m.complete(p, def); +} diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs new file mode 100644 index 000000000..751ce65f2 --- /dev/null +++ b/crates/parser/src/grammar/items/traits.rs @@ -0,0 +1,153 @@ +//! FIXME: write short doc here + +use super::*; + +// test trait_item +// trait T: Hash + Clone where U: Copy {} +// trait X: Hash + Clone where U: Copy {} +pub(super) fn trait_def(p: &mut Parser) { + assert!(p.at(T![trait])); + p.bump(T![trait]); + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + // test trait_alias + // trait Z = T; + // trait Z = T where U: Copy; + // trait Z = where Self: T; + if p.eat(T![=]) { + type_params::bounds_without_colon(p); + type_params::opt_where_clause(p); + p.expect(T![;]); + return; + } + if p.at(T![:]) { + type_params::bounds(p); + } + type_params::opt_where_clause(p); + if p.at(T!['{']) { + trait_item_list(p); + } else { + p.error("expected `{`"); + } +} + +// test trait_item_list +// impl F { +// type A: Clone; +// const B: i32; +// fn foo() {} +// fn bar(&self); +// } +pub(crate) fn trait_item_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 an item"); + continue; + } + item_or_macro(p, true); + } + p.expect(T!['}']); + m.complete(p, ASSOC_ITEM_LIST); +} + +// test impl_def +// impl Foo {} +pub(super) fn impl_def(p: &mut Parser) { + assert!(p.at(T![impl])); + p.bump(T![impl]); + if choose_type_params_over_qpath(p) { + type_params::opt_type_param_list(p); + } + + // FIXME: never type + // impl ! {} + + // test impl_def_neg + // impl !Send for X {} + p.eat(T![!]); + impl_type(p); + if p.eat(T![for]) { + impl_type(p); + } + type_params::opt_where_clause(p); + if p.at(T!['{']) { + impl_item_list(p); + } else { + p.error("expected `{`"); + } +} + +// test impl_item_list +// impl F { +// type A = i32; +// const B: i32 = 92; +// fn foo() {} +// fn bar(&self) {} +// } +pub(crate) fn impl_item_list(p: &mut Parser) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + // test impl_inner_attributes + // enum F{} + // impl F { + // //! This is a doc comment + // #![doc("This is also a doc comment")] + // } + attributes::inner_attributes(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 Bar {} +fn choose_type_params_over_qpath(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) `>` - single generic parameter + // `<` (LIFETIME|IDENT) `,` - first generic parameter in a list + // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds + // `<` (LIFETIME|IDENT) `=` - generic parameter with a default + // The only truly ambiguous case is + // `<` IDENT `>` `::` IDENT ... + // we disambiguate it in favor of generics (`impl ::absolute::Path { ... }`) + // because this is what almost always expected in practice, qualified paths in impls + // (`impl ::AssocTy { ... }`) aren't even allowed by type checker at the moment. + if !p.at(T![<]) { + return false; + } + if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == CONST_KW { + return true; + } + (p.nth(1) == LIFETIME || 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/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs new file mode 100644 index 000000000..8e836a77e --- /dev/null +++ b/crates/parser/src/grammar/items/use_item.rs @@ -0,0 +1,132 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn use_item(p: &mut Parser, m: Marker) { + assert!(p.at(T![use])); + p.bump(T![use]); + use_tree(p, true); + p.expect(T![;]); + m.complete(p, USE); +} + +/// Parse a use 'tree', such as `some::path` in `use some::path;` +/// Note that this is called both by `use_item` and `use_tree_list`, +/// so handles both `some::path::{inner::path}` and `inner::path` in +/// `use some::path::{inner::path};` +fn use_tree(p: &mut Parser, top_level: bool) { + let m = p.start(); + match p.current() { + // Finish the use_tree for cases of e.g. + // `use some::path::{self, *};` or `use *;` + // This does not handle cases such as `use some::path::*` + // N.B. in Rust 2015 `use *;` imports all from crate root + // however in Rust 2018 `use *;` errors: ('cannot glob-import all possible crates') + // FIXME: Add this error (if not out of scope) + + // test use_star + // use *; + // use ::*; + // use some::path::{*}; + // use some::path::{::*}; + T![*] => p.bump(T![*]), + T![:] if p.at(T![::]) && p.nth(2) == T![*] => { + // Parse `use ::*;`, which imports all from the crate root in Rust 2015 + // This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`) + // but still parses and errors later: ('crate root in paths can only be used in start position') + // FIXME: Add this error (if not out of scope) + // In Rust 2018, it is always invalid (see above) + p.bump(T![::]); + p.bump(T![*]); + } + // Open a use tree list + // Handles cases such as `use {some::path};` or `{inner::path}` in + // `use some::path::{{inner::path}, other::path}` + + // test use_tree_list + // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) + // use {path::from::root}; // Rust 2015 + // use ::{some::arbritrary::path}; // Rust 2015 + // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting + T!['{'] => { + use_tree_list(p); + } + T![:] if p.at(T![::]) && p.nth(2) == T!['{'] => { + p.bump(T![::]); + use_tree_list(p); + } + // Parse a 'standard' path. + // Also handles aliases (e.g. `use something as something_else`) + + // test use_path + // use ::crate_name; // Rust 2018 - All flavours + // use crate_name; // Rust 2018 - Anchored paths + // use item_in_scope_or_crate_name; // Rust 2018 - Uniform Paths + // + // use self::module::Item; + // use crate::Item; + // use self::some::Struct; + // use crate_name::some_item; + _ if paths::is_use_path_start(p) => { + paths::use_path(p); + match p.current() { + T![as] => { + // test use_alias + // use some::path as some_name; + // use some::{ + // other::path as some_other_name, + // different::path as different_name, + // yet::another::path, + // running::out::of::synonyms::for_::different::* + // }; + // use Trait as _; + opt_alias(p); + } + T![:] if p.at(T![::]) => { + p.bump(T![::]); + match p.current() { + T![*] => { + p.bump(T![*]); + } + // test use_tree_list_after_path + // use crate::{Item}; + // use self::{Item}; + T!['{'] => use_tree_list(p), + _ => { + // is this unreachable? + 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); +} + +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/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs new file mode 100644 index 000000000..f0da173cc --- /dev/null +++ b/crates/parser/src/grammar/params.rs @@ -0,0 +1,188 @@ +//! FIXME: write short doc here + +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)>(){} +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 m = p.start(); + p.bump(bra); + + if let FnDef = flavor { + // test self_param_outer_attr + // fn f(#[must_use] self) {} + attributes::outer_attributes(p); + opt_self_param(p); + } + + while !p.at(EOF) && !p.at(ket) { + // test param_outer_arg + // fn f(#[attr1] pat: Type) {} + attributes::outer_attributes(p); + + if !p.at_ts(VALUE_PARAMETER_FIRST) { + p.error("expected value parameter"); + break; + } + let param = value_parameter(p, flavor); + if !p.at(ket) { + p.expect(T![,]); + } + if let Variadic(true) = param { + break; + } + } + + p.expect(ket); + m.complete(p, PARAM_LIST); +} + +const VALUE_PARAMETER_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); + +struct Variadic(bool); + +fn value_parameter(p: &mut Parser, flavor: Flavor) -> Variadic { + let mut res = Variadic(false); + let m = p.start(); + match flavor { + // test param_list_vararg + // extern "C" { fn printf(format: *const i8, ...) -> i32; } + Flavor::FnDef | Flavor::FnPointer if p.eat(T![...]) => res = Variadic(true), + + // test fn_def_param + // fn foo((x, y): (i32, i32)) {} + Flavor::FnDef => { + patterns::pattern(p); + if variadic_param(p) { + res = Variadic(true) + } else { + types::ascription(p); + } + } + // test value_parameters_no_patterns + // type F = Box; + 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) { + res = Variadic(true) + } else { + types::ascription(p); + } + } 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); + res +} + +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) { + let m; + if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { + m = p.start(); + p.eat(T![mut]); + p.eat(T![self]); + // test arb_self_types + // impl S { + // fn a(self: &Self) {} + // fn b(mut self: Box) {} + // } + if p.at(T![:]) { + types::ascription(p); + } + } else { + let la1 = p.nth(1); + let la2 = p.nth(2); + let la3 = p.nth(3); + let n_toks = match (p.current(), la1, la2, la3) { + (T![&], T![self], _, _) => 2, + (T![&], T![mut], T![self], _) => 3, + (T![&], LIFETIME, T![self], _) => 3, + (T![&], LIFETIME, T![mut], T![self]) => 4, + _ => return, + }; + m = p.start(); + for _ in 0..n_toks { + p.bump_any(); + } + } + m.complete(p, SELF_PARAM); + if !p.at(T![')']) { + p.expect(T![,]); + } +} diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs new file mode 100644 index 000000000..b503af1dc --- /dev/null +++ b/crates/parser/src/grammar/paths.rs @@ -0,0 +1,115 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) const PATH_FIRST: TokenSet = + token_set![IDENT, T![self], T![super], T![crate], T![:], T![<]]; + +pub(super) fn is_path_start(p: &Parser) -> bool { + is_use_path_start(p) || p.at(T![<]) +} + +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) +} + +#[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 mut qual = path.complete(p, PATH); + loop { + let use_tree = 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 { + break; + } + } +} + +fn path_segment(p: &mut Parser, mode: Mode, first: bool) { + let m = p.start(); + // test qual_paths + // type X = ::Output; + // fn foo() { ::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] => p.bump_any(), + _ => { + 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 ()>; + if p.at(T!['(']) { + params::param_list_fn_trait(p); + opt_fn_ret_type(p); + } else { + type_args::opt_type_arg_list(p, false) + } + } + Mode::Expr => type_args::opt_type_arg_list(p, true), + } +} diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs new file mode 100644 index 000000000..716bdc978 --- /dev/null +++ b/crates/parser/src/grammar/patterns.rs @@ -0,0 +1,379 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST + .union(paths::PATH_FIRST) + .union(token_set![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=> (), + // } + // } + for &range_op in [T![...], T![..=], T![..]].iter() { + if p.at(range_op) { + let m = lhs.precede(p); + p.bump(range_op); + atom_pat(p, recovery_set); + m.complete(p, RANGE_PAT); + return; + } + } + } +} + +const PAT_RECOVERY_SET: TokenSet = + token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]; + +fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option { + let m = match p.nth(0) { + T![box] => box_pat(p), + T![ref] | T![mut] => bind_pat(p, true), + 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), + _ => bind_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![..]) => dot_dot_pat(p), + T![_] => placeholder_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_field_pat_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_field_pat_list +// fn foo() { +// let S {} = (); +// let S { f, ref mut g } = (); +// let S { h: _, ..} = (); +// let S { h: _, } = (); +// } +fn record_field_pat_list(p: &mut Parser) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + while !p.at(EOF) && !p.at(T!['}']) { + match p.current() { + // A trailing `..` is *not* treated as a REST_PAT. + T![.] if p.at(T![..]) => p.bump(T![..]), + T!['{'] => error_block(p, "expected ident"), + + c => { + let m = p.start(); + match c { + // test record_field_pat + // fn foo() { + // let S { 0: 1 } = (); + // let S { x: 1 } = (); + // } + 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); + } + _ => { + bind_pat(p, false); + } + } + 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 placeholder_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 dot_dot_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 bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker { + let m = p.start(); + p.eat(T![ref]); + p.eat(T![mut]); + name(p); + 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) +} diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs new file mode 100644 index 000000000..aef7cd6fb --- /dev/null +++ b/crates/parser/src/grammar/type_args.rs @@ -0,0 +1,63 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn opt_type_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![>]) { + type_arg(p); + if !p.at(T![>]) && !p.expect(T![,]) { + break; + } + } + p.expect(T![>]); + m.complete(p, GENERIC_ARG_LIST); +} + +// test type_arg +// type A = B<'static, i32, 1, { 2 }, Item=u64>; +fn type_arg(p: &mut Parser) { + let m = p.start(); + match p.current() { + LIFETIME => { + p.bump(LIFETIME); + m.complete(p, LIFETIME_ARG); + } + // test associated_type_bounds + // fn print_all>(printables: T) {} + IDENT if p.nth(1) == T![:] && p.nth(2) != T![:] => { + name_ref(p); + type_params::bounds(p); + m.complete(p, ASSOC_TYPE_ARG); + } + IDENT if p.nth(1) == T![=] => { + name_ref(p); + p.bump_any(); + types::type_(p); + m.complete(p, ASSOC_TYPE_ARG); + } + T!['{'] => { + expressions::block_expr(p); + m.complete(p, CONST_ARG); + } + k if k.is_literal() => { + expressions::literal(p); + m.complete(p, CONST_ARG); + } + _ => { + types::type_(p); + m.complete(p, TYPE_ARG); + } + } +} diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs new file mode 100644 index 000000000..90dabb4c0 --- /dev/null +++ b/crates/parser/src/grammar/type_params.rs @@ -0,0 +1,209 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) fn opt_type_param_list(p: &mut Parser) { + if !p.at(T![<]) { + return; + } + type_param_list(p); +} + +fn type_param_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 generic_lifetime_type_attribute + // fn foo<#[derive(Lifetime)] 'a, #[derive(Type)] T>(_: &'a T) { + // } + attributes::outer_attributes(p); + + match p.current() { + LIFETIME => lifetime_param(p, m), + IDENT => type_param(p, m), + CONST_KW => type_const_param(p, m), + _ => { + m.abandon(p); + p.err_and_bump("expected type parameter") + } + } + if !p.at(T![>]) && !p.expect(T![,]) { + break; + } + } + p.expect(T![>]); + m.complete(p, GENERIC_PARAM_LIST); +} + +fn lifetime_param(p: &mut Parser, m: Marker) { + assert!(p.at(LIFETIME)); + p.bump(LIFETIME); + if p.at(T![:]) { + lifetime_bounds(p); + } + m.complete(p, LIFETIME_PARAM); +} + +fn type_param(p: &mut Parser, m: Marker) { + assert!(p.at(IDENT)); + name(p); + if p.at(T![:]) { + bounds(p); + } + // test type_param_default + // struct S; + if p.at(T![=]) { + p.bump(T![=]); + types::type_(p) + } + m.complete(p, TYPE_PARAM); +} + +// test const_param +// struct S; +fn type_const_param(p: &mut Parser, m: Marker) { + assert!(p.at(CONST_KW)); + p.bump(T![const]); + name(p); + types::ascription(p); + m.complete(p, CONST_PARAM); +} + +// test type_param_bounds +// struct S; +pub(super) fn bounds(p: &mut Parser) { + assert!(p.at(T![:])); + p.bump(T![:]); + bounds_without_colon(p); +} + +fn lifetime_bounds(p: &mut Parser) { + assert!(p.at(T![:])); + p.bump(T![:]); + while p.at(LIFETIME) { + p.bump(LIFETIME); + if !p.eat(T![+]) { + break; + } + } +} + +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) +} + +pub(super) fn bounds_without_colon(p: &mut Parser) { + let m = p.start(); + bounds_without_colon_m(p, m); +} + +fn type_bound(p: &mut Parser) -> bool { + let m = p.start(); + let has_paren = p.eat(T!['(']); + p.eat(T![?]); + match p.current() { + LIFETIME => p.bump(LIFETIME), + T![for] => types::for_type(p), + _ if paths::is_use_path_start(p) => types::path_type_(p, false), + _ => { + 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, +// ::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![,]); + + if is_where_clause_end(p) { + break; + } + + if !comma { + p.error("expected comma"); + } + } + + m.complete(p, WHERE_CLAUSE); +} + +fn is_where_predicate(p: &mut Parser) -> bool { + match p.current() { + LIFETIME => true, + T![impl] => false, + token => types::TYPE_FIRST.contains(token), + } +} + +fn is_where_clause_end(p: &mut Parser) -> bool { + matches!(p.current(), T!['{'] | T![;] | T![=]) +} + +fn where_predicate(p: &mut Parser) { + let m = p.start(); + match p.current() { + LIFETIME => { + p.bump(LIFETIME); + if p.at(T![:]) { + bounds(p); + } else { + p.error("expected colon"); + } + } + T![impl] => { + p.error("expected lifetime or type"); + } + _ => { + // test where_pred_for + // fn for_trait() + // where + // for<'a> F: Fn(&'a str) + // { } + if p.at(T![for]) { + 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/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs new file mode 100644 index 000000000..0aa173a52 --- /dev/null +++ b/crates/parser/src/grammar/types.rs @@ -0,0 +1,324 @@ +//! FIXME: write short doc here + +use super::*; + +pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(token_set![ + T!['('], + T!['['], + T![<], + T![!], + T![*], + T![&], + T![_], + T![fn], + T![unsafe], + T![extern], + T![for], + T![impl], + T![dyn], +]); + +const TYPE_RECOVERY_SET: TokenSet = token_set![R_PAREN, COMMA, L_DOLLAR]; + +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![*] => pointer_type(p), + T!['['] => array_or_slice_type(p), + T![&] => reference_type(p), + T![_] => placeholder_type(p), + T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), + T![for] => for_type(p), + 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_use_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) { + p.expect(T![:]); + 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 pointer_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 reference_type(p: &mut Parser) { + assert!(p.at(T![&])); + let m = p.start(); + p.bump(T![&]); + p.eat(LIFETIME); + p.eat(T![mut]); + type_no_bounds(p); + m.complete(p, REF_TYPE); +} + +// test placeholder_type +// type Placeholder = _; +fn placeholder_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_pointer_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_fn_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![<]) { + type_params::opt_type_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) { + 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); + m.complete(p, FOR_TYPE); +} + +// test impl_trait_type +// type A = impl Iterator> + 'a; +fn impl_trait_type(p: &mut Parser) { + assert!(p.at(T![impl])); + let m = p.start(); + p.bump(T![impl]); + type_params::bounds_without_colon(p); + m.complete(p, IMPL_TRAIT_TYPE); +} + +// test dyn_trait_type +// type A = dyn Iterator> + 'a; +fn dyn_trait_type(p: &mut Parser) { + assert!(p.at(T![dyn])); + let m = p.start(); + p.bump(T![dyn]); + type_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 m = p.start(); + paths::type_path(p); + + let kind = if p.at(T![!]) && !p.at(T![!=]) { + items::macro_call_after_excl(p); + MACRO_CALL + } else { + PATH_TYPE + }; + + let path = m.complete(p, kind); + + if allow_bounds { + opt_path_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 {} + // fn foo() -> Box {} + let path = m.complete(p, PATH_TYPE); + if allow_bounds { + opt_path_type_bounds_as_dyn_trait_type(p, path); + } +} + +/// This turns a parsed PATH_TYPE optionally into a DYN_TRAIT_TYPE +/// with a TYPE_BOUND_LIST +fn opt_path_type_bounds_as_dyn_trait_type(p: &mut Parser, path_type_marker: CompletedMarker) { + if !p.at(T![+]) { + return; + } + + // First create a TYPE_BOUND from the completed PATH_TYPE + let m = path_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 = type_params::bounds_without_colon_m(p, m); + + // Finally precede everything with DYN_TRAIT_TYPE + m.precede(p).complete(p, DYN_TRAIT_TYPE); +} -- cgit v1.2.3