aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/expr_ext.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/expr_ext.rs')
-rw-r--r--crates/ra_syntax/src/ast/expr_ext.rs417
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
3use crate::{
4 ast::{self, support, AstChildren, AstNode},
5 SmolStr,
6 SyntaxKind::*,
7 SyntaxToken, T,
8};
9
10impl 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)]
26pub enum ElseBranch {
27 Block(ast::BlockExpr),
28 IfExpr(ast::IfExpr),
29}
30
31impl 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)]
52pub 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
61impl 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)]
77pub 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
138impl 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
157impl 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)]
221pub enum RangeOp {
222 /// `..`
223 Exclusive,
224 /// `..=`
225 Inclusive,
226}
227
228impl 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
266impl 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
275pub enum ArrayExprKind {
276 Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
277 ElementList(AstChildren<ast::Expr>),
278}
279
280impl 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)]
298pub 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
308impl 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)]
363pub 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
371impl 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
389impl 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]
407fn 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
413impl ast::RecordField {
414 pub fn parent_record_lit(&self) -> ast::RecordLit {
415 self.syntax().ancestors().find_map(ast::RecordLit::cast).unwrap()
416 }
417}