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