aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r--crates/ra_syntax/fuzz/fuzz_targets/parser.rs2
-rw-r--r--crates/ra_syntax/src/lib.rs11
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs6
-rw-r--r--crates/ra_syntax/src/syntax_node.rs48
-rw-r--r--crates/ra_syntax/src/utils.rs83
-rw-r--r--crates/ra_syntax/src/validation.rs41
-rw-r--r--crates/ra_syntax/tests/test.rs9
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
5fuzz_target!(|data: &[u8]| { 5fuzz_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
28pub mod algo; 28pub mod algo;
29pub mod ast; 29pub mod ast;
30/// Utilities for simple uses of the parser.
31pub mod utils;
32 30
33pub use rowan::{SmolStr, TextRange, TextUnit}; 31pub use rowan::{SmolStr, TextRange, TextUnit};
34pub use ra_parser::SyntaxKind; 32pub 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
84pub 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(
143mod tests { 143mod 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
9use std::{fmt, borrow::Borrow}; 9use std::{fmt::{self, Write}, borrow::Borrow};
10 10
11use rowan::{Types, TransparentNewType}; 11use rowan::{Types, TransparentNewType};
12 12
13use crate::{ 13use 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
139impl ToOwned for SyntaxNode { 183impl 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 @@
1use std::{str, fmt::Write};
2
3use crate::{SourceFile, SyntaxKind, WalkEvent, AstNode, SyntaxNode};
4
5/// Parse a file and create a string representation of the resulting parse tree.
6pub 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
50pub 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
57pub(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;
5mod block; 5mod block;
6 6
7use crate::{ 7use 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
28pub(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
10use test_utils::{project_dir, dir_tests, read_text, collect_tests}; 10use test_utils::{project_dir, dir_tests, read_text, collect_tests};
11use ra_syntax::{ 11use ra_syntax::{SourceFile, AstNode, check_fuzz_invariants};
12 SourceFile, AstNode,
13 utils::{check_fuzz_invariants, dump_tree},
14};
15 12
16#[test] 13#[test]
17fn lexer_tests() { 14fn 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