diff options
Diffstat (limited to 'crates/ra_syntax/src')
-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 | 176 | ||||
-rw-r--r-- | crates/ra_syntax/src/utils.rs | 83 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 41 |
5 files changed, 159 insertions, 158 deletions
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..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 | ||
9 | use std::{fmt, borrow::Borrow}; | 9 | use std::{ |
10 | fmt::{self, Write}, | ||
11 | borrow::Borrow, | ||
12 | }; | ||
10 | 13 | ||
11 | use rowan::{Types, TransparentNewType}; | 14 | use rowan::{Types, TransparentNewType}; |
12 | 15 | ||
13 | use crate::{ | 16 | use 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 | ||
27 | pub type GreenNode = rowan::GreenNode<RaTypes>; | 30 | pub(crate) type GreenNode = rowan::GreenNode<RaTypes>; |
31 | |||
32 | /// Marker trait for CST and AST nodes | ||
33 | pub trait SyntaxNodeWrapper: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>> {} | ||
34 | impl<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)] |
30 | pub struct TreeArc<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>>( | 38 | pub struct TreeArc<T: SyntaxNodeWrapper>(pub(crate) rowan::TreeArc<RaTypes, T>); |
31 | pub(crate) rowan::TreeArc<RaTypes, T>, | ||
32 | ); | ||
33 | 39 | ||
34 | impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> Borrow<T> for TreeArc<T> { | 40 | impl<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 | ||
40 | impl<T> TreeArc<T> | 46 | impl<T> TreeArc<T> |
41 | where | 47 | where |
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 | ||
52 | impl<T> std::ops::Deref for TreeArc<T> | 58 | impl<T> std::ops::Deref for TreeArc<T> |
53 | where | 59 | where |
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 | ||
62 | impl<T> PartialEq<T> for TreeArc<T> | 68 | impl<T> PartialEq<T> for TreeArc<T> |
63 | where | 69 | where |
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 | ||
73 | impl<T> Clone for TreeArc<T> | 79 | impl<T> Clone for TreeArc<T> |
74 | where | 80 | where |
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 | ||
82 | impl<T> fmt::Debug for TreeArc<T> | 88 | impl<T> fmt::Debug for TreeArc<T> |
83 | where | 89 | where |
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 | ||
99 | impl SyntaxNode { | 105 | impl 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 | ||
113 | impl 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)] |
107 | pub enum Direction { | 124 | pub enum Direction { |
108 | Next, | 125 | Next, |
@@ -110,48 +127,10 @@ pub enum Direction { | |||
110 | } | 127 | } |
111 | 128 | ||
112 | impl SyntaxNode { | 129 | impl 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 | |||
139 | impl 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 | |||
147 | impl 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 | ||
201 | impl 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 @@ | |||
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 | } | ||