diff options
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 46 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 95 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_error.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 10 |
6 files changed, 150 insertions, 13 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 1ec9881b9..277532a8c 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub use self::{ | 18 | pub use self::{ |
19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp}, | 19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, |
20 | extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, | 20 | extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, |
21 | generated::*, | 21 | generated::*, |
22 | tokens::*, | 22 | tokens::*, |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 25dbd0bed..7c53aa934 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -189,6 +189,52 @@ impl ast::BinExpr { | |||
189 | } | 189 | } |
190 | } | 190 | } |
191 | 191 | ||
192 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
193 | pub enum RangeOp { | ||
194 | /// `..` | ||
195 | Exclusive, | ||
196 | /// `..=` | ||
197 | Inclusive, | ||
198 | } | ||
199 | |||
200 | impl ast::RangeExpr { | ||
201 | fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> { | ||
202 | self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| { | ||
203 | let token = child.into_token()?; | ||
204 | let bin_op = match token.kind() { | ||
205 | T![..] => RangeOp::Exclusive, | ||
206 | T![..=] => RangeOp::Inclusive, | ||
207 | _ => return None, | ||
208 | }; | ||
209 | Some((ix, token, bin_op)) | ||
210 | }) | ||
211 | } | ||
212 | |||
213 | pub fn op_kind(&self) -> Option<RangeOp> { | ||
214 | self.op_details().map(|t| t.2) | ||
215 | } | ||
216 | |||
217 | pub fn op_token(&self) -> Option<SyntaxToken> { | ||
218 | self.op_details().map(|t| t.1) | ||
219 | } | ||
220 | |||
221 | pub fn start(&self) -> Option<ast::Expr> { | ||
222 | let op_ix = self.op_details()?.0; | ||
223 | self.syntax() | ||
224 | .children_with_tokens() | ||
225 | .take(op_ix) | ||
226 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
227 | } | ||
228 | |||
229 | pub fn end(&self) -> Option<ast::Expr> { | ||
230 | let op_ix = self.op_details()?.0; | ||
231 | self.syntax() | ||
232 | .children_with_tokens() | ||
233 | .skip(op_ix + 1) | ||
234 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
235 | } | ||
236 | } | ||
237 | |||
192 | impl ast::IndexExpr { | 238 | impl ast::IndexExpr { |
193 | pub fn base(&self) -> Option<ast::Expr> { | 239 | pub fn base(&self) -> Option<ast::Expr> { |
194 | children(self).nth(0) | 240 | children(self).nth(0) |
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 87cca325d..ed8661faf 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | ast::AstToken, | 4 | ast::AstToken, |
5 | SyntaxKind::{COMMENT, WHITESPACE}, | 5 | SyntaxKind::{COMMENT, RAW_STRING, STRING, WHITESPACE}, |
6 | SyntaxToken, | 6 | SyntaxToken, TextRange, TextUnit, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 9 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -11,10 +11,9 @@ pub struct Comment(SyntaxToken); | |||
11 | 11 | ||
12 | impl AstToken for Comment { | 12 | impl AstToken for Comment { |
13 | fn cast(token: SyntaxToken) -> Option<Self> { | 13 | fn cast(token: SyntaxToken) -> Option<Self> { |
14 | if token.kind() == COMMENT { | 14 | match token.kind() { |
15 | Some(Comment(token)) | 15 | COMMENT => Some(Comment(token)), |
16 | } else { | 16 | _ => None, |
17 | None | ||
18 | } | 17 | } |
19 | } | 18 | } |
20 | fn syntax(&self) -> &SyntaxToken { | 19 | fn syntax(&self) -> &SyntaxToken { |
@@ -94,10 +93,9 @@ pub struct Whitespace(SyntaxToken); | |||
94 | 93 | ||
95 | impl AstToken for Whitespace { | 94 | impl AstToken for Whitespace { |
96 | fn cast(token: SyntaxToken) -> Option<Self> { | 95 | fn cast(token: SyntaxToken) -> Option<Self> { |
97 | if token.kind() == WHITESPACE { | 96 | match token.kind() { |
98 | Some(Whitespace(token)) | 97 | WHITESPACE => Some(Whitespace(token)), |
99 | } else { | 98 | _ => None, |
100 | None | ||
101 | } | 99 | } |
102 | } | 100 | } |
103 | fn syntax(&self) -> &SyntaxToken { | 101 | fn syntax(&self) -> &SyntaxToken { |
@@ -111,3 +109,80 @@ impl Whitespace { | |||
111 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) | 109 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) |
112 | } | 110 | } |
113 | } | 111 | } |
112 | |||
113 | pub struct String(SyntaxToken); | ||
114 | |||
115 | impl AstToken for String { | ||
116 | fn cast(token: SyntaxToken) -> Option<Self> { | ||
117 | match token.kind() { | ||
118 | STRING => Some(String(token)), | ||
119 | _ => None, | ||
120 | } | ||
121 | } | ||
122 | fn syntax(&self) -> &SyntaxToken { | ||
123 | &self.0 | ||
124 | } | ||
125 | } | ||
126 | |||
127 | impl String { | ||
128 | pub fn value(&self) -> Option<std::string::String> { | ||
129 | let text = self.text().as_str(); | ||
130 | let usual_string_range = find_usual_string_range(text)?; | ||
131 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
132 | let end_of_inside = usual_string_range.end().to_usize(); | ||
133 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
134 | |||
135 | let mut buf = std::string::String::with_capacity(inside_str.len()); | ||
136 | let mut has_error = false; | ||
137 | rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { | ||
138 | match unescaped_char { | ||
139 | Ok(c) => buf.push(c), | ||
140 | Err(_) => has_error = true, | ||
141 | } | ||
142 | }); | ||
143 | |||
144 | if has_error { | ||
145 | return None; | ||
146 | } | ||
147 | Some(buf) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | pub struct RawString(SyntaxToken); | ||
152 | |||
153 | impl AstToken for RawString { | ||
154 | fn cast(token: SyntaxToken) -> Option<Self> { | ||
155 | match token.kind() { | ||
156 | RAW_STRING => Some(RawString(token)), | ||
157 | _ => None, | ||
158 | } | ||
159 | } | ||
160 | fn syntax(&self) -> &SyntaxToken { | ||
161 | &self.0 | ||
162 | } | ||
163 | } | ||
164 | |||
165 | impl RawString { | ||
166 | pub fn value(&self) -> Option<std::string::String> { | ||
167 | let text = self.text().as_str(); | ||
168 | let usual_string_range = find_usual_string_range(text)?; | ||
169 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
170 | let end_of_inside = usual_string_range.end().to_usize(); | ||
171 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
172 | Some(inside_str.to_string()) | ||
173 | } | ||
174 | } | ||
175 | |||
176 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | ||
177 | let left_quote = s.find('"')?; | ||
178 | let right_quote = s.rfind('"')?; | ||
179 | if left_quote == right_quote { | ||
180 | // `s` only contains one quote | ||
181 | None | ||
182 | } else { | ||
183 | Some(TextRange::from_to( | ||
184 | TextUnit::from(left_quote as u32), | ||
185 | TextUnit::from(right_quote as u32), | ||
186 | )) | ||
187 | } | ||
188 | } | ||
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 5dcb6a95a..9931fec84 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -176,9 +176,11 @@ impl SourceFile { | |||
176 | /// ``` | 176 | /// ``` |
177 | #[macro_export] | 177 | #[macro_export] |
178 | macro_rules! match_ast { | 178 | macro_rules! match_ast { |
179 | (match $node:ident { | 179 | (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; |
180 | |||
181 | (match ($node:expr) { | ||
180 | $( ast::$ast:ident($it:ident) => $res:block, )* | 182 | $( ast::$ast:ident($it:ident) => $res:block, )* |
181 | _ => $catch_all:expr, | 183 | _ => $catch_all:expr $(,)? |
182 | }) => {{ | 184 | }) => {{ |
183 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | 185 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* |
184 | { $catch_all } | 186 | { $catch_all } |
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs index 1f60a7aab..6c171df8d 100644 --- a/crates/ra_syntax/src/syntax_error.rs +++ b/crates/ra_syntax/src/syntax_error.rs | |||
@@ -83,6 +83,7 @@ pub enum SyntaxErrorKind { | |||
83 | InvalidMatchInnerAttr, | 83 | InvalidMatchInnerAttr, |
84 | InvalidTupleIndexFormat, | 84 | InvalidTupleIndexFormat, |
85 | VisibilityNotAllowed, | 85 | VisibilityNotAllowed, |
86 | InclusiveRangeMissingEnd, | ||
86 | } | 87 | } |
87 | 88 | ||
88 | impl fmt::Display for SyntaxErrorKind { | 89 | impl fmt::Display for SyntaxErrorKind { |
@@ -103,6 +104,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
103 | VisibilityNotAllowed => { | 104 | VisibilityNotAllowed => { |
104 | write!(f, "unnecessary visibility qualifier") | 105 | write!(f, "unnecessary visibility qualifier") |
105 | } | 106 | } |
107 | InclusiveRangeMissingEnd => { | ||
108 | write!(f, "An inclusive range must have an end expression") | ||
109 | } | ||
106 | } | 110 | } |
107 | } | 111 | } |
108 | } | 112 | } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 2d596763e..222ac15f8 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -103,6 +103,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, | 103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, | 104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | ast::Visibility(it) => { validate_visibility(it, &mut errors) }, | 105 | ast::Visibility(it) => { validate_visibility(it, &mut errors) }, |
106 | ast::RangeExpr(it) => { validate_range_expr(it, &mut errors) }, | ||
106 | _ => (), | 107 | _ => (), |
107 | } | 108 | } |
108 | } | 109 | } |
@@ -227,3 +228,12 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) { | |||
227 | .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) | 228 | .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) |
228 | } | 229 | } |
229 | } | 230 | } |
231 | |||
232 | fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | ||
233 | if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() { | ||
234 | errors.push(SyntaxError::new( | ||
235 | SyntaxErrorKind::InclusiveRangeMissingEnd, | ||
236 | expr.syntax().text_range(), | ||
237 | )); | ||
238 | } | ||
239 | } | ||