Skip to content

Commit 6d0a765

Browse files
Merge #4962
4962: Implement APIs for parsing expressions, types, paths, patterns and items r=davidlattimore a=davidlattimore Co-authored-by: David Lattimore <dml@google.com>
2 parents 50dad50 + bc99e95 commit 6d0a765

29 files changed

+222
-1
lines changed

crates/ra_syntax/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,41 @@ impl SourceFile {
168168
}
169169
}
170170

171+
impl ast::Path {
172+
/// Returns `text`, parsed as a path, but only if it has no errors.
173+
pub fn parse(text: &str) -> Result<Self, ()> {
174+
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Path)
175+
}
176+
}
177+
178+
impl ast::Pat {
179+
/// Returns `text`, parsed as a pattern, but only if it has no errors.
180+
pub fn parse(text: &str) -> Result<Self, ()> {
181+
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Pattern)
182+
}
183+
}
184+
185+
impl ast::Expr {
186+
/// Returns `text`, parsed as an expression, but only if it has no errors.
187+
pub fn parse(text: &str) -> Result<Self, ()> {
188+
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Expr)
189+
}
190+
}
191+
192+
impl ast::ModuleItem {
193+
/// Returns `text`, parsed as an item, but only if it has no errors.
194+
pub fn parse(text: &str) -> Result<Self, ()> {
195+
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Item)
196+
}
197+
}
198+
199+
impl ast::TypeRef {
200+
/// Returns `text`, parsed as an type reference, but only if it has no errors.
201+
pub fn parse(text: &str) -> Result<Self, ()> {
202+
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Type)
203+
}
204+
}
205+
171206
/// Matches a `SyntaxNode` against an `ast` type.
172207
///
173208
/// # Example:

crates/ra_syntax/src/parsing.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ mod text_token_source;
66
mod text_tree_sink;
77
mod reparsing;
88

9-
use crate::{syntax_node::GreenNode, SyntaxError};
9+
use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
1010
use text_token_source::TextTokenSource;
1111
use text_tree_sink::TextTreeSink;
1212

1313
pub use lexer::*;
1414

1515
pub(crate) use self::reparsing::incremental_reparse;
16+
use ra_parser::SyntaxKind;
1617

1718
pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
1819
let (tokens, lexer_errors) = tokenize(&text);
@@ -27,3 +28,32 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
2728

2829
(tree, parser_errors)
2930
}
31+
32+
/// Returns `text` parsed as a `T` provided there are no parse errors.
33+
pub(crate) fn parse_text_fragment<T: AstNode>(
34+
text: &str,
35+
fragment_kind: ra_parser::FragmentKind,
36+
) -> Result<T, ()> {
37+
let (tokens, lexer_errors) = tokenize(&text);
38+
if !lexer_errors.is_empty() {
39+
return Err(());
40+
}
41+
42+
let mut token_source = TextTokenSource::new(text, &tokens);
43+
let mut tree_sink = TextTreeSink::new(text, &tokens);
44+
45+
// TextTreeSink assumes that there's at least some root node to which it can attach errors and
46+
// tokens. We arbitrarily give it a SourceFile.
47+
use ra_parser::TreeSink;
48+
tree_sink.start_node(SyntaxKind::SOURCE_FILE);
49+
ra_parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind);
50+
tree_sink.finish_node();
51+
52+
let (tree, parser_errors) = tree_sink.finish();
53+
use ra_parser::TokenSource;
54+
if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF {
55+
return Err(());
56+
}
57+
58+
SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(())
59+
}

crates/ra_syntax/src/tests.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,51 @@ fn parser_tests() {
5454
});
5555
}
5656

