aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_cli/src/main.rs6
-rw-r--r--crates/ra_ide_api/src/lib.rs5
-rw-r--r--crates/ra_ide_api_light/src/lib.rs4
-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.rs176
-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
10 files changed, 168 insertions, 175 deletions
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 72e6ae4d5..294f4b8af 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -4,7 +4,7 @@ use std::{fs, io::Read, path::Path, time::Instant};
4 4
5use clap::{App, Arg, SubCommand}; 5use clap::{App, Arg, SubCommand};
6use join_to_string::join; 6use join_to_string::join;
7use ra_ide_api_light::{extend_selection, file_structure, syntax_tree}; 7use ra_ide_api_light::{extend_selection, file_structure};
8use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; 8use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
9use tools::collect_tests; 9use tools::collect_tests;
10use flexi_logger::Logger; 10use flexi_logger::Logger;
@@ -37,7 +37,7 @@ fn main() -> Result<()> {
37 let file = file()?; 37 let file = file()?;
38 let elapsed = start.elapsed(); 38 let elapsed = start.elapsed();
39 if !matches.is_present("no-dump") { 39 if !matches.is_present("no-dump") {
40 println!("{}", syntax_tree(&file)); 40 println!("{}", file.syntax().debug_dump());
41 } 41 }
42 eprintln!("parsing: {:?}", elapsed); 42 eprintln!("parsing: {:?}", elapsed);
43 ::std::mem::forget(file); 43 ::std::mem::forget(file);
@@ -94,7 +94,7 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
94 Some((_start_line, test)) => test, 94 Some((_start_line, test)) => test,
95 }; 95 };
96 let file = SourceFile::parse(&test.text); 96 let file = SourceFile::parse(&test.text);
97 let tree = syntax_tree(&file); 97 let tree = file.syntax().debug_dump();
98 Ok((test.text, tree)) 98 Ok((test.text, tree))
99} 99}
100 100
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 57a490fa7..4b9fc9372 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -38,7 +38,7 @@ mod marks;
38 38
39use std::sync::Arc; 39use std::sync::Arc;
40 40
41use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit}; 41use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode};
42use ra_text_edit::TextEdit; 42use ra_text_edit::TextEdit;
43use ra_db::{ 43use ra_db::{
44 SourceDatabase, CheckCanceled, 44 SourceDatabase, CheckCanceled,
@@ -244,8 +244,7 @@ impl Analysis {
244 /// Returns a syntax tree represented as `String`, for debug purposes. 244 /// Returns a syntax tree represented as `String`, for debug purposes.
245 // FIXME: use a better name here. 245 // FIXME: use a better name here.
246 pub fn syntax_tree(&self, file_id: FileId) -> String { 246 pub fn syntax_tree(&self, file_id: FileId) -> String {
247 let file = self.db.parse(file_id); 247 self.db.parse(file_id).syntax().debug_dump()
248 ra_ide_api_light::syntax_tree(&file)
249 } 248 }
250 249
251 /// Returns an edit to remove all newlines in the range, cleaning up minor 250 /// Returns an edit to remove all newlines in the range, cleaning up minor
diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs
index 6d1ce8dbf..43cdd6ea4 100644
--- a/crates/ra_ide_api_light/src/lib.rs
+++ b/crates/ra_ide_api_light/src/lib.rs
@@ -123,10 +123,6 @@ pub fn highlight(root: &SyntaxNode) -> Vec<HighlightedRange> {
123 res 123 res
124} 124}
125 125
126pub fn syntax_tree(file: &SourceFile) -> String {
127 ::ra_syntax::utils::dump_tree(file.syntax())
128}
129
130#[cfg(test)] 126#[cfg(test)]
131mod tests { 127mod tests {
132 use ra_syntax::AstNode; 128 use ra_syntax::AstNode;
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..4d54ae614 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -6,12 +6,15 @@
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::{
10 fmt::{self, Write},
11 borrow::Borrow,
12};
10 13
11use rowan::{Types, TransparentNewType}; 14use rowan::{Types, TransparentNewType};
12 15
13use crate::{ 16use crate::{
14 SmolStr, SyntaxKind, TextRange, SyntaxText, 17 SmolStr, SyntaxKind, TextRange, SyntaxText, SourceFile, AstNode,
15 syntax_error::SyntaxError, 18 syntax_error::SyntaxError,
16}; 19};
17 20
@@ -24,14 +27,17 @@ impl Types for RaTypes {
24 type RootData = Vec<SyntaxError>; 27 type RootData = Vec<SyntaxError>;
25} 28}
26 29
27pub type GreenNode = rowan::GreenNode<RaTypes>; 30pub(crate) type GreenNode = rowan::GreenNode<RaTypes>;
31
32/// Marker trait for CST and AST nodes
33pub trait SyntaxNodeWrapper: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>> {}
34impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> SyntaxNodeWrapper for T {}
28 35
36/// An owning smart pointer for CST or AST node.
29#[derive(PartialEq, Eq, Hash)] 37#[derive(PartialEq, Eq, Hash)]
30pub struct TreeArc<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>>( 38pub struct TreeArc<T: SyntaxNodeWrapper>(pub(crate) rowan::TreeArc<RaTypes, T>);
31 pub(crate) rowan::TreeArc<RaTypes, T>,
32);
33 39
34impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> Borrow<T> for TreeArc<T> { 40impl<T: SyntaxNodeWrapper> Borrow<T> for TreeArc<T> {
35 fn borrow(&self) -> &T { 41 fn borrow(&self) -> &T {
36 &*self 42 &*self
37 } 43 }
@@ -39,11 +45,11 @@ impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> Borrow<T> for Tre
39 45
40impl<T> TreeArc<T> 46impl<T> TreeArc<T>
41where 47where
42 T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 48 T: SyntaxNodeWrapper,
43{ 49{
44 pub(crate) fn cast<U>(this: TreeArc<T>) -> TreeArc<U> 50 pub(crate) fn cast<U>(this: TreeArc<T>) -> TreeArc<U>
45 where 51 where
46 U: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 52 U: SyntaxNodeWrapper,
47 { 53 {
48 TreeArc(rowan::TreeArc::cast(this.0)) 54 TreeArc(rowan::TreeArc::cast(this.0))
49 } 55 }
@@ -51,7 +57,7 @@ where
51 57
52impl<T> std::ops::Deref for TreeArc<T> 58impl<T> std::ops::Deref for TreeArc<T>
53where 59where
54 T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 60 T: SyntaxNodeWrapper,
55{ 61{
56 type Target = T; 62 type Target = T;
57 fn deref(&self) -> &T { 63 fn deref(&self) -> &T {
@@ -61,7 +67,7 @@ where
61 67
62impl<T> PartialEq<T> for TreeArc<T> 68impl<T> PartialEq<T> for TreeArc<T>
63where 69where
64 T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 70 T: SyntaxNodeWrapper,
65 T: PartialEq<T>, 71 T: PartialEq<T>,
66{ 72{
67 fn eq(&self, other: &T) -> bool { 73 fn eq(&self, other: &T) -> bool {
@@ -72,7 +78,7 @@ where
72 78
73impl<T> Clone for TreeArc<T> 79impl<T> Clone for TreeArc<T>
74where 80where
75 T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 81 T: SyntaxNodeWrapper,
76{ 82{
77 fn clone(&self) -> TreeArc<T> { 83 fn clone(&self) -> TreeArc<T> {
78 TreeArc(self.0.clone()) 84 TreeArc(self.0.clone())
@@ -81,7 +87,7 @@ where
81 87
82impl<T> fmt::Debug for TreeArc<T> 88impl<T> fmt::Debug for TreeArc<T>
83where 89where
84 T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>, 90 T: SyntaxNodeWrapper,
85 T: fmt::Debug, 91 T: fmt::Debug,
86{ 92{
87 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 93 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
@@ -96,13 +102,24 @@ unsafe impl TransparentNewType for SyntaxNode {
96 type Repr = rowan::SyntaxNode<RaTypes>; 102 type Repr = rowan::SyntaxNode<RaTypes>;
97} 103}
98 104
99impl SyntaxNode { 105impl ToOwned for SyntaxNode {
100 pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SyntaxNode> { 106 type Owned = TreeArc<SyntaxNode>;
101 let ptr = TreeArc(rowan::SyntaxNode::new(green, errors)); 107 fn to_owned(&self) -> TreeArc<SyntaxNode> {
108 let ptr = TreeArc(self.0.to_owned());
102 TreeArc::cast(ptr) 109 TreeArc::cast(ptr)
103 } 110 }
104} 111}
105 112
113impl fmt::Debug for SyntaxNode {
114 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
115 write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
116 if has_short_text(self.kind()) {
117 write!(fmt, " \"{}\"", self.text())?;
118 }
119 Ok(())
120 }
121}
122
106#[derive(Debug, Clone, Copy, PartialEq, Eq)] 123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum Direction { 124pub enum Direction {
108 Next, 125 Next,
@@ -110,48 +127,10 @@ pub enum Direction {
110} 127}
111 128
112impl SyntaxNode { 129impl SyntaxNode {
113 pub fn leaf_text(&self) -> Option<&SmolStr> { 130 pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SyntaxNode> {
114 self.0.leaf_text() 131 let ptr = TreeArc(rowan::SyntaxNode::new(green, errors));
115 }
116 pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode> {
117 crate::algo::generate(Some(self), |&node| node.parent())
118 }
119 pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode> {
120 self.preorder().filter_map(|event| match event {
121 WalkEvent::Enter(node) => Some(node),
122 WalkEvent::Leave(_) => None,
123 })
124 }
125 pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode> {
126 crate::algo::generate(Some(self), move |&node| match direction {
127 Direction::Next => node.next_sibling(),
128 Direction::Prev => node.prev_sibling(),
129 })
130 }
131 pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode>> {
132 self.0.preorder().map(|event| match event {
133 WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode::from_repr(n)),
134 WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)),
135 })
136 }
137}
138
139impl ToOwned for SyntaxNode {
140 type Owned = TreeArc<SyntaxNode>;
141 fn to_owned(&self) -> TreeArc<SyntaxNode> {
142 let ptr = TreeArc(self.0.to_owned());
143 TreeArc::cast(ptr) 132 TreeArc::cast(ptr)
144 } 133 }
145}
146
147impl SyntaxNode {
148 pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
149 self.0.root_data()
150 }
151
152 pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
153 self.0.replace_self(replacement)
154 }
155 134
156 pub fn kind(&self) -> SyntaxKind { 135 pub fn kind(&self) -> SyntaxKind {
157 self.0.kind() 136 self.0.kind()
@@ -169,6 +148,10 @@ impl SyntaxNode {
169 self.0.is_leaf() 148 self.0.is_leaf()
170 } 149 }
171 150
151 pub fn leaf_text(&self) -> Option<&SmolStr> {
152 self.0.leaf_text()
153 }
154
172 pub fn parent(&self) -> Option<&SyntaxNode> { 155 pub fn parent(&self) -> Option<&SyntaxNode> {
173 self.0.parent().map(SyntaxNode::from_repr) 156 self.0.parent().map(SyntaxNode::from_repr)
174 } 157 }
@@ -193,18 +176,85 @@ impl SyntaxNode {
193 SyntaxNodeChildren(self.0.children()) 176 SyntaxNodeChildren(self.0.children())
194 } 177 }
195 178
179 pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode> {
180 crate::algo::generate(Some(self), |&node| node.parent())
181 }
182
183 pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode> {
184 self.preorder().filter_map(|event| match event {
185 WalkEvent::Enter(node) => Some(node),
186 WalkEvent::Leave(_) => None,
187 })
188 }
189
190 pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode> {
191 crate::algo::generate(Some(self), move |&node| match direction {
192 Direction::Next => node.next_sibling(),
193 Direction::Prev => node.prev_sibling(),
194 })
195 }
196
197 pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode>> {
198 self.0.preorder().map(|event| match event {
199 WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode::from_repr(n)),
200 WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)),
201 })
202 }
203
196 pub fn memory_size_of_subtree(&self) -> usize { 204 pub fn memory_size_of_subtree(&self) -> usize {
197 self.0.memory_size_of_subtree() 205 self.0.memory_size_of_subtree()
198 } 206 }
199}
200 207
201impl fmt::Debug for SyntaxNode { 208 pub fn debug_dump(&self) -> String {
202 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 209 let mut errors: Vec<_> = match self.ancestors().find_map(SourceFile::cast) {
203 write!(fmt, "{:?}@{:?}", self.kind(), self.range())?; 210 Some(file) => file.errors(),
204 if has_short_text(self.kind()) { 211 None => self.root_data().to_vec(),
205 write!(fmt, " \"{}\"", self.text())?; 212 };
213 errors.sort_by_key(|e| e.offset());
214 let mut err_pos = 0;
215 let mut level = 0;
216 let mut buf = String::new();
217 macro_rules! indent {
218 () => {
219 for _ in 0..level {
220 buf.push_str(" ");
221 }
222 };
206 } 223 }
207 Ok(()) 224
225 for event in self.preorder() {
226 match event {
227 WalkEvent::Enter(node) => {
228 indent!();
229 writeln!(buf, "{:?}", node).unwrap();
230 if node.first_child().is_none() {
231 let off = node.range().end();
232 while err_pos < errors.len() && errors[err_pos].offset() <= off {
233 indent!();
234 writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
235 err_pos += 1;
236 }
237 }
238 level += 1;
239 }
240 WalkEvent::Leave(_) => level -= 1,
241 }
242 }
243
244 assert_eq!(level, 0);
245 for err in errors[err_pos..].iter() {
246 writeln!(buf, "err: `{}`", err).unwrap();
247 }
248
249 buf
250 }
251
252 pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
253 self.0.root_data()
254 }
255
256 pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
257 self.0.replace_self(replacement)
208 } 258 }
209} 259}
210 260
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