Skip to content

Commit 3b42dda

Browse files
committed
Introduce SyntaxErrorKind and TextRange in SyntaxError
1 parent 576b9a0 commit 3b42dda

File tree

10 files changed

+155
-64
lines changed

10 files changed

+155
-64
lines changed

crates/ra_editor/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ pub fn diagnostics(file: &File) -> Vec<Diagnostic> {
103103
file.errors()
104104
.into_iter()
105105
.map(|err| Diagnostic {
106-
range: TextRange::offset_len(err.offset, 1.into()),
107-
msg: "Syntax Error: ".to_string() + &err.msg,
106+
range: err.range,
107+
msg: format!("Syntax Error: {}", err.kind),
108108
})
109109
.collect()
110110
}

crates/ra_syntax/src/parser_impl/event.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ use crate::{
1313
SmolStr,
1414
SyntaxKind::{self, *},
1515
TextRange, TextUnit,
16+
yellow::syntax_error::{
17+
ParseError,
18+
SyntaxErrorKind,
19+
},
1620
};
1721
use std::mem;
1822

@@ -75,7 +79,7 @@ pub(crate) enum Event {
7579
},
7680

7781
Error {
78-
msg: String,
82+
msg: ParseError,
7983
},
8084
}
8185

@@ -157,7 +161,10 @@ impl<'a, S: Sink> EventProcessor<'a, S> {
157161
.sum::<TextUnit>();
158162
self.leaf(kind, len, n_raw_tokens);
159163
}
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+
),
161168
}
162169
}
163170
self.sink

crates/ra_syntax/src/parser_impl/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ use crate::{
1010
event::{Event, EventProcessor},
1111
input::{InputPosition, ParserInput},
1212
},
13-
SmolStr, TextUnit,
13+
SmolStr, TextRange,
14+
yellow::syntax_error::{
15+
ParseError,
16+
SyntaxErrorKind,
17+
},
1418
};
1519

1620
use crate::SyntaxKind::{self, EOF, TOMBSTONE};
@@ -21,7 +25,7 @@ pub(crate) trait Sink {
2125
fn leaf(&mut self, kind: SyntaxKind, text: SmolStr);
2226
fn start_internal(&mut self, kind: SyntaxKind);
2327
fn finish_internal(&mut self);
24-
fn error(&mut self, message: String, offset: TextUnit);
28+
fn error(&mut self, kind: SyntaxErrorKind, offset: TextRange);
2529
fn finish(self) -> Self::Tree;
2630
}
2731

@@ -144,7 +148,9 @@ impl<'t> ParserImpl<'t> {
144148
}
145149

146150
pub(super) fn error(&mut self, msg: String) {
147-
self.event(Event::Error { msg })
151+
self.event(Event::Error {
152+
msg: ParseError(msg),
153+
})
148154
}
149155

