@@ -24,7 +24,7 @@ use syntax::{
24
24
SyntaxNode , TextRange , TextSize , T ,
25
25
} ;
26
26
27
- use crate :: { FilePosition , NavigationTarget , TryToNav } ;
27
+ use crate :: { highlight_related , FilePosition , HighlightedRange , NavigationTarget , TryToNav } ;
28
28
29
29
#[ derive( Debug , Clone ) ]
30
30
pub struct ReferenceSearchResult {
@@ -103,6 +103,11 @@ pub(crate) fn find_all_refs(
103
103
}
104
104
} ;
105
105
106
+ // Find references for control-flow keywords.
107
+ if let Some ( res) = handle_control_flow_keywords ( sema, position) {
108
+ return Some ( vec ! [ res] ) ;
109
+ }
110
+
106
111
match name_for_constructor_search ( & syntax, position) {
107
112
Some ( name) => {
108
113
let def = match NameClass :: classify ( sema, & name) ? {
@@ -296,6 +301,34 @@ fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {
296
301
} ) . unwrap_or ( false )
297
302
}
298
303
304
+ fn handle_control_flow_keywords (
305
+ sema : & Semantics < ' _ , RootDatabase > ,
306
+ FilePosition { file_id, offset } : FilePosition ,
307
+ ) -> Option < ReferenceSearchResult > {
308
+ let file = sema. parse ( file_id) ;
309
+ let token = file. syntax ( ) . token_at_offset ( offset) . find ( |t| t. kind ( ) . is_keyword ( ) ) ?;
310
+
311
+ let refs = match token. kind ( ) {
312
+ T ! [ fn ] | T ! [ return ] | T ! [ try] => highlight_related:: highlight_exit_points ( sema, token) ?,
313
+ T ! [ async ] => highlight_related:: highlight_yield_points ( sema, token) ?,
314
+ T ! [ loop ] | T ! [ while ] | T ! [ break ] | T ! [ continue ] => {
315
+ highlight_related:: highlight_break_points ( sema, token) ?
316
+ }
317
+ T ! [ for ] if token. parent ( ) . and_then ( ast:: ForExpr :: cast) . is_some ( ) => {
318
+ highlight_related:: highlight_break_points ( sema, token) ?
319
+ }
320
+ _ => return None ,
321
+ }
322
+ . into_iter ( )
323
+ . map ( |HighlightedRange { range, category } | ( range, category) )
324
+ . collect ( ) ;
325
+
326
+ Some ( ReferenceSearchResult {
327
+ declaration : None ,
328
+ references : IntMap :: from_iter ( [ ( file_id, refs) ] ) ,
329
+ } )
330
+ }
331
+
299
332
#[ cfg( test) ]
300
333
mod tests {
301
334
use expect_test:: { expect, Expect } ;
@@ -2187,4 +2220,223 @@ fn test() {
2187
2220
"# ] ] ,
2188
2221
) ;
2189
2222
}
2223
+
2224
+ #[ test]
2225
+ fn goto_ref_fn_kw ( ) {
2226
+ check (
2227
+ r#"
2228
+ macro_rules! N {
2229
+ ($i:ident, $x:expr, $blk:expr) => {
2230
+ for $i in 0..$x {
2231
+ $blk
2232
+ }
2233
+ };
2234
+ }
2235
+
2236
+ fn main() {
2237
+ $0fn f() {
2238
+ N!(i, 5, {
2239
+ println!("{}", i);
2240
+ return;
2241
+ });
2242
+
2243
+ for i in 1..5 {
2244
+ return;
2245
+ }
2246
+
2247
+ (|| {
2248
+ return;
2249
+ })();
2250
+ }
2251
+ }
2252
+ "# ,
2253
+ expect ! [ [ r#"
2254
+ FileId(0) 136..138
2255
+ FileId(0) 207..213
2256
+ FileId(0) 264..270
2257
+ "# ] ] ,
2258
+ )
2259
+ }
2260
+
2261
+ #[ test]
2262
+ fn goto_ref_exit_points ( ) {
2263
+ check (
2264
+ r#"
2265
+ fn$0 foo() -> u32 {
2266
+ if true {
2267
+ return 0;
2268
+ }
2269
+
2270
+ 0?;
2271
+ 0xDEAD_BEEF
2272
+ }
2273
+ "# ,
2274
+ expect ! [ [ r#"
2275
+ FileId(0) 0..2
2276
+ FileId(0) 62..63
2277
+ FileId(0) 40..46
2278
+ FileId(0) 69..80
2279
+ "# ] ] ,
2280
+ ) ;
2281
+ }
2282
+
2283
+ #[ test]
2284
+ fn test_ref_yield_points ( ) {
2285
+ check (
2286
+ r#"
2287
+ pub async$0 fn foo() {
2288
+ let x = foo()
2289
+ .await
2290
+ .await;
2291
+ || { 0.await };
2292
+ (async { 0.await }).await
2293
+ }
2294
+ "# ,
2295
+ expect ! [ [ r#"
2296
+ FileId(0) 4..9
2297
+ FileId(0) 63..68
2298
+ FileId(0) 48..53
2299
+ FileId(0) 114..119
2300
+ "# ] ] ,
2301
+ ) ;
2302
+ }
2303
+
2304
+ #[ test]
2305
+ fn goto_ref_for_kw ( ) {
2306
+ check (
2307
+ r#"
2308
+ fn main() {
2309
+ $0for i in 1..5 {
2310
+ break;
2311
+ continue;
2312
+ }
2313
+ }
2314
+ "# ,
2315
+ expect ! [ [ r#"
2316
+ FileId(0) 16..19
2317
+ FileId(0) 40..45
2318
+ FileId(0) 55..63
2319
+ "# ] ] ,
2320
+ )
2321
+ }
2322
+
2323
+ #[ test]
2324
+ fn goto_ref_on_break_kw ( ) {
2325
+ check (
2326
+ r#"
2327
+ fn main() {
2328
+ for i in 1..5 {
2329
+ $0break;
2330
+ continue;
2331
+ }
2332
+ }
2333
+ "# ,
2334
+ expect ! [ [ r#"
2335
+ FileId(0) 16..19
2336
+ FileId(0) 40..45
2337
+ "# ] ] ,
2338
+ )
2339
+ }
2340
+
2341
+ #[ test]
2342
+ fn goto_ref_on_break_kw_for_block ( ) {
2343
+ check (
2344
+ r#"
2345
+ fn main() {
2346
+ 'a:{
2347
+ $0break 'a;
2348
+ }
2349
+ }
2350
+ "# ,
2351
+ expect ! [ [ r#"
2352
+ FileId(0) 16..19
2353
+ FileId(0) 29..37
2354
+ "# ] ] ,
2355
+ )
2356
+ }
2357
+
2358
+ #[ test]
2359
+ fn goto_ref_on_break_with_label ( ) {
2360
+ check (
2361
+ r#"
2362
+ fn foo() {
2363
+ 'outer: loop {
2364
+ break;
2365
+ 'inner: loop {
2366
+ 'innermost: loop {
2367
+ }
2368
+ $0break 'outer;
2369
+ break;
2370
+ }
2371
+ break;
2372
+ }
2373
+ }
2374
+ "# ,
2375
+ expect ! [ [ r#"
2376
+ FileId(0) 15..27
2377
+ FileId(0) 39..44
2378
+ FileId(0) 127..139
2379
+ FileId(0) 178..183
2380
+ "# ] ] ,
2381
+ ) ;
2382
+ }
2383
+
2384
+ #[ test]
2385
+ fn goto_ref_on_return_in_try ( ) {
2386
+ check (
2387
+ r#"
2388
+ fn main() {
2389
+ fn f() {
2390
+ try {
2391
+ $0return;
2392
+ }
2393
+
2394
+ return;
2395
+ }
2396
+ return;
2397
+ }
2398
+ "# ,
2399
+ expect ! [ [ r#"
2400
+ FileId(0) 16..18
2401
+ FileId(0) 51..57
2402
+ FileId(0) 78..84
2403
+ "# ] ] ,
2404
+ )
2405
+ }
2406
+
2407
+ #[ test]
2408
+ fn goto_ref_on_break_in_try ( ) {
2409
+ check (
2410
+ r#"
2411
+ fn main() {
2412
+ for i in 1..100 {
2413
+ let x: Result<(), ()> = try {
2414
+ $0break;
2415
+ };
2416
+ }
2417
+ }
2418
+ "# ,
2419
+ expect ! [ [ r#"
2420
+ FileId(0) 16..19
2421
+ FileId(0) 84..89
2422
+ "# ] ] ,
2423
+ )
2424
+ }
2425
+
2426
+ #[ test]
2427
+ fn goto_ref_on_return_in_async_block ( ) {
2428
+ check (
2429
+ r#"
2430
+ fn main() {
2431
+ $0async {
2432
+ return;
2433
+ }
2434
+ }
2435
+ "# ,
2436
+ expect ! [ [ r#"
2437
+ FileId(0) 16..21
2438
+ FileId(0) 32..38
2439
+ "# ] ] ,
2440
+ )
2441
+ }
2190
2442
}
0 commit comments