From 2fa90e736b026ee979d9eb59178dc1f792228250 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 28 Aug 2018 11:12:42 +0300 Subject: better recovery for exprs --- crates/libsyntax2/src/ast/generated.rs | 78 +++++++++++++++++++++- crates/libsyntax2/src/ast/mod.rs | 9 +++ crates/libsyntax2/src/grammar.ron | 10 ++- crates/libsyntax2/src/grammar/expressions/atom.rs | 5 +- crates/libsyntax2/src/parser_api.rs | 19 ++++-- .../data/parser/err/0017_incomplete_binexpr.txt | 5 +- .../tests/data/parser/err/0018_incomplete_fn.txt | 19 +++--- .../tests/data/parser/err/0019_let_recover.rs | 4 ++ .../tests/data/parser/err/0019_let_recover.txt | 39 +++++++++++ 9 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs create mode 100644 crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt (limited to 'crates/libsyntax2') diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index f99d1274a..6181aada8 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -439,6 +439,24 @@ impl<'a> ExprStmt<'a> { } } +// ExternCrateItem +#[derive(Debug, Clone, Copy)] +pub struct ExternCrateItem<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for ExternCrateItem<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + EXTERN_CRATE_ITEM => Some(ExternCrateItem { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> ExternCrateItem<'a> {} + // FieldExpr #[derive(Debug, Clone, Copy)] pub struct FieldExpr<'a> { @@ -839,11 +857,51 @@ impl<'a> AstNode<'a> for Module<'a> { impl<'a> ast::NameOwner<'a> for Module<'a> {} impl<'a> ast::AttrsOwner<'a> for Module<'a> {} impl<'a> Module<'a> { - pub fn modules(self) -> impl Iterator> + 'a { + pub fn items(self) -> impl Iterator> + 'a { super::children(self) } } +// ModuleItem +#[derive(Debug, Clone, Copy)] +pub enum ModuleItem<'a> { + StructDef(StructDef<'a>), + EnumDef(EnumDef<'a>), + FnDef(FnDef<'a>), + TraitDef(TraitDef<'a>), + ImplItem(ImplItem<'a>), + UseItem(UseItem<'a>), + ExternCrateItem(ExternCrateItem<'a>), +} + +impl<'a> AstNode<'a> for ModuleItem<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + STRUCT_DEF => Some(ModuleItem::StructDef(StructDef { syntax })), + ENUM_DEF => Some(ModuleItem::EnumDef(EnumDef { syntax })), + FN_DEF => Some(ModuleItem::FnDef(FnDef { syntax })), + TRAIT_DEF => Some(ModuleItem::TraitDef(TraitDef { syntax })), + IMPL_ITEM => Some(ModuleItem::ImplItem(ImplItem { syntax })), + USE_ITEM => Some(ModuleItem::UseItem(UseItem { syntax })), + EXTERN_CRATE_ITEM => Some(ModuleItem::ExternCrateItem(ExternCrateItem { syntax })), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { + match self { + ModuleItem::StructDef(inner) => inner.syntax(), + ModuleItem::EnumDef(inner) => inner.syntax(), + ModuleItem::FnDef(inner) => inner.syntax(), + ModuleItem::TraitDef(inner) => inner.syntax(), + ModuleItem::ImplItem(inner) => inner.syntax(), + ModuleItem::UseItem(inner) => inner.syntax(), + ModuleItem::ExternCrateItem(inner) => inner.syntax(), + } + } +} + +impl<'a> ModuleItem<'a> {} + // Name #[derive(Debug, Clone, Copy)] pub struct Name<'a> { @@ -1762,6 +1820,24 @@ impl<'a> AstNode<'a> for TypeRef<'a> { impl<'a> TypeRef<'a> {} +// UseItem +#[derive(Debug, Clone, Copy)] +pub struct UseItem<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for UseItem<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + USE_ITEM => Some(UseItem { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> UseItem<'a> {} + // WhereClause #[derive(Debug, Clone, Copy)] pub struct WhereClause<'a> { diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index 2ebee6a4f..9941138a7 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -115,6 +115,15 @@ impl<'a> Module<'a> { } } +impl<'a> LetStmt<'a> { + pub fn has_semi(self) -> bool { + match self.syntax().last_child() { + None => false, + Some(node) => node.kind() == SEMI, + } + } +} + impl<'a> IfExpr<'a> { pub fn then_branch(self) -> Option> { self.blocks().nth(0) diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index a98e9e2fd..7217a4633 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -273,7 +273,7 @@ Grammar( "Module": ( traits: ["NameOwner", "AttrsOwner"], collections: [ - ["modules", "Module"] + ["items", "ModuleItem"] ] ), "ConstDef": ( traits: [ @@ -331,6 +331,10 @@ Grammar( "AttrsOwner" ], ), + "ModuleItem": ( + enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "ImplItem", + "UseItem", "ExternCrateItem" ] + ), "TupleExpr": (), "ArrayExpr": (), @@ -479,6 +483,8 @@ Grammar( ), "Param": ( options: [["pat", "Pat"]], - ) + ), + "UseItem": (), + "ExternCrateItem": (), }, ) diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs index ab4aa49d2..0769bb5a8 100644 --- a/crates/libsyntax2/src/grammar/expressions/atom.rs +++ b/crates/libsyntax2/src/grammar/expressions/atom.rs @@ -33,6 +33,9 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], ]; +const EXPR_RECOVERY_SET: TokenSet = + token_set![LET_KW]; + pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option { match literal(p) { Some(m) => return Some(m), @@ -73,7 +76,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option continue_expr(p), BREAK_KW => break_expr(p), _ => { - p.err_and_bump("expected expression"); + p.err_recover("expected expression", EXPR_RECOVERY_SET); return None; } }; diff --git a/crates/libsyntax2/src/parser_api.rs b/crates/libsyntax2/src/parser_api.rs index 0a3b29b70..10b9b64ac 100644 --- a/crates/libsyntax2/src/parser_api.rs +++ b/crates/libsyntax2/src/parser_api.rs @@ -12,6 +12,8 @@ fn mask(kind: SyntaxKind) -> u128 { } impl TokenSet { + const EMPTY: TokenSet = TokenSet(0); + pub fn contains(&self, kind: SyntaxKind) -> bool { self.0 & mask(kind) != 0 } @@ -139,12 +141,21 @@ impl<'t> Parser<'t> { /// Create an error node and consume the next token. pub(crate) fn err_and_bump(&mut self, message: &str) { - let m = self.start(); - self.error(message); - if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) { + self.err_recover(message, TokenSet::EMPTY); + } + + /// Create an error node and consume the next token. + pub(crate) fn err_recover(&mut self, message: &str, recovery_set: TokenSet) { + if self.at(SyntaxKind::L_CURLY) + || self.at(SyntaxKind::R_CURLY) + || recovery_set.contains(self.current()) { + self.error(message); + } else { + let m = self.start(); + self.error(message); self.bump(); + m.complete(self, ERROR); } - m.complete(self, ERROR); } } diff --git a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt index db9a2f175..f0be287ad 100644 --- a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt +++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt @@ -35,13 +35,12 @@ ROOT@[0; 47) INT_NUMBER@[33; 35) "92" SEMI@[35; 36) WHITESPACE@[36; 41) - BIN_EXPR@[41; 45) + BIN_EXPR@[41; 44) LITERAL@[41; 42) INT_NUMBER@[41; 42) "1" WHITESPACE@[42; 43) PLUS@[43; 44) - WHITESPACE@[44; 45) err: `expected expression` - ERROR@[45; 45) + WHITESPACE@[44; 45) R_CURLY@[45; 46) WHITESPACE@[46; 47) diff --git a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt index 3f3a7784b..58e39a341 100644 --- a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt +++ b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt @@ -11,12 +11,12 @@ ROOT@[0; 183) ITEM_LIST@[14; 182) L_CURLY@[14; 15) WHITESPACE@[15; 20) - FN_DEF@[20; 181) + FN_DEF@[20; 180) FN_KW@[20; 22) WHITESPACE@[22; 23) NAME@[23; 32) IDENT@[23; 32) "new_scope" - PARAM_LIST@[32; 181) + PARAM_LIST@[32; 180) L_PAREN@[32; 33) PARAM@[33; 38) REF_PAT@[33; 35) @@ -163,17 +163,16 @@ ROOT@[0; 183) err: `expected parameters` err: `expected COMMA` WHITESPACE@[169; 170) - PARAM@[170; 181) + PARAM@[170; 180) BIND_PAT@[170; 180) NAME@[170; 180) IDENT@[170; 180) "set_parent" err: `expected COLON` - WHITESPACE@[180; 181) - err: `expected type` - err: `expected COMMA` - err: `expected value parameter` - err: `expected R_PAREN` - err: `expected a block` - ERROR@[181; 181) + err: `expected type` + err: `expected COMMA` + err: `expected value parameter` + err: `expected R_PAREN` + err: `expected a block` + WHITESPACE@[180; 181) R_CURLY@[181; 182) WHITESPACE@[182; 183) diff --git a/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs new file mode 100644 index 000000000..52bddd494 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs @@ -0,0 +1,4 @@ +fn foo() { + let foo = + let bar = 1; +} diff --git a/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt new file mode 100644 index 000000000..9ffa9d781 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt @@ -0,0 +1,39 @@ +ROOT@[0; 44) + FN_DEF@[0; 43) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 43) + L_CURLY@[9; 10) + WHITESPACE@[10; 15) + LET_STMT@[15; 24) + LET_KW@[15; 18) + WHITESPACE@[18; 19) + BIND_PAT@[19; 22) + NAME@[19; 22) + IDENT@[19; 22) "foo" + WHITESPACE@[22; 23) + EQ@[23; 24) + err: `expected expression` + err: `expected SEMI` + WHITESPACE@[24; 29) + LET_STMT@[29; 41) + LET_KW@[29; 32) + WHITESPACE@[32; 33) + BIND_PAT@[33; 36) + NAME@[33; 36) + IDENT@[33; 36) "bar" + WHITESPACE@[36; 37) + EQ@[37; 38) + WHITESPACE@[38; 39) + LITERAL@[39; 40) + INT_NUMBER@[39; 40) "1" + SEMI@[40; 41) + WHITESPACE@[41; 42) + R_CURLY@[42; 43) + WHITESPACE@[43; 44) -- cgit v1.2.3