]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/highlight_related.rs
fix: Fix search for associated trait items being inconsistent
[rust.git] / crates / ide / src / highlight_related.rs
1 use hir::Semantics;
2 use ide_db::{
3     base_db::{FileId, FilePosition},
4     defs::{Definition, IdentClass},
5     helpers::pick_best_token,
6     search::{FileReference, ReferenceCategory, SearchScope},
7     syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr},
8     FxHashSet, RootDatabase,
9 };
10 use syntax::{
11     ast::{self, HasLoopBody},
12     match_ast, AstNode,
13     SyntaxKind::{self, IDENT, INT_NUMBER},
14     SyntaxNode, SyntaxToken, TextRange, T,
15 };
16
17 use crate::{references, NavigationTarget, TryToNav};
18
19 #[derive(PartialEq, Eq, Hash)]
20 pub struct HighlightedRange {
21     pub range: TextRange,
22     // FIXME: This needs to be more precise. Reference category makes sense only
23     // for references, but we also have defs. And things like exit points are
24     // neither.
25     pub category: Option<ReferenceCategory>,
26 }
27
28 #[derive(Default, Clone)]
29 pub struct HighlightRelatedConfig {
30     pub references: bool,
31     pub exit_points: bool,
32     pub break_points: bool,
33     pub yield_points: bool,
34 }
35
36 // Feature: Highlight Related
37 //
38 // Highlights constructs related to the thing under the cursor:
39 //
40 // . if on an identifier, highlights all references to that identifier in the current file
41 // . if on an `async` or `await token, highlights all yield points for that async context
42 // . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
43 // . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
44 //
45 // Note: `?` and `->` do not currently trigger this behavior in the VSCode editor.
46 pub(crate) fn highlight_related(
47     sema: &Semantics<RootDatabase>,
48     config: HighlightRelatedConfig,
49     FilePosition { offset, file_id }: FilePosition,
50 ) -> Option<Vec<HighlightedRange>> {
51     let _p = profile::span("highlight_related");
52     let syntax = sema.parse(file_id).syntax().clone();
53
54     let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
55         T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
56         T![->] => 3,
57         kind if kind.is_keyword() => 2,
58         IDENT | INT_NUMBER => 1,
59         _ => 0,
60     })?;
61     match token.kind() {
62         T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => {
63             highlight_exit_points(sema, token)
64         }
65         T![fn] | T![return] | T![->] if config.exit_points => highlight_exit_points(sema, token),
66         T![await] | T![async] if config.yield_points => highlight_yield_points(token),
67         T![for] if config.break_points && token.parent().and_then(ast::ForExpr::cast).is_some() => {
68             highlight_break_points(token)
69         }
70         T![break] | T![loop] | T![while] | T![continue] if config.break_points => {
71             highlight_break_points(token)
72         }
73         _ if config.references => highlight_references(sema, &syntax, token, file_id),
74         _ => None,
75     }
76 }
77
78 fn highlight_references(
79     sema: &Semantics<RootDatabase>,
80     node: &SyntaxNode,
81     token: SyntaxToken,
82     file_id: FileId,
83 ) -> Option<Vec<HighlightedRange>> {
84     let defs = find_defs(sema, token);
85     let usages = defs
86         .iter()
87         .filter_map(|&d| {
88             d.usages(sema)
89                 .set_scope(Some(SearchScope::single_file(file_id)))
90                 .include_self_refs()
91                 .all()
92                 .references
93                 .remove(&file_id)
94         })
95         .flatten()
96         .map(|FileReference { category: access, range, .. }| HighlightedRange {
97             range,
98             category: access,
99         });
100     let mut res = FxHashSet::default();
101
102     let mut def_to_hl_range = |def| {
103         let hl_range = match def {
104             Definition::Module(module) => {
105                 Some(NavigationTarget::from_module_to_decl(sema.db, module))
106             }
107             def => def.try_to_nav(sema.db),
108         }
109         .filter(|decl| decl.file_id == file_id)
110         .and_then(|decl| decl.focus_range)
111         .map(|range| {
112             let category =
113                 references::decl_mutability(&def, node, range).then(|| ReferenceCategory::Write);
114             HighlightedRange { range, category }
115         });
116         if let Some(hl_range) = hl_range {
117             res.insert(hl_range);
118         }
119     };
120     for &def in &defs {
121         match def {
122             Definition::Local(local) => local
123                 .associated_locals(sema.db)
124                 .iter()
125                 .for_each(|&local| def_to_hl_range(Definition::Local(local))),
126             def => def_to_hl_range(def),
127         }
128     }
129
130     res.extend(usages);
131     if res.is_empty() {
132         None
133     } else {
134         Some(res.into_iter().collect())
135     }
136 }
137
138 fn highlight_exit_points(
139     sema: &Semantics<RootDatabase>,
140     token: SyntaxToken,
141 ) -> Option<Vec<HighlightedRange>> {
142     fn hl(
143         sema: &Semantics<RootDatabase>,
144         body: Option<ast::Expr>,
145     ) -> Option<Vec<HighlightedRange>> {
146         let mut highlights = Vec::new();
147         let body = body?;
148         walk_expr(&body, &mut |expr| match expr {
149             ast::Expr::ReturnExpr(expr) => {
150                 if let Some(token) = expr.return_token() {
151                     highlights.push(HighlightedRange { category: None, range: token.text_range() });
152                 }
153             }
154             ast::Expr::TryExpr(try_) => {
155                 if let Some(token) = try_.question_mark_token() {
156                     highlights.push(HighlightedRange { category: None, range: token.text_range() });
157                 }
158             }
159             ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => {
160                 if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) {
161                     highlights.push(HighlightedRange {
162                         category: None,
163                         range: expr.syntax().text_range(),
164                     });
165                 }
166             }
167             _ => (),
168         });
169         let tail = match body {
170             ast::Expr::BlockExpr(b) => b.tail_expr(),
171             e => Some(e),
172         };
173
174         if let Some(tail) = tail {
175             for_each_tail_expr(&tail, &mut |tail| {
176                 let range = match tail {
177                     ast::Expr::BreakExpr(b) => b
178                         .break_token()
179                         .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
180                     _ => tail.syntax().text_range(),
181                 };
182                 highlights.push(HighlightedRange { category: None, range })
183             });
184         }
185         Some(highlights)
186     }
187     for anc in token.parent_ancestors() {
188         return match_ast! {
189             match anc {
190                 ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
191                 ast::ClosureExpr(closure) => hl(sema, closure.body()),
192                 ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
193                     hl(sema, Some(block_expr.into()))
194                 } else {
195                     continue;
196                 },
197                 _ => continue,
198             }
199         };
200     }
201     None
202 }
203
204 fn highlight_break_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
205     fn hl(
206         cursor_token_kind: SyntaxKind,
207         token: Option<SyntaxToken>,
208         label: Option<ast::Label>,
209         body: Option<ast::StmtList>,
210     ) -> Option<Vec<HighlightedRange>> {
211         let mut highlights = Vec::new();
212         let range = cover_range(
213             token.map(|tok| tok.text_range()),
214             label.as_ref().map(|it| it.syntax().text_range()),
215         );
216         highlights.extend(range.map(|range| HighlightedRange { category: None, range }));
217         for_each_break_and_continue_expr(label, body, &mut |expr| {
218             let range: Option<TextRange> = match (cursor_token_kind, expr) {
219                 (T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => {
220                     cover_range(
221                         break_.break_token().map(|it| it.text_range()),
222                         break_.lifetime().map(|it| it.syntax().text_range()),
223                     )
224                 }
225                 (
226                     T![for] | T![while] | T![loop] | T![continue],
227                     ast::Expr::ContinueExpr(continue_),
228                 ) => cover_range(
229                     continue_.continue_token().map(|it| it.text_range()),
230                     continue_.lifetime().map(|it| it.syntax().text_range()),
231                 ),
232                 _ => None,
233             };
234             highlights.extend(range.map(|range| HighlightedRange { category: None, range }));
235         });
236         Some(highlights)
237     }
238     let parent = token.parent()?;
239     let lbl = match_ast! {
240         match parent {
241             ast::BreakExpr(b) => b.lifetime(),
242             ast::ContinueExpr(c) => c.lifetime(),
243             ast::LoopExpr(l) => l.label().and_then(|it| it.lifetime()),
244             ast::ForExpr(f) => f.label().and_then(|it| it.lifetime()),
245             ast::WhileExpr(w) => w.label().and_then(|it| it.lifetime()),
246             ast::BlockExpr(b) => Some(b.label().and_then(|it| it.lifetime())?),
247             _ => return None,
248         }
249     };
250     let lbl = lbl.as_ref();
251     let label_matches = |def_lbl: Option<ast::Label>| match lbl {
252         Some(lbl) => {
253             Some(lbl.text()) == def_lbl.and_then(|it| it.lifetime()).as_ref().map(|it| it.text())
254         }
255         None => true,
256     };
257     let token_kind = token.kind();
258     for anc in token.parent_ancestors().flat_map(ast::Expr::cast) {
259         return match anc {
260             ast::Expr::LoopExpr(l) if label_matches(l.label()) => hl(
261                 token_kind,
262                 l.loop_token(),
263                 l.label(),
264                 l.loop_body().and_then(|it| it.stmt_list()),
265             ),
266             ast::Expr::ForExpr(f) if label_matches(f.label()) => hl(
267                 token_kind,
268                 f.for_token(),
269                 f.label(),
270                 f.loop_body().and_then(|it| it.stmt_list()),
271             ),
272             ast::Expr::WhileExpr(w) if label_matches(w.label()) => hl(
273                 token_kind,
274                 w.while_token(),
275                 w.label(),
276                 w.loop_body().and_then(|it| it.stmt_list()),
277             ),
278             ast::Expr::BlockExpr(e) if e.label().is_some() && label_matches(e.label()) => {
279                 hl(token_kind, None, e.label(), e.stmt_list())
280             }
281             _ => continue,
282         };
283     }
284     None
285 }
286
287 fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
288     fn hl(
289         async_token: Option<SyntaxToken>,
290         body: Option<ast::Expr>,
291     ) -> Option<Vec<HighlightedRange>> {
292         let mut highlights =
293             vec![HighlightedRange { category: None, range: async_token?.text_range() }];
294         if let Some(body) = body {
295             walk_expr(&body, &mut |expr| {
296                 if let ast::Expr::AwaitExpr(expr) = expr {
297                     if let Some(token) = expr.await_token() {
298                         highlights
299                             .push(HighlightedRange { category: None, range: token.text_range() });
300                     }
301                 }
302             });
303         }
304         Some(highlights)
305     }
306     for anc in token.parent_ancestors() {
307         return match_ast! {
308             match anc {
309                 ast::Fn(fn_) => hl(fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)),
310                 ast::BlockExpr(block_expr) => {
311                     if block_expr.async_token().is_none() {
312                         continue;
313                     }
314                     hl(block_expr.async_token(), Some(block_expr.into()))
315                 },
316                 ast::ClosureExpr(closure) => hl(closure.async_token(), closure.body()),
317                 _ => continue,
318             }
319         };
320     }
321     None
322 }
323
324 fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange> {
325     match (r0, r1) {
326         (Some(r0), Some(r1)) => Some(r0.cover(r1)),
327         (Some(range), None) => Some(range),
328         (None, Some(range)) => Some(range),
329         (None, None) => None,
330     }
331 }
332
333 fn find_defs(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
334     sema.descend_into_macros(token)
335         .into_iter()
336         .filter_map(|token| IdentClass::classify_token(sema, &token).map(IdentClass::definitions))
337         .flatten()
338         .collect()
339 }
340
341 #[cfg(test)]
342 mod tests {
343     use crate::fixture;
344
345     use super::*;
346
347     #[track_caller]
348     fn check(ra_fixture: &str) {
349         let config = HighlightRelatedConfig {
350             break_points: true,
351             exit_points: true,
352             references: true,
353             yield_points: true,
354         };
355
356         check_with_config(ra_fixture, config);
357     }
358
359     #[track_caller]
360     fn check_with_config(ra_fixture: &str, config: HighlightRelatedConfig) {
361         let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
362
363         let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
364
365         let mut expected = annotations
366             .into_iter()
367             .map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
368             .collect::<Vec<_>>();
369
370         let mut actual = hls
371             .into_iter()
372             .map(|hl| {
373                 (
374                     hl.range,
375                     hl.category.map(|it| {
376                         match it {
377                             ReferenceCategory::Read => "read",
378                             ReferenceCategory::Write => "write",
379                         }
380                         .to_string()
381                     }),
382                 )
383             })
384             .collect::<Vec<_>>();
385         actual.sort_by_key(|(range, _)| range.start());
386         expected.sort_by_key(|(range, _)| range.start());
387
388         assert_eq!(expected, actual);
389     }
390
391     #[test]
392     fn test_hl_tuple_fields() {
393         check(
394             r#"
395 struct Tuple(u32, u32);
396
397 fn foo(t: Tuple) {
398     t.0$0;
399    // ^ read
400     t.0;
401    // ^ read
402 }
403 "#,
404         );
405     }
406
407     #[test]
408     fn test_hl_module() {
409         check(
410             r#"
411 //- /lib.rs
412 mod foo$0;
413  // ^^^
414 //- /foo.rs
415 struct Foo;
416 "#,
417         );
418     }
419
420     #[test]
421     fn test_hl_self_in_crate_root() {
422         check(
423             r#"
424 use crate$0;
425   //^^^^^
426 use self;
427   //^^^^
428 mod __ {
429     use super;
430       //^^^^^
431 }
432 "#,
433         );
434         check(
435             r#"
436 //- /main.rs crate:main deps:lib
437 use lib$0;
438   //^^^
439 //- /lib.rs crate:lib
440 "#,
441         );
442     }
443
444     #[test]
445     fn test_hl_self_in_module() {
446         check(
447             r#"
448 //- /lib.rs
449 mod foo;
450 //- /foo.rs
451 use self$0;
452  // ^^^^
453 "#,
454         );
455     }
456
457     #[test]
458     fn test_hl_local() {
459         check(
460             r#"
461 fn foo() {
462     let mut bar = 3;
463          // ^^^ write
464     bar$0;
465  // ^^^ read
466 }
467 "#,
468         );
469     }
470
471     #[test]
472     fn test_hl_local_in_attr() {
473         check(
474             r#"
475 //- proc_macros: identity
476 #[proc_macros::identity]
477 fn foo() {
478     let mut bar = 3;
479          // ^^^ write
480     bar$0;
481  // ^^^ read
482 }
483 "#,
484         );
485     }
486
487     #[test]
488     fn test_multi_macro_usage() {
489         check(
490             r#"
491 macro_rules! foo {
492     ($ident:ident) => {
493         fn $ident() -> $ident { loop {} }
494         struct $ident;
495     }
496 }
497
498 foo!(bar$0);
499   // ^^^
500 fn foo() {
501     let bar: bar = bar();
502           // ^^^
503                 // ^^^
504 }
505 "#,
506         );
507         check(
508             r#"
509 macro_rules! foo {
510     ($ident:ident) => {
511         fn $ident() -> $ident { loop {} }
512         struct $ident;
513     }
514 }
515
516 foo!(bar);
517   // ^^^
518 fn foo() {
519     let bar: bar$0 = bar();
520           // ^^^
521 }
522 "#,
523         );
524     }
525
526     #[test]
527     fn test_hl_yield_points() {
528         check(
529             r#"
530 pub async fn foo() {
531  // ^^^^^
532     let x = foo()
533         .await$0
534       // ^^^^^
535         .await;
536       // ^^^^^
537     || { 0.await };
538     (async { 0.await }).await
539                      // ^^^^^
540 }
541 "#,
542         );
543     }
544
545     #[test]
546     fn test_hl_yield_points2() {
547         check(
548             r#"
549 pub async$0 fn foo() {
550  // ^^^^^
551     let x = foo()
552         .await
553       // ^^^^^
554         .await;
555       // ^^^^^
556     || { 0.await };
557     (async { 0.await }).await
558                      // ^^^^^
559 }
560 "#,
561         );
562     }
563
564     #[test]
565     fn test_hl_yield_nested_fn() {
566         check(
567             r#"
568 async fn foo() {
569     async fn foo2() {
570  // ^^^^^
571         async fn foo3() {
572             0.await
573         }
574         0.await$0
575        // ^^^^^
576     }
577     0.await
578 }
579 "#,
580         );
581     }
582
583     #[test]
584     fn test_hl_yield_nested_async_blocks() {
585         check(
586             r#"
587 async fn foo() {
588     (async {
589   // ^^^^^
590         (async {
591            0.await
592         }).await$0 }
593         // ^^^^^
594     ).await;
595 }
596 "#,
597         );
598     }
599
600     #[test]
601     fn test_hl_exit_points() {
602         check(
603             r#"
604 fn foo() -> u32 {
605     if true {
606         return$0 0;
607      // ^^^^^^
608     }
609
610     0?;
611   // ^
612     0xDEAD_BEEF
613  // ^^^^^^^^^^^
614 }
615 "#,
616         );
617     }
618
619     #[test]
620     fn test_hl_exit_points2() {
621         check(
622             r#"
623 fn foo() ->$0 u32 {
624     if true {
625         return 0;
626      // ^^^^^^
627     }
628
629     0?;
630   // ^
631     0xDEAD_BEEF
632  // ^^^^^^^^^^^
633 }
634 "#,
635         );
636     }
637
638     #[test]
639     fn test_hl_exit_points3() {
640         check(
641             r#"
642 fn$0 foo() -> u32 {
643     if true {
644         return 0;
645      // ^^^^^^
646     }
647
648     0?;
649   // ^
650     0xDEAD_BEEF
651  // ^^^^^^^^^^^
652 }
653 "#,
654         );
655     }
656
657     #[test]
658     fn test_hl_prefer_ref_over_tail_exit() {
659         check(
660             r#"
661 fn foo() -> u32 {
662 // ^^^
663     if true {
664         return 0;
665     }
666
667     0?;
668
669     foo$0()
670  // ^^^
671 }
672 "#,
673         );
674     }
675
676     #[test]
677     fn test_hl_never_call_is_exit_point() {
678         check(
679             r#"
680 struct Never;
681 impl Never {
682     fn never(self) -> ! { loop {} }
683 }
684 macro_rules! never {
685     () => { never() }
686 }
687 fn never() -> ! { loop {} }
688 fn foo() ->$0 u32 {
689     never();
690  // ^^^^^^^
691     never!();
692  // ^^^^^^^^
693
694     Never.never();
695  // ^^^^^^^^^^^^^
696
697     0
698  // ^
699 }
700 "#,
701         );
702     }
703
704     #[test]
705     fn test_hl_inner_tail_exit_points() {
706         check(
707             r#"
708 fn foo() ->$0 u32 {
709     if true {
710         unsafe {
711             return 5;
712          // ^^^^^^
713             5
714          // ^
715         }
716     } else if false {
717         0
718      // ^
719     } else {
720         match 5 {
721             6 => 100,
722               // ^^^
723             7 => loop {
724                 break 5;
725              // ^^^^^
726             }
727             8 => 'a: loop {
728                 'b: loop {
729                     break 'a 5;
730                  // ^^^^^
731                     break 'b 5;
732                     break 5;
733                 };
734             }
735             //
736             _ => 500,
737               // ^^^
738         }
739     }
740 }
741 "#,
742         );
743     }
744
745     #[test]
746     fn test_hl_inner_tail_exit_points_labeled_block() {
747         check(
748             r#"
749 fn foo() ->$0 u32 {
750     'foo: {
751         break 'foo 0;
752      // ^^^^^
753         loop {
754             break;
755             break 'foo 0;
756          // ^^^^^
757         }
758         0
759      // ^
760     }
761 }
762 "#,
763         );
764     }
765
766     #[test]
767     fn test_hl_break_loop() {
768         check(
769             r#"
770 fn foo() {
771     'outer: loop {
772  // ^^^^^^^^^^^^
773          break;
774       // ^^^^^
775          'inner: loop {
776             break;
777             'innermost: loop {
778                 break 'outer;
779              // ^^^^^^^^^^^^
780                 break 'inner;
781             }
782             break$0 'outer;
783          // ^^^^^^^^^^^^
784             break;
785         }
786         break;
787      // ^^^^^
788     }
789 }
790 "#,
791         );
792     }
793
794     #[test]
795     fn test_hl_break_loop2() {
796         check(
797             r#"
798 fn foo() {
799     'outer: loop {
800         break;
801         'inner: loop {
802      // ^^^^^^^^^^^^
803             break;
804          // ^^^^^
805             'innermost: loop {
806                 break 'outer;
807                 break 'inner;
808              // ^^^^^^^^^^^^
809             }
810             break 'outer;
811             break$0;
812          // ^^^^^
813         }
814         break;
815     }
816 }
817 "#,
818         );
819     }
820
821     #[test]
822     fn test_hl_break_for() {
823         check(
824             r#"
825 fn foo() {
826     'outer: for _ in () {
827  // ^^^^^^^^^^^
828          break;
829       // ^^^^^
830          'inner: for _ in () {
831             break;
832             'innermost: for _ in () {
833                 break 'outer;
834              // ^^^^^^^^^^^^
835                 break 'inner;
836             }
837             break$0 'outer;
838          // ^^^^^^^^^^^^
839             break;
840         }
841         break;
842      // ^^^^^
843     }
844 }
845 "#,
846         );
847     }
848
849     #[test]
850     fn test_hl_break_for_but_not_continue() {
851         check(
852             r#"
853 fn foo() {
854     'outer: for _ in () {
855  // ^^^^^^^^^^^
856         break;
857      // ^^^^^
858         continue;
859         'inner: for _ in () {
860             break;
861             continue;
862             'innermost: for _ in () {
863                 continue 'outer;
864                 break 'outer;
865              // ^^^^^^^^^^^^
866                 continue 'inner;
867                 break 'inner;
868             }
869             break$0 'outer;
870          // ^^^^^^^^^^^^
871             continue 'outer;
872             break;
873             continue;
874         }
875         break;
876      // ^^^^^
877         continue;
878     }
879 }
880 "#,
881         );
882     }
883
884     #[test]
885     fn test_hl_continue_for_but_not_break() {
886         check(
887             r#"
888 fn foo() {
889     'outer: for _ in () {
890  // ^^^^^^^^^^^
891         break;
892         continue;
893      // ^^^^^^^^
894         'inner: for _ in () {
895             break;
896             continue;
897             'innermost: for _ in () {
898                 continue 'outer;
899              // ^^^^^^^^^^^^^^^
900                 break 'outer;
901                 continue 'inner;
902                 break 'inner;
903             }
904             break 'outer;
905             continue$0 'outer;
906          // ^^^^^^^^^^^^^^^
907             break;
908             continue;
909         }
910         break;
911         continue;
912      // ^^^^^^^^
913     }
914 }
915 "#,
916         );
917     }
918
919     #[test]
920     fn test_hl_break_and_continue() {
921         check(
922             r#"
923 fn foo() {
924     'outer: fo$0r _ in () {
925  // ^^^^^^^^^^^
926         break;
927      // ^^^^^
928         continue;
929      // ^^^^^^^^
930         'inner: for _ in () {
931             break;
932             continue;
933             'innermost: for _ in () {
934                 continue 'outer;
935              // ^^^^^^^^^^^^^^^
936                 break 'outer;
937              // ^^^^^^^^^^^^
938                 continue 'inner;
939                 break 'inner;
940             }
941             break 'outer;
942          // ^^^^^^^^^^^^
943             continue 'outer;
944          // ^^^^^^^^^^^^^^^
945             break;
946             continue;
947         }
948         break;
949      // ^^^^^
950         continue;
951      // ^^^^^^^^
952     }
953 }
954 "#,
955         );
956     }
957
958     #[test]
959     fn test_hl_break_while() {
960         check(
961             r#"
962 fn foo() {
963     'outer: while true {
964  // ^^^^^^^^^^^^^
965          break;
966       // ^^^^^
967          'inner: while true {
968             break;
969             'innermost: while true {
970                 break 'outer;
971              // ^^^^^^^^^^^^
972                 break 'inner;
973             }
974             break$0 'outer;
975          // ^^^^^^^^^^^^
976             break;
977         }
978         break;
979      // ^^^^^
980     }
981 }
982 "#,
983         );
984     }
985
986     #[test]
987     fn test_hl_break_labeled_block() {
988         check(
989             r#"
990 fn foo() {
991     'outer: {
992  // ^^^^^^^
993          break;
994       // ^^^^^
995          'inner: {
996             break;
997             'innermost: {
998                 break 'outer;
999              // ^^^^^^^^^^^^
1000                 break 'inner;
1001             }
1002             break$0 'outer;
1003          // ^^^^^^^^^^^^
1004             break;
1005         }
1006         break;
1007      // ^^^^^
1008     }
1009 }
1010 "#,
1011         );
1012     }
1013
1014     #[test]
1015     fn test_hl_break_unlabeled_loop() {
1016         check(
1017             r#"
1018 fn foo() {
1019     loop {
1020  // ^^^^
1021         break$0;
1022      // ^^^^^
1023     }
1024 }
1025 "#,
1026         );
1027     }
1028
1029     #[test]
1030     fn test_hl_break_unlabeled_block_in_loop() {
1031         check(
1032             r#"
1033 fn foo() {
1034     loop {
1035  // ^^^^
1036         {
1037             break$0;
1038          // ^^^^^
1039         }
1040     }
1041 }
1042 "#,
1043         );
1044     }
1045
1046     #[test]
1047     fn test_hl_field_shorthand() {
1048         check(
1049             r#"
1050 struct Struct { field: u32 }
1051               //^^^^^
1052 fn function(field: u32) {
1053           //^^^^^
1054     Struct { field$0 }
1055            //^^^^^ read
1056 }
1057 "#,
1058         );
1059     }
1060
1061     #[test]
1062     fn test_hl_disabled_ref_local() {
1063         let config = HighlightRelatedConfig {
1064             references: false,
1065             break_points: true,
1066             exit_points: true,
1067             yield_points: true,
1068         };
1069
1070         check_with_config(
1071             r#"
1072 fn foo() {
1073     let x$0 = 5;
1074     let y = x * 2;
1075 }
1076 "#,
1077             config,
1078         );
1079     }
1080
1081     #[test]
1082     fn test_hl_disabled_ref_local_preserved_break() {
1083         let config = HighlightRelatedConfig {
1084             references: false,
1085             break_points: true,
1086             exit_points: true,
1087             yield_points: true,
1088         };
1089
1090         check_with_config(
1091             r#"
1092 fn foo() {
1093     let x$0 = 5;
1094     let y = x * 2;
1095
1096     loop {
1097         break;
1098     }
1099 }
1100 "#,
1101             config.clone(),
1102         );
1103
1104         check_with_config(
1105             r#"
1106 fn foo() {
1107     let x = 5;
1108     let y = x * 2;
1109
1110     loop$0 {
1111 //  ^^^^
1112         break;
1113 //      ^^^^^
1114     }
1115 }
1116 "#,
1117             config,
1118         );
1119     }
1120
1121     #[test]
1122     fn test_hl_disabled_ref_local_preserved_yield() {
1123         let config = HighlightRelatedConfig {
1124             references: false,
1125             break_points: true,
1126             exit_points: true,
1127             yield_points: true,
1128         };
1129
1130         check_with_config(
1131             r#"
1132 async fn foo() {
1133     let x$0 = 5;
1134     let y = x * 2;
1135
1136     0.await;
1137 }
1138 "#,
1139             config.clone(),
1140         );
1141
1142         check_with_config(
1143             r#"
1144     async fn foo() {
1145 //  ^^^^^
1146         let x = 5;
1147         let y = x * 2;
1148
1149         0.await$0;
1150 //        ^^^^^
1151 }
1152 "#,
1153             config,
1154         );
1155     }
1156
1157     #[test]
1158     fn test_hl_disabled_ref_local_preserved_exit() {
1159         let config = HighlightRelatedConfig {
1160             references: false,
1161             break_points: true,
1162             exit_points: true,
1163             yield_points: true,
1164         };
1165
1166         check_with_config(
1167             r#"
1168 fn foo() -> i32 {
1169     let x$0 = 5;
1170     let y = x * 2;
1171
1172     if true {
1173         return y;
1174     }
1175
1176     0?
1177 }
1178 "#,
1179             config.clone(),
1180         );
1181
1182         check_with_config(
1183             r#"
1184 fn foo() ->$0 i32 {
1185     let x = 5;
1186     let y = x * 2;
1187
1188     if true {
1189         return y;
1190 //      ^^^^^^
1191     }
1192
1193     0?
1194 //   ^
1195 "#,
1196             config,
1197         );
1198     }
1199
1200     #[test]
1201     fn test_hl_disabled_break() {
1202         let config = HighlightRelatedConfig {
1203             references: true,
1204             break_points: false,
1205             exit_points: true,
1206             yield_points: true,
1207         };
1208
1209         check_with_config(
1210             r#"
1211 fn foo() {
1212     loop {
1213         break$0;
1214     }
1215 }
1216 "#,
1217             config,
1218         );
1219     }
1220
1221     #[test]
1222     fn test_hl_disabled_yield() {
1223         let config = HighlightRelatedConfig {
1224             references: true,
1225             break_points: true,
1226             exit_points: true,
1227             yield_points: false,
1228         };
1229
1230         check_with_config(
1231             r#"
1232 async$0 fn foo() {
1233     0.await;
1234 }
1235 "#,
1236             config,
1237         );
1238     }
1239
1240     #[test]
1241     fn test_hl_disabled_exit() {
1242         let config = HighlightRelatedConfig {
1243             references: true,
1244             break_points: true,
1245             exit_points: false,
1246             yield_points: true,
1247         };
1248
1249         check_with_config(
1250             r#"
1251 fn foo() ->$0 i32 {
1252     if true {
1253         return -1;
1254     }
1255
1256     42
1257 }"#,
1258             config,
1259         );
1260     }
1261
1262     #[test]
1263     fn test_hl_multi_local() {
1264         check(
1265             r#"
1266 fn foo((
1267     foo$0
1268   //^^^
1269     | foo
1270     //^^^
1271     | foo
1272     //^^^
1273 ): ()) {
1274     foo;
1275   //^^^read
1276     let foo;
1277 }
1278 "#,
1279         );
1280         check(
1281             r#"
1282 fn foo((
1283     foo
1284   //^^^
1285     | foo$0
1286     //^^^
1287     | foo
1288     //^^^
1289 ): ()) {
1290     foo;
1291   //^^^read
1292     let foo;
1293 }
1294 "#,
1295         );
1296         check(
1297             r#"
1298 fn foo((
1299     foo
1300   //^^^
1301     | foo
1302     //^^^
1303     | foo
1304     //^^^
1305 ): ()) {
1306     foo$0;
1307   //^^^read
1308     let foo;
1309 }
1310 "#,
1311         );
1312     }
1313
1314     #[test]
1315     fn test_hl_trait_impl_methods() {
1316         check(
1317             r#"
1318 trait Trait {
1319     fn func$0(self) {}
1320      //^^^^
1321 }
1322
1323 impl Trait for () {
1324     fn func(self) {}
1325      //^^^^
1326 }
1327
1328 fn main() {
1329     <()>::func(());
1330         //^^^^
1331     ().func();
1332      //^^^^
1333 }
1334 "#,
1335         );
1336         check(
1337             r#"
1338 trait Trait {
1339     fn func(self) {}
1340      //^^^^
1341 }
1342
1343 impl Trait for () {
1344     fn func$0(self) {}
1345      //^^^^
1346 }
1347
1348 fn main() {
1349     <()>::func(());
1350         //^^^^
1351     ().func();
1352      //^^^^
1353 }
1354 "#,
1355         );
1356         check(
1357             r#"
1358 trait Trait {
1359     fn func(self) {}
1360      //^^^^
1361 }
1362
1363 impl Trait for () {
1364     fn func(self) {}
1365      //^^^^
1366 }
1367
1368 fn main() {
1369     <()>::func(());
1370         //^^^^
1371     ().func$0();
1372      //^^^^
1373 }
1374 "#,
1375         );
1376     }
1377 }