diff options
author | Adolfo OchagavĂa <[email protected]> | 2018-11-04 15:45:22 +0000 |
---|---|---|
committer | Adolfo OchagavĂa <[email protected]> | 2018-11-04 20:16:38 +0000 |
commit | 3b42ddae601fbd73f672e82028e04c3abdf1252d (patch) | |
tree | d63c4114d2fccf1c085502dfd1048b4b5f68266b /crates/ra_syntax | |
parent | 576b9a0727ebbf00521bc1131cda808145696d06 (diff) |
Introduce SyntaxErrorKind and TextRange in SyntaxError
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/src/parser_impl/event.rs | 11 | ||||
-rw-r--r-- | crates/ra_syntax/src/parser_impl/mod.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/reparsing.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/string_lexing/mod.rs | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/utils.rs | 8 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 99 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/builder.rs | 11 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/mod.rs | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/syntax_error.rs | 42 |
9 files changed, 153 insertions, 62 deletions
diff --git a/crates/ra_syntax/src/parser_impl/event.rs b/crates/ra_syntax/src/parser_impl/event.rs index 79fa21389..ced09bcff 100644 --- a/crates/ra_syntax/src/parser_impl/event.rs +++ b/crates/ra_syntax/src/parser_impl/event.rs | |||
@@ -13,6 +13,10 @@ use crate::{ | |||
13 | SmolStr, | 13 | SmolStr, |
14 | SyntaxKind::{self, *}, | 14 | SyntaxKind::{self, *}, |
15 | TextRange, TextUnit, | 15 | TextRange, TextUnit, |
16 | yellow::syntax_error::{ | ||
17 | ParseError, | ||
18 | SyntaxErrorKind, | ||
19 | }, | ||
16 | }; | 20 | }; |
17 | use std::mem; | 21 | use std::mem; |
18 | 22 | ||
@@ -75,7 +79,7 @@ pub(crate) enum Event { | |||
75 | }, | 79 | }, |
76 | 80 | ||
77 | Error { | 81 | Error { |
78 | msg: String, | 82 | msg: ParseError, |
79 | }, | 83 | }, |
80 | } | 84 | } |
81 | 85 | ||
@@ -157,7 +161,10 @@ impl<'a, S: Sink> EventProcessor<'a, S> { | |||
157 | .sum::<TextUnit>(); | 161 | .sum::<TextUnit>(); |
158 | self.leaf(kind, len, n_raw_tokens); | 162 | self.leaf(kind, len, n_raw_tokens); |
159 | } | 163 | } |
160 | Event::Error { msg } => self.sink.error(msg, self.text_pos), | 164 | Event::Error { msg } => self.sink.error( |
165 | SyntaxErrorKind::ParseError(msg), | ||
166 | TextRange::offset_len(self.text_pos, 1.into()), | ||
167 | ), | ||
161 | } | 168 | } |
162 | } | 169 | } |
163 | self.sink | 170 | self.sink |
diff --git a/crates/ra_syntax/src/parser_impl/mod.rs b/crates/ra_syntax/src/parser_impl/mod.rs index 2b026d61e..ade25770b 100644 --- a/crates/ra_syntax/src/parser_impl/mod.rs +++ b/crates/ra_syntax/src/parser_impl/mod.rs | |||
@@ -10,7 +10,11 @@ use crate::{ | |||
10 | event::{Event, EventProcessor}, | 10 | event::{Event, EventProcessor}, |
11 | input::{InputPosition, ParserInput}, | 11 | input::{InputPosition, ParserInput}, |
12 | }, | 12 | }, |
13 | SmolStr, TextUnit, | 13 | SmolStr, TextRange, |
14 | yellow::syntax_error::{ | ||
15 | ParseError, | ||
16 | SyntaxErrorKind, | ||
17 | }, | ||
14 | }; | 18 | }; |
15 | 19 | ||
16 | use crate::SyntaxKind::{self, EOF, TOMBSTONE}; | 20 | use crate::SyntaxKind::{self, EOF, TOMBSTONE}; |
@@ -21,7 +25,7 @@ pub(crate) trait Sink { | |||
21 | fn leaf(&mut self, kind: SyntaxKind, text: SmolStr); | 25 | fn leaf(&mut self, kind: SyntaxKind, text: SmolStr); |
22 | fn start_internal(&mut self, kind: SyntaxKind); | 26 | fn start_internal(&mut self, kind: SyntaxKind); |
23 | fn finish_internal(&mut self); | 27 | fn finish_internal(&mut self); |
24 | fn error(&mut self, message: String, offset: TextUnit); | 28 | fn error(&mut self, kind: SyntaxErrorKind, offset: TextRange); |
25 | fn finish(self) -> Self::Tree; | 29 | fn finish(self) -> Self::Tree; |
26 | } | 30 | } |
27 | 31 | ||
@@ -144,7 +148,9 @@ impl<'t> ParserImpl<'t> { | |||
144 | } | 148 | } |
145 | 149 | ||
146 | pub(super) fn error(&mut self, msg: String) { | 150 | pub(super) fn error(&mut self, msg: String) { |
147 | self.event(Event::Error { msg }) | 151 | self.event(Event::Error { |
152 | msg: ParseError(msg), | ||
153 | }) | ||
148 | } | 154 | } |
149 | 155 | ||
150 | pub(super) fn complete(&mut self, pos: u32, kind: SyntaxKind) { | 156 | pub(super) fn complete(&mut self, pos: u32, kind: SyntaxKind) { |
diff --git a/crates/ra_syntax/src/reparsing.rs b/crates/ra_syntax/src/reparsing.rs index b3b51b3e4..9f5baf1ef 100644 --- a/crates/ra_syntax/src/reparsing.rs +++ b/crates/ra_syntax/src/reparsing.rs | |||
@@ -165,19 +165,19 @@ fn merge_errors( | |||
165 | ) -> Vec<SyntaxError> { | 165 | ) -> Vec<SyntaxError> { |
166 | let mut res = Vec::new(); | 166 | let mut res = Vec::new(); |
167 | for e in old_errors { | 167 | for e in old_errors { |
168 | if e.offset <= old_node.range().start() { | 168 | if e.range.start() <= old_node.range().start() { |
169 | res.push(e) | 169 | res.push(e) |
170 | } else if e.offset >= old_node.range().end() { | 170 | } else if e.range.start() >= old_node.range().end() { |
171 | res.push(SyntaxError { | 171 | res.push(SyntaxError { |
172 | msg: e.msg, | 172 | kind: e.kind, |
173 | offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(), | 173 | range: e.range + TextUnit::of_str(&edit.insert) - edit.delete.len(), |
174 | }) | 174 | }) |
175 | } | 175 | } |
176 | } | 176 | } |
177 | for e in new_errors { | 177 | for e in new_errors { |
178 | res.push(SyntaxError { | 178 | res.push(SyntaxError { |
179 | msg: e.msg, | 179 | kind: e.kind, |
180 | offset: e.offset + old_node.range().start(), | 180 | range: e.range + old_node.range().start(), |
181 | }) | 181 | }) |
182 | } | 182 | } |
183 | res | 183 | res |
diff --git a/crates/ra_syntax/src/string_lexing/mod.rs b/crates/ra_syntax/src/string_lexing/mod.rs index 6b52c62c3..f0812ff28 100644 --- a/crates/ra_syntax/src/string_lexing/mod.rs +++ b/crates/ra_syntax/src/string_lexing/mod.rs | |||
@@ -100,10 +100,6 @@ impl<'a> Parser<'a> { | |||
100 | // Char parsing methods | 100 | // Char parsing methods |
101 | 101 | ||
102 | fn parse_unicode_escape(&mut self, start: TextUnit) -> CharComponent { | 102 | fn parse_unicode_escape(&mut self, start: TextUnit) -> CharComponent { |
103 | // Note: validation of UnicodeEscape will be done elsewhere: | ||
104 | // * Only hex digits or underscores allowed | ||
105 | // * Max 6 chars | ||
106 | // * Within allowed range (must be at most 10FFFF) | ||
107 | match self.peek() { | 103 | match self.peek() { |
108 | Some('{') => { | 104 | Some('{') => { |
109 | self.advance(); | 105 | self.advance(); |
@@ -127,9 +123,6 @@ impl<'a> Parser<'a> { | |||
127 | } | 123 | } |
128 | 124 | ||
129 | fn parse_ascii_code_escape(&mut self, start: TextUnit) -> CharComponent { | 125 | fn parse_ascii_code_escape(&mut self, start: TextUnit) -> CharComponent { |
130 | // Note: validation of AsciiCodeEscape will be done elsewhere: | ||
131 | // * First digit is octal | ||
132 | // * Second digit is hex | ||
133 | let code_start = self.get_pos(); | 126 | let code_start = self.get_pos(); |
134 | while let Some(next) = self.peek() { | 127 | while let Some(next) = self.peek() { |
135 | if next == '\'' || (self.get_pos() - code_start == 2.into()) { | 128 | if next == '\'' || (self.get_pos() - code_start == 2.into()) { |
@@ -144,9 +137,6 @@ impl<'a> Parser<'a> { | |||
144 | } | 137 | } |
145 | 138 | ||
146 | fn parse_escape(&mut self, start: TextUnit) -> CharComponent { | 139 | fn parse_escape(&mut self, start: TextUnit) -> CharComponent { |
147 | // Note: validation of AsciiEscape will be done elsewhere: | ||
148 | // * The escape sequence is non-empty | ||
149 | // * The escape sequence is valid | ||
150 | if self.peek().is_none() { | 140 | if self.peek().is_none() { |
151 | return CharComponent::new(TextRange::from_to(start, start), AsciiEscape); | 141 | return CharComponent::new(TextRange::from_to(start, start), AsciiEscape); |
152 | } | 142 | } |
diff --git a/crates/ra_syntax/src/utils.rs b/crates/ra_syntax/src/utils.rs index 00f00139a..f55568d94 100644 --- a/crates/ra_syntax/src/utils.rs +++ b/crates/ra_syntax/src/utils.rs | |||
@@ -4,7 +4,7 @@ use std::fmt::Write; | |||
4 | /// Parse a file and create a string representation of the resulting parse tree. | 4 | /// Parse a file and create a string representation of the resulting parse tree. |
5 | pub fn dump_tree(syntax: SyntaxNodeRef) -> String { | 5 | pub fn dump_tree(syntax: SyntaxNodeRef) -> String { |
6 | let mut errors: Vec<_> = syntax.root_data().to_vec(); | 6 | let mut errors: Vec<_> = syntax.root_data().to_vec(); |
7 | errors.sort_by_key(|e| e.offset); | 7 | errors.sort_by_key(|e| e.range.start()); |
8 | let mut err_pos = 0; | 8 | let mut err_pos = 0; |
9 | let mut level = 0; | 9 | let mut level = 0; |
10 | let mut buf = String::new(); | 10 | let mut buf = String::new(); |
@@ -23,9 +23,9 @@ pub fn dump_tree(syntax: SyntaxNodeRef) -> String { | |||
23 | writeln!(buf, "{:?}", node).unwrap(); | 23 | writeln!(buf, "{:?}", node).unwrap(); |
24 | if node.first_child().is_none() { | 24 | if node.first_child().is_none() { |
25 | let off = node.range().end(); | 25 | let off = node.range().end(); |
26 | while err_pos < errors.len() && errors[err_pos].offset <= off { | 26 | while err_pos < errors.len() && errors[err_pos].range.start() <= off { |
27 | indent!(); | 27 | indent!(); |
28 | writeln!(buf, "err: `{}`", errors[err_pos].msg).unwrap(); | 28 | writeln!(buf, "err: `{}`", errors[err_pos].kind).unwrap(); |
29 | err_pos += 1; | 29 | err_pos += 1; |
30 | } | 30 | } |
31 | } | 31 | } |
@@ -37,7 +37,7 @@ pub fn dump_tree(syntax: SyntaxNodeRef) -> String { | |||
37 | 37 | ||
38 | assert_eq!(level, 0); | 38 | assert_eq!(level, 0); |
39 | for err in errors[err_pos..].iter() { | 39 | for err in errors[err_pos..].iter() { |
40 | writeln!(buf, "err: `{}`", err.msg).unwrap(); | 40 | writeln!(buf, "err: `{}`", err.kind).unwrap(); |
41 | } | 41 | } |
42 | 42 | ||
43 | buf | 43 | buf |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 03d98eff4..06e6e7505 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -1,40 +1,93 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | algo::visit::{visitor_ctx, VisitorCtx}, | ||
2 | ast::{self, AstNode}, | 3 | ast::{self, AstNode}, |
3 | File, | 4 | File, |
4 | string_lexing, | 5 | string_lexing::{self, CharComponentKind}, |
5 | yellow::{ | 6 | yellow::{ |
6 | SyntaxError, | 7 | SyntaxError, |
8 | SyntaxErrorKind::*, | ||
7 | }, | 9 | }, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn validate(file: &File) -> Vec<SyntaxError> { | 12 | pub(crate) fn validate(file: &File) -> Vec<SyntaxError> { |
11 | let mut errors = Vec::new(); | 13 | let mut errors = Vec::new(); |
12 | for d in file.root.borrowed().descendants() { | 14 | for node in file.root.borrowed().descendants() { |
13 | if let Some(c) = ast::Char::cast(d) { | 15 | let _ = visitor_ctx(&mut errors) |
14 | let components = &mut string_lexing::parse_char_literal(c.text()); | 16 | .visit::<ast::Char, _>(validate_char) |
15 | let len = components.count(); | 17 | .accept(node); |
18 | } | ||
19 | errors | ||
20 | } | ||
16 | 21 | ||
17 | if !components.has_closing_quote { | 22 | fn validate_char(node: ast::Char, errors: &mut Vec<SyntaxError>) { |
18 | errors.push(SyntaxError { | 23 | let mut components = string_lexing::parse_char_literal(node.text()); |
19 | msg: "Unclosed char literal".to_string(), | 24 | let mut len = 0; |
20 | offset: d.range().start(), | 25 | for component in &mut components { |
21 | }); | 26 | len += 1; |
22 | } | ||
23 | 27 | ||
24 | if len == 0 { | 28 | // Validate escapes |
25 | errors.push(SyntaxError { | 29 | let text = &node.text()[component.range]; |
26 | msg: "Empty char literal".to_string(), | 30 | let range = component.range + node.syntax().range().start(); |
27 | offset: d.range().start(), | 31 | use self::CharComponentKind::*; |
28 | }); | 32 | match component.kind { |
33 | AsciiEscape => { | ||
34 | if text.len() == 1 { | ||
35 | // Escape sequence consists only of leading `\` | ||
36 | errors.push(SyntaxError { | ||
37 | kind: EmptyAsciiEscape, | ||
38 | range: range, | ||
39 | }); | ||
40 | } else { | ||
41 | let escape_code = text.chars().skip(1).next().unwrap(); | ||
42 | if !is_ascii_escape(escape_code) { | ||
43 | errors.push(SyntaxError { | ||
44 | kind: InvalidAsciiEscape, | ||
45 | range: range, | ||
46 | }); | ||
47 | } | ||
48 | } | ||
29 | } | 49 | } |
30 | 50 | AsciiCodeEscape => { | |
31 | if len > 1 { | 51 | // TODO: |
32 | errors.push(SyntaxError { | 52 | // * First digit is octal |
33 | msg: "Character literal should be only one character long".to_string(), | 53 | // * Second digit is hex |
34 | offset: d.range().start(), | 54 | } |
35 | }); | 55 | UnicodeEscape => { |
56 | // TODO: | ||
57 | // * Only hex digits or underscores allowed | ||
58 | // * Max 6 chars | ||
59 | // * Within allowed range (must be at most 10FFFF) | ||
36 | } | 60 | } |
61 | // Code points are always valid | ||
62 | CodePoint => (), | ||
37 | } | 63 | } |
38 | } | 64 | } |
39 | errors | 65 | |
66 | if !components.has_closing_quote { | ||
67 | errors.push(SyntaxError { | ||
68 | kind: UnclosedChar, | ||
69 | range: node.syntax().range(), | ||
70 | }); | ||
71 | } | ||
72 | |||
73 | if len == 0 { | ||
74 | errors.push(SyntaxError { | ||
75 | kind: EmptyChar, | ||
76 | range: node.syntax().range(), | ||
77 | }); | ||
78 | } | ||
79 | |||
80 | if len > 1 { | ||
81 | errors.push(SyntaxError { | ||
82 | kind: LongChar, | ||
83 | range: node.syntax().range(), | ||
84 | }); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | fn is_ascii_escape(code: char) -> bool { | ||
89 | match code { | ||
90 | '\'' | '"' | 'n' | 'r' | 't' | '0' => true, | ||
91 | _ => false, | ||
92 | } | ||
40 | } | 93 | } |
diff --git a/crates/ra_syntax/src/yellow/builder.rs b/crates/ra_syntax/src/yellow/builder.rs index d64053409..dbe2df125 100644 --- a/crates/ra_syntax/src/yellow/builder.rs +++ b/crates/ra_syntax/src/yellow/builder.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | parser_impl::Sink, | 2 | parser_impl::Sink, |
3 | yellow::{GreenNode, RaTypes, SyntaxError}, | 3 | yellow::{GreenNode, RaTypes, SyntaxError, SyntaxErrorKind}, |
4 | SmolStr, SyntaxKind, TextUnit, | 4 | SmolStr, SyntaxKind, TextRange, |
5 | }; | 5 | }; |
6 | use rowan::GreenNodeBuilder; | 6 | use rowan::GreenNodeBuilder; |
7 | 7 | ||
@@ -34,11 +34,8 @@ impl Sink for GreenBuilder { | |||
34 | self.inner.finish_internal(); | 34 | self.inner.finish_internal(); |
35 | } | 35 | } |
36 | 36 | ||
37 | fn error(&mut self, message: String, offset: TextUnit) { | 37 | fn error(&mut self, kind: SyntaxErrorKind, range: TextRange) { |
38 | let error = SyntaxError { | 38 | let error = SyntaxError { kind, range }; |
39 | msg: message, | ||
40 | offset, | ||
41 | }; | ||
42 | self.errors.push(error) | 39 | self.errors.push(error) |
43 | } | 40 | } |
44 | 41 | ||
diff --git a/crates/ra_syntax/src/yellow/mod.rs b/crates/ra_syntax/src/yellow/mod.rs index 650917214..fd2b5bd33 100644 --- a/crates/ra_syntax/src/yellow/mod.rs +++ b/crates/ra_syntax/src/yellow/mod.rs | |||
@@ -1,8 +1,9 @@ | |||
1 | mod builder; | 1 | mod builder; |
2 | pub mod syntax_error; | ||
2 | mod syntax_text; | 3 | mod syntax_text; |
3 | 4 | ||
4 | use self::syntax_text::SyntaxText; | 5 | use self::syntax_text::SyntaxText; |
5 | use crate::{SmolStr, SyntaxKind, TextRange, TextUnit}; | 6 | use crate::{SmolStr, SyntaxKind, TextRange}; |
6 | use rowan::Types; | 7 | use rowan::Types; |
7 | use std::{ | 8 | use std::{ |
8 | fmt, | 9 | fmt, |
@@ -10,6 +11,7 @@ use std::{ | |||
10 | }; | 11 | }; |
11 | 12 | ||
12 | pub(crate) use self::builder::GreenBuilder; | 13 | pub(crate) use self::builder::GreenBuilder; |
14 | pub use self::syntax_error::{SyntaxError, SyntaxErrorKind}; | ||
13 | pub use rowan::{TreeRoot, WalkEvent}; | 15 | pub use rowan::{TreeRoot, WalkEvent}; |
14 | 16 | ||
15 | #[derive(Debug, Clone, Copy)] | 17 | #[derive(Debug, Clone, Copy)] |
@@ -24,12 +26,6 @@ pub type RefRoot<'a> = ::rowan::RefRoot<'a, RaTypes>; | |||
24 | 26 | ||
25 | pub type GreenNode = ::rowan::GreenNode<RaTypes>; | 27 | pub type GreenNode = ::rowan::GreenNode<RaTypes>; |
26 | 28 | ||
27 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
28 | pub struct SyntaxError { | ||
29 | pub msg: String, | ||
30 | pub offset: TextUnit, | ||
31 | } | ||
32 | |||
33 | #[derive(Clone, Copy)] | 29 | #[derive(Clone, Copy)] |
34 | pub struct SyntaxNode<R: TreeRoot<RaTypes> = OwnedRoot>(::rowan::SyntaxNode<RaTypes, R>); | 30 | pub struct SyntaxNode<R: TreeRoot<RaTypes> = OwnedRoot>(::rowan::SyntaxNode<RaTypes, R>); |
35 | pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>; | 31 | pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>; |
diff --git a/crates/ra_syntax/src/yellow/syntax_error.rs b/crates/ra_syntax/src/yellow/syntax_error.rs new file mode 100644 index 000000000..e8c818dc6 --- /dev/null +++ b/crates/ra_syntax/src/yellow/syntax_error.rs | |||
@@ -0,0 +1,42 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | use crate::TextRange; | ||
4 | |||
5 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
6 | pub struct SyntaxError { | ||
7 | pub kind: SyntaxErrorKind, | ||
8 | pub range: TextRange, | ||
9 | } | ||
10 | |||
11 | impl SyntaxError { | ||
12 | pub fn new(kind: SyntaxErrorKind, range: TextRange) -> SyntaxError { | ||
13 | SyntaxError { kind, range } | ||
14 | } | ||
15 | } | ||
16 | |||
17 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
18 | pub enum SyntaxErrorKind { | ||
19 | ParseError(ParseError), | ||
20 | EmptyChar, | ||
21 | UnclosedChar, | ||
22 | LongChar, | ||
23 | EmptyAsciiEscape, | ||
24 | InvalidAsciiEscape, | ||
25 | } | ||
26 | |||
27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
28 | pub struct ParseError(pub String); | ||
29 | |||
30 | impl fmt::Display for SyntaxErrorKind { | ||
31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
32 | use self::SyntaxErrorKind::*; | ||
33 | match self { | ||
34 | EmptyAsciiEscape => write!(f, "Empty escape sequence"), | ||
35 | InvalidAsciiEscape => write!(f, "Invalid escape sequence"), | ||
36 | EmptyChar => write!(f, "Empty char literal"), | ||
37 | UnclosedChar => write!(f, "Unclosed char literal"), | ||
38 | LongChar => write!(f, "Char literal should be one character long"), | ||
39 | ParseError(msg) => write!(f, "{}", msg.0), | ||
40 | } | ||
41 | } | ||
42 | } | ||