aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/validation.rs
diff options
context:
space:
mode:
authorAdolfo OchagavĂ­a <[email protected]>2018-11-04 15:45:22 +0000
committerAdolfo OchagavĂ­a <[email protected]>2018-11-04 20:16:38 +0000
commit3b42ddae601fbd73f672e82028e04c3abdf1252d (patch)
treed63c4114d2fccf1c085502dfd1048b4b5f68266b /crates/ra_syntax/src/validation.rs
parent576b9a0727ebbf00521bc1131cda808145696d06 (diff)
Introduce SyntaxErrorKind and TextRange in SyntaxError
Diffstat (limited to 'crates/ra_syntax/src/validation.rs')
-rw-r--r--crates/ra_syntax/src/validation.rs99
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 @@
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 {
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
88fn is_ascii_escape(code: char) -> bool {
89 match code {
90 '\'' | '"' | 'n' | 'r' | 't' | '0' => true,
91 _ => false,
92 }
40} 93}