From 3b42ddae601fbd73f672e82028e04c3abdf1252d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Sun, 4 Nov 2018 16:45:22 +0100 Subject: Introduce SyntaxErrorKind and TextRange in SyntaxError --- crates/ra_syntax/src/validation.rs | 99 +++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 23 deletions(-) (limited to 'crates/ra_syntax/src/validation.rs') 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 @@ use crate::{ + algo::visit::{visitor_ctx, VisitorCtx}, ast::{self, AstNode}, File, - string_lexing, + string_lexing::{self, CharComponentKind}, yellow::{ SyntaxError, + SyntaxErrorKind::*, }, }; pub(crate) fn validate(file: &File) -> Vec { let mut errors = Vec::new(); - for d in file.root.borrowed().descendants() { - if let Some(c) = ast::Char::cast(d) { - let components = &mut string_lexing::parse_char_literal(c.text()); - let len = components.count(); + for node in file.root.borrowed().descendants() { + let _ = visitor_ctx(&mut errors) + .visit::(validate_char) + .accept(node); + } + errors +} - if !components.has_closing_quote { - errors.push(SyntaxError { - msg: "Unclosed char literal".to_string(), - offset: d.range().start(), - }); - } +fn validate_char(node: ast::Char, errors: &mut Vec) { + let mut components = string_lexing::parse_char_literal(node.text()); + let mut len = 0; + for component in &mut components { + len += 1; - if len == 0 { - errors.push(SyntaxError { - msg: "Empty char literal".to_string(), - offset: d.range().start(), - }); + // Validate escapes + let text = &node.text()[component.range]; + let range = component.range + node.syntax().range().start(); + use self::CharComponentKind::*; + match component.kind { + AsciiEscape => { + if text.len() == 1 { + // Escape sequence consists only of leading `\` + errors.push(SyntaxError { + kind: EmptyAsciiEscape, + range: range, + }); + } else { + let escape_code = text.chars().skip(1).next().unwrap(); + if !is_ascii_escape(escape_code) { + errors.push(SyntaxError { + kind: InvalidAsciiEscape, + range: range, + }); + } + } } - - if len > 1 { - errors.push(SyntaxError { - msg: "Character literal should be only one character long".to_string(), - offset: d.range().start(), - }); + AsciiCodeEscape => { + // TODO: + // * First digit is octal + // * Second digit is hex + } + UnicodeEscape => { + // TODO: + // * Only hex digits or underscores allowed + // * Max 6 chars + // * Within allowed range (must be at most 10FFFF) } + // Code points are always valid + CodePoint => (), } } - errors + + if !components.has_closing_quote { + errors.push(SyntaxError { + kind: UnclosedChar, + range: node.syntax().range(), + }); + } + + if len == 0 { + errors.push(SyntaxError { + kind: EmptyChar, + range: node.syntax().range(), + }); + } + + if len > 1 { + errors.push(SyntaxError { + kind: LongChar, + range: node.syntax().range(), + }); + } +} + +fn is_ascii_escape(code: char) -> bool { + match code { + '\'' | '"' | 'n' | 'r' | 't' | '0' => true, + _ => false, + } } -- cgit v1.2.3