aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/diagnostics.rs118
-rw-r--r--crates/ide/src/display/navigation_target.rs25
-rw-r--r--crates/ide/src/doc_links.rs3
-rw-r--r--crates/ide/src/goto_definition.rs15
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs30
-rw-r--r--crates/ide/src/references.rs24
-rw-r--r--crates/ide/src/references/rename.rs44
-rw-r--r--crates/ide/src/runnables.rs40
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html1
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs5
21 files changed, 239 insertions, 101 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3ad30f0c9..038273750 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -248,35 +248,7 @@ mod tests {
248 /// * a diagnostic is produced 248 /// * a diagnostic is produced
249 /// * this diagnostic fix trigger range touches the input cursor position 249 /// * this diagnostic fix trigger range touches the input cursor position
250 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 250 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
251 pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 251 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
252 let after = trim_indent(ra_fixture_after);
253
254 let (analysis, file_position) = fixture::position(ra_fixture_before);
255 let diagnostic = analysis
256 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
257 .unwrap()
258 .pop()
259 .unwrap();
260 let mut fix = diagnostic.fix.unwrap();
261 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
262 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
263 let actual = {
264 let mut actual = target_file_contents.to_string();
265 edit.apply(&mut actual);
266 actual
267 };
268
269 assert_eq_text!(&after, &actual);
270 assert!(
271 fix.fix_trigger_range.contains_inclusive(file_position.offset),
272 "diagnostic fix range {:?} does not touch cursor position {:?}",
273 fix.fix_trigger_range,
274 file_position.offset
275 );
276 }
277
278 /// Similar to `check_fix`, but applies all the available fixes.
279 fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
280 let after = trim_indent(ra_fixture_after); 252 let after = trim_indent(ra_fixture_after);
281 253
282 let (analysis, file_position) = fixture::position(ra_fixture_before); 254 let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -286,10 +258,12 @@ mod tests {
286 .pop() 258 .pop()
287 .unwrap(); 259 .unwrap();
288 let fix = diagnostic.fix.unwrap(); 260 let fix = diagnostic.fix.unwrap();
289 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
290 let actual = { 261 let actual = {
291 let mut actual = target_file_contents.to_string(); 262 let file_id = fix.source_change.source_file_edits.first().unwrap().file_id;
263 let mut actual = analysis.file_text(file_id).unwrap().to_string();
264
292 // Go from the last one to the first one, so that ranges won't be affected by previous edits. 265 // Go from the last one to the first one, so that ranges won't be affected by previous edits.
266 // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309
293 for edit in fix.source_change.source_file_edits.iter().rev() { 267 for edit in fix.source_change.source_file_edits.iter().rev() {
294 edit.edit.apply(&mut actual); 268 edit.edit.apply(&mut actual);
295 } 269 }
@@ -305,29 +279,6 @@ mod tests {
305 ); 279 );
306 } 280 }
307 281
308 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
309 /// which has a fix that can apply to other files.
310 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
311 let ra_fixture_after = &trim_indent(ra_fixture_after);
312 let (analysis, file_pos) = fixture::position(ra_fixture_before);
313 let current_file_id = file_pos.file_id;
314 let diagnostic = analysis
315 .diagnostics(&DiagnosticsConfig::default(), current_file_id)
316 .unwrap()
317 .pop()
318 .unwrap();
319 let mut fix = diagnostic.fix.unwrap();
320 let edit = fix.source_change.source_file_edits.pop().unwrap();
321 let changed_file_id = edit.file_id;
322 let before = analysis.file_text(changed_file_id).unwrap();
323 let actual = {
324 let mut actual = before.to_string();
325 edit.edit.apply(&mut actual);
326 actual
327 };
328 assert_eq_text!(ra_fixture_after, &actual);
329 }
330
331 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 282 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
332 /// apply to the file containing the cursor. 283 /// apply to the file containing the cursor.
333 pub(crate) fn check_no_diagnostics(ra_fixture: &str) { 284 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
@@ -764,25 +715,25 @@ struct Foo {
764 715
765 #[test] 716 #[test]
766 fn test_add_field_in_other_file_from_usage() { 717 fn test_add_field_in_other_file_from_usage() {
767 check_apply_diagnostic_fix_in_other_file( 718 check_fix(
768 r" 719 r#"
769 //- /main.rs 720//- /main.rs
770 mod foo; 721mod foo;
771 722
772 fn main() { 723fn main() {
773 <|>foo::Foo { bar: 3, baz: false}; 724 foo::Foo { bar: 3, <|>baz: false};
774 } 725}
775 //- /foo.rs 726//- /foo.rs
776 struct Foo { 727struct Foo {
777 bar: i32 728 bar: i32
778 } 729}
779 ", 730"#,
780 r" 731 r#"
781 struct Foo { 732struct Foo {
782 bar: i32, 733 bar: i32,
783 pub(crate) baz: bool 734 pub(crate) baz: bool
784 } 735}
785 ", 736"#,
786 ) 737 )
787 } 738 }
788 739
@@ -802,7 +753,7 @@ struct Foo {
802 753
803 #[test] 754 #[test]
804 fn test_rename_incorrect_case() { 755 fn test_rename_incorrect_case() {
805 check_fixes( 756 check_fix(
806 r#" 757 r#"
807pub struct test_struct<|> { one: i32 } 758pub struct test_struct<|> { one: i32 }
808 759
@@ -819,7 +770,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct {
819"#, 770"#,
820 ); 771 );
821 772
822 check_fixes( 773 check_fix(
823 r#" 774 r#"
824pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 775pub fn some_fn(NonSnakeCase<|>: u8) -> u8 {
825 NonSnakeCase 776 NonSnakeCase
@@ -832,7 +783,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 {
832"#, 783"#,
833 ); 784 );
834 785
835 check_fixes( 786 check_fix(
836 r#" 787 r#"
837pub fn SomeFn<|>(val: u8) -> u8 { 788pub fn SomeFn<|>(val: u8) -> u8 {
838 if val != 0 { SomeFn(val - 1) } else { val } 789 if val != 0 { SomeFn(val - 1) } else { val }
@@ -845,7 +796,7 @@ pub fn some_fn(val: u8) -> u8 {
845"#, 796"#,
846 ); 797 );
847 798
848 check_fixes( 799 check_fix(
849 r#" 800 r#"
850fn some_fn() { 801fn some_fn() {
851 let whatAWeird_Formatting<|> = 10; 802 let whatAWeird_Formatting<|> = 10;
@@ -874,7 +825,7 @@ fn foo() {
874 825
875 #[test] 826 #[test]
876 fn test_rename_incorrect_case_struct_method() { 827 fn test_rename_incorrect_case_struct_method() {
877 check_fixes( 828 check_fix(
878 r#" 829 r#"
879pub struct TestStruct; 830pub struct TestStruct;
880 831
@@ -895,4 +846,17 @@ impl TestStruct {
895"#, 846"#,
896 ); 847 );
897 } 848 }
849
850 #[test]
851 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
852 let input = r#"fn FOO<|>() {}"#;
853 let expected = r#"fn foo() {}"#;
854
855 let (analysis, file_position) = fixture::position(input);
856 let diagnostics =
857 analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap();
858 assert_eq!(diagnostics.len(), 1);
859
860 check_fix(input, expected);
861 }
898} 862}
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index cd8ec54fa..6431e7d6d 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -5,7 +5,7 @@ use std::fmt;
5use either::Either; 5use either::Either;
6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; 6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
7use ide_db::{ 7use ide_db::{
8 base_db::{FileId, SourceDatabase}, 8 base_db::{FileId, FileRange, SourceDatabase},
9 symbol_index::FileSymbolKind, 9 symbol_index::FileSymbolKind,
10}; 10};
11use ide_db::{defs::Definition, RootDatabase}; 11use ide_db::{defs::Definition, RootDatabase};
@@ -28,6 +28,7 @@ pub enum SymbolKind {
28 ValueParam, 28 ValueParam,
29 SelfParam, 29 SelfParam,
30 Local, 30 Local,
31 Label,
31 Function, 32 Function,
32 Const, 33 Const,
33 Static, 34 Static,
@@ -223,6 +224,7 @@ impl TryToNav for Definition {
223 Definition::Local(it) => Some(it.to_nav(db)), 224 Definition::Local(it) => Some(it.to_nav(db)),
224 Definition::TypeParam(it) => Some(it.to_nav(db)), 225 Definition::TypeParam(it) => Some(it.to_nav(db)),
225 Definition::LifetimeParam(it) => Some(it.to_nav(db)), 226 Definition::LifetimeParam(it) => Some(it.to_nav(db)),
227 Definition::Label(it) => Some(it.to_nav(db)),
226 } 228 }
227 } 229 }
228} 230}
@@ -421,6 +423,27 @@ impl ToNav for hir::Local {
421 } 423 }
422} 424}
423 425
426impl ToNav for hir::Label {
427 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
428 let src = self.source(db);
429 let node = src.value.syntax();
430 let FileRange { file_id, range } = src.with_value(node).original_file_range(db);
431 let focus_range =
432 src.value.lifetime().and_then(|lt| lt.lifetime_ident_token()).map(|lt| lt.text_range());
433 let name = self.name(db).to_string().into();
434 NavigationTarget {
435 file_id,
436 name,
437 kind: Some(SymbolKind::Label),
438 full_range: range,
439 focus_range,
440 container_name: None,
441 description: None,
442 docs: None,
443 }
444 }
445}
446
424impl ToNav for hir::TypeParam { 447impl ToNav for hir::TypeParam {
425 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 448 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
426 let src = self.source(db); 449 let src = self.source(db);
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index b61ea0b3e..e10516f43 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -193,7 +193,8 @@ fn rewrite_intra_doc_link(
193 Definition::SelfType(_) 193 Definition::SelfType(_)
194 | Definition::Local(_) 194 | Definition::Local(_)
195 | Definition::TypeParam(_) 195 | Definition::TypeParam(_)
196 | Definition::LifetimeParam(_) => return None, 196 | Definition::LifetimeParam(_)
197 | Definition::Label(_) => return None,
197 }?; 198 }?;
198 let krate = resolved.module(db)?.krate(); 199 let krate = resolved.module(db)?.krate();
199 let canonical_path = resolved.canonical_path(db)?; 200 let canonical_path = resolved.canonical_path(db)?;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 47dd85ceb..912144f8b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1130,4 +1130,19 @@ fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
1130"#, 1130"#,
1131 ); 1131 );
1132 } 1132 }
1133
1134 #[test]
1135 fn goto_label() {
1136 check(
1137 r#"
1138fn foo<'foo>(_: &'foo ()) {
1139 'foo: {
1140 //^^^^
1141 'bar: loop {
1142 break 'foo<|>;
1143 }
1144 }
1145}"#,
1146 )
1147 }
1133} 1148}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 52f993cc9..73245fbe7 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 Adt::Enum(it) => from_def_source(db, it, mod_path), 370 Adt::Enum(it) => from_def_source(db, it, mod_path),
371 }) 371 })
372 } 372 }
373 Definition::TypeParam(_) | Definition::LifetimeParam(_) => { 373 Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => {
374 // FIXME: Hover for generic param 374 // FIXME: Hover for generic param
375 None 375 None
376 } 376 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index dbad9a84f..41eb139d1 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -79,7 +79,7 @@ pub use crate::{
79 HighlightedRange, 79 HighlightedRange,
80 }, 80 },
81}; 81};
82pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; 82pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
83pub use completion::{ 83pub use completion::{
84 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, 84 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
85 CompletionScore, ImportEdit, InsertTextFormat, 85 CompletionScore, ImportEdit, InsertTextFormat,
@@ -490,23 +490,17 @@ impl Analysis {
490 .unwrap_or_default()) 490 .unwrap_or_default())
491 } 491 }
492 492
493 /// Computes resolved assists with source changes for the given position. 493 /// Computes assists (aka code actions aka intentions) for the given
494 pub fn resolved_assists( 494 /// position. If `resolve == false`, computes enough info to show the
495 &self, 495 /// lightbulb list in the editor, but doesn't compute actual edits, to
496 config: &AssistConfig, 496 /// improve performance.
497 frange: FileRange, 497 pub fn assists(
498 ) -> Cancelable<Vec<ResolvedAssist>> {
499 self.with_db(|db| assists::Assist::resolved(db, config, frange))
500 }
501
502 /// Computes unresolved assists (aka code actions aka intentions) for the given
503 /// position.
504 pub fn unresolved_assists(
505 &self, 498 &self,
506 config: &AssistConfig, 499 config: &AssistConfig,
500 resolve: bool,
507 frange: FileRange, 501 frange: FileRange,
508 ) -> Cancelable<Vec<Assist>> { 502 ) -> Cancelable<Vec<Assist>> {
509 self.with_db(|db| Assist::unresolved(db, config, frange)) 503 self.with_db(|db| Assist::get(db, config, resolve, frange))
510 } 504 }
511 505
512 /// Computes the set of diagnostics for the given file. 506 /// Computes the set of diagnostics for the given file.
@@ -535,6 +529,14 @@ impl Analysis {
535 self.with_db(|db| references::rename::prepare_rename(db, position)) 529 self.with_db(|db| references::rename::prepare_rename(db, position))
536 } 530 }
537 531
532 pub fn will_rename_file(
533 &self,
534 file_id: FileId,
535 new_name_stem: &str,
536 ) -> Cancelable<Option<SourceChange>> {
537 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
538 }
539
538 pub fn structural_search_replace( 540 pub fn structural_search_replace(
539 &self, 541 &self,
540 query: &str, 542 query: &str,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 33b7358f7..21b2d7ca1 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -130,7 +130,7 @@ pub(crate) fn find_all_refs(
130 kind = ReferenceKind::FieldShorthandForLocal; 130 kind = ReferenceKind::FieldShorthandForLocal;
131 } 131 }
132 } 132 }
133 } else if let Definition::LifetimeParam(_) = def { 133 } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) {
134 kind = ReferenceKind::Lifetime; 134 kind = ReferenceKind::Lifetime;
135 }; 135 };
136 136
@@ -1122,4 +1122,26 @@ fn main() {
1122 "#]], 1122 "#]],
1123 ); 1123 );
1124 } 1124 }
1125
1126 #[test]
1127 fn test_find_labels() {
1128 check(
1129 r#"
1130fn foo<'a>() -> &'a () {
1131 'a: loop {
1132 'b: loop {
1133 continue 'a<|>;
1134 }
1135 break 'a;
1136 }
1137}
1138"#,
1139 expect![[r#"
1140 'a Label FileId(0) 29..32 29..31 Lifetime
1141
1142 FileId(0) 80..82 Lifetime
1143 FileId(0) 108..110 Lifetime
1144 "#]],
1145 );
1146 }
1125} 1147}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index cd721b7eb..854bf194e 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -6,7 +6,7 @@ use std::{
6}; 6};
7 7
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 8use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileRange, SourceDatabaseExt}; 9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 10use ide_db::{
11 defs::{Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
12 RootDatabase, 12 RootDatabase,
@@ -110,6 +110,23 @@ pub(crate) fn rename_with_semantics(
110 } 110 }
111} 111}
112 112
113pub(crate) fn will_rename_file(
114 db: &RootDatabase,
115 file_id: FileId,
116 new_name_stem: &str,
117) -> Option<SourceChange> {
118 let sema = Semantics::new(db);
119 let module = sema.to_module_def(file_id)?;
120
121 let decl = module.declaration_source(db)?;
122 let range = decl.value.name()?.syntax().text_range();
123
124 let position = FilePosition { file_id: decl.file_id.original_file(db), offset: range.start() };
125 let mut change = rename_mod(&sema, position, module, new_name_stem).ok()?.info;
126 change.file_system_edits.clear();
127 Some(change)
128}
129
113fn find_module_at_offset( 130fn find_module_at_offset(
114 sema: &Semantics<RootDatabase>, 131 sema: &Semantics<RootDatabase>,
115 position: FilePosition, 132 position: FilePosition,
@@ -1523,4 +1540,29 @@ fn main() {
1523}"#, 1540}"#,
1524 ); 1541 );
1525 } 1542 }
1543
1544 #[test]
1545 fn test_rename_label() {
1546 check(
1547 "'foo",
1548 r#"
1549fn foo<'a>() -> &'a () {
1550 'a: {
1551 'b: loop {
1552 break 'a<|>;
1553 }
1554 }
1555}
1556"#,
1557 r#"
1558fn foo<'a>() -> &'a () {
1559 'foo: {
1560 'b: loop {
1561 break 'foo;
1562 }
1563 }
1564}
1565"#,
1566 )
1567 }
1526} 1568}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 2f2b99130..891183266 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -193,7 +193,7 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
193 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { 193 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
194 if let Some(adt) = imp.target_ty(sema.db).as_adt() { 194 if let Some(adt) = imp.target_ty(sema.db).as_adt() {
195 let name = adt.name(sema.db).to_string(); 195 let name = adt.name(sema.db).to_string();
196 let idx = path.rfind(':').unwrap_or(0); 196 let idx = path.rfind(':').map_or(0, |idx| idx + 1);
197 let (prefix, suffix) = path.split_at(idx); 197 let (prefix, suffix) = path.split_at(idx);
198 return format!("{}{}::{}", prefix, name, suffix); 198 return format!("{}{}::{}", prefix, name, suffix);
199 } 199 }
@@ -931,4 +931,42 @@ mod test_mod {
931 "#]], 931 "#]],
932 ); 932 );
933 } 933 }
934
935 #[test]
936 fn test_doc_runnables_impl_mod() {
937 check(
938 r#"
939//- /lib.rs
940mod foo;
941//- /foo.rs
942struct Foo;<|>
943impl Foo {
944 /// ```
945 /// let x = 5;
946 /// ```
947 fn foo() {}
948}
949 "#,
950 &[&DOCTEST],
951 expect![[r#"
952 [
953 Runnable {
954 nav: NavigationTarget {
955 file_id: FileId(
956 1,
957 ),
958 full_range: 27..81,
959 name: "foo",
960 },
961 kind: DocTest {
962 test_id: Path(
963 "foo::Foo::foo",
964 ),
965 },
966 cfg: None,
967 },
968 ]
969 "#]],
970 );
971 }
934} 972}
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 00c717c7c..5ad96581b 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -560,10 +560,20 @@ fn highlight_element(
560 CHAR => HighlightTag::CharLiteral.into(), 560 CHAR => HighlightTag::CharLiteral.into(),
561 QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, 561 QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
562 LIFETIME => { 562 LIFETIME => {
563 let h = Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)); 563 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
564 match element.parent().map(|it| it.kind()) { 564
565 Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition, 565 match NameClass::classify_lifetime(sema, &lifetime) {
566 _ => h, 566 Some(NameClass::Definition(def)) => {
567 highlight_def(db, def) | HighlightModifier::Definition
568 }
569 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
570 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
571 _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)),
572 },
573 _ => {
574 Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam))
575 | HighlightModifier::Definition
576 }
567 } 577 }
568 } 578 }
569 p if p.is_punct() => match p { 579 p if p.is_punct() => match p {
@@ -825,6 +835,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
825 return h; 835 return h;
826 } 836 }
827 Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), 837 Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam),
838 Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label),
828 } 839 }
829 .into() 840 .into()
830} 841}
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index abcc5cccc..99ba3a59d 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -64,6 +64,7 @@ body { margin: 0; }
64pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 64pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
65 65
66.lifetime { color: #DFAF8F; font-style: italic; } 66.lifetime { color: #DFAF8F; font-style: italic; }
67.label { color: #DFAF8F; font-style: italic; }
67.comment { color: #7F9F7F; } 68.comment { color: #7F9F7F; }
68.documentation { color: #629755; } 69.documentation { color: #629755; }
69.injected { opacity: 0.65 ; } 70.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 974f54fa0..2a6cc0cab 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -80,6 +80,7 @@ impl HighlightTag {
80 SymbolKind::LifetimeParam => "lifetime", 80 SymbolKind::LifetimeParam => "lifetime",
81 SymbolKind::Macro => "macro", 81 SymbolKind::Macro => "macro",
82 SymbolKind::Local => "variable", 82 SymbolKind::Local => "variable",
83 SymbolKind::Label => "label",
83 SymbolKind::ValueParam => "value_param", 84 SymbolKind::ValueParam => "value_param",
84 SymbolKind::SelfParam => "self_keyword", 85 SymbolKind::SelfParam => "self_keyword",
85 SymbolKind::Impl => "self_type", 86 SymbolKind::Impl => "self_type",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index db6f32d33..506ebe60e 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 4e511baa9..4dd7413ba 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 800d894c7..ed452586a 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 7f18ad297..92e7dc3e4 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index c843b5085..31dad5d42 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index d26f48516..e3a0aa317 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 588e86a34..72ff9dd40 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
@@ -194,6 +195,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
194 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> 195 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span>
195 196
196 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> 197 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span>
198
199 <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span>
200 <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span>
201 <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span>
202 <span class="punctuation">}</span>
197<span class="punctuation">}</span> 203<span class="punctuation">}</span>
198 204
199<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 205<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index c7589605f..8b3dfa69f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -4,6 +4,7 @@ body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } 4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5 5
6.lifetime { color: #DFAF8F; font-style: italic; } 6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; } 8.comment { color: #7F9F7F; }
8.documentation { color: #629755; } 9.documentation { color: #629755; }
9.injected { opacity: 0.65 ; } 10.injected { opacity: 0.65 ; }
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index f53d2c3ba..e0df0d2b5 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -168,6 +168,11 @@ fn main() {
168 let baz = -baz; 168 let baz = -baz;
169 169
170 let _ = !true; 170 let _ = !true;
171
172 'foo: loop {
173 break 'foo;
174 continue 'foo;
175 }
171} 176}
172 177
173enum Option<T> { 178enum Option<T> {