aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/validation.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-18 12:57:26 +0000
committerGitHub <[email protected]>2020-02-18 12:57:26 +0000
commitc447fe9bc06006a7080da782cf67d739c91b534c (patch)
tree45cbc9578b24437da3eedc6a234784be22b1f38c /crates/ra_syntax/src/validation.rs
parent742459c8fe08e359ae380e3e1dc0d059c0b4f871 (diff)
parent053ccf4121797e4e559e3225d46d3f23cb1ad70b (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.rs155
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;
5use rustc_lexer::unescape; 5use rustc_lexer::unescape;
6 6
7use crate::{ 7use 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)] 13fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
14pub 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
36impl 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
90impl 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
96pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 80pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
@@ -118,6 +102,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
118} 102}
119 103
120fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { 104fn 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
229fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { 216fn 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 }