1 //! Various helper functions to work with SyntaxNodes.
2 use itertools
::Itertools
;
5 ast
::{self, HasLoopBody, PathSegmentKind, VisibilityKind}
,
6 AstNode
, Preorder
, RustLanguage
, WalkEvent
,
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()
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
)
25 pub fn block_as_lone_tail(block
: &ast
::BlockExpr
) -> Option
<ast
::Expr
> {
26 block
.statements().next().is_none().then(|| block
.tail_expr()).flatten()
29 /// Preorder walk all the expression's child expressions.
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
{
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.
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
));
54 if let Some(let_stmt
) = node
.parent().and_then(ast
::LetStmt
::cast
) {
55 if Some(node
.clone()) != let_stmt
.initializer().map(|it
| it
.syntax().clone()) {
56 // skipping potential const pat expressions in let statements
57 preorder
.skip_subtree();
62 match ast
::Stmt
::cast(node
.clone()) {
63 // Don't skip subtree since we want to process the expression child next
64 Some(ast
::Stmt
::ExprStmt(_
)) | Some(ast
::Stmt
::LetStmt(_
)) => (),
65 // skip inner items which might have their own expressions
66 Some(ast
::Stmt
::Item(_
)) => preorder
.skip_subtree(),
68 // skip const args, those expressions are a different context
69 if ast
::GenericArg
::can_cast(node
.kind()) {
70 preorder
.skip_subtree();
71 } else if let Some(expr
) = ast
::Expr
::cast(node
) {
72 let is_different_context
= match &expr
{
73 ast
::Expr
::BlockExpr(block_expr
) => {
75 block_expr
.modifier(),
77 ast
::BlockModifier
::Async(_
)
78 | ast
::BlockModifier
::Try(_
)
79 | ast
::BlockModifier
::Const(_
)
83 ast
::Expr
::ClosureExpr(_
) => true,
85 } && expr
.syntax() != start
.syntax();
86 let skip
= cb(WalkEvent
::Enter(expr
));
87 if skip
|| is_different_context
{
88 preorder
.skip_subtree();
96 /// Preorder walk all the expression's child patterns.
97 pub fn walk_patterns_in_expr(start
: &ast
::Expr
, cb
: &mut dyn FnMut(ast
::Pat
)) {
98 let mut preorder
= start
.syntax().preorder();
99 while let Some(event
) = preorder
.next() {
100 let node
= match event
{
101 WalkEvent
::Enter(node
) => node
,
102 WalkEvent
::Leave(_
) => continue,
104 match ast
::Stmt
::cast(node
.clone()) {
105 Some(ast
::Stmt
::LetStmt(l
)) => {
106 if let Some(pat
) = l
.pat() {
109 if let Some(expr
) = l
.initializer() {
110 walk_patterns_in_expr(&expr
, cb
);
112 preorder
.skip_subtree();
114 // Don't skip subtree since we want to process the expression child next
115 Some(ast
::Stmt
::ExprStmt(_
)) => (),
116 // skip inner items which might have their own patterns
117 Some(ast
::Stmt
::Item(_
)) => preorder
.skip_subtree(),
119 // skip const args, those are a different context
120 if ast
::GenericArg
::can_cast(node
.kind()) {
121 preorder
.skip_subtree();
122 } else if let Some(expr
) = ast
::Expr
::cast(node
.clone()) {
123 let is_different_context
= match &expr
{
124 ast
::Expr
::BlockExpr(block_expr
) => {
126 block_expr
.modifier(),
128 ast
::BlockModifier
::Async(_
)
129 | ast
::BlockModifier
::Try(_
)
130 | ast
::BlockModifier
::Const(_
)
134 ast
::Expr
::ClosureExpr(_
) => true,
136 } && expr
.syntax() != start
.syntax();
137 if is_different_context
{
138 preorder
.skip_subtree();
140 } else if let Some(pat
) = ast
::Pat
::cast(node
) {
141 preorder
.skip_subtree();
149 /// Preorder walk all the pattern's sub patterns.
150 pub fn walk_pat(pat
: &ast
::Pat
, cb
: &mut dyn FnMut(ast
::Pat
)) {
151 let mut preorder
= pat
.syntax().preorder();
152 while let Some(event
) = preorder
.next() {
153 let node
= match event
{
154 WalkEvent
::Enter(node
) => node
,
155 WalkEvent
::Leave(_
) => continue,
157 let kind
= node
.kind();
158 match ast
::Pat
::cast(node
) {
159 Some(pat @ ast
::Pat
::ConstBlockPat(_
)) => {
160 preorder
.skip_subtree();
167 None
if ast
::GenericArg
::can_cast(kind
) => {
168 preorder
.skip_subtree();
175 /// Preorder walk all the type's sub types.
176 pub fn walk_ty(ty
: &ast
::Type
, cb
: &mut dyn FnMut(ast
::Type
)) {
177 let mut preorder
= ty
.syntax().preorder();
178 while let Some(event
) = preorder
.next() {
179 let node
= match event
{
180 WalkEvent
::Enter(node
) => node
,
181 WalkEvent
::Leave(_
) => continue,
183 let kind
= node
.kind();
184 match ast
::Type
::cast(node
) {
185 Some(ty @ ast
::Type
::MacroType(_
)) => {
186 preorder
.skip_subtree();
193 None
if ast
::ConstArg
::can_cast(kind
) => {
194 preorder
.skip_subtree();
201 pub fn vis_eq(this
: &ast
::Visibility
, other
: &ast
::Visibility
) -> bool
{
202 match (this
.kind(), other
.kind()) {
203 (VisibilityKind
::In(this
), VisibilityKind
::In(other
)) => {
204 stdx
::iter_eq_by(this
.segments(), other
.segments(), |lhs
, rhs
| {
205 lhs
.kind().zip(rhs
.kind()).map_or(false, |it
| match it
{
206 (PathSegmentKind
::CrateKw
, PathSegmentKind
::CrateKw
)
207 | (PathSegmentKind
::SelfKw
, PathSegmentKind
::SelfKw
)
208 | (PathSegmentKind
::SuperKw
, PathSegmentKind
::SuperKw
) => true,
209 (PathSegmentKind
::Name(lhs
), PathSegmentKind
::Name(rhs
)) => {
210 lhs
.text() == rhs
.text()
216 (VisibilityKind
::PubSelf
, VisibilityKind
::PubSelf
)
217 | (VisibilityKind
::PubSuper
, VisibilityKind
::PubSuper
)
218 | (VisibilityKind
::PubCrate
, VisibilityKind
::PubCrate
)
219 | (VisibilityKind
::Pub
, VisibilityKind
::Pub
) => true,
224 /// Returns the `let` only if there is exactly one (that is, `let pat = expr`
225 /// or `((let pat = expr))`, but not `let pat = expr && expr` or `non_let_expr`).
226 pub fn single_let(expr
: ast
::Expr
) -> Option
<ast
::LetExpr
> {
228 ast
::Expr
::ParenExpr(expr
) => expr
.expr().and_then(single_let
),
229 ast
::Expr
::LetExpr(expr
) => Some(expr
),
234 pub fn is_pattern_cond(expr
: ast
::Expr
) -> bool
{
236 ast
::Expr
::BinExpr(expr
)
237 if expr
.op_kind() == Some(ast
::BinaryOp
::LogicOp(ast
::LogicOp
::And
)) =>
240 .map(is_pattern_cond
)
241 .or_else(|| expr
.rhs().map(is_pattern_cond
))
244 ast
::Expr
::ParenExpr(expr
) => expr
.expr().map_or(false, is_pattern_cond
),
245 ast
::Expr
::LetExpr(_
) => true,
250 /// Calls `cb` on each expression inside `expr` that is at "tail position".
251 /// Does not walk into `break` or `return` expressions.
252 /// Note that modifying the tree while iterating it will cause undefined iteration which might
253 /// potentially results in an out of bounds panic.
254 pub fn for_each_tail_expr(expr
: &ast
::Expr
, cb
: &mut dyn FnMut(&ast
::Expr
)) {
256 ast
::Expr
::BlockExpr(b
) => {
259 ast
::BlockModifier
::Async(_
)
260 | ast
::BlockModifier
::Try(_
)
261 | ast
::BlockModifier
::Const(_
),
262 ) => return cb(expr
),
264 Some(ast
::BlockModifier
::Label(label
)) => {
265 for_each_break_expr(Some(label
), b
.stmt_list(), &mut |b
| {
266 cb(&ast
::Expr
::BreakExpr(b
))
269 Some(ast
::BlockModifier
::Unsafe(_
)) => (),
272 if let Some(stmt_list
) = b
.stmt_list() {
273 if let Some(e
) = stmt_list
.tail_expr() {
274 for_each_tail_expr(&e
, cb
);
278 ast
::Expr
::IfExpr(if_
) => {
279 let mut if_
= if_
.clone();
281 if let Some(block
) = if_
.then_branch() {
282 for_each_tail_expr(&ast
::Expr
::BlockExpr(block
), cb
);
284 match if_
.else_branch() {
285 Some(ast
::ElseBranch
::IfExpr(it
)) => if_
= it
,
286 Some(ast
::ElseBranch
::Block(block
)) => {
287 for_each_tail_expr(&ast
::Expr
::BlockExpr(block
), cb
);
294 ast
::Expr
::LoopExpr(l
) => {
295 for_each_break_expr(l
.label(), l
.loop_body().and_then(|it
| it
.stmt_list()), &mut |b
| {
296 cb(&ast
::Expr
::BreakExpr(b
))
299 ast
::Expr
::MatchExpr(m
) => {
300 if let Some(arms
) = m
.match_arm_list() {
301 arms
.arms().filter_map(|arm
| arm
.expr()).for_each(|e
| for_each_tail_expr(&e
, cb
));
304 ast
::Expr
::ArrayExpr(_
)
305 | ast
::Expr
::AwaitExpr(_
)
306 | ast
::Expr
::BinExpr(_
)
307 | ast
::Expr
::BoxExpr(_
)
308 | ast
::Expr
::BreakExpr(_
)
309 | ast
::Expr
::CallExpr(_
)
310 | ast
::Expr
::CastExpr(_
)
311 | ast
::Expr
::ClosureExpr(_
)
312 | ast
::Expr
::ContinueExpr(_
)
313 | ast
::Expr
::FieldExpr(_
)
314 | ast
::Expr
::ForExpr(_
)
315 | ast
::Expr
::IndexExpr(_
)
316 | ast
::Expr
::Literal(_
)
317 | ast
::Expr
::MacroExpr(_
)
318 | ast
::Expr
::MethodCallExpr(_
)
319 | ast
::Expr
::ParenExpr(_
)
320 | ast
::Expr
::PathExpr(_
)
321 | ast
::Expr
::PrefixExpr(_
)
322 | ast
::Expr
::RangeExpr(_
)
323 | ast
::Expr
::RecordExpr(_
)
324 | ast
::Expr
::RefExpr(_
)
325 | ast
::Expr
::ReturnExpr(_
)
326 | ast
::Expr
::TryExpr(_
)
327 | ast
::Expr
::TupleExpr(_
)
328 | ast
::Expr
::WhileExpr(_
)
329 | ast
::Expr
::LetExpr(_
)
330 | ast
::Expr
::UnderscoreExpr(_
)
331 | ast
::Expr
::YieldExpr(_
) => cb(expr
),
335 pub fn for_each_break_and_continue_expr(
336 label
: Option
<ast
::Label
>,
337 body
: Option
<ast
::StmtList
>,
338 cb
: &mut dyn FnMut(ast
::Expr
),
340 let label
= label
.and_then(|lbl
| lbl
.lifetime());
341 if let Some(b
) = body
{
342 let tree_depth_iterator
= TreeWithDepthIterator
::new(b
);
343 for (expr
, depth
) in tree_depth_iterator
{
345 ast
::Expr
::BreakExpr(b
)
346 if (depth
== 0 && b
.lifetime().is_none())
347 || eq_label_lt(&label
, &b
.lifetime()) =>
349 cb(ast
::Expr
::BreakExpr(b
));
351 ast
::Expr
::ContinueExpr(c
)
352 if (depth
== 0 && c
.lifetime().is_none())
353 || eq_label_lt(&label
, &c
.lifetime()) =>
355 cb(ast
::Expr
::ContinueExpr(c
));
363 fn for_each_break_expr(
364 label
: Option
<ast
::Label
>,
365 body
: Option
<ast
::StmtList
>,
366 cb
: &mut dyn FnMut(ast
::BreakExpr
),
368 let label
= label
.and_then(|lbl
| lbl
.lifetime());
369 if let Some(b
) = body
{
370 let tree_depth_iterator
= TreeWithDepthIterator
::new(b
);
371 for (expr
, depth
) in tree_depth_iterator
{
373 ast
::Expr
::BreakExpr(b
)
374 if (depth
== 0 && b
.lifetime().is_none())
375 || eq_label_lt(&label
, &b
.lifetime()) =>
385 fn eq_label_lt(lt1
: &Option
<ast
::Lifetime
>, lt2
: &Option
<ast
::Lifetime
>) -> bool
{
386 lt1
.as_ref().zip(lt2
.as_ref()).map_or(false, |(lt
, lbl
)| lt
.text() == lbl
.text())
389 struct TreeWithDepthIterator
{
390 preorder
: Preorder
<RustLanguage
>,
394 impl TreeWithDepthIterator
{
395 fn new(body
: ast
::StmtList
) -> Self {
396 let preorder
= body
.syntax().preorder();
397 Self { preorder, depth: 0 }
401 impl Iterator
for TreeWithDepthIterator
{
402 type Item
= (ast
::Expr
, u32);
404 fn next(&mut self) -> Option
<Self::Item
> {
405 while let Some(event
) = self.preorder
.find_map(|ev
| match ev
{
406 WalkEvent
::Enter(it
) => ast
::Expr
::cast(it
).map(WalkEvent
::Enter
),
407 WalkEvent
::Leave(it
) => ast
::Expr
::cast(it
).map(WalkEvent
::Leave
),
411 ast
::Expr
::LoopExpr(_
) | ast
::Expr
::WhileExpr(_
) | ast
::Expr
::ForExpr(_
),
416 ast
::Expr
::LoopExpr(_
) | ast
::Expr
::WhileExpr(_
) | ast
::Expr
::ForExpr(_
),
420 WalkEvent
::Enter(ast
::Expr
::BlockExpr(e
)) if e
.label().is_some() => {
423 WalkEvent
::Leave(ast
::Expr
::BlockExpr(e
)) if e
.label().is_some() => {
426 WalkEvent
::Enter(expr
) => return Some((expr
, self.depth
)),
434 /// Parses the input token tree as comma separated plain paths.
435 pub fn parse_tt_as_comma_sep_paths(input
: ast
::TokenTree
) -> Option
<Vec
<ast
::Path
>> {
436 let r_paren
= input
.r_paren_token();
438 input
.syntax().children_with_tokens().skip(1).map_while(|it
| match it
.into_token() {
439 // seeing a keyword means the attribute is unclosed so stop parsing here
440 Some(tok
) if tok
.kind().is_keyword() => None
,
441 // don't include the right token tree parenthesis if it exists
442 tok @
Some(_
) if tok
== r_paren
=> None
,
443 // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
445 Some(tok
) => Some(tok
),
447 let input_expressions
= tokens
.group_by(|tok
| tok
.kind() == T
![,]);
448 let paths
= input_expressions
450 .filter_map(|(is_sep
, group
)| (!is_sep
).then(|| group
))
451 .filter_map(|mut tokens
| {
452 syntax
::hacks
::parse_expr_from_str(&tokens
.join("")).and_then(|expr
| match expr
{
453 ast
::Expr
::PathExpr(it
) => it
.path(),