diff options
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/fuzz/fuzz_targets/parser.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/ra_syntax/src/parsing/reparsing.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_node.rs | 48 | ||||
-rw-r--r-- | crates/ra_syntax/src/utils.rs | 83 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 41 | ||||
-rw-r--r-- | crates/ra_syntax/tests/test.rs | 9 |
7 files changed, 96 insertions, 104 deletions
diff --git a/crates/ra_syntax/fuzz/fuzz_targets/parser.rs b/crates/ra_syntax/fuzz/fuzz_targets/parser.rs index 396c0ecaf..4667d5579 100644 --- a/crates/ra_syntax/fuzz/fuzz_targets/parser.rs +++ b/crates/ra_syntax/fuzz/fuzz_targets/parser.rs | |||
@@ -4,6 +4,6 @@ extern crate ra_syntax; | |||
4 | 4 | ||
5 | fuzz_target!(|data: &[u8]| { | 5 | fuzz_target!(|data: &[u8]| { |
6 | if let Ok(text) = std::str::from_utf8(data) { | 6 | if let Ok(text) = std::str::from_utf8(data) { |
7 | ra_syntax::utils::check_fuzz_invariants(text) | 7 | ra_syntax::check_fuzz_invariants(text) |
8 | } | 8 | } |
9 | }); | 9 | }); |
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 6982b9815..dc4b779e8 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -27,8 +27,6 @@ mod ptr; | |||
27 | 27 | ||
28 | pub mod algo; | 28 | pub mod algo; |
29 | pub mod ast; | 29 | pub mod ast; |
30 | /// Utilities for simple uses of the parser. | ||
31 | pub mod utils; | ||
32 | 30 | ||
33 | pub use rowan::{SmolStr, TextRange, TextUnit}; | 31 | pub use rowan::{SmolStr, TextRange, TextUnit}; |
34 | pub use ra_parser::SyntaxKind; | 32 | pub use ra_parser::SyntaxKind; |
@@ -51,7 +49,7 @@ impl SourceFile { | |||
51 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> { | 49 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> { |
52 | let root = SyntaxNode::new(green, errors); | 50 | let root = SyntaxNode::new(green, errors); |
53 | if cfg!(debug_assertions) { | 51 | if cfg!(debug_assertions) { |
54 | utils::validate_block_structure(&root); | 52 | validation::validate_block_structure(&root); |
55 | } | 53 | } |
56 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); | 54 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); |
57 | TreeArc::cast(root) | 55 | TreeArc::cast(root) |
@@ -82,3 +80,10 @@ impl SourceFile { | |||
82 | errors | 80 | errors |
83 | } | 81 | } |
84 | } | 82 | } |
83 | |||
84 | pub fn check_fuzz_invariants(text: &str) { | ||
85 | let file = SourceFile::parse(text); | ||
86 | let root = file.syntax(); | ||
87 | validation::validate_block_structure(root); | ||
88 | let _ = file.errors(); | ||
89 | } | ||
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs index 6957c26c0..19d8adcfb 100644 --- a/crates/ra_syntax/src/parsing/reparsing.rs +++ b/crates/ra_syntax/src/parsing/reparsing.rs | |||
@@ -143,7 +143,7 @@ fn merge_errors( | |||
143 | mod tests { | 143 | mod tests { |
144 | use test_utils::{extract_range, assert_eq_text}; | 144 | use test_utils::{extract_range, assert_eq_text}; |
145 | 145 | ||
146 | use crate::{SourceFile, AstNode, utils::dump_tree}; | 146 | use crate::{SourceFile, AstNode}; |
147 | use super::*; | 147 | use super::*; |
148 | 148 | ||
149 | fn do_check<F>(before: &str, replace_with: &str, reparser: F) | 149 | fn do_check<F>(before: &str, replace_with: &str, reparser: F) |
@@ -169,8 +169,8 @@ mod tests { | |||
169 | }; | 169 | }; |
170 | 170 | ||
171 | assert_eq_text!( | 171 | assert_eq_text!( |
172 | &dump_tree(fully_reparsed.syntax()), | 172 | &fully_reparsed.syntax().debug_dump(), |
173 | &dump_tree(incrementally_reparsed.syntax()), | 173 | &incrementally_reparsed.syntax().debug_dump(), |
174 | ) | 174 | ) |
175 | } | 175 | } |
176 | 176 | ||
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index a1bc0b499..1ca1c992b 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs | |||
@@ -6,12 +6,12 @@ | |||
6 | //! The *real* implementation is in the (language-agnostic) `rowan` crate, this | 6 | //! The *real* implementation is in the (language-agnostic) `rowan` crate, this |
7 | //! modules just wraps its API. | 7 | //! modules just wraps its API. |
8 | 8 | ||
9 | use std::{fmt, borrow::Borrow}; | 9 | use std::{fmt::{self, Write}, borrow::Borrow}; |
10 | 10 | ||
11 | use rowan::{Types, TransparentNewType}; | 11 | use rowan::{Types, TransparentNewType}; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | SmolStr, SyntaxKind, TextRange, SyntaxText, | 14 | SmolStr, SyntaxKind, TextRange, SyntaxText, SourceFile, AstNode, |
15 | syntax_error::SyntaxError, | 15 | syntax_error::SyntaxError, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -134,6 +134,50 @@ impl SyntaxNode { | |||
134 | WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)), | 134 | WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)), |
135 | }) | 135 | }) |
136 | } | 136 | } |
137 | |||
138 | pub fn debug_dump(&self) -> String { | ||
139 | let mut errors: Vec<_> = match self.ancestors().find_map(SourceFile::cast) { | ||
140 | Some(file) => file.errors(), | ||
141 | None => self.root_data().to_vec(), | ||
142 | }; | ||
143 | errors.sort_by_key(|e| e.offset()); | ||
144 | let mut err_pos = 0; | ||
145 | let mut level = 0; | ||
146 | let mut buf = String::new(); | ||
147 | macro_rules! indent { | ||
148 | () => { | ||
149 | for _ in 0..level { | ||
150 | buf.push_str(" "); | ||
151 | } | ||
152 | }; | ||
153 | } | ||
154 | |||
155 | for event in self.preorder() { | ||
156 | match event { | ||
157 | WalkEvent::Enter(node) => { | ||
158 | indent!(); | ||
159 | writeln!(buf, "{:?}", node).unwrap(); | ||
160 | if node.first_child().is_none() { | ||
161 | let off = node.range().end(); | ||
162 | while err_pos < errors.len() && errors[err_pos].offset() <= off { | ||
163 | indent!(); | ||
164 | writeln!(buf, "err: `{}`", errors[err_pos]).unwrap(); | ||
165 | err_pos += 1; | ||
166 | } | ||
167 | } | ||
168 | level += 1; | ||
169 | } | ||
170 | WalkEvent::Leave(_) => level -= 1, | ||
171 | } | ||
172 | } | ||
173 | |||
174 | assert_eq!(level, 0); | ||
175 | for err in errors[err_pos..].iter() { | ||
176 | writeln!(buf, "err: `{}`", err).unwrap(); | ||
177 | } | ||
178 | |||
179 | buf | ||
180 | } | ||
137 | } | 181 | } |
138 | 182 | ||
139 | impl ToOwned for SyntaxNode { | 183 | impl ToOwned for SyntaxNode { |
diff --git a/crates/ra_syntax/src/utils.rs b/crates/ra_syntax/src/utils.rs deleted file mode 100644 index 2e1b42da0..000000000 --- a/crates/ra_syntax/src/utils.rs +++ /dev/null | |||
@@ -1,83 +0,0 @@ | |||
1 | use std::{str, fmt::Write}; | ||
2 | |||
3 | use crate::{SourceFile, SyntaxKind, WalkEvent, AstNode, SyntaxNode}; | ||
4 | |||
5 | /// Parse a file and create a string representation of the resulting parse tree. | ||
6 | pub fn dump_tree(syntax: &SyntaxNode) -> String { | ||
7 | let mut errors: Vec<_> = match syntax.ancestors().find_map(SourceFile::cast) { | ||
8 | Some(file) => file.errors(), | ||
9 | None => syntax.root_data().to_vec(), | ||
10 | }; | ||
11 | errors.sort_by_key(|e| e.offset()); | ||
12 | let mut err_pos = 0; | ||
13 | let mut level = 0; | ||
14 | let mut buf = String::new(); | ||
15 | macro_rules! indent { | ||
16 | () => { | ||
17 | for _ in 0..level { | ||
18 | buf.push_str(" "); | ||
19 | } | ||
20 | }; | ||
21 | } | ||
22 | |||
23 | for event in syntax.preorder() { | ||
24 | match event { | ||
25 | WalkEvent::Enter(node) => { | ||
26 | indent!(); | ||
27 | writeln!(buf, "{:?}", node).unwrap(); | ||
28 | if node.first_child().is_none() { | ||
29 | let off = node.range().end(); | ||
30 | while err_pos < errors.len() && errors[err_pos].offset() <= off { | ||
31 | indent!(); | ||
32 | writeln!(buf, "err: `{}`", errors[err_pos]).unwrap(); | ||
33 | err_pos += 1; | ||
34 | } | ||
35 | } | ||
36 | level += 1; | ||
37 | } | ||
38 | WalkEvent::Leave(_) => level -= 1, | ||
39 | } | ||
40 | } | ||
41 | |||
42 | assert_eq!(level, 0); | ||
43 | for err in errors[err_pos..].iter() { | ||
44 | writeln!(buf, "err: `{}`", err).unwrap(); | ||
45 | } | ||
46 | |||
47 | buf | ||
48 | } | ||
49 | |||
50 | pub fn check_fuzz_invariants(text: &str) { | ||
51 | let file = SourceFile::parse(text); | ||
52 | let root = file.syntax(); | ||
53 | validate_block_structure(root); | ||
54 | let _ = file.errors(); | ||
55 | } | ||
56 | |||
57 | pub(crate) fn validate_block_structure(root: &SyntaxNode) { | ||
58 | let mut stack = Vec::new(); | ||
59 | for node in root.descendants() { | ||
60 | match node.kind() { | ||
61 | SyntaxKind::L_CURLY => stack.push(node), | ||
62 | SyntaxKind::R_CURLY => { | ||
63 | if let Some(pair) = stack.pop() { | ||
64 | assert_eq!( | ||
65 | node.parent(), | ||
66 | pair.parent(), | ||
67 | "\nunpaired curleys:\n{}\n{}\n", | ||
68 | root.text(), | ||
69 | dump_tree(root), | ||
70 | ); | ||
71 | assert!( | ||
72 | node.next_sibling().is_none() && pair.prev_sibling().is_none(), | ||
73 | "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", | ||
74 | node, | ||
75 | root.text(), | ||
76 | node.text(), | ||
77 | ); | ||
78 | } | ||
79 | } | ||
80 | _ => (), | ||
81 | } | ||
82 | } | ||
83 | } | ||
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 69958f0d7..69f344d65 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -5,7 +5,8 @@ mod string; | |||
5 | mod block; | 5 | mod block; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | SourceFile, SyntaxError, AstNode, | 8 | SourceFile, SyntaxError, AstNode, SyntaxNode, |
9 | SyntaxKind::{L_CURLY, R_CURLY}, | ||
9 | ast, | 10 | ast, |
10 | algo::visit::{visitor_ctx, VisitorCtx}, | 11 | algo::visit::{visitor_ctx, VisitorCtx}, |
11 | }; | 12 | }; |
@@ -14,12 +15,40 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> { | |||
14 | let mut errors = Vec::new(); | 15 | let mut errors = Vec::new(); |
15 | for node in file.syntax().descendants() { | 16 | for node in file.syntax().descendants() { |
16 | let _ = visitor_ctx(&mut errors) | 17 | let _ = visitor_ctx(&mut errors) |
17 | .visit::<ast::Byte, _>(self::byte::validate_byte_node) | 18 | .visit::<ast::Byte, _>(byte::validate_byte_node) |
18 | .visit::<ast::ByteString, _>(self::byte_string::validate_byte_string_node) | 19 | .visit::<ast::ByteString, _>(byte_string::validate_byte_string_node) |
19 | .visit::<ast::Char, _>(self::char::validate_char_node) | 20 | .visit::<ast::Char, _>(char::validate_char_node) |
20 | .visit::<ast::String, _>(self::string::validate_string_node) | 21 | .visit::<ast::String, _>(string::validate_string_node) |
21 | .visit::<ast::Block, _>(self::block::validate_block_node) | 22 | .visit::<ast::Block, _>(block::validate_block_node) |
22 | .accept(node); | 23 | .accept(node); |
23 | } | 24 | } |
24 | errors | 25 | errors |
25 | } | 26 | } |
27 | |||
28 | pub(crate) fn validate_block_structure(root: &SyntaxNode) { | ||
29 | let mut stack = Vec::new(); | ||
30 | for node in root.descendants() { | ||
31 | match node.kind() { | ||
32 | L_CURLY => stack.push(node), | ||
33 | R_CURLY => { | ||
34 | if let Some(pair) = stack.pop() { | ||
35 | assert_eq!( | ||
36 | node.parent(), | ||
37 | pair.parent(), | ||
38 | "\nunpaired curleys:\n{}\n{}\n", | ||
39 | root.text(), | ||
40 | root.debug_dump(), | ||
41 | ); | ||
42 | assert!( | ||
43 | node.next_sibling().is_none() && pair.prev_sibling().is_none(), | ||
44 | "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", | ||
45 | node, | ||
46 | root.text(), | ||
47 | node.text(), | ||
48 | ); | ||
49 | } | ||
50 | } | ||
51 | _ => (), | ||
52 | } | ||
53 | } | ||
54 | } | ||
diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs index 168d0623d..458740c13 100644 --- a/crates/ra_syntax/tests/test.rs +++ b/crates/ra_syntax/tests/test.rs | |||
@@ -8,10 +8,7 @@ use std::{ | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | use test_utils::{project_dir, dir_tests, read_text, collect_tests}; | 10 | use test_utils::{project_dir, dir_tests, read_text, collect_tests}; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{SourceFile, AstNode, check_fuzz_invariants}; |
12 | SourceFile, AstNode, | ||
13 | utils::{check_fuzz_invariants, dump_tree}, | ||
14 | }; | ||
15 | 12 | ||
16 | #[test] | 13 | #[test] |
17 | fn lexer_tests() { | 14 | fn lexer_tests() { |
@@ -32,7 +29,7 @@ fn parser_tests() { | |||
32 | "There should be no errors in the file {:?}", | 29 | "There should be no errors in the file {:?}", |
33 | path.display() | 30 | path.display() |
34 | ); | 31 | ); |
35 | dump_tree(file.syntax()) | 32 | file.syntax().debug_dump() |
36 | }); | 33 | }); |
37 | dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], |text, path| { | 34 | dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], |text, path| { |
38 | let file = SourceFile::parse(text); | 35 | let file = SourceFile::parse(text); |
@@ -43,7 +40,7 @@ fn parser_tests() { | |||
43 | "There should be errors in the file {:?}", | 40 | "There should be errors in the file {:?}", |
44 | path.display() | 41 | path.display() |
45 | ); | 42 | ); |
46 | dump_tree(file.syntax()) | 43 | file.syntax().debug_dump() |
47 | }); | 44 | }); |
48 | } | 45 | } |
49 | 46 | ||