aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/algo.rs8
-rw-r--r--crates/ra_syntax/src/ast/make.rs4
-rw-r--r--crates/ra_syntax/src/lib.rs35
-rw-r--r--crates/ra_syntax/src/parsing.rs32
-rw-r--r--crates/ra_syntax/src/parsing/text_token_source.rs89
-rw-r--r--crates/ra_syntax/src/tests.rs66
6 files changed, 187 insertions, 47 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index 664894d1f..f7a885eb3 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -290,6 +290,11 @@ impl<'a> SyntaxRewriter<'a> {
290 N::cast(self.rewrite(node.syntax())).unwrap() 290 N::cast(self.rewrite(node.syntax())).unwrap()
291 } 291 }
292 292
293 /// Returns a node that encompasses all replacements to be done by this rewriter.
294 ///
295 /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`.
296 ///
297 /// Returns `None` when there are no replacements.
293 pub fn rewrite_root(&self) -> Option<SyntaxNode> { 298 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
294 assert!(self.f.is_none()); 299 assert!(self.f.is_none());
295 self.replacements 300 self.replacements
@@ -298,6 +303,9 @@ impl<'a> SyntaxRewriter<'a> {
298 SyntaxElement::Node(it) => it.clone(), 303 SyntaxElement::Node(it) => it.clone(),
299 SyntaxElement::Token(it) => it.parent(), 304 SyntaxElement::Token(it) => it.parent(),
300 }) 305 })
306 // If we only have one replacement, we must return its parent node, since `rewrite` does
307 // not replace the node passed to it.
308 .map(|it| it.parent().unwrap_or(it))
301 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) 309 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
302 } 310 }
303 311
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index da0eb0926..192c610f1 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -75,6 +75,10 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordF
75 } 75 }
76} 76}
77 77
78pub fn record_field_def(name: ast::NameRef, ty: ast::TypeRef) -> ast::RecordFieldDef {
79 ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty))
80}
81
78pub fn block_expr( 82pub fn block_expr(
79 stmts: impl IntoIterator<Item = ast::Stmt>, 83 stmts: impl IntoIterator<Item = ast::Stmt>,
80 tail_expr: Option<ast::Expr>, 84 tail_expr: Option<ast::Expr>,
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 {
168 } 168 }
169} 169}
170 170
171impl 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
178impl 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
185impl 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
192impl 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
199impl 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
171/// Matches a `SyntaxNode` against an `ast` type. 206/// Matches a `SyntaxNode` against an `ast` type.
172/// 207///
173/// # Example: 208/// # 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;
6mod text_tree_sink; 6mod text_tree_sink;
7mod reparsing; 7mod reparsing;
8 8
9use crate::{syntax_node::GreenNode, SyntaxError}; 9use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
10use text_token_source::TextTokenSource; 10use text_token_source::TextTokenSource;
11use text_tree_sink::TextTreeSink; 11use text_tree_sink::TextTreeSink;
12 12
13pub use lexer::*; 13pub use lexer::*;
14 14
15pub(crate) use self::reparsing::incremental_reparse; 15pub(crate) use self::reparsing::incremental_reparse;
16use ra_parser::SyntaxKind;
16 17
17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { 18pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
18 let (tokens, lexer_errors) = tokenize(&text); 19 let (tokens, lexer_errors) = tokenize(&text);
@@ -27,3 +28,32 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
27 28
28 (tree, parser_errors) 29 (tree, parser_errors)
29} 30}
31
32/// Returns `text` parsed as a `T` provided there are no parse errors.
33pub(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}
diff --git a/crates/ra_syntax/src/parsing/text_token_source.rs b/crates/ra_syntax/src/parsing/text_token_source.rs
index 7ddc2c2c3..97aa3e795 100644
--- a/crates/ra_syntax/src/parsing/text_token_source.rs
+++ b/crates/ra_syntax/src/parsing/text_token_source.rs
@@ -1,40 +1,35 @@
1//! FIXME: write short doc here 1//! See `TextTokenSource` docs.
2 2
3use ra_parser::Token as PToken;
4use ra_parser::TokenSource; 3use ra_parser::TokenSource;
5 4
6use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize}; 5use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize};
7 6
7/// Implementation of `ra_parser::TokenSource` that takes tokens from source code text.
8pub(crate) struct TextTokenSource<'t> { 8pub(crate) struct TextTokenSource<'t> {
9 text: &'t str, 9 text: &'t str,
10 /// start position of each token(expect whitespace and comment) 10 /// token and its start position (non-whitespace/comment tokens)
11 /// ```non-rust 11 /// ```non-rust
12 /// struct Foo; 12 /// struct Foo;
13 /// ^------^--- 13 /// ^------^--^-
14 /// | | ^- 14 /// | | \________
15 /// 0 7 10 15 /// | \____ \
16 /// | \ |
17 /// (struct, 0) (Foo, 7) (;, 10)
16 /// ``` 18 /// ```
17 /// (token, start_offset): `[(struct, 0), (Foo, 7), (;, 10)]` 19 /// `[(struct, 0), (Foo, 7), (;, 10)]`
18 start_offsets: Vec<TextSize>, 20 token_offset_pairs: Vec<(Token, TextSize)>,
19 /// non-whitespace/comment tokens
20 /// ```non-rust
21 /// struct Foo {}
22 /// ^^^^^^ ^^^ ^^
23 /// ```
24 /// tokens: `[struct, Foo, {, }]`
25 tokens: Vec<Token>,
26 21
27 /// Current token and position 22 /// Current token and position
28 curr: (PToken, usize), 23 curr: (ra_parser::Token, usize),
29} 24}
30 25
31impl<'t> TokenSource for TextTokenSource<'t> { 26impl<'t> TokenSource for TextTokenSource<'t> {
32 fn current(&self) -> PToken { 27 fn current(&self) -> ra_parser::Token {
33 self.curr.0 28 self.curr.0
34 } 29 }
35 30
36 fn lookahead_nth(&self, n: usize) -> PToken { 31 fn lookahead_nth(&self, n: usize) -> ra_parser::Token {
37 mk_token(self.curr.1 + n, &self.start_offsets, &self.tokens) 32 mk_token(self.curr.1 + n, &self.token_offset_pairs)
38 } 33 }
39 34
40 fn bump(&mut self) { 35 fn bump(&mut self) {
@@ -43,45 +38,47 @@ impl<'t> TokenSource for TextTokenSource<'t> {
43 } 38 }
44 39
45 let pos = self.curr.1 + 1; 40 let pos = self.curr.1 + 1;
46 self.curr = (mk_token(pos, &self.start_offsets, &self.tokens), pos); 41 self.curr = (mk_token(pos, &self.token_offset_pairs), pos);
47 } 42 }
48 43
49 fn is_keyword(&self, kw: &str) -> bool { 44 fn is_keyword(&self, kw: &str) -> bool {
50 let pos = self.curr.1; 45 self.token_offset_pairs
51 if pos >= self.tokens.len() { 46 .get(self.curr.1)
52 return false; 47 .map(|(token, offset)| &self.text[TextRange::at(*offset, token.len)] == kw)
53 } 48 .unwrap_or(false)
54 let range = TextRange::at(self.start_offsets[pos], self.tokens[pos].len);
55 self.text[range] == *kw
56 } 49 }
57} 50}
58 51
59fn mk_token(pos: usize, start_offsets: &[TextSize], tokens: &[Token]) -> PToken { 52fn mk_token(pos: usize, token_offset_pairs: &[(Token, TextSize)]) -> ra_parser::Token {
60 let kind = tokens.get(pos).map(|t| t.kind).unwrap_or(EOF); 53 let (kind, is_jointed_to_next) = match token_offset_pairs.get(pos) {
61 let is_jointed_to_next = if pos + 1 < start_offsets.len() { 54 Some((token, offset)) => (
62 start_offsets[pos] + tokens[pos].len == start_offsets[pos + 1] 55 token.kind,
63 } else { 56 token_offset_pairs
64 false 57 .get(pos + 1)
58 .map(|(_, next_offset)| offset + token.len == *next_offset)
59 .unwrap_or(false),
60 ),
61 None => (EOF, false),
65 }; 62 };
66 63 ra_parser::Token { kind, is_jointed_to_next }
67 PToken { kind, is_jointed_to_next }
68} 64}
69 65
70impl<'t> TextTokenSource<'t> { 66impl<'t> TextTokenSource<'t> {
71 /// Generate input from tokens(expect comment and whitespace). 67 /// Generate input from tokens(expect comment and whitespace).
72 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> { 68 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> {
73 let mut tokens = Vec::new(); 69 let token_offset_pairs: Vec<_> = raw_tokens
74 let mut start_offsets = Vec::new(); 70 .iter()
75 let mut len = 0.into(); 71 .filter_map({
76 for &token in raw_tokens.iter() { 72 let mut len = 0.into();
77 if !token.kind.is_trivia() { 73 move |token| {
78 tokens.push(token); 74 let pair = if token.kind.is_trivia() { None } else { Some((*token, len)) };
79 start_offsets.push(len); 75 len += token.len;
80 } 76 pair
81 len += token.len; 77 }
82 } 78 })
79 .collect();
83 80
84 let first = mk_token(0, &start_offsets, &tokens); 81 let first = mk_token(0, &token_offset_pairs);
85 TextTokenSource { text, start_offsets, tokens, curr: (first, 0) } 82 TextTokenSource { text, token_offset_pairs, curr: (first, 0) }
86 } 83 }
87} 84}
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
@@ -55,6 +55,51 @@ fn parser_tests() {
55} 55}
56 56
57#[test] 57#[test]
58fn 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]
67fn 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]
76fn 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]
85fn 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]
94fn 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
102#[test]
58fn parser_fuzz_tests() { 103fn parser_fuzz_tests() {
59 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { 104 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) {
60 fuzz::check_parser(&text) 105 fuzz::check_parser(&text)
@@ -134,3 +179,24 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str)
134 } 179 }
135 acc 180 acc
136} 181}
182
183fn fragment_parser_dir_test<T, F>(ok_paths: &[&str], err_paths: &[&str], f: F)
184where
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}