1use std::ops::ControlFlow;
3
4use itertools::Itertools;
5use parser::T;
6use span::Edition;
7use syntax::{
8 AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
9 ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
10};
11
12pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
13 if let ast::Expr::PathExpr(expr) = expr {
14 let path = expr.path()?;
15 path.as_single_name_ref()
16 } else {
17 None
18 }
19}
20
21pub fn full_path_of_name_ref(name_ref: &ast::NameRef) -> Option<ast::Path> {
22 let mut ancestors = name_ref.syntax().ancestors();
23 let _ = ancestors.next()?; let _ = ancestors.next().filter(|it| ast::PathSegment::can_cast(it.kind()))?; ancestors.take_while(|it| ast::Path::can_cast(it.kind())).last().and_then(ast::Path::cast)
26}
27
28pub fn block_as_lone_tail(block: &ast::BlockExpr) -> Option<ast::Expr> {
29 block.statements().next().is_none().then(|| block.tail_expr()).flatten()
30}
31
32pub fn walk_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
34 preorder_expr(expr, &mut |ev| {
35 if let WalkEvent::Enter(expr) = ev {
36 cb(expr);
37 }
38 false
39 })
40}
41
42pub fn is_closure_or_blk_with_modif(expr: &ast::Expr) -> bool {
43 match expr {
44 ast::Expr::BlockExpr(block_expr) => {
45 matches!(
46 block_expr.modifier(),
47 Some(
48 ast::BlockModifier::Async(_)
49 | ast::BlockModifier::Try(_)
50 | ast::BlockModifier::Const(_)
51 )
52 )
53 }
54 ast::Expr::ClosureExpr(_) => true,
55 _ => false,
56 }
57}
58
59pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
63 preorder_expr_with_ctx_checker(start, &is_closure_or_blk_with_modif, cb);
64}
65
66pub fn preorder_expr_with_ctx_checker(
67 start: &ast::Expr,
68 check_ctx: &dyn Fn(&ast::Expr) -> bool,
69 cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool,
70) {
71 let mut preorder = start.syntax().preorder();
72 while let Some(event) = preorder.next() {
73 let node = match event {
74 WalkEvent::Enter(node) => node,
75 WalkEvent::Leave(node) => {
76 if let Some(expr) = ast::Expr::cast(node) {
77 cb(WalkEvent::Leave(expr));
78 }
79 continue;
80 }
81 };
82 if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast)
83 && let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true)
84 && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true)
85 {
86 preorder.skip_subtree();
88 continue;
89 }
90
91 match ast::Stmt::cast(node.clone()) {
92 Some(ast::Stmt::ExprStmt(_)) | Some(ast::Stmt::LetStmt(_)) => (),
94 Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
96 None => {
97 if ast::GenericArg::can_cast(node.kind()) {
99 preorder.skip_subtree();
100 } else if let Some(expr) = ast::Expr::cast(node) {
101 let is_different_context = check_ctx(&expr) && expr.syntax() != start.syntax();
102 let skip = cb(WalkEvent::Enter(expr));
103 if skip || is_different_context {
104 preorder.skip_subtree();
105 }
106 }
107 }
108 }
109 }
110}
111
112pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
114 let mut preorder = start.syntax().preorder();
115 while let Some(event) = preorder.next() {
116 let node = match event {
117 WalkEvent::Enter(node) => node,
118 WalkEvent::Leave(_) => continue,
119 };
120 match ast::Stmt::cast(node.clone()) {
121 Some(ast::Stmt::LetStmt(l)) => {
122 if let Some(pat) = l.pat() {
123 _ = walk_pat(&pat, &mut |pat| {
124 cb(pat);
125 ControlFlow::<(), ()>::Continue(())
126 });
127 }
128 if let Some(expr) = l.initializer() {
129 walk_patterns_in_expr(&expr, cb);
130 }
131 preorder.skip_subtree();
132 }
133 Some(ast::Stmt::ExprStmt(_)) => (),
135 Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
137 None => {
138 if ast::GenericArg::can_cast(node.kind()) {
140 preorder.skip_subtree();
141 } else if let Some(expr) = ast::Expr::cast(node.clone()) {
142 let is_different_context = match &expr {
143 ast::Expr::BlockExpr(block_expr) => {
144 matches!(
145 block_expr.modifier(),
146 Some(
147 ast::BlockModifier::Async(_)
148 | ast::BlockModifier::Try(_)
149 | ast::BlockModifier::Const(_)
150 )
151 )
152 }
153 ast::Expr::ClosureExpr(_) => true,
154 _ => false,
155 } && expr.syntax() != start.syntax();
156 if is_different_context {
157 preorder.skip_subtree();
158 }
159 } else if let Some(pat) = ast::Pat::cast(node) {
160 preorder.skip_subtree();
161 _ = walk_pat(&pat, &mut |pat| {
162 cb(pat);
163 ControlFlow::<(), ()>::Continue(())
164 });
165 }
166 }
167 }
168 }
169}
170
171pub fn walk_pat<T>(
173 pat: &ast::Pat,
174 cb: &mut dyn FnMut(ast::Pat) -> ControlFlow<T>,
175) -> ControlFlow<T> {
176 let mut preorder = pat.syntax().preorder();
177 while let Some(event) = preorder.next() {
178 let node = match event {
179 WalkEvent::Enter(node) => node,
180 WalkEvent::Leave(_) => continue,
181 };
182 let kind = node.kind();
183 match ast::Pat::cast(node) {
184 Some(pat @ ast::Pat::ConstBlockPat(_)) => {
185 preorder.skip_subtree();
186 cb(pat)?;
187 }
188 Some(pat) => {
189 cb(pat)?;
190 }
191 None if ast::GenericArg::can_cast(kind) => {
193 preorder.skip_subtree();
194 }
195 None => (),
196 }
197 }
198 ControlFlow::Continue(())
199}
200
201pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) {
204 let mut preorder = ty.syntax().preorder();
205 while let Some(event) = preorder.next() {
206 let node = match event {
207 WalkEvent::Enter(node) => node,
208 WalkEvent::Leave(_) => continue,
209 };
210 let kind = node.kind();
211 match ast::Type::cast(node) {
212 Some(ty @ ast::Type::MacroType(_)) => {
213 preorder.skip_subtree();
214 cb(ty);
215 }
216 Some(ty) => {
217 if cb(ty) {
218 preorder.skip_subtree();
219 }
220 }
221 None if ast::ConstArg::can_cast(kind) => {
223 preorder.skip_subtree();
224 }
225 None => (),
226 }
227 }
228}
229
230pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
231 match (this.kind(), other.kind()) {
232 (VisibilityKind::In(this), VisibilityKind::In(other)) => {
233 stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
234 lhs.kind().zip(rhs.kind()).is_some_and(|it| match it {
235 (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
236 | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
237 | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
238 (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
239 lhs.text() == rhs.text()
240 }
241 _ => false,
242 })
243 })
244 }
245 (VisibilityKind::PubSelf, VisibilityKind::PubSelf)
246 | (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
247 | (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
248 | (VisibilityKind::Pub, VisibilityKind::Pub) => true,
249 _ => false,
250 }
251}
252
253pub fn single_let(expr: ast::Expr) -> Option<ast::LetExpr> {
256 match expr {
257 ast::Expr::ParenExpr(expr) => expr.expr().and_then(single_let),
258 ast::Expr::LetExpr(expr) => Some(expr),
259 _ => None,
260 }
261}
262
263pub fn is_pattern_cond(expr: ast::Expr) -> bool {
264 match expr {
265 ast::Expr::BinExpr(expr)
266 if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
267 {
268 expr.lhs().map_or(false, is_pattern_cond) || expr.rhs().map_or(false, is_pattern_cond)
269 }
270 ast::Expr::ParenExpr(expr) => expr.expr().is_some_and(is_pattern_cond),
271 ast::Expr::LetExpr(_) => true,
272 _ => false,
273 }
274}
275
276pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
281 let walk_loop = |cb: &mut dyn FnMut(&ast::Expr), label, body: Option<ast::BlockExpr>| {
282 for_each_break_expr(label, body.and_then(|it| it.stmt_list()), &mut |b| {
283 cb(&ast::Expr::BreakExpr(b))
284 })
285 };
286 match expr {
287 ast::Expr::BlockExpr(b) => {
288 match b.modifier() {
289 Some(
290 ast::BlockModifier::Async(_)
291 | ast::BlockModifier::Try(_)
292 | ast::BlockModifier::Const(_),
293 ) => return cb(expr),
294
295 Some(ast::BlockModifier::Label(label)) => {
296 for_each_break_expr(Some(label), b.stmt_list(), &mut |b| {
297 cb(&ast::Expr::BreakExpr(b))
298 });
299 }
300 Some(ast::BlockModifier::Unsafe(_)) => (),
301 Some(ast::BlockModifier::Gen(_)) => (),
302 Some(ast::BlockModifier::AsyncGen(_)) => (),
303 None => (),
304 }
305 if let Some(stmt_list) = b.stmt_list()
306 && let Some(e) = stmt_list.tail_expr()
307 {
308 for_each_tail_expr(&e, cb);
309 }
310 }
311 ast::Expr::IfExpr(if_) => {
312 let mut if_ = if_.clone();
313 loop {
314 if let Some(block) = if_.then_branch() {
315 for_each_tail_expr(&ast::Expr::BlockExpr(block), cb);
316 }
317 match if_.else_branch() {
318 Some(ast::ElseBranch::IfExpr(it)) => if_ = it,
319 Some(ast::ElseBranch::Block(block)) => {
320 for_each_tail_expr(&ast::Expr::BlockExpr(block), cb);
321 break;
322 }
323 None => break,
324 }
325 }
326 }
327 ast::Expr::LoopExpr(l) => walk_loop(cb, l.label(), l.loop_body()),
328 ast::Expr::WhileExpr(w) => walk_loop(cb, w.label(), w.loop_body()),
329 ast::Expr::ForExpr(f) => walk_loop(cb, f.label(), f.loop_body()),
330 ast::Expr::MatchExpr(m) => {
331 if let Some(arms) = m.match_arm_list() {
332 arms.arms().filter_map(|arm| arm.expr()).for_each(|e| for_each_tail_expr(&e, cb));
333 }
334 }
335 ast::Expr::ArrayExpr(_)
336 | ast::Expr::AwaitExpr(_)
337 | ast::Expr::BinExpr(_)
338 | ast::Expr::BreakExpr(_)
339 | ast::Expr::CallExpr(_)
340 | ast::Expr::CastExpr(_)
341 | ast::Expr::ClosureExpr(_)
342 | ast::Expr::ContinueExpr(_)
343 | ast::Expr::FieldExpr(_)
344 | ast::Expr::IndexExpr(_)
345 | ast::Expr::Literal(_)
346 | ast::Expr::MacroExpr(_)
347 | ast::Expr::MethodCallExpr(_)
348 | ast::Expr::ParenExpr(_)
349 | ast::Expr::PathExpr(_)
350 | ast::Expr::PrefixExpr(_)
351 | ast::Expr::RangeExpr(_)
352 | ast::Expr::RecordExpr(_)
353 | ast::Expr::RefExpr(_)
354 | ast::Expr::ReturnExpr(_)
355 | ast::Expr::BecomeExpr(_)
356 | ast::Expr::TryExpr(_)
357 | ast::Expr::TupleExpr(_)
358 | ast::Expr::LetExpr(_)
359 | ast::Expr::UnderscoreExpr(_)
360 | ast::Expr::YieldExpr(_)
361 | ast::Expr::YeetExpr(_)
362 | ast::Expr::OffsetOfExpr(_)
363 | ast::Expr::FormatArgsExpr(_)
364 | ast::Expr::AsmExpr(_) => cb(expr),
365 }
366}
367
368pub fn for_each_break_and_continue_expr(
369 label: Option<ast::Label>,
370 body: Option<ast::StmtList>,
371 cb: &mut dyn FnMut(ast::Expr),
372) {
373 let label = label.and_then(|lbl| lbl.lifetime());
374 if let Some(b) = body {
375 let tree_depth_iterator = TreeWithDepthIterator::new(b);
376 for (expr, depth) in tree_depth_iterator {
377 match expr {
378 ast::Expr::BreakExpr(b)
379 if (depth == 0 && b.lifetime().is_none())
380 || eq_label_lt(&label, &b.lifetime()) =>
381 {
382 cb(ast::Expr::BreakExpr(b));
383 }
384 ast::Expr::ContinueExpr(c)
385 if (depth == 0 && c.lifetime().is_none())
386 || eq_label_lt(&label, &c.lifetime()) =>
387 {
388 cb(ast::Expr::ContinueExpr(c));
389 }
390 _ => (),
391 }
392 }
393 }
394}
395
396fn for_each_break_expr(
397 label: Option<ast::Label>,
398 body: Option<ast::StmtList>,
399 cb: &mut dyn FnMut(ast::BreakExpr),
400) {
401 let label = label.and_then(|lbl| lbl.lifetime());
402 if let Some(b) = body {
403 let tree_depth_iterator = TreeWithDepthIterator::new(b);
404 for (expr, depth) in tree_depth_iterator {
405 match expr {
406 ast::Expr::BreakExpr(b)
407 if (depth == 0 && b.lifetime().is_none())
408 || eq_label_lt(&label, &b.lifetime()) =>
409 {
410 cb(b);
411 }
412 _ => (),
413 }
414 }
415 }
416}
417
418pub fn eq_label_lt(lt1: &Option<ast::Lifetime>, lt2: &Option<ast::Lifetime>) -> bool {
419 lt1.as_ref().zip(lt2.as_ref()).is_some_and(|(lt, lbl)| lt.text() == lbl.text())
420}
421
422struct TreeWithDepthIterator {
423 preorder: Preorder<RustLanguage>,
424 depth: u32,
425}
426
427impl TreeWithDepthIterator {
428 fn new(body: ast::StmtList) -> Self {
429 let preorder = body.syntax().preorder();
430 Self { preorder, depth: 0 }
431 }
432}
433
434impl Iterator for TreeWithDepthIterator {
435 type Item = (ast::Expr, u32);
436
437 fn next(&mut self) -> Option<Self::Item> {
438 while let Some(event) = self.preorder.find_map(|ev| match ev {
439 WalkEvent::Enter(it) => ast::Expr::cast(it).map(WalkEvent::Enter),
440 WalkEvent::Leave(it) => ast::Expr::cast(it).map(WalkEvent::Leave),
441 }) {
442 match event {
443 WalkEvent::Enter(
444 ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
445 ) => {
446 self.depth += 1;
447 }
448 WalkEvent::Leave(
449 ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
450 ) => {
451 self.depth -= 1;
452 }
453 WalkEvent::Enter(ast::Expr::BlockExpr(e)) if e.label().is_some() => {
454 self.depth += 1;
455 }
456 WalkEvent::Leave(ast::Expr::BlockExpr(e)) if e.label().is_some() => {
457 self.depth -= 1;
458 }
459 WalkEvent::Enter(expr) => return Some((expr, self.depth)),
460 _ => (),
461 }
462 }
463 None
464 }
465}
466
467pub fn parse_tt_as_comma_sep_paths(
469 input: ast::TokenTree,
470 edition: Edition,
471) -> Option<Vec<ast::Path>> {
472 let r_paren = input.r_paren_token();
473 let tokens =
474 input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
475 Some(tok) if tok.kind().is_keyword(edition) => None,
477 tok @ Some(_) if tok == r_paren => None,
479 None => None,
481 Some(tok) => Some(tok),
482 });
483 let input_expressions = tokens.chunk_by(|tok| tok.kind() == T![,]);
484 let paths = input_expressions
485 .into_iter()
486 .filter_map(|(is_sep, group)| (!is_sep).then_some(group))
487 .filter_map(|mut tokens| {
488 syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT).and_then(
489 |expr| match expr {
490 ast::Expr::PathExpr(it) => it.path(),
491 _ => None,
492 },
493 )
494 })
495 .collect();
496 Some(paths)
497}
498
499pub fn macro_call_for_string_token(string: &ast::String) -> Option<MacroCall> {
500 let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
501 Some(macro_call)
502}