@@ -16,7 +16,8 @@ use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics};
16
16
use itertools:: Itertools ;
17
17
use syntax:: {
18
18
ast:: { self , make, HasLoopBody } ,
19
- AstNode , AstToken , SyntaxKind , SyntaxToken , TokenAtOffset , WalkEvent , T ,
19
+ AstNode , AstToken , Preorder , RustLanguage , SyntaxKind , SyntaxToken , TokenAtOffset , WalkEvent ,
20
+ T ,
20
21
} ;
21
22
22
23
use crate :: { defs:: Definition , RootDatabase } ;
@@ -120,7 +121,9 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
120
121
) => return cb ( expr) ,
121
122
122
123
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
+ } ) ;
124
127
}
125
128
Some ( ast:: BlockModifier :: Unsafe ( _) ) => ( ) ,
126
129
None => ( ) ,
@@ -147,16 +150,14 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
147
150
}
148
151
}
149
152
}
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
+ }
155
158
ast:: Expr :: MatchExpr ( m) => {
156
159
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) ) ;
160
161
}
161
162
}
162
163
ast:: Expr :: ArrayExpr ( _)
@@ -190,51 +191,104 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
190
191
}
191
192
}
192
193
193
- /// Calls `cb` on each break expr and continue expr inside of `body` that is applicable for the given label.
194
194
pub fn for_each_break_and_continue_expr (
195
195
label : Option < ast:: Label > ,
196
196
body : Option < ast:: StmtList > ,
197
197
cb : & mut dyn FnMut ( ast:: Expr ) ,
198
198
) {
199
199
let label = label. and_then ( |lbl| lbl. lifetime ( ) ) ;
200
- let mut depth = 0 ;
201
200
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
+ _ => ( ) ,
236
289
}
237
290
}
291
+ None
238
292
}
239
293
}
240
294
0 commit comments