150156
pub(super) fn complete(&mut self, pos: u32, kind: SyntaxKind) {

crates/ra_syntax/src/reparsing.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,19 @@ fn merge_errors(
165165
) -> Vec<SyntaxError> {
166166
let mut res = Vec::new();
167167
for e in old_errors {
168-
if e.offset <= old_node.range().start() {
168+
if e.range.start() <= old_node.range().start() {
169169
res.push(e)
170-
} else if e.offset >= old_node.range().end() {
170+
} else if e.range.start() >= old_node.range().end() {
171171
res.push(SyntaxError {
172-
msg: e.msg,
173-
offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
172+
kind: e.kind,
173+
range: e.range + TextUnit::of_str(&edit.insert) - edit.delete.len(),
174174
})
175175
}
176176
}
177177
for e in new_errors {
178178
res.push(SyntaxError {
179-
msg: e.msg,
180-
offset: e.offset + old_node.range().start(),
179+
kind: e.kind,
180+
range: e.range + old_node.range().start(),
181181
})
182182
}
183183
res

crates/ra_syntax/src/string_lexing/mod.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,6 @@ impl<'a> Parser<'a> {
100100
// Char parsing methods
101101

102102
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)
107103
match self.peek() {
108104
Some('{') => {
109105
self.advance();
@@ -127,9 +123,6 @@ impl<'a> Parser<'a> {
127123
}
128124

129125
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
133126
let code_start = self.get_pos();
134127
while let Some(next) = self.peek() {
135128
if next == '\'' || (self.get_pos() - code_start == 2.into()) {
@@ -144,9 +137,6 @@ impl<'a> Parser<'a> {
144137
}
145138

146139
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
150140
if self.peek().is_none() {
151141
return CharComponent::new(TextRange::from_to(start, start), AsciiEscape);
152142
}

crates/ra_syntax/src/utils.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt::Write;
44
/// Parse a file and create a string representation of the resulting parse tree.
55
pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
66
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());
88
let mut err_pos = 0;
99
let mut level = 0;
1010
let mut buf = String::new();
@@ -23,9 +23,9 @@ pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
2323
writeln!(buf, "{:?}", node).unwrap();
2424
if node.first_child().is_none() {
2525
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 {
2727
indent!();
28-
writeln!(buf, "err: `{}`", errors[err_pos].msg).unwrap();
28+
writeln!(buf, "err: `{}`", errors[err_pos].kind).unwrap();
2929
err_pos += 1;
3030
}
3131
}
@@ -37,7 +37,7 @@ pub fn dump_tree(syntax: SyntaxNodeRef) -> String {
3737

3838
assert_eq!(level, 0);
3939
for err in errors[err_pos..].iter() {
40-
writeln!(buf, "err: `{}`", err.msg).unwrap();
40+
writeln!(buf, "err: `{}`", err.kind).unwrap();
4141
}
4242

4343
buf

crates/ra_syntax/src/validation.rs

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,93 @@
11
use crate::{
2+
algo::visit::{visitor_ctx, VisitorCtx},
23
ast::{self, AstNode},
34
File,
4-
string_lexing,
5+
string_lexing::{self, CharComponentKind},
56
yellow::{
67
SyntaxError,
8+
SyntaxErrorKind::*,
79
},
810
};
911

1012
pub(crate) fn validate(file: &File) -> Vec<SyntaxError> {
1113
let mut errors = Vec::new();
12-
for d in file.root.borrowed().descendants() {
13-
if let Some(c) = ast::Char::cast(d) {
14-
let components = &mut string_lexing::parse_char_literal(c.text());
15-
let len = components.count();
14+
for node in file.root.borrowed().descendants() {
15+
let _ = visitor_ctx(&mut errors)
16+
.visit::<ast::Char, _>(validate_char)
17+
.accept(node);
18+
}
19+
errors
20+
}
1621

17-
if !components.has_closing_quote {
18-
errors.push(SyntaxError {
19-
msg: "Unclosed char literal".to_string(),
20-
offset: d.range().start(),
21-
});
22-
}
22+
fn validate_char(node: ast::Char, errors: &mut Vec<SyntaxError>) {
23+
let mut components = string_lexing::parse_char_literal(node.text());
24+
let mut len = 0;
25+
for component in &mut components {
26+
len += 1;
2327

24-
if len == 0 {
25-
errors.push(SyntaxError {
26-
msg: "Empty char literal".to_string(),
27-
offset: d.range().start(),
28-
});
28+
// Validate escapes
29+
let text = &node.text()[component.range];
30+
let range = component.range + node.syntax().range().start();
31+
use self::CharComponentKind::*;
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+
}
2949
}
30-
31-
if len > 1 {
32-
errors.push(SyntaxError {
33-
msg: "Character literal should be only one character long".to_string(),
34-
offset: d.range().start(),
35-
});
50+
AsciiCodeEscape => {
51+
// TODO:
52+
// * First digit is octal
53+
// * Second digit is hex
54+
}
55+
UnicodeEscape => {
56+
// TODO:
57+
// * Only hex digits or underscores allowed
58+
// * Max 6 chars
59+
// * Within allowed range (must be at most 10FFFF)
3660
}
61+
// Code points are always valid
62+
CodePoint => (),
3763
}
3864
}
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+
}
4093
}

crates/ra_syntax/src/yellow/builder.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
parser_impl::Sink,
3-
yellow::{GreenNode, RaTypes, SyntaxError},
4-
SmolStr, SyntaxKind, TextUnit,
3+
yellow::{GreenNode, RaTypes, SyntaxError, SyntaxErrorKind},
4+
SmolStr, SyntaxKind, TextRange,
55
};
66
use rowan::GreenNodeBuilder;
77

@@ -34,11 +34,8 @@ impl Sink for GreenBuilder {
3434
self.inner.finish_internal();
3535
}
3636

37-
fn error(&mut self, message: String, offset: TextUnit) {
38-
let error = SyntaxError {
39-
msg: message,
40-
offset,
41-
};
37+
fn error(&mut self, kind: SyntaxErrorKind, range: TextRange) {
38+
let error = SyntaxError { kind, range };
4239
self.errors.push(error)
4340
}
4441

crates/ra_syntax/src/yellow/mod.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
mod builder;
2+
pub mod syntax_error;
23
mod syntax_text;
34

45
use self::syntax_text::SyntaxText;
5-
use crate::{SmolStr, SyntaxKind, TextRange, TextUnit};
6+
use crate::{SmolStr, SyntaxKind, TextRange};
67
use rowan::Types;
78
use std::{
89
fmt,
910
hash::{Hash, Hasher},
1011
};
1112

1213
pub(crate) use self::builder::GreenBuilder;
14+
pub use self::syntax_error::{SyntaxError, SyntaxErrorKind};
1315
pub use rowan::{TreeRoot, WalkEvent};
1416

1517
#[derive(Debug, Clone, Copy)]
@@ -24,12 +26,6 @@ pub type RefRoot<'a> = ::rowan::RefRoot<'a, RaTypes>;
2426

2527
pub type GreenNode = ::rowan::GreenNode<RaTypes>;
2628

27-
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
28-
pub struct SyntaxError {
29-
pub msg: String,
30-
pub offset: TextUnit,
31-
}
32-
3329
#[derive(Clone, Copy)]
3430
pub struct SyntaxNode<R: TreeRoot<RaTypes> = OwnedRoot>(::rowan::SyntaxNode<RaTypes, R>);
3531
pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)