57+
#[test]
58+
fn expr_parser_tests() {
59+
fragment_parser_dir_test(
60+
&["parser/fragments/expr/ok"],
61+
&["parser/fragments/expr/err"],
62+
crate::ast::Expr::parse,
63+
);
64+
}
65+
66+
#[test]
67+
fn path_parser_tests() {
68+
fragment_parser_dir_test(
69+
&["parser/fragments/path/ok"],
70+
&["parser/fragments/path/err"],
71+
crate::ast::Path::parse,
72+
);
73+
}
74+
75+
#[test]
76+
fn pattern_parser_tests() {
77+
fragment_parser_dir_test(
78+
&["parser/fragments/pattern/ok"],
79+
&["parser/fragments/pattern/err"],
80+
crate::ast::Pat::parse,
81+
);
82+
}
83+
84+
#[test]
85+
fn item_parser_tests() {
86+
fragment_parser_dir_test(
87+
&["parser/fragments/item/ok"],
88+
&["parser/fragments/item/err"],
89+
crate::ast::ModuleItem::parse,
90+
);
91+
}
92+
93+
#[test]
94+
fn type_parser_tests() {
95+
fragment_parser_dir_test(
96+
&["parser/fragments/type/ok"],
97+
&["parser/fragments/type/err"],
98+
crate::ast::TypeRef::parse,
99+
);
100+
}
101+
57102
#[test]
58103
fn parser_fuzz_tests() {
59104
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)
134179
}
135180
acc
136181
}
182+
183+
fn fragment_parser_dir_test<T, F>(ok_paths: &[&str], err_paths: &[&str], f: F)
184+
where
185+
T: crate::AstNode,
186+
F: Fn(&str) -> Result<T, ()>,
187+
{
188+
dir_tests(&test_data_dir(), ok_paths, "rast", |text, path| {
189+
if let Ok(node) = f(text) {
190+
format!("{:#?}", crate::ast::AstNode::syntax(&node))
191+
} else {
192+
panic!("Failed to parse '{:?}'", path);
193+
}
194+
});
195+
dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
196+
if let Ok(_) = f(text) {
197+
panic!("'{:?}' successfully parsed when it should have errored", path);
198+
} else {
199+
"ERROR\n".to_owned()
200+
}
201+
});
202+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1 +
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
BIN_EXPR@0..5
2+
LITERAL@0..1
3+
INT_NUMBER@0..1 "1"
4+
WHITESPACE@1..2 " "
5+
PLUS@2..3 "+"
6+
WHITESPACE@3..4 " "
7+
LITERAL@4..5
8+
INT_NUMBER@4..5 "2"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1 + 2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn fn foo() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FN_DEF@0..11
2+
FN_KW@0..2 "fn"
3+
WHITESPACE@2..3 " "
4+
NAME@3..6
5+
IDENT@3..6 "foo"
6+
PARAM_LIST@6..8
7+
L_PAREN@6..7 "("
8+
R_PAREN@7..8 ")"
9+
WHITESPACE@8..9 " "
10+
BLOCK_EXPR@9..11
11+
L_CURLY@9..10 "{"
12+
R_CURLY@10..11 "}"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn foo() {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
struct
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a + b
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
PATH@0..3
2+
PATH_SEGMENT@0..3
3+
NAME_REF@0..3
4+
IDENT@0..3 "foo"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
PATH@0..13
2+
PATH@0..8
3+
PATH@0..3
4+
PATH_SEGMENT@0..3
5+
NAME_REF@0..3
6+
IDENT@0..3 "foo"
7+
COLON2@3..5 "::"
8+
PATH_SEGMENT@5..8
9+
NAME_REF@5..8
10+
IDENT@5..8 "bar"
11+
COLON2@8..10 "::"
12+
PATH_SEGMENT@10..13
13+
NAME_REF@10..13
14+
IDENT@10..13 "baz"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo::bar::baz
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fn
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Some(x
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
TUPLE_STRUCT_PAT@0..7
2+
PATH@0..4
3+
PATH_SEGMENT@0..4
4+
NAME_REF@0..4
5+
IDENT@0..4 "Some"
6+
L_PAREN@4..5 "("
7+
BIND_PAT@5..6
8+
NAME@5..6
9+
IDENT@5..6 "x"
10+
R_PAREN@6..7 ")"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Some(x)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Result<Foo, Bar
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
PATH_TYPE@0..16
2+
PATH@0..16
3+
PATH_SEGMENT@0..16
4+
NAME_REF@0..6
5+
IDENT@0..6 "Result"
6+
TYPE_ARG_LIST@6..16
7+
L_ANGLE@6..7 "<"
8+
TYPE_ARG@7..10
9+
PATH_TYPE@7..10
10+
PATH@7..10
11+
PATH_SEGMENT@7..10
12+
NAME_REF@7..10
13+
IDENT@7..10 "Foo"
14+
COMMA@10..11 ","
15+
WHITESPACE@11..12 " "
16+
TYPE_ARG@12..15
17+
PATH_TYPE@12..15
18+
PATH@12..15
19+
PATH_SEGMENT@12..15
20+
NAME_REF@12..15
21+
IDENT@12..15 "Bar"
22+
R_ANGLE@15..16 ">"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Result<Foo, Bar>

0 commit comments

Comments
 (0)