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/src/validation.rs | |
parent | 576b9a0727ebbf00521bc1131cda808145696d06 (diff) |
Introduce SyntaxErrorKind and TextRange in SyntaxError
Diffstat (limited to 'crates/ra_syntax/src/validation.rs')
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 99 |
1 files changed, 76 insertions, 23 deletions
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 | } |