1 //! Various helper functions to work with SyntaxNodes.
2 use syntax::{
3     ast::{self, PathSegmentKind, VisibilityKind},
4     AstNode, WalkEvent,
5 };
6 
expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef>7 pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
8     if let ast::Expr::PathExpr(expr) = expr {
9         let path = expr.path()?;
10         path.as_single_name_ref()
11     } else {
12         None
13     }
14 }
15 
block_as_lone_tail(block: &ast::BlockExpr) -> Option<ast::Expr>16 pub fn block_as_lone_tail(block: &ast::BlockExpr) -> Option<ast::Expr> {
17     block.statements().next().is_none().then(|| block.tail_expr()).flatten()
18 }
19 
20 /// Preorder walk all the expression's child expressions.
walk_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr))21 pub fn walk_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
22     preorder_expr(expr, &mut |ev| {
23         if let WalkEvent::Enter(expr) = ev {
24             cb(expr);
25         }
26         false
27     })
28 }
29 
30 /// Preorder walk all the expression's child expressions preserving events.
31 /// If the callback returns true on an [`WalkEvent::Enter`], the subtree of the expression will be skipped.
32 /// 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)33 pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
34     let mut preorder = start.syntax().preorder();
35     while let Some(event) = preorder.next() {
36         let node = match event {
37             WalkEvent::Enter(node) => node,
38             WalkEvent::Leave(node) => {
39                 if let Some(expr) = ast::Expr::cast(node) {
40                     cb(WalkEvent::Leave(expr));
41                 }
42                 continue;
43             }
44         };
45         if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) {
46             if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) {
47                 // skipping potential const pat expressions in  let statements
48                 preorder.skip_subtree();
49                 continue;
50             }
51         }
52 
53         match ast::Stmt::cast(node.clone()) {
54             // Don't skip subtree since we want to process the expression child next
55             Some(ast::Stmt::ExprStmt(_)) | Some(ast::Stmt::LetStmt(_)) => (),
56             // This might be an expression
57             Some(ast::Stmt::Item(ast::Item::MacroCall(mcall))) => {
58                 cb(WalkEvent::Enter(ast::Expr::MacroCall(mcall)));
59                 preorder.skip_subtree();
60             }
61             // skip inner items which might have their own expressions
62             Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
63             None => {
64                 // skip const args, those expressions are a different context
65                 if ast::GenericArg::can_cast(node.kind()) {
66                     preorder.skip_subtree();
67                 } else if let Some(expr) = ast::Expr::cast(node) {
68                     let is_different_context = match &expr {
69                         ast::Expr::BlockExpr(block_expr) => {
70                             matches!(
71                                 block_expr.modifier(),
72                                 Some(
73                                     ast::BlockModifier::Async(_)
74                                         | ast::BlockModifier::Try(_)
75                                         | ast::BlockModifier::Const(_)
76                                 )
77                             )
78                         }
79                         ast::Expr::ClosureExpr(_) => true,
80                         _ => false,
81                     } && expr.syntax() != start.syntax();
82                     let skip = cb(WalkEvent::Enter(expr));
83                     if skip || is_different_context {
84                         preorder.skip_subtree();
85                     }
86                 }
87             }
88         }
89     }
90 }
91 
92 /// Preorder walk all the expression's child patterns.
walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat))93 pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
94     let mut preorder = start.syntax().preorder();
95     while let Some(event) = preorder.next() {
96         let node = match event {
97             WalkEvent::Enter(node) => node,
98             WalkEvent::Leave(_) => continue,
99         };
100         match ast::Stmt::cast(node.clone()) {
101             Some(ast::Stmt::LetStmt(l)) => {
102                 if let Some(pat) = l.pat() {
103                     walk_pat(&pat, cb);
104                 }
105                 if let Some(expr) = l.initializer() {
106                     walk_patterns_in_expr(&expr, cb);
107                 }
108                 preorder.skip_subtree();
109             }
110             // Don't skip subtree since we want to process the expression child next
111             Some(ast::Stmt::ExprStmt(_)) => (),
112             // skip inner items which might have their own patterns
113             Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
114             None => {
115                 // skip const args, those are a different context
116                 if ast::GenericArg::can_cast(node.kind()) {
117                     preorder.skip_subtree();
118                 } else if let Some(expr) = ast::Expr::cast(node.clone()) {
119                     let is_different_context = match &expr {
120                         ast::Expr::BlockExpr(block_expr) => {
121                             matches!(
122                                 block_expr.modifier(),
123                                 Some(
124                                     ast::BlockModifier::Async(_)
125                                         | ast::BlockModifier::Try(_)
126                                         | ast::BlockModifier::Const(_)
127                                 )
128                             )
129                         }
130                         ast::Expr::ClosureExpr(_) => true,
131                         _ => false,
132                     } && expr.syntax() != start.syntax();
133                     if is_different_context {
134                         preorder.skip_subtree();
135                     }
136                 } else if let Some(pat) = ast::Pat::cast(node) {
137                     preorder.skip_subtree();
138                     walk_pat(&pat, cb);
139                 }
140             }
141         }
142     }
143 }
144 
145 /// Preorder walk all the pattern's sub patterns.
walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat))146 pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
147     let mut preorder = pat.syntax().preorder();
148     while let Some(event) = preorder.next() {
149         let node = match event {
150             WalkEvent::Enter(node) => node,
151             WalkEvent::Leave(_) => continue,
152         };
153         let kind = node.kind();
154         match ast::Pat::cast(node) {
155             Some(pat @ ast::Pat::ConstBlockPat(_)) => {
156                 preorder.skip_subtree();
157                 cb(pat);
158             }
159             Some(pat) => {
160                 cb(pat);
161             }
162             // skip const args
163             None if ast::GenericArg::can_cast(kind) => {
164                 preorder.skip_subtree();
165             }
166             None => (),
167         }
168     }
169 }
170 
171 /// Preorder walk all the type's sub types.
walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type))172 pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type)) {
173     let mut preorder = ty.syntax().preorder();
174     while let Some(event) = preorder.next() {
175         let node = match event {
176             WalkEvent::Enter(node) => node,
177             WalkEvent::Leave(_) => continue,
178         };
179         let kind = node.kind();
180         match ast::Type::cast(node) {
181             Some(ty @ ast::Type::MacroType(_)) => {
182                 preorder.skip_subtree();
183                 cb(ty)
184             }
185             Some(ty) => {
186                 cb(ty);
187             }
188             // skip const args
189             None if ast::ConstArg::can_cast(kind) => {
190                 preorder.skip_subtree();
191             }
192             None => (),
193         }
194     }
195 }
196 
vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool197 pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
198     match (this.kind(), other.kind()) {
199         (VisibilityKind::In(this), VisibilityKind::In(other)) => {
200             stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
201                 lhs.kind().zip(rhs.kind()).map_or(false, |it| match it {
202                     (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
203                     | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
204                     | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
205                     (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
206                         lhs.text() == rhs.text()
207                     }
208                     _ => false,
209                 })
210             })
211         }
212         (VisibilityKind::PubSelf, VisibilityKind::PubSelf)
213         | (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
214         | (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
215         | (VisibilityKind::Pub, VisibilityKind::Pub) => true,
216         _ => false,
217     }
218 }
219