diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs index 417366026b..b0e270426e 100644 --- a/crates/libsyntax2/src/grammar/expressions/atom.rs +++ b/crates/libsyntax2/src/grammar/expressions/atom.rs @@ -13,7 +13,7 @@ use super::*; // let _ = b"e"; // let _ = br"f"; // } -const LITERAL_FIRST: TokenSet = +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]; diff --git a/crates/libsyntax2/src/grammar/expressions/mod.rs b/crates/libsyntax2/src/grammar/expressions/mod.rs index e133c1d9b9..59a0564d9f 100644 --- a/crates/libsyntax2/src/grammar/expressions/mod.rs +++ b/crates/libsyntax2/src/grammar/expressions/mod.rs @@ -1,7 +1,7 @@ mod atom; use super::*; -pub(super) use self::atom::literal; +pub(super) use self::atom::{literal, LITERAL_FIRST}; const EXPR_FIRST: TokenSet = LHS_FIRST; diff --git a/crates/libsyntax2/src/grammar/items/mod.rs b/crates/libsyntax2/src/grammar/items/mod.rs index d236fb5067..206c852802 100644 --- a/crates/libsyntax2/src/grammar/items/mod.rs +++ b/crates/libsyntax2/src/grammar/items/mod.rs @@ -250,8 +250,10 @@ fn function(p: &mut Parser, flavor: ItemFlavor) { // test fn_decl // trait T { fn foo(); } - if !p.eat(SEMI) { + if p.at(L_CURLY) { expressions::block(p); + } else { + p.expect(SEMI); } } diff --git a/crates/libsyntax2/src/grammar/params.rs b/crates/libsyntax2/src/grammar/params.rs index 5b1322b3a9..bc0cb44ba7 100644 --- a/crates/libsyntax2/src/grammar/params.rs +++ b/crates/libsyntax2/src/grammar/params.rs @@ -48,6 +48,10 @@ fn list_(p: &mut Parser, flavor: Flavor) { opt_self_param(p); } while !p.at(EOF) && !p.at(ket) { + if !VALUE_PARAMETER_FIRST.contains(p.current()) { + p.error("expected value parameter"); + break; + } value_parameter(p, flavor); if !p.at(ket) { p.expect(COMMA); @@ -57,6 +61,13 @@ fn list_(p: &mut Parser, flavor: Flavor) { m.complete(p, PARAM_LIST); } + +const VALUE_PARAMETER_FIRST: TokenSet = + token_set_union![ + patterns::PATTERN_FIRST, + types::TYPE_FIRST, + ]; + fn value_parameter(p: &mut Parser, flavor: Flavor) { let m = p.start(); match flavor { diff --git a/crates/libsyntax2/src/grammar/paths.rs b/crates/libsyntax2/src/grammar/paths.rs index 8f5e82d91e..7c9fb8be2c 100644 --- a/crates/libsyntax2/src/grammar/paths.rs +++ b/crates/libsyntax2/src/grammar/paths.rs @@ -1,5 +1,8 @@ use super::*; +pub(super) const PATH_FIRST: TokenSet = + token_set![IDENT, SELF_KW, SUPER_KW, COLONCOLON, L_ANGLE]; + pub(super) fn is_path_start(p: &Parser) -> bool { match p.current() { IDENT | SELF_KW | SUPER_KW | COLONCOLON => true, diff --git a/crates/libsyntax2/src/grammar/patterns.rs b/crates/libsyntax2/src/grammar/patterns.rs index 7ddbfa318a..11852e0d30 100644 --- a/crates/libsyntax2/src/grammar/patterns.rs +++ b/crates/libsyntax2/src/grammar/patterns.rs @@ -1,5 +1,12 @@ use super::*; +pub(super) const PATTERN_FIRST: TokenSet = + token_set_union![ + token_set![REF_KW, MUT_KW, L_PAREN, L_BRACK, AMP], + expressions::LITERAL_FIRST, + paths::PATH_FIRST, + ]; + pub(super) fn pattern(p: &mut Parser) { if let Some(lhs) = atom_pat(p) { // test range_pat diff --git a/crates/libsyntax2/src/grammar/types.rs b/crates/libsyntax2/src/grammar/types.rs index 2088a38e30..89030e66cc 100644 --- a/crates/libsyntax2/src/grammar/types.rs +++ b/crates/libsyntax2/src/grammar/types.rs @@ -1,5 +1,13 @@ use super::*; +pub(super) const TYPE_FIRST: TokenSet = + token_set_union![ + token_set![ + L_PAREN, EXCL, STAR, L_BRACK, AMP, UNDERSCORE, FN_KW, UNSAFE_KW, EXTERN_KW, FOR_KW, IMPL_KW, DYN_KW, L_ANGLE, + ], + paths::PATH_FIRST, + ]; + pub(super) fn type_(p: &mut Parser) { match p.current() { L_PAREN => paren_or_tuple_type(p), diff --git a/crates/libsyntax2/src/lib.rs b/crates/libsyntax2/src/lib.rs index d3ecbe4704..9f9f3ab3a3 100644 --- a/crates/libsyntax2/src/lib.rs +++ b/crates/libsyntax2/src/lib.rs @@ -58,6 +58,10 @@ pub fn parse(text: &str) -> SyntaxNode { res } +#[cfg(not(debug_assertions))] +fn validate_block_structure(_: SyntaxNodeRef) {} + +#[cfg(debug_assertions)] fn validate_block_structure(root: SyntaxNodeRef) { let mut stack = Vec::new(); for node in algo::walk::preorder(root) { @@ -67,7 +71,12 @@ fn validate_block_structure(root: SyntaxNodeRef) { } SyntaxKind::R_CURLY => { if let Some(pair) = stack.pop() { - assert_eq!(node.parent(), pair.parent()); + assert_eq!( + node.parent(), + pair.parent(), + "unpaired curleys:\n{}", + utils::dump_tree(root), + ); assert!( node.next_sibling().is_none() && pair.prev_sibling().is_none(), "floating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", diff --git a/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.rs b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.rs new file mode 100644 index 0000000000..156e70251a --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.rs @@ -0,0 +1,2 @@ +fn foo(}) { +} diff --git a/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt new file mode 100644 index 0000000000..8413797974 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0015_curly_in_params.txt @@ -0,0 +1,24 @@ +FILE@[0; 14) + FN_DEF@[0; 7) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 7) + L_PAREN@[6; 7) + err: `expected value parameter` + err: `expected R_PAREN` + err: `expected SEMI` + err: `expected an item` + ERROR@[7; 8) + R_CURLY@[7; 8) + err: `expected an item` + ERROR@[8; 9) + R_PAREN@[8; 9) + WHITESPACE@[9; 10) + err: `expected an item` + ERROR@[10; 13) + L_CURLY@[10; 11) + WHITESPACE@[11; 12) + R_CURLY@[12; 13) + WHITESPACE@[13; 14)