aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_editor/src/lib.rs12
-rw-r--r--crates/ra_syntax/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/parser_impl/event.rs12
-rw-r--r--crates/ra_syntax/src/parser_impl/mod.rs12
-rw-r--r--crates/ra_syntax/src/reparsing.rs14
-rw-r--r--crates/ra_syntax/src/string_lexing/mod.rs10
-rw-r--r--crates/ra_syntax/src/utils.rs8
-rw-r--r--crates/ra_syntax/src/validation.rs84
-rw-r--r--crates/ra_syntax/src/yellow/builder.rs8
-rw-r--r--crates/ra_syntax/src/yellow/mod.rs10
-rw-r--r--crates/ra_syntax/src/yellow/syntax_error.rs89
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
102pub fn diagnostics(file: &File) -> Vec<Diagnostic> { 103pub 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};
17use std::mem; 22use 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
16use crate::SyntaxKind::{self, EOF, TOMBSTONE}; 20use 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.
5pub fn dump_tree(syntax: SyntaxNodeRef) -> String { 5pub 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 @@
1use crate::{ 1use 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
10pub(crate) fn validate(file: &File) -> Vec<SyntaxError> { 12pub(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 { 22fn 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
73fn 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 @@
1use crate::{ 1use 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};
6use rowan::GreenNodeBuilder; 6use 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 @@
1mod builder; 1mod builder;
2pub mod syntax_error;
2mod syntax_text; 3mod syntax_text;
3 4
4use self::syntax_text::SyntaxText; 5use self::syntax_text::SyntaxText;
5use crate::{SmolStr, SyntaxKind, TextRange, TextUnit}; 6use crate::{SmolStr, SyntaxKind, TextRange};
6use rowan::Types; 7use rowan::Types;
7use std::{ 8use std::{
8 fmt, 9 fmt,
@@ -10,6 +11,7 @@ use std::{
10}; 11};
11 12
12pub(crate) use self::builder::GreenBuilder; 13pub(crate) use self::builder::GreenBuilder;
14pub use self::syntax_error::{SyntaxError, SyntaxErrorKind, Location};
13pub use rowan::{TreeRoot, WalkEvent}; 15pub 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
25pub type GreenNode = ::rowan::GreenNode<RaTypes>; 27pub type GreenNode = ::rowan::GreenNode<RaTypes>;
26 28
27#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
28pub struct SyntaxError {
29 pub msg: String,
30 pub offset: TextUnit,
31}
32
33#[derive(Clone, Copy)] 29#[derive(Clone, Copy)]
34pub struct SyntaxNode<R: TreeRoot<RaTypes> = OwnedRoot>(::rowan::SyntaxNode<RaTypes, R>); 30pub struct SyntaxNode<R: TreeRoot<RaTypes> = OwnedRoot>(::rowan::SyntaxNode<RaTypes, R>);
35pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>; 31pub 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 @@
1use std::fmt;
2
3use crate::{TextRange, TextUnit};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct SyntaxError {
7 kind: SyntaxErrorKind,
8 location: Location,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum Location {
13 Offset(TextUnit),
14 Range(TextRange),
15}
16
17impl Into<Location> for TextUnit {
18 fn into(self) -> Location {
19 Location::Offset(self)
20 }
21}
22
23impl Into<Location> for TextRange {
24 fn into(self) -> Location {
25 Location::Range(self)
26 }
27}
28
29impl 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
58impl 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)]
65pub enum SyntaxErrorKind {
66 ParseError(ParseError),
67 EmptyChar,
68 UnclosedChar,
69 LongChar,
70 EmptyAsciiEscape,
71 InvalidAsciiEscape,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash)]
75pub struct ParseError(pub String);
76
77impl 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}