diff options
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 | } |