Skip to content

Commit 8848186

Browse files
author
Moritz Vetter
committed
fix(11422): have two different funuctions - one for iterating breaks, one for iteraating breaks and continues
1 parent 3da0807 commit 8848186

File tree

2 files changed

+104
-49
lines changed

2 files changed

+104
-49
lines changed

crates/ide_db/src/helpers.rs

Lines changed: 100 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics};
1616
use itertools::Itertools;
1717
use syntax::{
1818
ast::{self, make, HasLoopBody},
19-
AstNode, AstToken, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
19+
AstNode, AstToken, Preorder, RustLanguage, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent,
20+
T,
2021
};
2122

2223
use crate::{defs::Definition, RootDatabase};
@@ -120,7 +121,9 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
120121
) => return cb(expr),
121122

122123
Some(ast::BlockModifier::Label(label)) => {
123-
for_each_break_and_continue_expr(Some(label), b.stmt_list(), &mut |b| cb(&b));
124+
for_each_break_expr(Some(label), b.stmt_list(), &mut |b| {
125+
cb(&ast::Expr::BreakExpr(b))
126+
});
124127
}
125128
Some(ast::BlockModifier::Unsafe(_)) => (),
126129
None => (),
@@ -147,16 +150,14 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
147150
}
148151
}
149152
}
150-
ast::Expr::LoopExpr(l) => for_each_break_and_continue_expr(
151-
l.label(),
152-
l.loop_body().and_then(|it| it.stmt_list()),
153-
&mut |b| cb(&b),
154-
),
153+
ast::Expr::LoopExpr(l) => {
154+
for_each_break_expr(l.label(), l.loop_body().and_then(|it| it.stmt_list()), &mut |b| {
155+
cb(&ast::Expr::BreakExpr(b))
156+
})
157+
}
155158
ast::Expr::MatchExpr(m) => {
156159
if let Some(arms) = m.match_arm_list() {
157-
arms.arms()
158-
.filter_map(|arm| arm.expr())
159-
.for_each(|e| for_each_tail_expr(&e, &mut |b| cb(&b)));
160+
arms.arms().filter_map(|arm| arm.expr()).for_each(|e| for_each_tail_expr(&e, cb));
160161
}
161162
}
162163
ast::Expr::ArrayExpr(_)
@@ -190,51 +191,104 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
190191
}
191192
}
192193

