diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-18 12:57:26 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-18 12:57:26 +0000 |
commit | c447fe9bc06006a7080da782cf67d739c91b534c (patch) | |
tree | 45cbc9578b24437da3eedc6a234784be22b1f38c /crates/ra_syntax/src/validation.rs | |
parent | 742459c8fe08e359ae380e3e1dc0d059c0b4f871 (diff) | |
parent | 053ccf4121797e4e559e3225d46d3f23cb1ad70b (diff) |
Merge #3026
3026: ra_syntax: reshape SyntaxError for the sake of removing redundancy r=matklad a=Veetaha
Followup of #2911, also puts some crosses to the todo list of #223.
**AHTUNG!** A big part of the diff of this PR are test data files changes.
Simplified `SyntaxError` that was `SyntaxError { kind: { /* big enum */ }, location: Location }` to `SyntaxError(String, TextRange)`. I am not sure whether the tuple struct here is best fit, I am inclined to add names to the fields, because I already provide getters `SyntaxError::message()`, `SyntaxError::range()`.
I also removed `Location` altogether ...
This is currently WIP, because the following is not done:
- [ ] ~~Add tests to `test_data` dir for unescape errors *// I don't know where to put these errors in particular, because they are out of the scope of the lexer and parser. However, I have an idea in mind that we move all validators we have right now to parsing stage, but this is up to discussion...*~~ **[UPD]** I came to a conclusion that tree validation logic, which unescape errors are a part of, should be rethought of, we currently have no tests and no place to put tests for tree validations. So I'd like to extract potential redesign (maybe move of tree validation to ra_parser) and adding tests for this into a separate task.
Co-authored-by: Veetaha <[email protected]>
Co-authored-by: Veetaha <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/validation.rs')
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 155 |
1 files changed, 71 insertions, 84 deletions
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 8a5f0e4b7..863859dca 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -5,92 +5,76 @@ mod block; | |||
5 | use rustc_lexer::unescape; | 5 | use rustc_lexer::unescape; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind, | 8 | ast, match_ast, AstNode, SyntaxError, |
9 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, | 9 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, |
10 | SyntaxNode, SyntaxToken, TextUnit, T, | 10 | SyntaxNode, SyntaxToken, TextUnit, T, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 13 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { |
14 | pub enum EscapeError { | 14 | use unescape::EscapeError as EE; |
15 | ZeroChars, | ||
16 | MoreThanOneChar, | ||
17 | LoneSlash, | ||
18 | InvalidEscape, | ||
19 | BareCarriageReturn, | ||
20 | EscapeOnlyChar, | ||
21 | TooShortHexEscape, | ||
22 | InvalidCharInHexEscape, | ||
23 | OutOfRangeHexEscape, | ||
24 | NoBraceInUnicodeEscape, | ||
25 | InvalidCharInUnicodeEscape, | ||
26 | EmptyUnicodeEscape, | ||
27 | UnclosedUnicodeEscape, | ||
28 | LeadingUnderscoreUnicodeEscape, | ||
29 | OverlongUnicodeEscape, | ||
30 | LoneSurrogateUnicodeEscape, | ||
31 | OutOfRangeUnicodeEscape, | ||
32 | UnicodeEscapeInByte, | ||
33 | NonAsciiCharInByte, | ||
34 | } | ||
35 | 15 | ||
36 | impl From<rustc_lexer::unescape::EscapeError> for EscapeError { | 16 | #[rustfmt::skip] |
37 | fn from(err: rustc_lexer::unescape::EscapeError) -> Self { | 17 | let err_message = match err { |
38 | match err { | 18 | EE::ZeroChars => { |
39 | rustc_lexer::unescape::EscapeError::ZeroChars => EscapeError::ZeroChars, | 19 | "Literal must not be empty" |
40 | rustc_lexer::unescape::EscapeError::MoreThanOneChar => EscapeError::MoreThanOneChar, | ||
41 | rustc_lexer::unescape::EscapeError::LoneSlash => EscapeError::LoneSlash, | ||
42 | rustc_lexer::unescape::EscapeError::InvalidEscape => EscapeError::InvalidEscape, | ||
43 | rustc_lexer::unescape::EscapeError::BareCarriageReturn | ||
44 | | rustc_lexer::unescape::EscapeError::BareCarriageReturnInRawString => { | ||
45 | EscapeError::BareCarriageReturn | ||
46 | } | ||
47 | rustc_lexer::unescape::EscapeError::EscapeOnlyChar => EscapeError::EscapeOnlyChar, | ||
48 | rustc_lexer::unescape::EscapeError::TooShortHexEscape => EscapeError::TooShortHexEscape, | ||
49 | rustc_lexer::unescape::EscapeError::InvalidCharInHexEscape => { | ||
50 | EscapeError::InvalidCharInHexEscape | ||
51 | } | ||
52 | rustc_lexer::unescape::EscapeError::OutOfRangeHexEscape => { | ||
53 | EscapeError::OutOfRangeHexEscape | ||
54 | } | ||
55 | rustc_lexer::unescape::EscapeError::NoBraceInUnicodeEscape => { | ||
56 | EscapeError::NoBraceInUnicodeEscape | ||
57 | } | ||
58 | rustc_lexer::unescape::EscapeError::InvalidCharInUnicodeEscape => { | ||
59 | EscapeError::InvalidCharInUnicodeEscape | ||
60 | } | ||
61 | rustc_lexer::unescape::EscapeError::EmptyUnicodeEscape => { | ||
62 | EscapeError::EmptyUnicodeEscape | ||
63 | } | ||
64 | rustc_lexer::unescape::EscapeError::UnclosedUnicodeEscape => { | ||
65 | EscapeError::UnclosedUnicodeEscape | ||
66 | } | ||
67 | rustc_lexer::unescape::EscapeError::LeadingUnderscoreUnicodeEscape => { | ||
68 | EscapeError::LeadingUnderscoreUnicodeEscape | ||
69 | } | ||
70 | rustc_lexer::unescape::EscapeError::OverlongUnicodeEscape => { | ||
71 | EscapeError::OverlongUnicodeEscape | ||
72 | } | ||
73 | rustc_lexer::unescape::EscapeError::LoneSurrogateUnicodeEscape => { | ||
74 | EscapeError::LoneSurrogateUnicodeEscape | ||
75 | } | ||
76 | rustc_lexer::unescape::EscapeError::OutOfRangeUnicodeEscape => { | ||
77 | EscapeError::OutOfRangeUnicodeEscape | ||
78 | } | ||
79 | rustc_lexer::unescape::EscapeError::UnicodeEscapeInByte => { | ||
80 | EscapeError::UnicodeEscapeInByte | ||
81 | } | ||
82 | rustc_lexer::unescape::EscapeError::NonAsciiCharInByte | ||
83 | | rustc_lexer::unescape::EscapeError::NonAsciiCharInByteString => { | ||
84 | EscapeError::NonAsciiCharInByte | ||
85 | } | ||
86 | } | 20 | } |
87 | } | 21 | EE::MoreThanOneChar => { |
88 | } | 22 | "Literal must be one character long" |
23 | } | ||
24 | EE::LoneSlash => { | ||
25 | "Character must be escaped: `\\`" | ||
26 | } | ||
27 | EE::InvalidEscape => { | ||
28 | "Invalid escape" | ||
29 | } | ||
30 | EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => { | ||
31 | "Character must be escaped: `\r`" | ||
32 | } | ||
33 | EE::EscapeOnlyChar => { | ||
34 | "Escape character `\\` must be escaped itself" | ||
35 | } | ||
36 | EE::TooShortHexEscape => { | ||
37 | "ASCII hex escape code must have exactly two digits" | ||
38 | } | ||
39 | EE::InvalidCharInHexEscape => { | ||
40 | "ASCII hex escape code must contain only hex characters" | ||
41 | } | ||
42 | EE::OutOfRangeHexEscape => { | ||
43 | "ASCII hex escape code must be at most 0x7F" | ||
44 | } | ||
45 | EE::NoBraceInUnicodeEscape => { | ||
46 | "Missing `{` to begin the unicode escape" | ||
47 | } | ||
48 | EE::InvalidCharInUnicodeEscape => { | ||
49 | "Unicode escape must contain only hex characters and underscores" | ||
50 | } | ||
51 | EE::EmptyUnicodeEscape => { | ||
52 | "Unicode escape must not be empty" | ||
53 | } | ||
54 | EE::UnclosedUnicodeEscape => { | ||
55 | "Missing '}' to terminate the unicode escape" | ||
56 | } | ||
57 | EE::LeadingUnderscoreUnicodeEscape => { | ||
58 | "Unicode escape code must not begin with an underscore" | ||
59 | } | ||
60 | EE::OverlongUnicodeEscape => { | ||
61 | "Unicode escape code must have at most 6 digits" | ||
62 | } | ||
63 | EE::LoneSurrogateUnicodeEscape => { | ||
64 | "Unicode escape code must not be a surrogate" | ||
65 | } | ||
66 | EE::OutOfRangeUnicodeEscape => { | ||
67 | "Unicode escape code must be at most 0x10FFFF" | ||
68 | } | ||
69 | EE::UnicodeEscapeInByte => { | ||
70 | "Byte literals must not contain unicode escapes" | ||
71 | } | ||
72 | EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => { | ||
73 | "Byte literals must not contain non-ASCII characters" | ||
74 | } | ||
75 | }; | ||
89 | 76 | ||
90 | impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind { | 77 | err_message |
91 | fn from(err: rustc_lexer::unescape::EscapeError) -> Self { | ||
92 | SyntaxErrorKind::EscapeError(err.into()) | ||
93 | } | ||
94 | } | 78 | } |
95 | 79 | ||
96 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 80 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { |
@@ -118,6 +102,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
118 | } | 102 | } |
119 | 103 | ||
120 | fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | 104 | fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { |
105 | // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658) | ||
121 | fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> { | 106 | fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> { |
122 | text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end)) | 107 | text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end)) |
123 | } | 108 | } |
@@ -125,9 +110,10 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | |||
125 | let token = literal.token(); | 110 | let token = literal.token(); |
126 | let text = token.text().as_str(); | 111 | let text = token.text().as_str(); |
127 | 112 | ||
113 | // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205) | ||
128 | let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { | 114 | let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { |
129 | let off = token.text_range().start() + TextUnit::from_usize(off + prefix_len); | 115 | let off = token.text_range().start() + TextUnit::from_usize(off + prefix_len); |
130 | acc.push(SyntaxError::new(err.into(), off)); | 116 | acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off)); |
131 | }; | 117 | }; |
132 | 118 | ||
133 | match token.kind() { | 119 | match token.kind() { |
@@ -195,7 +181,8 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax | |||
195 | if let Some(int_token) = int_token(name_ref) { | 181 | if let Some(int_token) = int_token(name_ref) { |
196 | if int_token.text().chars().any(|c| !c.is_digit(10)) { | 182 | if int_token.text().chars().any(|c| !c.is_digit(10)) { |
197 | errors.push(SyntaxError::new( | 183 | errors.push(SyntaxError::new( |
198 | SyntaxErrorKind::InvalidTupleIndexFormat, | 184 | "Tuple (struct) field access is only allowed through \ |
185 | decimal integers with no underscores or suffix", | ||
199 | int_token.text_range(), | 186 | int_token.text_range(), |
200 | )); | 187 | )); |
201 | } | 188 | } |
@@ -215,21 +202,21 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) { | |||
215 | FN_DEF | CONST_DEF | TYPE_ALIAS_DEF => (), | 202 | FN_DEF | CONST_DEF | TYPE_ALIAS_DEF => (), |
216 | _ => return, | 203 | _ => return, |
217 | } | 204 | } |
205 | |||
218 | let impl_block = match parent.parent().and_then(|it| it.parent()).and_then(ast::ImplBlock::cast) | 206 | let impl_block = match parent.parent().and_then(|it| it.parent()).and_then(ast::ImplBlock::cast) |
219 | { | 207 | { |
220 | Some(it) => it, | 208 | Some(it) => it, |
221 | None => return, | 209 | None => return, |
222 | }; | 210 | }; |
223 | if impl_block.target_trait().is_some() { | 211 | if impl_block.target_trait().is_some() { |
224 | errors | 212 | errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range())); |
225 | .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) | ||
226 | } | 213 | } |
227 | } | 214 | } |
228 | 215 | ||
229 | fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | 216 | fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { |
230 | if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() { | 217 | if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() { |
231 | errors.push(SyntaxError::new( | 218 | errors.push(SyntaxError::new( |
232 | SyntaxErrorKind::InclusiveRangeMissingEnd, | 219 | "An inclusive range must have an end expression", |
233 | expr.syntax().text_range(), | 220 | expr.syntax().text_range(), |
234 | )); | 221 | )); |
235 | } | 222 | } |