From bc99e95d7d954701c36142881302bb70e791bec1 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 19 Jun 2020 07:43:19 +1000 Subject: Implement APIs for parsing expressions, types, paths, patterns and items --- crates/ra_syntax/src/lib.rs | 35 ++++++++++++ crates/ra_syntax/src/parsing.rs | 32 ++++++++++- crates/ra_syntax/src/tests.rs | 66 ++++++++++++++++++++++ .../fragments/expr/err/0000_truncated_add.rast | 1 + .../fragments/expr/err/0000_truncated_add.rs | 1 + .../parser/fragments/expr/ok/0000_add.rast | 8 +++ .../test_data/parser/fragments/expr/ok/0000_add.rs | 1 + .../fragments/item/err/0000_extra_keyword.rast | 1 + .../fragments/item/err/0000_extra_keyword.rs | 1 + .../parser/fragments/item/ok/0000_fn.rast | 12 ++++ .../test_data/parser/fragments/item/ok/0000_fn.rs | 1 + .../fragments/path/err/0000_reserved_word.rast | 1 + .../fragments/path/err/0000_reserved_word.rs | 1 + .../parser/fragments/path/err/0001_expression.rast | 1 + .../parser/fragments/path/err/0001_expression.rs | 1 + .../fragments/path/ok/0000_single_ident.rast | 4 ++ .../parser/fragments/path/ok/0000_single_ident.rs | 1 + .../parser/fragments/path/ok/0001_multipart.rast | 14 +++++ .../parser/fragments/path/ok/0001_multipart.rs | 1 + .../fragments/pattern/err/0000_reserved_word.rast | 1 + .../fragments/pattern/err/0000_reserved_word.rs | 1 + .../fragments/pattern/err/0001_missing_paren.rast | 1 + .../fragments/pattern/err/0001_missing_paren.rs | 1 + .../parser/fragments/pattern/ok/0000_enum.rast | 10 ++++ .../parser/fragments/pattern/ok/0000_enum.rs | 1 + .../fragments/type/err/0000_missing_close.rast | 1 + .../fragments/type/err/0000_missing_close.rs | 1 + .../parser/fragments/type/ok/0000_result.rast | 22 ++++++++ .../parser/fragments/type/ok/0000_result.rs | 1 + 29 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs create mode 100644 crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rast create mode 100644 crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index a33a35cc1..9b7664576 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs @@ -168,6 +168,41 @@ impl SourceFile { } } +impl ast::Path { + /// Returns `text`, parsed as a path, but only if it has no errors. + pub fn parse(text: &str) -> Result { + parsing::parse_text_fragment(text, ra_parser::FragmentKind::Path) + } +} + +impl ast::Pat { + /// Returns `text`, parsed as a pattern, but only if it has no errors. + pub fn parse(text: &str) -> Result { + parsing::parse_text_fragment(text, ra_parser::FragmentKind::Pattern) + } +} + +impl ast::Expr { + /// Returns `text`, parsed as an expression, but only if it has no errors. + pub fn parse(text: &str) -> Result { + parsing::parse_text_fragment(text, ra_parser::FragmentKind::Expr) + } +} + +impl ast::ModuleItem { + /// Returns `text`, parsed as an item, but only if it has no errors. + pub fn parse(text: &str) -> Result { + parsing::parse_text_fragment(text, ra_parser::FragmentKind::Item) + } +} + +impl ast::TypeRef { + /// Returns `text`, parsed as an type reference, but only if it has no errors. + pub fn parse(text: &str) -> Result { + parsing::parse_text_fragment(text, ra_parser::FragmentKind::Type) + } +} + /// Matches a `SyntaxNode` against an `ast` type. /// /// # Example: diff --git a/crates/ra_syntax/src/parsing.rs b/crates/ra_syntax/src/parsing.rs index e5eb80850..0ed3c20ef 100644 --- a/crates/ra_syntax/src/parsing.rs +++ b/crates/ra_syntax/src/parsing.rs @@ -6,13 +6,14 @@ mod text_token_source; mod text_tree_sink; mod reparsing; -use crate::{syntax_node::GreenNode, SyntaxError}; +use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode}; use text_token_source::TextTokenSource; use text_tree_sink::TextTreeSink; pub use lexer::*; pub(crate) use self::reparsing::incremental_reparse; +use ra_parser::SyntaxKind; pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { let (tokens, lexer_errors) = tokenize(&text); @@ -27,3 +28,32 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { (tree, parser_errors) } + +/// Returns `text` parsed as a `T` provided there are no parse errors. +pub(crate) fn parse_text_fragment( + text: &str, + fragment_kind: ra_parser::FragmentKind, +) -> Result { + let (tokens, lexer_errors) = tokenize(&text); + if !lexer_errors.is_empty() { + return Err(()); + } + + let mut token_source = TextTokenSource::new(text, &tokens); + let mut tree_sink = TextTreeSink::new(text, &tokens); + + // TextTreeSink assumes that there's at least some root node to which it can attach errors and + // tokens. We arbitrarily give it a SourceFile. + use ra_parser::TreeSink; + tree_sink.start_node(SyntaxKind::SOURCE_FILE); + ra_parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind); + tree_sink.finish_node(); + + let (tree, parser_errors) = tree_sink.finish(); + use ra_parser::TokenSource; + if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF { + return Err(()); + } + + SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(()) +} diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs index aee57db62..959967b79 100644 --- a/crates/ra_syntax/src/tests.rs +++ b/crates/ra_syntax/src/tests.rs @@ -54,6 +54,51 @@ fn parser_tests() { }); } +#[test] +fn expr_parser_tests() { + fragment_parser_dir_test( + &["parser/fragments/expr/ok"], + &["parser/fragments/expr/err"], + crate::ast::Expr::parse, + ); +} + +#[test] +fn path_parser_tests() { + fragment_parser_dir_test( + &["parser/fragments/path/ok"], + &["parser/fragments/path/err"], + crate::ast::Path::parse, + ); +} + +#[test] +fn pattern_parser_tests() { + fragment_parser_dir_test( + &["parser/fragments/pattern/ok"], + &["parser/fragments/pattern/err"], + crate::ast::Pat::parse, + ); +} + +#[test] +fn item_parser_tests() { + fragment_parser_dir_test( + &["parser/fragments/item/ok"], + &["parser/fragments/item/err"], + crate::ast::ModuleItem::parse, + ); +} + +#[test] +fn type_parser_tests() { + fragment_parser_dir_test( + &["parser/fragments/type/ok"], + &["parser/fragments/type/err"], + crate::ast::TypeRef::parse, + ); +} + #[test] fn parser_fuzz_tests() { for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { @@ -134,3 +179,24 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str) } acc } + +fn fragment_parser_dir_test(ok_paths: &[&str], err_paths: &[&str], f: F) +where + T: crate::AstNode, + F: Fn(&str) -> Result, +{ + dir_tests(&test_data_dir(), ok_paths, "rast", |text, path| { + if let Ok(node) = f(text) { + format!("{:#?}", crate::ast::AstNode::syntax(&node)) + } else { + panic!("Failed to parse '{:?}'", path); + } + }); + dir_tests(&test_data_dir(), err_paths, "rast", |text, path| { + if let Ok(_) = f(text) { + panic!("'{:?}' successfully parsed when it should have errored", path); + } else { + "ERROR\n".to_owned() + } + }); +} diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs new file mode 100644 index 000000000..ca49acb07 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs @@ -0,0 +1 @@ +1 + diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast new file mode 100644 index 000000000..fa78a02a6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast @@ -0,0 +1,8 @@ +BIN_EXPR@0..5 + LITERAL@0..1 + INT_NUMBER@0..1 "1" + WHITESPACE@1..2 " " + PLUS@2..3 "+" + WHITESPACE@3..4 " " + LITERAL@4..5 + INT_NUMBER@4..5 "2" diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs new file mode 100644 index 000000000..e0ef58402 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs @@ -0,0 +1 @@ +1 + 2 diff --git a/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs new file mode 100644 index 000000000..dc32389bb --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs @@ -0,0 +1 @@ +fn fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast new file mode 100644 index 000000000..f1e78f388 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast @@ -0,0 +1,12 @@ +FN_DEF@0..11 + FN_KW@0..2 "fn" + 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_EXPR@9..11 + L_CURLY@9..10 "{" + R_CURLY@10..11 "}" diff --git a/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs new file mode 100644 index 000000000..8f3b7ef11 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs @@ -0,0 +1 @@ +fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs new file mode 100644 index 000000000..2046de049 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs @@ -0,0 +1 @@ +struct diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs new file mode 100644 index 000000000..745e8d376 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs @@ -0,0 +1 @@ +a + b diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast new file mode 100644 index 000000000..0c5d4360f --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast @@ -0,0 +1,4 @@ +PATH@0..3 + PATH_SEGMENT@0..3 + NAME_REF@0..3 + IDENT@0..3 "foo" diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs @@ -0,0 +1 @@ +foo diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast new file mode 100644 index 000000000..4a2b45e6a --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast @@ -0,0 +1,14 @@ +PATH@0..13 + PATH@0..8 + PATH@0..3 + PATH_SEGMENT@0..3 + NAME_REF@0..3 + IDENT@0..3 "foo" + COLON2@3..5 "::" + PATH_SEGMENT@5..8 + NAME_REF@5..8 + IDENT@5..8 "bar" + COLON2@8..10 "::" + PATH_SEGMENT@10..13 + NAME_REF@10..13 + IDENT@10..13 "baz" diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs new file mode 100644 index 000000000..81e0b21cd --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs @@ -0,0 +1 @@ +foo::bar::baz diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs new file mode 100644 index 000000000..ae26fc455 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs @@ -0,0 +1 @@ +fn diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs new file mode 100644 index 000000000..61a391d08 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs @@ -0,0 +1 @@ +Some(x diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast new file mode 100644 index 000000000..15eb7f9c6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast @@ -0,0 +1,10 @@ +TUPLE_STRUCT_PAT@0..7 + PATH@0..4 + PATH_SEGMENT@0..4 + NAME_REF@0..4 + IDENT@0..4 "Some" + L_PAREN@4..5 "(" + BIND_PAT@5..6 + NAME@5..6 + IDENT@5..6 "x" + R_PAREN@6..7 ")" diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs new file mode 100644 index 000000000..87114dd78 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs @@ -0,0 +1 @@ +Some(x) diff --git a/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast @@ -0,0 +1 @@ +ERROR diff --git a/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs new file mode 100644 index 000000000..caa4d7c09 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs @@ -0,0 +1 @@ +Result" diff --git a/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs new file mode 100644 index 000000000..b50b3bb3b --- /dev/null +++ b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs @@ -0,0 +1 @@ +Result -- cgit v1.2.3