diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-07-31 03:12:44 +0100 |
commit | f05d7b41a719d848844b054a16477b29d0f063c6 (patch) | |
tree | 0a8a0946e8aef2ce64d4c13d0035ba41cce2daf3 /crates/ra_syntax/src/ast/expr_ext.rs | |
parent | 73ff610e41959e3e7c78a2b4b25b086883132956 (diff) | |
parent | 6b7cb8b5ab539fc4333ce34bc29bf77c976f232a (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Hasn't fixed tests yet.
Diffstat (limited to 'crates/ra_syntax/src/ast/expr_ext.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/expr_ext.rs | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast/expr_ext.rs b/crates/ra_syntax/src/ast/expr_ext.rs new file mode 100644 index 000000000..f5ba87223 --- /dev/null +++ b/crates/ra_syntax/src/ast/expr_ext.rs | |||
@@ -0,0 +1,418 @@ | |||
1 | //! Various extension methods to ast Expr Nodes, which are hard to code-generate. | ||
2 | |||
3 | use crate::{ | ||
4 | ast::{self, support, AstChildren, AstNode}, | ||
5 | SmolStr, | ||
6 | SyntaxKind::*, | ||
7 | SyntaxToken, T, | ||
8 | }; | ||
9 | |||
10 | impl ast::AttrsOwner for ast::Expr {} | ||
11 | |||
12 | impl ast::Expr { | ||
13 | pub fn is_block_like(&self) -> bool { | ||
14 | match self { | ||
15 | ast::Expr::IfExpr(_) | ||
16 | | ast::Expr::LoopExpr(_) | ||
17 | | ast::Expr::ForExpr(_) | ||
18 | | ast::Expr::WhileExpr(_) | ||
19 | | ast::Expr::BlockExpr(_) | ||
20 | | ast::Expr::MatchExpr(_) | ||
21 | | ast::Expr::EffectExpr(_) => true, | ||
22 | _ => false, | ||
23 | } | ||
24 | } | ||
25 | } | ||
26 | |||
27 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
28 | pub enum ElseBranch { | ||
29 | Block(ast::BlockExpr), | ||
30 | IfExpr(ast::IfExpr), | ||
31 | } | ||
32 | |||
33 | impl ast::IfExpr { | ||
34 | pub fn then_branch(&self) -> Option<ast::BlockExpr> { | ||
35 | self.blocks().next() | ||
36 | } | ||
37 | pub fn else_branch(&self) -> Option<ElseBranch> { | ||
38 | let res = match self.blocks().nth(1) { | ||
39 | Some(block) => ElseBranch::Block(block), | ||
40 | None => { | ||
41 | let elif: ast::IfExpr = support::child(self.syntax())?; | ||
42 | ElseBranch::IfExpr(elif) | ||
43 | } | ||
44 | }; | ||
45 | Some(res) | ||
46 | } | ||
47 | |||
48 | pub fn blocks(&self) -> AstChildren<ast::BlockExpr> { | ||
49 | support::children(self.syntax()) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
54 | pub enum PrefixOp { | ||
55 | /// The `*` operator for dereferencing | ||
56 | Deref, | ||
57 | /// The `!` operator for logical inversion | ||
58 | Not, | ||
59 | /// The `-` operator for negation | ||
60 | Neg, | ||
61 | } | ||
62 | |||
63 | impl ast::PrefixExpr { | ||
64 | pub fn op_kind(&self) -> Option<PrefixOp> { | ||
65 | match self.op_token()?.kind() { | ||
66 | T![*] => Some(PrefixOp::Deref), | ||
67 | T![!] => Some(PrefixOp::Not), | ||
68 | T![-] => Some(PrefixOp::Neg), | ||
69 | _ => None, | ||
70 | } | ||
71 | } | ||
72 | |||
73 | pub fn op_token(&self) -> Option<SyntaxToken> { | ||
74 | self.syntax().first_child_or_token()?.into_token() | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
79 | pub enum BinOp { | ||
80 | /// The `||` operator for boolean OR | ||
81 | BooleanOr, | ||
82 | /// The `&&` operator for boolean AND | ||
83 | BooleanAnd, | ||
84 | /// The `==` operator for equality testing | ||
85 | EqualityTest, | ||
86 | /// The `!=` operator for equality testing | ||
87 | NegatedEqualityTest, | ||
88 | /// The `<=` operator for lesser-equal testing | ||
89 | LesserEqualTest, | ||
90 | /// The `>=` operator for greater-equal testing | ||
91 | GreaterEqualTest, | ||
92 | /// The `<` operator for comparison | ||
93 | LesserTest, | ||
94 | /// The `>` operator for comparison | ||
95 | GreaterTest, | ||
96 | /// The `+` operator for addition | ||
97 | Addition, | ||
98 | /// The `*` operator for multiplication | ||
99 | Multiplication, | ||
100 | /// The `-` operator for subtraction | ||
101 | Subtraction, | ||
102 | /// The `/` operator for division | ||
103 | Division, | ||
104 | /// The `%` operator for remainder after division | ||
105 | Remainder, | ||
106 | /// The `<<` operator for left shift | ||
107 | LeftShift, | ||
108 | /// The `>>` operator for right shift | ||
109 | RightShift, | ||
110 | /// The `^` operator for bitwise XOR | ||
111 | BitwiseXor, | ||
112 | /// The `|` operator for bitwise OR | ||
113 | BitwiseOr, | ||
114 | /// The `&` operator for bitwise AND | ||
115 | BitwiseAnd, | ||
116 | /// The `=` operator for assignment | ||
117 | Assignment, | ||
118 | /// The `+=` operator for assignment after addition | ||
119 | AddAssign, | ||
120 | /// The `/=` operator for assignment after division | ||
121 | DivAssign, | ||
122 | /// The `*=` operator for assignment after multiplication | ||
123 | MulAssign, | ||
124 | /// The `%=` operator for assignment after remainders | ||
125 | RemAssign, | ||
126 | /// The `>>=` operator for assignment after shifting right | ||
127 | ShrAssign, | ||
128 | /// The `<<=` operator for assignment after shifting left | ||
129 | ShlAssign, | ||
130 | /// The `-=` operator for assignment after subtraction | ||
131 | SubAssign, | ||
132 | /// The `|=` operator for assignment after bitwise OR | ||
133 | BitOrAssign, | ||
134 | /// The `&=` operator for assignment after bitwise AND | ||
135 | BitAndAssign, | ||
136 | /// The `^=` operator for assignment after bitwise XOR | ||
137 | BitXorAssign, | ||
138 | } | ||
139 | |||
140 | impl BinOp { | ||
141 | pub fn is_assignment(self) -> bool { | ||
142 | match self { | ||
143 | BinOp::Assignment | ||
144 | | BinOp::AddAssign | ||
145 | | BinOp::DivAssign | ||
146 | | BinOp::MulAssign | ||
147 | | BinOp::RemAssign | ||
148 | | BinOp::ShrAssign | ||
149 | | BinOp::ShlAssign | ||
150 | | BinOp::SubAssign | ||
151 | | BinOp::BitOrAssign | ||
152 | | BinOp::BitAndAssign | ||
153 | | BinOp::BitXorAssign => true, | ||
154 | _ => false, | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl ast::BinExpr { | ||
160 | pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> { | ||
161 | self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| { | ||
162 | let bin_op = match c.kind() { | ||
163 | T![||] => BinOp::BooleanOr, | ||
164 | T![&&] => BinOp::BooleanAnd, | ||
165 | T![==] => BinOp::EqualityTest, | ||
166 | T![!=] => BinOp::NegatedEqualityTest, | ||
167 | T![<=] => BinOp::LesserEqualTest, | ||
168 | T![>=] => BinOp::GreaterEqualTest, | ||
169 | T![<] => BinOp::LesserTest, | ||
170 | T![>] => BinOp::GreaterTest, | ||
171 | T![+] => BinOp::Addition, | ||
172 | T![*] => BinOp::Multiplication, | ||
173 | T![-] => BinOp::Subtraction, | ||
174 | T![/] => BinOp::Division, | ||
175 | T![%] => BinOp::Remainder, | ||
176 | T![<<] => BinOp::LeftShift, | ||
177 | T![>>] => BinOp::RightShift, | ||
178 | T![^] => BinOp::BitwiseXor, | ||
179 | T![|] => BinOp::BitwiseOr, | ||
180 | T![&] => BinOp::BitwiseAnd, | ||
181 | T![=] => BinOp::Assignment, | ||
182 | T![+=] => BinOp::AddAssign, | ||
183 | T![/=] => BinOp::DivAssign, | ||
184 | T![*=] => BinOp::MulAssign, | ||
185 | T![%=] => BinOp::RemAssign, | ||
186 | T![>>=] => BinOp::ShrAssign, | ||
187 | T![<<=] => BinOp::ShlAssign, | ||
188 | T![-=] => BinOp::SubAssign, | ||
189 | T![|=] => BinOp::BitOrAssign, | ||
190 | T![&=] => BinOp::BitAndAssign, | ||
191 | T![^=] => BinOp::BitXorAssign, | ||
192 | _ => return None, | ||
193 | }; | ||
194 | Some((c, bin_op)) | ||
195 | }) | ||
196 | } | ||
197 | |||
198 | pub fn op_kind(&self) -> Option<BinOp> { | ||
199 | self.op_details().map(|t| t.1) | ||
200 | } | ||
201 | |||
202 | pub fn op_token(&self) -> Option<SyntaxToken> { | ||
203 | self.op_details().map(|t| t.0) | ||
204 | } | ||
205 | |||
206 | pub fn lhs(&self) -> Option<ast::Expr> { | ||
207 | support::children(self.syntax()).next() | ||
208 | } | ||
209 | |||
210 | pub fn rhs(&self) -> Option<ast::Expr> { | ||
211 | support::children(self.syntax()).nth(1) | ||
212 | } | ||
213 | |||
214 | pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) { | ||
215 | let mut children = support::children(self.syntax()); | ||
216 | let first = children.next(); | ||
217 | let second = children.next(); | ||
218 | (first, second) | ||
219 | } | ||
220 | } | ||
221 | |||
222 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
223 | pub enum RangeOp { | ||
224 | /// `..` | ||
225 | Exclusive, | ||
226 | /// `..=` | ||
227 | Inclusive, | ||
228 | } | ||
229 | |||
230 | impl ast::RangeExpr { | ||
231 | fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> { | ||
232 | self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| { | ||
233 | let token = child.into_token()?; | ||
234 | let bin_op = match token.kind() { | ||
235 | T![..] => RangeOp::Exclusive, | ||
236 | T![..=] => RangeOp::Inclusive, | ||
237 | _ => return None, | ||
238 | }; | ||
239 | Some((ix, token, bin_op)) | ||
240 | }) | ||
241 | } | ||
242 | |||
243 | pub fn op_kind(&self) -> Option<RangeOp> { | ||
244 | self.op_details().map(|t| t.2) | ||
245 | } | ||
246 | |||
247 | pub fn op_token(&self) -> Option<SyntaxToken> { | ||
248 | self.op_details().map(|t| t.1) | ||
249 | } | ||
250 | |||
251 | pub fn start(&self) -> Option<ast::Expr> { | ||
252 | let op_ix = self.op_details()?.0; | ||
253 | self.syntax() | ||
254 | .children_with_tokens() | ||
255 | .take(op_ix) | ||
256 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
257 | } | ||
258 | |||
259 | pub fn end(&self) -> Option<ast::Expr> { | ||
260 | let op_ix = self.op_details()?.0; | ||
261 | self.syntax() | ||
262 | .children_with_tokens() | ||
263 | .skip(op_ix + 1) | ||
264 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
265 | } | ||
266 | } | ||
267 | |||
268 | impl ast::IndexExpr { | ||
269 | pub fn base(&self) -> Option<ast::Expr> { | ||
270 | support::children(self.syntax()).next() | ||
271 | } | ||
272 | pub fn index(&self) -> Option<ast::Expr> { | ||
273 | support::children(self.syntax()).nth(1) | ||
274 | } | ||
275 | } | ||
276 | |||
277 | pub enum ArrayExprKind { | ||
278 | Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> }, | ||
279 | ElementList(AstChildren<ast::Expr>), | ||
280 | } | ||
281 | |||
282 | impl ast::ArrayExpr { | ||
283 | pub fn kind(&self) -> ArrayExprKind { | ||
284 | if self.is_repeat() { | ||
285 | ArrayExprKind::Repeat { | ||
286 | initializer: support::children(self.syntax()).next(), | ||
287 | repeat: support::children(self.syntax()).nth(1), | ||
288 | } | ||
289 | } else { | ||
290 | ArrayExprKind::ElementList(support::children(self.syntax())) | ||
291 | } | ||
292 | } | ||
293 | |||
294 | fn is_repeat(&self) -> bool { | ||
295 | self.syntax().children_with_tokens().any(|it| it.kind() == T![;]) | ||
296 | } | ||
297 | } | ||
298 | |||
299 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
300 | pub enum LiteralKind { | ||
301 | String, | ||
302 | ByteString, | ||
303 | Char, | ||
304 | Byte, | ||
305 | IntNumber { suffix: Option<SmolStr> }, | ||
306 | FloatNumber { suffix: Option<SmolStr> }, | ||
307 | Bool(bool), | ||
308 | } | ||
309 | |||
310 | impl ast::Literal { | ||
311 | pub fn token(&self) -> SyntaxToken { | ||
312 | self.syntax() | ||
313 | .children_with_tokens() | ||
314 | .find(|e| e.kind() != ATTR && !e.kind().is_trivia()) | ||
315 | .and_then(|e| e.into_token()) | ||
316 | .unwrap() | ||
317 | } | ||
318 | |||
319 | fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> { | ||
320 | possible_suffixes | ||
321 | .iter() | ||
322 | .find(|&suffix| text.ends_with(suffix)) | ||
323 | .map(|&suffix| SmolStr::new(suffix)) | ||
324 | } | ||
325 | |||
326 | pub fn kind(&self) -> LiteralKind { | ||
327 | const INT_SUFFIXES: [&str; 12] = [ | ||
328 | "u64", "u32", "u16", "u8", "usize", "isize", "i64", "i32", "i16", "i8", "u128", "i128", | ||
329 | ]; | ||
330 | const FLOAT_SUFFIXES: [&str; 2] = ["f32", "f64"]; | ||
331 | |||
332 | let token = self.token(); | ||
333 | |||
334 | match token.kind() { | ||
335 | INT_NUMBER => { | ||
336 | // FYI: there was a bug here previously, thus the if statement below is necessary. | ||
337 | // The lexer treats e.g. `1f64` as an integer literal. See | ||
338 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 | ||
339 | // and the comments on the linked PR. | ||
340 | |||
341 | let text = token.text(); | ||
342 | if let suffix @ Some(_) = Self::find_suffix(&text, &FLOAT_SUFFIXES) { | ||
343 | LiteralKind::FloatNumber { suffix } | ||
344 | } else { | ||
345 | LiteralKind::IntNumber { suffix: Self::find_suffix(&text, &INT_SUFFIXES) } | ||
346 | } | ||
347 | } | ||
348 | FLOAT_NUMBER => { | ||
349 | let text = token.text(); | ||
350 | LiteralKind::FloatNumber { suffix: Self::find_suffix(&text, &FLOAT_SUFFIXES) } | ||
351 | } | ||
352 | STRING | RAW_STRING => LiteralKind::String, | ||
353 | T![true] => LiteralKind::Bool(true), | ||
354 | T![false] => LiteralKind::Bool(false), | ||
355 | BYTE_STRING | RAW_BYTE_STRING => LiteralKind::ByteString, | ||
356 | CHAR => LiteralKind::Char, | ||
357 | BYTE => LiteralKind::Byte, | ||
358 | _ => unreachable!(), | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
364 | pub enum Effect { | ||
365 | Async(SyntaxToken), | ||
366 | Unsafe(SyntaxToken), | ||
367 | Try(SyntaxToken), | ||
368 | // Very much not an effect, but we stuff it into this node anyway | ||
369 | Label(ast::Label), | ||
370 | } | ||
371 | |||
372 | impl ast::EffectExpr { | ||
373 | pub fn effect(&self) -> Effect { | ||
374 | if let Some(token) = self.async_token() { | ||
375 | return Effect::Async(token); | ||
376 | } | ||
377 | if let Some(token) = self.unsafe_token() { | ||
378 | return Effect::Unsafe(token); | ||
379 | } | ||
380 | if let Some(token) = self.try_token() { | ||
381 | return Effect::Try(token); | ||
382 | } | ||
383 | if let Some(label) = self.label() { | ||
384 | return Effect::Label(label); | ||
385 | } | ||
386 | unreachable!("ast::EffectExpr without Effect") | ||
387 | } | ||
388 | } | ||
389 | |||
390 | impl ast::BlockExpr { | ||
391 | /// false if the block is an intrinsic part of the syntax and can't be | ||
392 | /// replaced with arbitrary expression. | ||
393 | /// | ||
394 | /// ```not_rust | ||
395 | /// fn foo() { not_stand_alone } | ||
396 | /// const FOO: () = { stand_alone }; | ||
397 | /// ``` | ||
398 | pub fn is_standalone(&self) -> bool { | ||
399 | let parent = match self.syntax().parent() { | ||
400 | Some(it) => it, | ||
401 | None => return true, | ||
402 | }; | ||
403 | !matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR) | ||
404 | } | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn test_literal_with_attr() { | ||
409 | let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#); | ||
410 | let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap(); | ||
411 | assert_eq!(lit.token().text(), r#""Hello""#); | ||
412 | } | ||
413 | |||
414 | impl ast::RecordExprField { | ||
415 | pub fn parent_record_lit(&self) -> ast::RecordExpr { | ||
416 | self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap() | ||
417 | } | ||
418 | } | ||