Skip to content

Commit f605f6e

Browse files
bors[bot]aochagavia
andcommitted
Merge #188
188: Introduce `SyntaxErrorKind` and `TextRange` to `SyntaxError` r=matklad a=aochagavia Co-authored-by: Adolfo Ochagavía <[email protected]> Co-authored-by: Adolfo Ochagavía <[email protected]>
2 parents 43665eb + 59405bf commit f605f6e

File tree

11 files changed

+193
-68
lines changed

11 files changed

+193
-68
lines changed

crates/ra_editor/src/lib.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use ra_syntax::{
3131
algo::find_leaf_at_offset,
3232
ast::{self, AstNode, NameOwner},
3333
File,
34+
Location,
3435
SyntaxKind::{self, *},
3536
SyntaxNodeRef, TextRange, TextUnit,
3637
};
@@ -100,11 +101,18 @@ pub fn highlight(file: &File) -> Vec<HighlightedRange> {
100101
}
101102

102103
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+
103111
file.errors()
104112
.into_iter()
105113
.map(|err| Diagnostic {
106-
range: TextRange::offset_len(err.offset, 1.into()),
107-
msg: "Syntax Error: ".to_string() + &err.msg,
114+
range: location_to_range(err.location()),
115+
msg: format!("Syntax Error: {}", err),
108116
})
109117
.collect()
110118
}

crates/ra_syntax/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub use crate::{
5454
rowan::{SmolStr, TextRange, TextUnit},
5555
syntax_kinds::SyntaxKind,
5656
yellow::{
57-
Direction, OwnedRoot, RefRoot, SyntaxError, SyntaxNode, SyntaxNodeRef, TreeRoot, WalkEvent,
57+
Direction, OwnedRoot, RefRoot, SyntaxError, SyntaxNode, SyntaxNodeRef, TreeRoot, WalkEvent, Location,
5858
},
5959
};
6060

crates/ra_syntax/src/parser_impl/event.rs

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

@@ -75,7 +80,7 @@ pub(crate) enum Event {
7580
},
7681

7782
Error {
78-
msg: String,
83+
msg: ParseError,
7984
},
8085
}
8186

@@ -157,7 +162,10 @@ impl<'a, S: Sink> EventProcessor<'a, S> {
157162
.sum::<TextUnit>();
158163
self.leaf(kind, len, n_raw_tokens);
159164
}
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+
)),
161169
}
162170
}
163171
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,
14+
yellow::syntax_error::{
15+
ParseError,
16+
SyntaxError,
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, error: SyntaxError);
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: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,20 +165,14 @@ 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.offset() <= old_node.range().start() {
169169
res.push(e)
170-
} else if e.offset >= old_node.range().end() {
171-
res.push(SyntaxError {
172-
msg: e.msg,
173-
offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
174-
})
170+
} else if e.offset() >= old_node.range().end() {
171+
res.push(e.add_offset(TextUnit::of_str(&edit.insert) - edit.delete.len()));
175172
}
176173
}
177174
for e in new_errors {
178-
res.push(SyntaxError {
179-
msg: e.msg,
180-
offset: e.offset + old_node.range().start(),
181-
})
175+
res.push(e.add_offset(old_node.range().start()));
182176
}
183177
res
184178
}

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.offset());
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].offset() <= off {
2727
indent!();
28-
writeln!(buf, "err: `{}`", errors[err_pos].msg).unwrap();
28+
writeln!(buf, "err: `{}`", errors[err_pos]).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).unwrap();
4141
}
4242

4343
buf

crates/ra_syntax/src/validation.rs

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,78 @@
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::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+
}
2943
}
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-
});
44+
AsciiCodeEscape => {
45+
// TODO:
46+
// * First digit is octal
47+
// * Second digit is hex
48+
}
49+
UnicodeEscape => {
50+
// TODO:
51+
// * Only hex digits or underscores allowed
52+
// * Max 6 chars
53+
// * Within allowed range (must be at most 10FFFF)
3654
}
55+
// Code points are always valid
56+
CodePoint => (),
3757
}
3858
}
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+
}
4078
}

crates/ra_syntax/src/yellow/builder.rs

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

@@ -34,11 +34,7 @@ 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, error: SyntaxError) {
4238
self.errors.push(error)
4339
}
4440

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, Location};
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>>;

0 commit comments

Comments
 (0)