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