diff options
Diffstat (limited to 'crates/ra_syntax/src/validation.rs')
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 83 |
1 files changed, 76 insertions, 7 deletions
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 009f5052f..a550ce0ab 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::u32; | ||
2 | |||
1 | use crate::{ | 3 | use crate::{ |
2 | algo::visit::{visitor_ctx, VisitorCtx}, | 4 | algo::visit::{visitor_ctx, VisitorCtx}, |
3 | ast::{self, AstNode}, | 5 | ast::{self, AstNode}, |
@@ -42,15 +44,82 @@ fn validate_char(node: ast::Char, errors: &mut Vec<SyntaxError>) { | |||
42 | } | 44 | } |
43 | } | 45 | } |
44 | AsciiCodeEscape => { | 46 | AsciiCodeEscape => { |
45 | // TODO: | 47 | // An AsciiCodeEscape has 4 chars, example: `\xDD` |
46 | // * First digit is octal | 48 | if text.len() < 4 { |
47 | // * Second digit is hex | 49 | errors.push(SyntaxError::new(TooShortAsciiCodeEscape, range)); |
50 | } else { | ||
51 | assert!(text.chars().count() == 4, "AsciiCodeEscape cannot be longer than 4 chars"); | ||
52 | |||
53 | match u8::from_str_radix(&text[2..], 16) { | ||
54 | Ok(code) if code < 128 => { /* Escape code is valid */ }, | ||
55 | Ok(_) => errors.push(SyntaxError::new(AsciiCodeEscapeOutOfRange, range)), | ||
56 | Err(_) => errors.push(SyntaxError::new(MalformedAsciiCodeEscape, range)), | ||
57 | } | ||
58 | |||
59 | } | ||
48 | } | 60 | } |
49 | UnicodeEscape => { | 61 | UnicodeEscape => { |
50 | // TODO: | 62 | assert!(&text[..2] == "\\u", "UnicodeEscape always starts with \\u"); |
51 | // * Only hex digits or underscores allowed | 63 | |
52 | // * Max 6 chars | 64 | if text.len() == 2 { |
53 | // * Within allowed range (must be at most 10FFFF) | 65 | // No starting `{` |
66 | errors.push(SyntaxError::new(MalformedUnicodeEscape, range)); | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | if text.len() == 3 { | ||
71 | // Only starting `{` | ||
72 | errors.push(SyntaxError::new(UnclosedUnicodeEscape, range)); | ||
73 | return; | ||
74 | } | ||
75 | |||
76 | let mut code = String::new(); | ||
77 | let mut closed = false; | ||
78 | for c in text[3..].chars() { | ||
79 | assert!(!closed, "no characters after escape is closed"); | ||
80 | |||
81 | if c.is_digit(16) { | ||
82 | code.push(c); | ||
83 | } else if c == '_' { | ||
84 | // Reject leading _ | ||
85 | if code.len() == 0 { | ||
86 | errors.push(SyntaxError::new(MalformedUnicodeEscape, range)); | ||
87 | return; | ||
88 | } | ||
89 | } else if c == '}' { | ||
90 | closed = true; | ||
91 | } else { | ||
92 | errors.push(SyntaxError::new(MalformedUnicodeEscape, range)); | ||
93 | return; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | if !closed { | ||
98 | errors.push(SyntaxError::new(UnclosedUnicodeEscape, range)) | ||
99 | } | ||
100 | |||
101 | if code.len() == 0 { | ||
102 | errors.push(SyntaxError::new(EmptyUnicodeEcape, range)); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | if code.len() > 6 { | ||
107 | errors.push(SyntaxError::new(OverlongUnicodeEscape, range)); | ||
108 | } | ||
109 | |||
110 | match u32::from_str_radix(&code, 16) { | ||
111 | Ok(code_u32) if code_u32 > 0x10FFFF => { | ||
112 | errors.push(SyntaxError::new(UnicodeEscapeOutOfRange, range)); | ||
113 | } | ||
114 | Ok(_) => { | ||
115 | // Valid escape code | ||
116 | } | ||
117 | Err(_) => { | ||
118 | errors.push(SyntaxError::new(MalformedUnicodeEscape, range)); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | // FIXME: we really need tests for this | ||
54 | } | 123 | } |
55 | // Code points are always valid | 124 | // Code points are always valid |
56 | CodePoint => (), | 125 | CodePoint => (), |