diff options
-rw-r--r-- | crates/ra_editor/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/parser_impl/event.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/parser_impl/mod.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/reparsing.rs | 14 | ||||
-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 | 84 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/builder.rs | 8 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/mod.rs | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/yellow/syntax_error.rs | 89 |
11 files changed, 193 insertions, 68 deletions
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index 3186eaf1a..f92181b86 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -31,6 +31,7 @@ use ra_syntax::{ | |||
31 | algo::find_leaf_at_offset, | 31 | algo::find_leaf_at_offset, |
32 | ast::{self, AstNode, NameOwner}, | 32 | ast::{self, AstNode, NameOwner}, |
33 | File, | 33 | File, |
34 | Location, | ||
34 | SyntaxKind::{self, *}, | 35 | SyntaxKind::{self, *}, |
35 | SyntaxNodeRef, TextRange, TextUnit, | 36 | SyntaxNodeRef, TextRange, TextUnit, |
36 | }; | 37 | }; |
@@ -100,11 +101,18 @@ pub fn highlight(file: &File) -> Vec<HighlightedRange> { | |||
100 | } | 101 | } |
101 | 102 | ||
102 | pub fn diagnostics(file: &File) -> Vec<Diagnostic> { | 103 | pub fn diagnostics(file: &File) -> Vec<Diagnostic> { |
104 | fn location_to_range(location: Location) -> TextRange { | ||
105 | match location { | ||
106 | Location::Offset(offset) => TextRange::offset_len(offset, 1.into()), | ||
107 | Location::Range(range) => range, | ||
108 | } | ||
109 | } | ||
110 | |||
103 | file.errors() | 111 | file.errors() |
104 | .into_iter() | 112 | .into_iter() |
105 | .map(|err| Diagnostic { | 113 | .map(|err| Diagnostic { |
106 | range: TextRange::offset_len(err.offset, 1.into()), | 114 | range: location_to_range(err.location()), |
107 | msg: "Syntax Error: ".to_string() + &err.msg, | 115 | msg: format!("Syntax Error: {}", err), |
108 | }) | 116 | }) |
109 | .collect() | 117 | .collect() |
110 | } | 118 | } |
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index acc2d9603..123002825 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -54,7 +54,7 @@ pub use crate::{ | |||
54 | rowan::{SmolStr, TextRange, TextUnit}, | 54 | rowan::{SmolStr, TextRange, TextUnit}, |
55 | syntax_kinds::SyntaxKind, | 55 | syntax_kinds::SyntaxKind, |
56 | yellow::{ | 56 | yellow::{ |
57 | Direction, OwnedRoot, RefRoot, SyntaxError, SyntaxNode, SyntaxNodeRef, TreeRoot, WalkEvent, | 57 | Direction, OwnedRoot, RefRoot, SyntaxError, SyntaxNode, SyntaxNodeRef, TreeRoot, WalkEvent, Location, |
58 | }, | 58 | }, |
59 | }; | 59 | }; |
60 | 60 | ||
diff --git a/crates/ra_syntax/src/parser_impl/event.rs b/crates/ra_syntax/src/parser_impl/event.rs index 79fa21389..bf9c1cef0 100644 --- a/crates/ra_syntax/src/parser_impl/event.rs +++ b/crates/ra_syntax/src/parser_impl/event.rs | |||
@@ -13,6 +13,11 @@ 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 | SyntaxError, | ||
19 | SyntaxErrorKind, | ||
20 | }, | ||
16 | }; | 21 | }; |
17 | use std::mem; | 22 | use std::mem; |
18 | 23 | ||
@@ -75,7 +80,7 @@ pub(crate) enum Event { | |||
75 | }, | 80 | }, |
76 | 81 | ||
77 | Error { | 82 | Error { |
78 | msg: String, | 83 | msg: ParseError, |
79 | }, | 84 | }, |
80 | } | 85 | } |
81 | 86 | ||
@@ -157,7 +162,10 @@ impl<'a, S: Sink> EventProcessor<'a, S> { | |||
157 | .sum::<TextUnit>(); | 162 | .sum::<TextUnit>(); |
158 | self.leaf(kind, len, n_raw_tokens); | 163 | self.leaf(kind, len, n_raw_tokens); |
159 | } | 164 | } |
160 | Event::Error { msg } => self.sink.error(msg, self.text_pos), | 165 | Event::Error { msg } => self.sink.error(SyntaxError::new( |
166 | SyntaxErrorKind::ParseError(msg), | ||
167 | self.text_pos, | ||
168 | )), | ||
161 | } | 169 | } |
162 | } | 170 | } |
163 | self.sink | 171 | self.sink |
diff --git a/crates/ra_syntax/src/parser_impl/mod.rs b/crates/ra_syntax/src/parser_impl/mod.rs index 2b026d61e..cb6e370ac 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, |
14 | yellow::syntax_error::{ | ||
15 | ParseError, | ||
16 | SyntaxError, | ||
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, error: SyntaxError); |
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..3c4ea5c22 100644 --- a/crates/ra_syntax/src/reparsing.rs +++ b/crates/ra_syntax/src/reparsing.rs | |||
@@ -165,20 +165,14 @@ 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.offset() <= 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.offset() >= old_node.range().end() { |
171 | res.push(SyntaxError { | 171 | res.push(e.add_offset(TextUnit::of_str(&edit.insert) - edit.delete.len())); |
172 | msg: e.msg, | ||
173 | offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(), | ||
174 | }) | ||
175 | } | 172 | } |
176 | } | 173 | } |
177 | for e in new_errors { | 174 | for e in new_errors { |
178 | res.push(SyntaxError { | 175 | res.push(e.add_offset(old_node.range().start())); |
179 | msg: e.msg, | ||
180 | offset: e.offset + old_node.range().start(), | ||
181 | }) | ||
182 | } | 176 | } |
183 | res | 177 | res |
184 | } | 178 | } |
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..288d7edd4 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.offset()); |
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].offset() <= off { |
27 | indent!(); | 27 | indent!(); |
28 | writeln!(buf, "err: `{}`", errors[err_pos].msg).unwrap(); | 28 | writeln!(buf, "err: `{}`", errors[err_pos]).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).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..009f5052f 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -1,40 +1,78 @@ | |||
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::new(EmptyAsciiEscape, range)); | ||
37 | } else { | ||
38 | let escape_code = text.chars().skip(1).next().unwrap(); | ||
39 | if !is_ascii_escape(escape_code) { | ||
40 | errors.push(SyntaxError::new(InvalidAsciiEscape, range)); | ||
41 | } | ||
42 | } | ||
29 | } | 43 | } |
30 | 44 | AsciiCodeEscape => { | |
31 | if len > 1 { | 45 | // TODO: |
32 | errors.push(SyntaxError { | 46 | // * First digit is octal |
33 | msg: "Character literal should be only one character long".to_string(), | 47 | // * Second digit is hex |
34 | offset: d.range().start(), | 48 | } |
35 | }); | 49 | UnicodeEscape => { |
50 | // TODO: | ||
51 | // * Only hex digits or underscores allowed | ||
52 | // * Max 6 chars | ||
53 | // * Within allowed range (must be at most 10FFFF) | ||
36 | } | 54 | } |
55 | // Code points are always valid | ||
56 | CodePoint => (), | ||
37 | } | 57 | } |
38 | } | 58 | } |
39 | errors | 59 | |
60 | if !components.has_closing_quote { | ||
61 | errors.push(SyntaxError::new(UnclosedChar, node.syntax().range())); | ||
62 | } | ||
63 | |||
64 | if len == 0 { | ||
65 | errors.push(SyntaxError::new(EmptyChar, node.syntax().range())); | ||
66 | } | ||
67 | |||
68 | if len > 1 { | ||
69 | errors.push(SyntaxError::new(LongChar, node.syntax().range())); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn is_ascii_escape(code: char) -> bool { | ||
74 | match code { | ||
75 | '\'' | '"' | 'n' | 'r' | 't' | '0' => true, | ||
76 | _ => false, | ||
77 | } | ||
40 | } | 78 | } |
diff --git a/crates/ra_syntax/src/yellow/builder.rs b/crates/ra_syntax/src/yellow/builder.rs index d64053409..9fcebfb93 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}, |
4 | SmolStr, SyntaxKind, TextUnit, | 4 | SmolStr, SyntaxKind, |
5 | }; | 5 | }; |
6 | use rowan::GreenNodeBuilder; | 6 | use rowan::GreenNodeBuilder; |
7 | 7 | ||
@@ -34,11 +34,7 @@ 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, error: SyntaxError) { |
38 | let error = SyntaxError { | ||
39 | msg: message, | ||
40 | offset, | ||
41 | }; | ||
42 | self.errors.push(error) | 38 | self.errors.push(error) |
43 | } | 39 | } |
44 | 40 | ||
diff --git a/crates/ra_syntax/src/yellow/mod.rs b/crates/ra_syntax/src/yellow/mod.rs index 650917214..6da948648 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, Location}; | ||
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..f3df6bc15 --- /dev/null +++ b/crates/ra_syntax/src/yellow/syntax_error.rs | |||
@@ -0,0 +1,89 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | use crate::{TextRange, TextUnit}; | ||
4 | |||
5 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
6 | pub struct SyntaxError { | ||
7 | kind: SyntaxErrorKind, | ||
8 | location: Location, | ||
9 | } | ||
10 | |||
11 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
12 | pub enum Location { | ||
13 | Offset(TextUnit), | ||
14 | Range(TextRange), | ||
15 | } | ||
16 | |||
17 | impl Into<Location> for TextUnit { | ||
18 | fn into(self) -> Location { | ||
19 | Location::Offset(self) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl Into<Location> for TextRange { | ||
24 | fn into(self) -> Location { | ||
25 | Location::Range(self) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl SyntaxError { | ||
30 | pub fn new<L: Into<Location>>(kind: SyntaxErrorKind, loc: L) -> SyntaxError { | ||
31 | SyntaxError { | ||
32 | kind, | ||
33 | location: loc.into(), | ||
34 | } | ||
35 | } | ||
36 | |||
37 | pub fn location(&self) -> Location { | ||
38 | self.location.clone() | ||
39 | } | ||
40 | |||
41 | pub fn offset(&self) -> TextUnit { | ||
42 | match self.location { | ||
43 | Location::Offset(offset) => offset, | ||
44 | Location::Range(range) => range.start(), | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub fn add_offset(mut self, plus_offset: TextUnit) -> SyntaxError { | ||
49 | self.location = match self.location { | ||
50 | Location::Range(range) => Location::Range(range + plus_offset), | ||
51 | Location::Offset(offset) => Location::Offset(offset + plus_offset), | ||
52 | }; | ||
53 | |||
54 | self | ||
55 | } | ||
56 | } | ||
57 | |||
58 | impl fmt::Display for SyntaxError { | ||
59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
60 | self.kind.fmt(f) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
65 | pub enum SyntaxErrorKind { | ||
66 | ParseError(ParseError), | ||
67 | EmptyChar, | ||
68 | UnclosedChar, | ||
69 | LongChar, | ||
70 | EmptyAsciiEscape, | ||
71 | InvalidAsciiEscape, | ||
72 | } | ||
73 | |||
74 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
75 | pub struct ParseError(pub String); | ||
76 | |||
77 | impl fmt::Display for SyntaxErrorKind { | ||
78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
79 | use self::SyntaxErrorKind::*; | ||
80 | match self { | ||
81 | EmptyAsciiEscape => write!(f, "Empty escape sequence"), | ||
82 | InvalidAsciiEscape => write!(f, "Invalid escape sequence"), | ||
83 | EmptyChar => write!(f, "Empty char literal"), | ||
84 | UnclosedChar => write!(f, "Unclosed char literal"), | ||
85 | LongChar => write!(f, "Char literal should be one character long"), | ||
86 | ParseError(msg) => write!(f, "{}", msg.0), | ||
87 | } | ||
88 | } | ||
89 | } | ||