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