Skip to content

Commit 1fa82ad

Browse files
bors[bot]Veykril
andauthored
Merge #9413
9413: internal: Deduplicate ast expression walking logic r=Veykril a=Veykril Deduplicates the duplication introduced in #9375 and #9396 while also fixing a few bugs in both the assist and related highlighting. Co-authored-by: Lukas Wirth <[email protected]>
2 parents ddce8b6 + 3ce5c66 commit 1fa82ad

File tree

4 files changed

+218
-441
lines changed

4 files changed

+218
-441
lines changed

crates/ide/src/highlight_related.rs

Lines changed: 6 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use hir::Semantics;
22
use ide_db::{
33
base_db::FilePosition,
44
defs::Definition,
5-
helpers::pick_best_token,
5+
helpers::{for_each_break_expr, for_each_tail_expr, pick_best_token},
66
search::{FileReference, ReferenceAccess, SearchScope},
77
RootDatabase,
88
};
99
use syntax::{
1010
ast::{self, LoopBodyOwner},
11-
match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
11+
match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, T,
1212
};
1313

1414
use crate::{display::TryToNav, references, NavigationTarget};
@@ -95,7 +95,7 @@ fn highlight_exit_points(
9595
) -> Option<Vec<HighlightedRange>> {
9696
let mut highlights = Vec::new();
9797
let body = body?;
98-
walk(&body, &mut |expr| match expr {
98+
body.walk(&mut |expr| match expr {
9999
ast::Expr::ReturnExpr(expr) => {
100100
if let Some(token) = expr.return_token() {
101101
highlights.push(HighlightedRange { access: None, range: token.text_range() });
@@ -120,7 +120,7 @@ fn highlight_exit_points(
120120
};
121121

122122
if let Some(tail) = tail {
123-
for_each_inner_tail(&tail, &mut |tail| {
123+
for_each_tail_expr(&tail, &mut |tail| {
124124
let range = match tail {
125125
ast::Expr::BreakExpr(b) => b
126126
.break_token()
@@ -161,7 +161,7 @@ fn highlight_break_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
161161
label.as_ref().map(|it| it.syntax().text_range()),
162162
);
163163
highlights.extend(range.map(|range| HighlightedRange { access: None, range }));
164-
for_each_break(label, body, &mut |break_| {
164+
for_each_break_expr(label, body, &mut |break_| {
165165
let range = cover_range(
166166
break_.break_token().map(|it| it.text_range()),
167167
break_.lifetime().map(|it| it.syntax().text_range()),
@@ -216,7 +216,7 @@ fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
216216
let mut highlights = Vec::new();
217217
highlights.push(HighlightedRange { access: None, range: async_token?.text_range() });
218218
if let Some(body) = body {
219-
walk(&body, &mut |expr| {
219+
body.walk(&mut |expr| {
220220
if let ast::Expr::AwaitExpr(expr) = expr {
221221
if let Some(token) = expr.await_token() {
222222
highlights
@@ -240,156 +240,6 @@ fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
240240
None
241241
}
242242

243-
/// Preorder walk all the expression's child expressions
244-
fn walk(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
245-
let mut preorder = expr.syntax().preorder();
246-
while let Some(event) = preorder.next() {
247-
let node = match event {
248-
WalkEvent::Enter(node) => node,
249-
WalkEvent::Leave(_) => continue,
250-
};
251-
match ast::Stmt::cast(node.clone()) {
252-
// recursively walk the initializer, skipping potential const pat expressions
253-
// lets statements aren't usually nested too deeply so this is fine to recurse on
254-
Some(ast::Stmt::LetStmt(l)) => {
255-
if let Some(expr) = l.initializer() {
256-
walk(&expr, cb);
257-
}
258-
preorder.skip_subtree();
259-
}
260-
// Don't skip subtree since we want to process the expression child next
261-
Some(ast::Stmt::ExprStmt(_)) => (),
262-
// skip inner items which might have their own expressions
263-
Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
264-
None => {
265-
if let Some(expr) = ast::Expr::cast(node) {
266-
let is_different_context = match &expr {
267-
ast::Expr::EffectExpr(effect) => {
268-
matches!(
269-
effect.effect(),
270-
ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_)
271-
)
272-
}
273-
ast::Expr::ClosureExpr(__) => true,
274-
_ => false,
275-
};
276-
cb(expr);
277-
if is_different_context {
278-
preorder.skip_subtree();
279-
}
280-
} else {
281-
preorder.skip_subtree();
282-
}
283-
}
284-
}
285-
}
286-
}
287-
288-
// FIXME: doesn't account for labeled breaks in labeled blocks
289-
fn for_each_inner_tail(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
290-
match expr {
291-
ast::Expr::BlockExpr(b) => {
292-
if let Some(e) = b.tail_expr() {
293-
for_each_inner_tail(&e, cb);
294-
}
295-
}
296-
ast::Expr::EffectExpr(e) => match e.effect() {
297-
ast::Effect::Label(label) => {
298-
for_each_break(Some(label), e.block_expr(), &mut |b| cb(&ast::Expr::BreakExpr(b)));
299-
if let Some(b) = e.block_expr() {
300-
for_each_inner_tail(&ast::Expr::BlockExpr(b), cb);
301-
}
302-
}
303-
ast::Effect::Unsafe(_) => {
304-
if let Some(e) = e.block_expr().and_then(|b| b.tail_expr()) {
305-
for_each_inner_tail(&e, cb);
306-
}
307-
}
308-
ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_) => cb(expr),
309-
},
310-
ast::Expr::IfExpr(if_) => {
311-
if_.blocks().for_each(|block| for_each_inner_tail(&ast::Expr::BlockExpr(block), cb))
312-
}
313-
ast::Expr::LoopExpr(l) => {
314-
for_each_break(l.label(), l.loop_body(), &mut |b| cb(&ast::Expr::BreakExpr(b)))
315-
}
316-
ast::Expr::MatchExpr(m) => {
317-
if let Some(arms) = m.match_arm_list() {
318-
arms.arms().filter_map(|arm| arm.expr()).for_each(|e| for_each_inner_tail(&e, cb));
319-
}
320-
}
321-
ast::Expr::ArrayExpr(_)
322-
| ast::Expr::AwaitExpr(_)
323-
| ast::Expr::BinExpr(_)
324-
| ast::Expr::BoxExpr(_)
325-
| ast::Expr::BreakExpr(_)
326-
| ast::Expr::CallExpr(_)
327-
| ast::Expr::CastExpr(_)
328-
| ast::Expr::ClosureExpr(_)
329-
| ast::Expr::ContinueExpr(_)
330-
| ast::Expr::FieldExpr(_)
331-
| ast::Expr::ForExpr(_)
332-
| ast::Expr::IndexExpr(_)
333-
| ast::Expr::Literal(_)
334-
| ast::Expr::MacroCall(_)
335-
| ast::Expr::MacroStmts(_)
336-
| ast::Expr::MethodCallExpr(_)
337-
| ast::Expr::ParenExpr(_)
338-
| ast::Expr::PathExpr(_)
339-
| ast::Expr::PrefixExpr(_)
340-
| ast::Expr::RangeExpr(_)
341-
| ast::Expr::RecordExpr(_)
342-
| ast::Expr::RefExpr(_)
343-
| ast::Expr::ReturnExpr(_)
344-
| ast::Expr::TryExpr(_)
345-
| ast::Expr::TupleExpr(_)
346-
| ast::Expr::WhileExpr(_)
347-
| ast::Expr::YieldExpr(_) => cb(expr),
348-
}
349-
}
350-
351-
fn for_each_break(
352-
label: Option<ast::Label>,
353-
body: Option<ast::BlockExpr>,
354-
cb: &mut dyn FnMut(ast::BreakExpr),
355-
) {
356-
let label = label.and_then(|lbl| lbl.lifetime());
357-
let mut depth = 0;
358-
if let Some(b) = body {
359-
let preorder = &mut b.syntax().preorder();
360-
let ev_as_expr = |ev| match ev {
361-
WalkEvent::Enter(it) => Some(WalkEvent::Enter(ast::Expr::cast(it)?)),
362-
WalkEvent::Leave(it) => Some(WalkEvent::Leave(ast::Expr::cast(it)?)),
363-
};
364-
let eq_label = |lt: Option<ast::Lifetime>| {
365-
lt.zip(label.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text())
366-
};
367-
while let Some(node) = preorder.find_map(ev_as_expr) {
368-
match node {
369-
WalkEvent::Enter(expr) => match expr {
370-
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
371-
depth += 1
372-
}
373-
ast::Expr::EffectExpr(e) if e.label().is_some() => depth += 1,
374-
ast::Expr::BreakExpr(b)
375-
if (depth == 0 && b.lifetime().is_none()) || eq_label(b.lifetime()) =>
376-
{
377-
cb(b);
378-
}
379-
_ => (),
380-
},
381-
WalkEvent::Leave(expr) => match expr {
382-
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
383-
depth -= 1
384-
}
385-
ast::Expr::EffectExpr(e) if e.label().is_some() => depth -= 1,
386-
_ => (),
387-
},
388-
}
389-
}
390-
}
391-
}
392-
393243
fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange> {
394244
match (r0, r1) {
395245
(Some(r0), Some(r1)) => Some(r0.cover(r1)),

0 commit comments

Comments
 (0)