193-
/// Calls `cb` on each break expr and continue expr inside of `body` that is applicable for the given label.
194194
pub fn for_each_break_and_continue_expr(
195195
label: Option<ast::Label>,
196196
body: Option<ast::StmtList>,
197197
cb: &mut dyn FnMut(ast::Expr),
198198
) {
199199
let label = label.and_then(|lbl| lbl.lifetime());
200-
let mut depth = 0;
201200
if let Some(b) = body {
202-
let preorder = &mut b.syntax().preorder();
203-
let ev_as_expr = |ev| match ev {
204-
WalkEvent::Enter(it) => Some(WalkEvent::Enter(ast::Expr::cast(it)?)),
205-
WalkEvent::Leave(it) => Some(WalkEvent::Leave(ast::Expr::cast(it)?)),
206-
};
207-
let eq_label = |lt: Option<ast::Lifetime>| {
208-
lt.zip(label.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text())
209-
};
210-
while let Some(node) = preorder.find_map(ev_as_expr) {
211-
match node {
212-
WalkEvent::Enter(expr) => match expr {
213-
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
214-
depth += 1
215-
}
216-
ast::Expr::BlockExpr(e) if e.label().is_some() => depth += 1,
217-
ast::Expr::BreakExpr(b)
218-
if (depth == 0 && b.lifetime().is_none()) || eq_label(b.lifetime()) =>
219-
{
220-
cb(ast::Expr::BreakExpr(b));
221-
}
222-
ast::Expr::ContinueExpr(c)
223-
if (depth == 0 && c.lifetime().is_none()) || eq_label(c.lifetime()) =>
224-
{
225-
cb(ast::Expr::ContinueExpr(c))
226-
}
227-
_ => (),
228-
},
229-
WalkEvent::Leave(expr) => match expr {
230-
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
231-
depth -= 1
232-
}
233-
ast::Expr::BlockExpr(e) if e.label().is_some() => depth -= 1,
234-
_ => (),
235-
},
201+
let tree_depth_iterator = TreeWithDepthIterator::new(b);
202+
for (expr, depth) in tree_depth_iterator {
203+
match expr {
204+
ast::Expr::BreakExpr(b)
205+
if (depth == 0 && b.lifetime().is_none())
206+
|| eq_label_lt(&label, &b.lifetime()) =>
207+
{
208+
cb(ast::Expr::BreakExpr(b));
209+
}
210+
ast::Expr::ContinueExpr(c)
211+
if (depth == 0 && c.lifetime().is_none())
212+
|| eq_label_lt(&label, &c.lifetime()) =>
213+
{
214+
cb(ast::Expr::ContinueExpr(c));
215+
}
216+
_ => (),
217+
}
218+
}
219+
}
220+
}
221+
222+
fn for_each_break_expr(
223+
label: Option<ast::Label>,
224+
body: Option<ast::StmtList>,
225+
cb: &mut dyn FnMut(ast::BreakExpr),
226+
) {
227+
let label = label.and_then(|lbl| lbl.lifetime());
228+
if let Some(b) = body {
229+
let tree_depth_iterator = TreeWithDepthIterator::new(b);
230+
for (expr, depth) in tree_depth_iterator {
231+
match expr {
232+
ast::Expr::BreakExpr(b)
233+
if (depth == 0 && b.lifetime().is_none())
234+
|| eq_label_lt(&label, &b.lifetime()) =>
235+
{
236+
cb(b);
237+
}
238+
_ => (),
239+
}
240+
}
241+
}
242+
}
243+
244+
fn eq_label_lt(lt1: &Option<ast::Lifetime>, lt2: &Option<ast::Lifetime>) -> bool {
245+
lt1.as_ref().zip(lt2.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text())
246+
}
247+
248+
struct TreeWithDepthIterator {
249+
preorder: Preorder<RustLanguage>,
250+
depth: i32,
251+
}
252+
253+
impl TreeWithDepthIterator {
254+
fn new(body: ast::StmtList) -> Self {
255+
let preorder = body.syntax().preorder();
256+
Self { preorder, depth: 0 }
257+
}
258+
}
259+
260+
impl<'a> Iterator for TreeWithDepthIterator {
261+
type Item = (ast::Expr, i32);
262+
263+
fn next(&mut self) -> Option<Self::Item> {
264+
while let Some((event, expr)) = self.preorder.find_map(|ev| match ev {
265+
WalkEvent::Enter(it) => Some(WalkEvent::Enter(())).zip(ast::Expr::cast(it)),
266+
WalkEvent::Leave(it) => Some(WalkEvent::Leave(())).zip(ast::Expr::cast(it)),
267+
}) {
268+
match (event, expr) {
269+
(
270+
WalkEvent::Enter(_),
271+
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
272+
) => {
273+
self.depth += 1;
274+
}
275+
(
276+
WalkEvent::Leave(_),
277+
ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_),
278+
) => {
279+
self.depth -= 1;
280+
}
281+
(WalkEvent::Enter(_), ast::Expr::BlockExpr(e)) if e.label().is_some() => {
282+
self.depth += 1;
283+
}
284+
(WalkEvent::Leave(_), ast::Expr::BlockExpr(e)) if e.label().is_some() => {
285+
self.depth -= 1;
286+
}
287+
(WalkEvent::Enter(_), expr) => return Some((expr, self.depth)),
288+
_ => (),
236289
}
237290
}
291+
None
238292
}
239293
}
240294

crates/syntax/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ pub use crate::{
5252
ptr::{AstPtr, SyntaxNodePtr},
5353
syntax_error::SyntaxError,
5454
syntax_node::{
55-
PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren,
56-
SyntaxToken, SyntaxTreeBuilder,
55+
PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode,
56+
SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
5757
},
5858
token_text::TokenText,
5959
};
6060
pub use parser::{SyntaxKind, T};
6161
pub use rowan::{
62-
Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
62+
api::Preorder, Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize,
63+
TokenAtOffset, WalkEvent,
6364
};
6465
pub use smol_str::SmolStr;
6566

0 commit comments

Comments
 (0)