]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
39710b8f13eb5f7024b8fe2a385ead1badda3a21
[rust.git] / src / tools / rust-analyzer / crates / ide-db / src / syntax_helpers / node_ext.rs
1 //! Various helper functions to work with SyntaxNodes.
2 use itertools::Itertools;
3 use parser::T;
4 use syntax::{
5     ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
6     AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
7 };
8
9 pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
10     if let ast::Expr::PathExpr(expr) = expr {
11         let path = expr.path()?;
12         path.as_single_name_ref()
13     } else {
14         None
15     }
16 }
17
18 pub fn full_path_of_name_ref(name_ref: &ast::NameRef) -> Option<ast::Path> {
19     let mut ancestors = name_ref.syntax().ancestors();
20     let _ = ancestors.next()?; // skip self
21     let _ = ancestors.next().filter(|it| ast::PathSegment::can_cast(it.kind()))?; // skip self
22     ancestors.take_while(|it| ast::Path::can_cast(it.kind())).last().and_then(ast::Path::cast)
23 }
24
25 pub fn block_as_lone_tail(block: &ast::BlockExpr) -> Option<ast::Expr> {
26     block.statements().next().is_none().then(|| block.tail_expr()).flatten()
27 }
28
29 /// Preorder walk all the expression's child expressions.
30 pub fn walk_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
31     preorder_expr(expr, &mut |ev| {
32         if let WalkEvent::Enter(expr) = ev {
33             cb(expr);
34         }
35         false
36     })
37 }
38
39 /// Preorder walk all the expression's child expressions preserving events.
40 /// If the callback returns true on an [`WalkEvent::Enter`], the subtree of the expression will be skipped.
41 /// Note that the subtree may already be skipped due to the context analysis this function does.
42 pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
43     let mut preorder = start.syntax().preorder();
44     while let Some(event) = preorder.next() {
45         let node = match event {
46             WalkEvent::Enter(node) => node,
47             WalkEvent::Leave(node) => {
48                 if let Some(expr) = ast::Expr::cast(node) {
49                     cb(WalkEvent::Leave(expr));
50                 }
51                 continue;
52             }
53         };
54         if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) {
55             if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) {
56                 // skipping potential const pat expressions in  let statements
57                 preorder.skip_subtree();
58                 continue;
59             }
60         }
61
62         match ast::Stmt::cast(node.clone()) {
63             // Don't skip subtree since we want to process the expression child next
64             Some(ast::Stmt::ExprStmt(_)) | Some(ast::Stmt::LetStmt(_)) => (),
65             // skip inner items which might have their own expressions
66             Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
67             None => {
68                 // skip const args, those expressions are a different context
69                 if ast::GenericArg::can_cast(node.kind()) {
70                     preorder.skip_subtree();
71                 } else if let Some(expr) = ast::Expr::cast(node) {
72                     let is_different_context = match &expr {
73                         ast::Expr::BlockExpr(block_expr) => {
74                             matches!(
75                                 block_expr.modifier(),
76                                 Some(
77                                     ast::BlockModifier::Async(_)
78                                         | ast::BlockModifier::Try(_)
79                                         | ast::BlockModifier::Const(_)
80                                 )
81                             )
82                         }
83                         ast::Expr::ClosureExpr(_) => true,
84                         _ => false,
85                     } && expr.syntax() != start.syntax();
86                     let skip = cb(WalkEvent::Enter(expr));
87                     if skip || is_different_context {
88                         preorder.skip_subtree();
89                     }
90                 }
91             }
92         }
93     }
94 }
95
96 /// Preorder walk all the expression's child patterns.
97 pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
98     let mut preorder = start.syntax().preorder();
99     while let Some(event) = preorder.next() {
100         let node = match event {
101             WalkEvent::Enter(node) => node,
102             WalkEvent::Leave(_) => continue,
103         };
104         match ast::Stmt::cast(node.clone()) {
105             Some(ast::Stmt::LetStmt(l)) => {
106                 if let Some(pat) = l.pat() {
107                     walk_pat(&pat, cb);
108                 }
109                 if let Some(expr) = l.initializer() {
110                     walk_patterns_in_expr(&expr, cb);
111                 }
112                 preorder.skip_subtree();
113             }
114             // Don't skip subtree since we want to process the expression child next
115             Some(ast::Stmt::ExprStmt(_)) => (),
116             // skip inner items which might have their own patterns
117             Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
118             None => {
119                 // skip const args, those are a different context
120                 if ast::GenericArg::can_cast(node.kind()) {
121                     preorder.skip_subtree();
122                 } else if let Some(expr) = ast::Expr::cast(node.clone()) {
123                     let is_different_context = match &expr {
124                         ast::Expr::BlockExpr(block_expr) => {
125                             matches!(
126                                 block_expr.modifier(),
127                                 Some(
128                                     ast::BlockModifier::Async(_)
129                                         | ast::BlockModifier::Try(_)
130                                         | ast::BlockModifier::Const(_)
131                                 )
132                             )
133                         }
134                         ast::Expr::ClosureExpr(_) => true,
135                         _ => false,
136                     } && expr.syntax() != start.syntax();
137                     if is_different_context {
138                         preorder.skip_subtree();
139                     }
140                 } else if let Some(pat) = ast::Pat::cast(node) {
141                     preorder.skip_subtree();
142                     walk_pat(&pat, cb);
143                 }
144             }
145         }
146     }
147 }
148
149 /// Preorder walk all the pattern's sub patterns.
150 pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
151     let mut preorder = pat.syntax().preorder();
152     while let Some(event) = preorder.next() {
153         let node = match event {
154             WalkEvent::Enter(node) => node,
155             WalkEvent::Leave(_) => continue,
156         };
157         let kind = node.kind();
158         match ast::Pat::cast(node) {
159             Some(pat @ ast::Pat::ConstBlockPat(_)) => {
160                 preorder.skip_subtree();
161                 cb(pat);
162             }
163             Some(pat) => {
164                 cb(pat);
165             }
166             // skip const args
167             None if ast::GenericArg::can_cast(kind) => {
168                 preorder.skip_subtree();
169             }
170             None => (),
171         }
172     }
173 }
174
175 /// Preorder walk all the type's sub types.
176 pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type)) {
177     let mut preorder = ty.syntax().preorder();
178     while let Some(event) = preorder.next() {
179         let node = match event {
180             WalkEvent::Enter(node) => node,
181             WalkEvent::Leave(_) => continue,
182         };
183         let kind = node.kind();
184         match ast::Type::cast(node) {
185             Some(ty @ ast::Type::MacroType(_)) => {
186                 preorder.skip_subtree();
187                 cb(ty)
188             }
189             Some(ty) => {
190                 cb(ty);
191             }
192             // skip const args
193             None if ast::ConstArg::can_cast(kind) => {
194                 preorder.skip_subtree();
195             }
196             None => (),
197         }
198     }
199 }
200
201 pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
202     match (this.kind(), other.kind()) {
203         (VisibilityKind::In(this), VisibilityKind::In(other)) => {
204             stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
205                 lhs.kind().zip(rhs.kind()).map_or(false, |it| match it {
206                     (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
207                     | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
208                     | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
209                     (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
210                         lhs.text() == rhs.text()
211                     }
212                     _ => false,
213                 })
214             })
215         }
216         (VisibilityKind::PubSelf, VisibilityKind::PubSelf)
217         | (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
218         | (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
219         | (VisibilityKind::Pub, VisibilityKind::Pub) => true,
220         _ => false,
221     }
222 }
223
224 /// Returns the `let` only if there is exactly one (that is, `let pat = expr`
225 /// or `((let pat = expr))`, but not `let pat = expr && expr` or `non_let_expr`).
226 pub fn single_let(expr: ast::Expr) -> Option<ast::LetExpr> {
227     match expr {
228         ast::Expr::ParenExpr(expr) => expr.expr().and_then(single_let),
229         ast::Expr::LetExpr(expr) => Some(expr),
230         _ => None,
231     }
232 }
233
234 pub fn is_pattern_cond(expr: ast::Expr) -> bool {
235     match expr {
236         ast::Expr::BinExpr(expr)
237             if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
238         {
239             expr.lhs()
240                 .map(is_pattern_cond)
241                 .or_else(|| expr.rhs().map(is_pattern_cond))
242                 .unwrap_or(false)
243         }
244         ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond),
245         ast::Expr::LetExpr(_) => true,
246         _ => false,
247     }
248 }
249
250 /// Calls `cb` on each expression inside `expr` that is at "tail position".
251 /// Does not walk into `break` or `return` expressions.
252 /// Note that modifying the tree while iterating it will cause undefined iteration which might
253 /// potentially results in an out of bounds panic.
254 pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
255     match expr {
256         ast::Expr::BlockExpr(b) => {
257             match b.modifier() {
258                 Some(
259                     ast::BlockModifier::Async(_)
260                     | ast::BlockModifier::Try(_)
261                     | ast::BlockModifier::Const(_),
262                 ) => return cb(expr),
263
264                 Some(ast::BlockModifier::Label(label)) => {
265                     for_each_break_expr(Some(label), b.stmt_list(), &mut |b| {
266                         cb(&ast::Expr::BreakExpr(b))
267                     });
268                 }
269                 Some(ast::BlockModifier::Unsafe(_)) => (),
270                 None => (),
271             }
272             if let Some(stmt_list) = b.stmt_list() {
273                 if let Some(e) = stmt_list.tail_expr() {
274                     for_each_tail_expr(&e, cb);
275                 }
276             }
277         }
278         ast::Expr::IfExpr(if_) => {
279             let mut if_ = if_.clone();
280             loop {
281                 if let Some(block) = if_.then_branch() {
282                     for_each_tail_expr(&ast::Expr::BlockExpr(block), cb);
283                 }
284                 match if_.else_branch() {
285                     Some(ast::ElseBranch::IfExpr(it)) => if_ = it,
286                     Some(ast::ElseBranch::Block(block)) => {
287                         for_each_tail_expr(&ast::Expr::BlockExpr(block), cb);
288                         break;
289                     }
290                     None => break,
291                 }
292             }
293         }
294         ast::Expr::LoopExpr(l) => {
295             for_each_break_expr(l.label(), l.loop_body().and_then(|it| it.stmt_list()), &mut |b| {
296                 cb(&ast::Expr::BreakExpr(b))
297             })
298         }
299         ast::Expr::MatchExpr(m) => {
300             if let Some(arms) = m.match_arm_list() {
301                 arms.arms().filter_map(|arm| arm.expr()).for_each(|e| for_each_tail_expr(&e, cb));
302             }
303         }
304         ast::Expr::ArrayExpr(_)
305         | ast::Expr::AwaitExpr(_)
306         | ast::Expr::BinExpr(_)
307         | ast::Expr::BoxExpr(_)
308         | ast::Expr::BreakExpr(_)
309         | ast::Expr::CallExpr(_)
310         | ast::Expr::CastExpr(_)
311         | ast::Expr::ClosureExpr(_)
312         | ast::Expr::ContinueExpr(_)
313         | ast::Expr::FieldExpr(_)
314         | ast::Expr::ForExpr(_)
315         | ast::Expr::IndexExpr(_)
316         | ast::Expr::Literal(_)
317         | ast::Expr::MacroExpr(_)
318         | ast::Expr::MethodCallExpr(_)
319         | ast::Expr::ParenExpr(_)
320         | ast::Expr::PathExpr(_)
321         | ast::Expr::PrefixExpr(_)
322         | ast::Expr::RangeExpr(_)
323         | ast::Expr::RecordExpr(_)
324         | ast::Expr::RefExpr(_)
325         | ast::Expr::ReturnExpr(_)
326         | ast::Expr::TryExpr(_)
327         | ast::Expr::TupleExpr(_)
328         | ast::Expr::WhileExpr(_)
329         | ast::Expr::LetExpr(_)
330         | ast::Expr::UnderscoreExpr(_)
331         | ast::Expr::YieldExpr(_) => cb(expr),
332     }
333 }
334
335 pub fn for_each_break_and_continue_expr(
336     label: Option<ast::Label>,
337     body: Option<ast::StmtList>,
338     cb: &mut dyn FnMut(ast::Expr),
339 ) {
340     let label = label.and_then(|lbl| lbl.lifetime());
341     if let Some(b) = body {
342         let tree_depth_iterator = TreeWithDepthIterator::new(b);
343         for (expr, depth) in tree_depth_iterator {
344             match expr {
345                 ast::Expr::BreakExpr(b)
346                     if (depth == 0 && b.lifetime().is_none())
347                         || eq_label_lt(&label, &b.lifetime()) =>
348                 {
349                     cb(ast::Expr::BreakExpr(b));
350                 }
351                 ast::Expr::ContinueExpr(c)
352                     if (depth == 0 && c.lifetime().is_none())
353                         || eq_label_lt(&label, &c.lifetime()) =>
354                 {
355                     cb(ast::Expr::ContinueExpr(c));
356                 }
357                 _ => (),
358             }
359         }
360     }
361 }
362
363 fn for_each_break_expr(
364     label: Option<ast::Label>,
365     body: Option<ast::StmtList>,
366     cb: &mut dyn FnMut(ast::BreakExpr),
367 ) {
368     let label = label.and_then(|lbl| lbl.lifetime());
369     if let Some(b) = body {
370         let tree_depth_iterator = TreeWithDepthIterator::new(b);
371         for (expr, depth) in tree_depth_iterator {
372             match expr {
373                 ast::Expr::BreakExpr(b)
374                     if (depth == 0 && b.lifetime().is_none())
375                         || eq_label_lt(&label, &b.lifetime()) =>
376                 {
377                     cb(b);
378                 }
379                 _ => (),
380             }
381         }
382     }
383 }
384
385 fn eq_label_lt(lt1: &Option<ast::Lifetime>, lt2: &Option<ast::Lifetime>) -> bool {
386     lt1.as_ref().zip(lt2.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text())
387 }
388
389 struct TreeWithDepthIterator {
390     preorder: Preorder<RustLanguage>,
391     depth: u32,
392 }
393
394 impl TreeWithDepthIterator {
395     fn new(body: ast::StmtList) -> Self {
396         let preorder = body.syntax().preorder();
397         Self { preorder, depth: 0 }
398     }
399 }
400
401 impl Iterator for TreeWithDepthIterator {
402     type Item = (ast::Expr, u32);
403
404     fn next(&mut self) -> Option<Self::Item> {
405         while let Some(event) = self.preorder.find_map(|ev| match ev {
406             WalkEvent::Enter(it) => ast::Expr::cast(it).map(WalkEvent::Enter),
407             WalkEvent::Leave(it) => ast::Expr::cast(it).map(WalkEvent::Leave),
408         }) {
409             match event {
410                 WalkEvent::Enter(
411                     ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
412                 ) => {
413                     self.depth += 1;
414                 }
415                 WalkEvent::Leave(
416                     ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
417                 ) => {
418                     self.depth -= 1;
419                 }
420                 WalkEvent::Enter(ast::Expr::BlockExpr(e)) if e.label().is_some() => {
421                     self.depth += 1;
422                 }
423                 WalkEvent::Leave(ast::Expr::BlockExpr(e)) if e.label().is_some() => {
424                     self.depth -= 1;
425                 }
426                 WalkEvent::Enter(expr) => return Some((expr, self.depth)),
427                 _ => (),
428             }
429         }
430         None
431     }
432 }
433
434 /// Parses the input token tree as comma separated plain paths.
435 pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
436     let r_paren = input.r_paren_token();
437     let tokens =
438         input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
439             // seeing a keyword means the attribute is unclosed so stop parsing here
440             Some(tok) if tok.kind().is_keyword() => None,
441             // don't include the right token tree parenthesis if it exists
442             tok @ Some(_) if tok == r_paren => None,
443             // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
444             None => None,
445             Some(tok) => Some(tok),
446         });
447     let input_expressions = tokens.group_by(|tok| tok.kind() == T![,]);
448     let paths = input_expressions
449         .into_iter()
450         .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
451         .filter_map(|mut tokens| {
452             syntax::hacks::parse_expr_from_str(&tokens.join("")).and_then(|expr| match expr {
453                 ast::Expr::PathExpr(it) => it.path(),
454                 _ => None,
455             })
456         })
457         .collect();
458     Some(paths)
459 }
460
461 pub fn macro_call_for_string_token(string: &ast::String) -> Option<MacroCall> {
462     let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
463     Some(macro_call)
464 }