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