aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-05-29 07:40:39 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-05-29 07:40:39 +0100
commit7a1cae59acf72f821343b2ba10ef69fb92a5b952 (patch)
tree26e606ccd132a24e9bc89cf174e4adadf60c5992 /crates/ra_syntax/src
parentb0d84cb8faefedde7ace4ff152a2a13408e79e5d (diff)
parent80a17251473bd6213a4c8a51ea7f732394d173c5 (diff)
Merge #1337
1337: Move syntax errors our of syntax tree r=matklad a=matklad I am not really sure if it's a good idea, but `SyntaxError` do not really belong to a `SyntaxTree`. So let's just store them on the side? Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/ast.rs16
-rw-r--r--crates/ra_syntax/src/fuzz.rs21
-rw-r--r--crates/ra_syntax/src/lib.rs94
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs12
-rw-r--r--crates/ra_syntax/src/ptr.rs2
-rw-r--r--crates/ra_syntax/src/syntax_node.rs55
6 files changed, 105 insertions, 95 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index f7e33366e..319110b6a 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -80,7 +80,9 @@ fn test_doc_comment_none() {
80 // non-doc 80 // non-doc
81 mod foo {} 81 mod foo {}
82 "#, 82 "#,
83 ); 83 )
84 .ok()
85 .unwrap();
84 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 86 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
85 assert!(module.doc_comment_text().is_none()); 87 assert!(module.doc_comment_text().is_none());
86} 88}
@@ -93,7 +95,9 @@ fn test_doc_comment_of_items() {
93 // non-doc 95 // non-doc
94 mod foo {} 96 mod foo {}
95 "#, 97 "#,
96 ); 98 )
99 .ok()
100 .unwrap();
97 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 101 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
98 assert_eq!("doc", module.doc_comment_text().unwrap()); 102 assert_eq!("doc", module.doc_comment_text().unwrap());
99} 103}
@@ -110,7 +114,9 @@ fn test_doc_comment_preserves_indents() {
110 /// ``` 114 /// ```
111 mod foo {} 115 mod foo {}
112 "#, 116 "#,
113 ); 117 )
118 .ok()
119 .unwrap();
114 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 120 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
115 assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); 121 assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap());
116} 122}
@@ -133,7 +139,9 @@ where
133 for<'a> F: Fn(&'a str) 139 for<'a> F: Fn(&'a str)
134{} 140{}
135 "#, 141 "#,
136 ); 142 )
143 .ok()
144 .unwrap();
137 let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap(); 145 let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
138 146
139 let mut predicates = where_clause.predicates(); 147 let mut predicates = where_clause.predicates();
diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs
index af11b2e1a..6a9905bd1 100644
--- a/crates/ra_syntax/src/fuzz.rs
+++ b/crates/ra_syntax/src/fuzz.rs
@@ -5,12 +5,11 @@ use std::str::{self, FromStr};
5fn check_file_invariants(file: &SourceFile) { 5fn check_file_invariants(file: &SourceFile) {
6 let root = file.syntax(); 6 let root = file.syntax();
7 validation::validate_block_structure(root); 7 validation::validate_block_structure(root);
8 let _ = file.errors();
9} 8}
10 9
11pub fn check_parser(text: &str) { 10pub fn check_parser(text: &str) {
12 let file = SourceFile::parse(text); 11 let file = SourceFile::parse(text);
13 check_file_invariants(&file); 12 check_file_invariants(&file.tree);
14} 13}
15 14
16#[derive(Debug, Clone)] 15#[derive(Debug, Clone)]
@@ -44,16 +43,18 @@ impl CheckReparse {
44 } 43 }
45 44
46 pub fn run(&self) { 45 pub fn run(&self) {
47 let file = SourceFile::parse(&self.text); 46 let parse = SourceFile::parse(&self.text);
48 let new_file = file.reparse(&self.edit); 47 let new_parse = parse.reparse(&self.edit);
49 check_file_invariants(&new_file); 48 check_file_invariants(&new_parse.tree);
50 assert_eq!(&new_file.syntax().text().to_string(), &self.edited_text); 49 assert_eq!(&new_parse.tree.syntax().text().to_string(), &self.edited_text);
51 let full_reparse = SourceFile::parse(&self.edited_text); 50 let full_reparse = SourceFile::parse(&self.edited_text);
52 for (a, b) in new_file.syntax().descendants().zip(full_reparse.syntax().descendants()) { 51 for (a, b) in
52 new_parse.tree.syntax().descendants().zip(full_reparse.tree.syntax().descendants())
53 {
53 if (a.kind(), a.range()) != (b.kind(), b.range()) { 54 if (a.kind(), a.range()) != (b.kind(), b.range()) {
54 eprint!("original:\n{}", file.syntax().debug_dump()); 55 eprint!("original:\n{}", parse.tree.syntax().debug_dump());
55 eprint!("reparsed:\n{}", new_file.syntax().debug_dump()); 56 eprint!("reparsed:\n{}", new_parse.tree.syntax().debug_dump());
56 eprint!("full reparse:\n{}", full_reparse.syntax().debug_dump()); 57 eprint!("full reparse:\n{}", full_reparse.tree.syntax().debug_dump());
57 assert_eq!( 58 assert_eq!(
58 format!("{:?}", a), 59 format!("{:?}", a),
59 format!("{:?}", b), 60 format!("{:?}", b),
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 0ceabc203..930a643b7 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -31,6 +31,12 @@ pub mod ast;
31#[doc(hidden)] 31#[doc(hidden)]
32pub mod fuzz; 32pub mod fuzz;
33 33
34use std::{sync::Arc, fmt::Write};
35
36use ra_text_edit::AtomTextEdit;
37
38use crate::syntax_node::GreenNode;
39
34pub use rowan::{SmolStr, TextRange, TextUnit}; 40pub use rowan::{SmolStr, TextRange, TextUnit};
35pub use ra_parser::SyntaxKind; 41pub use ra_parser::SyntaxKind;
36pub use ra_parser::T; 42pub use ra_parser::T;
@@ -43,45 +49,72 @@ pub use crate::{
43 parsing::{tokenize, classify_literal, Token}, 49 parsing::{tokenize, classify_literal, Token},
44}; 50};
45 51
46use ra_text_edit::AtomTextEdit; 52/// `Parse` is the result of the parsing: a syntax tree and a collection of
47use crate::syntax_node::GreenNode; 53/// errors.
48 54///
49/// `SourceFile` represents a parse tree for a single Rust file. 55/// Note that we always produce a syntax tree, even for completely invalid
50pub use crate::ast::SourceFile; 56/// files.
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct Parse {
59 pub tree: TreeArc<SourceFile>,
60 pub errors: Arc<Vec<SyntaxError>>,
61}
51 62
52impl SourceFile { 63impl Parse {
53 fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> { 64 pub fn ok(self) -> Result<TreeArc<SourceFile>, Arc<Vec<SyntaxError>>> {
54 let root = SyntaxNode::new(green, errors); 65 if self.errors.is_empty() {
55 if cfg!(debug_assertions) { 66 Ok(self.tree)
56 validation::validate_block_structure(&root); 67 } else {
68 Err(self.errors)
57 } 69 }
58 assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
59 TreeArc::cast(root)
60 } 70 }
61 71
62 pub fn parse(text: &str) -> TreeArc<SourceFile> { 72 pub fn reparse(&self, edit: &AtomTextEdit) -> Parse {
63 let (green, errors) = parsing::parse_text(text); 73 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
64 SourceFile::new(green, errors)
65 } 74 }
66 75
67 pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { 76 pub fn debug_dump(&self) -> String {
68 self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) 77 let mut buf = self.tree.syntax().debug_dump();
78 for err in self.errors.iter() {
79 writeln!(buf, "err: `{}`", err).unwrap();
80 }
81 buf
69 } 82 }
70 83
71 pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> { 84 fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse> {
72 parsing::incremental_reparse(self.syntax(), edit, self.errors()) 85 // FIXME: validation errors are not handled here
73 .map(|(green_node, errors, _reparsed_range)| SourceFile::new(green_node, errors)) 86 parsing::incremental_reparse(self.tree.syntax(), edit, self.errors.to_vec()).map(
87 |(green_node, errors, _reparsed_range)| Parse {
88 tree: SourceFile::new(green_node),
89 errors: Arc::new(errors),
90 },
91 )
74 } 92 }
75 93
76 fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { 94 fn full_reparse(&self, edit: &AtomTextEdit) -> Parse {
77 let text = edit.apply(self.syntax().text().to_string()); 95 let text = edit.apply(self.tree.syntax().text().to_string());
78 SourceFile::parse(&text) 96 SourceFile::parse(&text)
79 } 97 }
98}
99
100/// `SourceFile` represents a parse tree for a single Rust file.
101pub use crate::ast::SourceFile;
102
103impl SourceFile {
104 fn new(green: GreenNode) -> TreeArc<SourceFile> {
105 let root = SyntaxNode::new(green);
106 if cfg!(debug_assertions) {
107 validation::validate_block_structure(&root);
108 }
109 assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
110 TreeArc::cast(root)
111 }
80 112
81 pub fn errors(&self) -> Vec<SyntaxError> { 113 pub fn parse(text: &str) -> Parse {
82 let mut errors = self.syntax.root_data().to_vec(); 114 let (green, mut errors) = parsing::parse_text(text);
83 errors.extend(validation::validate(self)); 115 let tree = SourceFile::new(green);
84 errors 116 errors.extend(validation::validate(&tree));
117 Parse { tree, errors: Arc::new(errors) }
85 } 118 }
86} 119}
87 120
@@ -98,14 +131,15 @@ fn api_walkthrough() {
98 "; 131 ";
99 // `SourceFile` is the main entry point. 132 // `SourceFile` is the main entry point.
100 // 133 //
101 // Note how `parse` does not return a `Result`: even completely invalid 134 // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
102 // source code might be parsed. 135 // of errors. That is, syntax tree is constructed even in presence of errors.
103 let file = SourceFile::parse(source_code); 136 let parse = SourceFile::parse(source_code);
137 assert!(parse.errors.is_empty());
104 138
105 // Due to the way ownership is set up, owned syntax Nodes always live behind 139 // Due to the way ownership is set up, owned syntax Nodes always live behind
106 // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which 140 // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which
107 // points to the whole file instead of an individual node. 141 // points to the whole file instead of an individual node.
108 let file: TreeArc<SourceFile> = file; 142 let file: TreeArc<SourceFile> = parse.tree;
109 143
110 // `SourceFile` is the root of the syntax tree. We can iterate file's items: 144 // `SourceFile` is the root of the syntax tree. We can iterate file's items:
111 let mut func = None; 145 let mut func = None;
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index 3b6687f61..cf27a3393 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -166,9 +166,11 @@ fn merge_errors(
166 166
167#[cfg(test)] 167#[cfg(test)]
168mod tests { 168mod tests {
169 use std::sync::Arc;
170
169 use test_utils::{extract_range, assert_eq_text}; 171 use test_utils::{extract_range, assert_eq_text};
170 172
171 use crate::{SourceFile, AstNode}; 173 use crate::{SourceFile, AstNode, Parse};
172 use super::*; 174 use super::*;
173 175
174 fn do_check(before: &str, replace_with: &str, reparsed_len: u32) { 176 fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
@@ -181,14 +183,14 @@ mod tests {
181 let f = SourceFile::parse(&before); 183 let f = SourceFile::parse(&before);
182 let edit = AtomTextEdit { delete: range, insert: replace_with.to_string() }; 184 let edit = AtomTextEdit { delete: range, insert: replace_with.to_string() };
183 let (green, new_errors, range) = 185 let (green, new_errors, range) =
184 incremental_reparse(f.syntax(), &edit, f.errors()).unwrap(); 186 incremental_reparse(f.tree.syntax(), &edit, f.errors.to_vec()).unwrap();
185 assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length"); 187 assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
186 SourceFile::new(green, new_errors) 188 Parse { tree: SourceFile::new(green), errors: Arc::new(new_errors) }
187 }; 189 };
188 190
189 assert_eq_text!( 191 assert_eq_text!(
190 &fully_reparsed.syntax().debug_dump(), 192 &fully_reparsed.tree.syntax().debug_dump(),
191 &incrementally_reparsed.syntax().debug_dump(), 193 &incrementally_reparsed.tree.syntax().debug_dump(),
192 ); 194 );
193 } 195 }
194 196
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index cee9503ca..10cddb852 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -76,7 +76,7 @@ impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
76fn test_local_syntax_ptr() { 76fn test_local_syntax_ptr() {
77 use crate::{ast, AstNode, SourceFile}; 77 use crate::{ast, AstNode, SourceFile};
78 78
79 let file = SourceFile::parse("struct Foo { f: u32, }"); 79 let file = SourceFile::parse("struct Foo { f: u32, }").ok().unwrap();
80 let field = file.syntax().descendants().find_map(ast::NamedFieldDef::cast).unwrap(); 80 let field = file.syntax().descendants().find_map(ast::NamedFieldDef::cast).unwrap();
81 let ptr = SyntaxNodePtr::new(field.syntax()); 81 let ptr = SyntaxNodePtr::new(field.syntax());
82 let field_syntax = ptr.to_node(file.syntax()); 82 let field_syntax = ptr.to_node(file.syntax());
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index 80054f529..4105b5220 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -9,7 +9,6 @@
9use std::{ 9use std::{
10 ops::RangeInclusive, 10 ops::RangeInclusive,
11 fmt::{self, Write}, 11 fmt::{self, Write},
12 any::Any,
13 borrow::Borrow, 12 borrow::Borrow,
14 iter::successors, 13 iter::successors,
15}; 14};
@@ -133,10 +132,8 @@ pub enum Direction {
133} 132}
134 133
135impl SyntaxNode { 134impl SyntaxNode {
136 pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SyntaxNode> { 135 pub(crate) fn new(green: GreenNode) -> TreeArc<SyntaxNode> {
137 let errors: Option<Box<Any + Send + Sync>> = 136 let ptr = TreeArc(rowan::SyntaxNode::new(green, None));
138 if errors.is_empty() { None } else { Some(Box::new(errors)) };
139 let ptr = TreeArc(rowan::SyntaxNode::new(green, errors));
140 TreeArc::cast(ptr) 137 TreeArc::cast(ptr)
141 } 138 }
142 139
@@ -259,37 +256,18 @@ impl SyntaxNode {
259 } 256 }
260 257
261 pub fn debug_dump(&self) -> String { 258 pub fn debug_dump(&self) -> String {
262 let mut errors: Vec<_> = match self.ancestors().find_map(SourceFile::cast) {
263 Some(file) => file.errors(),
264 None => self.root_data().to_vec(),
265 };
266 errors.sort_by_key(|e| e.offset());
267 let mut err_pos = 0;
268 let mut level = 0; 259 let mut level = 0;
269 let mut buf = String::new(); 260 let mut buf = String::new();
270 macro_rules! indent {
271 () => {
272 for _ in 0..level {
273 buf.push_str(" ");
274 }
275 };
276 }
277 261
278 for event in self.preorder_with_tokens() { 262 for event in self.preorder_with_tokens() {
279 match event { 263 match event {
280 WalkEvent::Enter(element) => { 264 WalkEvent::Enter(element) => {
281 indent!(); 265 for _ in 0..level {
266 buf.push_str(" ");
267 }
282 match element { 268 match element {
283 SyntaxElement::Node(node) => writeln!(buf, "{:?}", node).unwrap(), 269 SyntaxElement::Node(node) => writeln!(buf, "{:?}", node).unwrap(),
284 SyntaxElement::Token(token) => { 270 SyntaxElement::Token(token) => writeln!(buf, "{:?}", token).unwrap(),
285 writeln!(buf, "{:?}", token).unwrap();
286 let off = token.range().end();
287 while err_pos < errors.len() && errors[err_pos].offset() <= off {
288 indent!();
289 writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
290 err_pos += 1;
291 }
292 }
293 } 271 }
294 level += 1; 272 level += 1;
295 } 273 }
@@ -298,23 +276,10 @@ impl SyntaxNode {
298 } 276 }
299 277
300 assert_eq!(level, 0); 278 assert_eq!(level, 0);
301 for err in errors[err_pos..].iter() {
302 writeln!(buf, "err: `{}`", err).unwrap();
303 }
304 279
305 buf 280 buf
306 } 281 }
307 282
308 pub(crate) fn root_data(&self) -> &[SyntaxError] {
309 match self.0.root_data() {
310 None => &[],
311 Some(data) => {
312 let data: &Vec<SyntaxError> = std::any::Any::downcast_ref(data).unwrap();
313 data.as_slice()
314 }
315 }
316 }
317
318 pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode { 283 pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
319 self.0.replace_with(replacement) 284 self.0.replace_with(replacement)
320 } 285 }
@@ -386,7 +351,7 @@ impl SyntaxNode {
386 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>(); 351 let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
387 let new_node = GreenNode::new(rowan::SyntaxKind(self.kind() as u16), new_children); 352 let new_node = GreenNode::new(rowan::SyntaxKind(self.kind() as u16), new_children);
388 let new_file_node = self.replace_with(new_node); 353 let new_file_node = self.replace_with(new_node);
389 let file = SourceFile::new(new_file_node, Vec::new()); 354 let file = SourceFile::new(new_file_node);
390 355
391 // FIXME: use a more elegant way to re-fetch the node (#1185), make 356 // FIXME: use a more elegant way to re-fetch the node (#1185), make
392 // `range` private afterwards 357 // `range` private afterwards
@@ -629,13 +594,13 @@ impl SyntaxTreeBuilder {
629 (green, self.errors) 594 (green, self.errors)
630 } 595 }
631 596
632 pub fn finish(self) -> TreeArc<SyntaxNode> { 597 pub fn finish(self) -> (TreeArc<SyntaxNode>, Vec<SyntaxError>) {
633 let (green, errors) = self.finish_raw(); 598 let (green, errors) = self.finish_raw();
634 let node = SyntaxNode::new(green, errors); 599 let node = SyntaxNode::new(green);
635 if cfg!(debug_assertions) { 600 if cfg!(debug_assertions) {
636 crate::validation::validate_block_structure(&node); 601 crate::validation::validate_block_structure(&node);
637 } 602 }
638 node 603 (node, errors)
639 } 604 }
640 605
641 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { 606 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {