diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index cd8ec54fa72..6431e7d6dbf 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs @@ -5,7 +5,7 @@ use either::Either; use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; use ide_db::{ - base_db::{FileId, SourceDatabase}, + base_db::{FileId, FileRange, SourceDatabase}, symbol_index::FileSymbolKind, }; use ide_db::{defs::Definition, RootDatabase}; @@ -28,6 +28,7 @@ pub enum SymbolKind { ValueParam, SelfParam, Local, + Label, Function, Const, Static, @@ -223,6 +224,7 @@ fn try_to_nav(&self, db: &RootDatabase) -> Option { Definition::Local(it) => Some(it.to_nav(db)), Definition::TypeParam(it) => Some(it.to_nav(db)), Definition::LifetimeParam(it) => Some(it.to_nav(db)), + Definition::Label(it) => Some(it.to_nav(db)), } } } @@ -421,6 +423,27 @@ fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { } } +impl ToNav for hir::Label { + fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + let src = self.source(db); + let node = src.value.syntax(); + let FileRange { file_id, range } = src.with_value(node).original_file_range(db); + let focus_range = + src.value.lifetime().and_then(|lt| lt.lifetime_ident_token()).map(|lt| lt.text_range()); + let name = self.name(db).to_string().into(); + NavigationTarget { + file_id, + name, + kind: Some(SymbolKind::Label), + full_range: range, + focus_range, + container_name: None, + description: None, + docs: None, + } + } +} + impl ToNav for hir::TypeParam { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index b61ea0b3e40..e10516f43ac 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -193,7 +193,8 @@ fn rewrite_intra_doc_link( Definition::SelfType(_) | Definition::Local(_) | Definition::TypeParam(_) - | Definition::LifetimeParam(_) => return None, + | Definition::LifetimeParam(_) + | Definition::Label(_) => return None, }?; let krate = resolved.module(db)?.krate(); let canonical_path = resolved.canonical_path(db)?; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 431da5d9c75..65e45c92024 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1105,4 +1105,19 @@ fn foo() where T: for<'a> Foo<&'a<|> (u8, u16)>, {} "#, ); } + + #[test] + fn goto_label() { + check( + r#" +fn foo<'foo>(_: &'foo ()) { + 'foo: { + //^^^^ + 'bar: loop { + break 'foo<|>; + } + } +}"#, + ) + } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 52f993cc92b..73245fbe752 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 { Adt::Enum(it) => from_def_source(db, it, mod_path), }) } - Definition::TypeParam(_) | Definition::LifetimeParam(_) => { + Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { // FIXME: Hover for generic param None } diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 33b7358f722..21b2d7ca1cd 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -130,7 +130,7 @@ pub(crate) fn find_all_refs( kind = ReferenceKind::FieldShorthandForLocal; } } - } else if let Definition::LifetimeParam(_) = def { + } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { kind = ReferenceKind::Lifetime; }; @@ -1122,4 +1122,26 @@ fn main() { "#]], ); } + + #[test] + fn test_find_labels() { + check( + r#" +fn foo<'a>() -> &'a () { + 'a: loop { + 'b: loop { + continue 'a<|>; + } + break 'a; + } +} +"#, + expect![[r#" + 'a Label FileId(0) 29..32 29..31 Lifetime + + FileId(0) 80..82 Lifetime + FileId(0) 108..110 Lifetime + "#]], + ); + } } diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index cd721b7ebcb..4f353852f89 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs @@ -1523,4 +1523,29 @@ enum CustomOption { }"#, ); } + + #[test] + fn test_rename_label() { + check( + "'foo", + r#" +fn foo<'a>() -> &'a () { + 'a: { + 'b: loop { + break 'a<|>; + } + } +} +"#, + r#" +fn foo<'a>() -> &'a () { + 'foo: { + 'b: loop { + break 'foo; + } + } +} +"#, + ) + } } diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 00c717c7c7e..5ad96581bd3 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -560,10 +560,20 @@ fn highlight_element( CHAR => HighlightTag::CharLiteral.into(), QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow, LIFETIME => { - let h = Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)); - match element.parent().map(|it| it.kind()) { - Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition, - _ => h, + let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); + + match NameClass::classify_lifetime(sema, &lifetime) { + Some(NameClass::Definition(def)) => { + highlight_def(db, def) | HighlightModifier::Definition + } + None => match NameRefClass::classify_lifetime(sema, &lifetime) { + Some(NameRefClass::Definition(def)) => highlight_def(db, def), + _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)), + }, + _ => { + Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)) + | HighlightModifier::Definition + } } } p if p.is_punct() => match p { @@ -825,6 +835,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { return h; } Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam), + Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label), } .into() } diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index abcc5cccc75..99ba3a59d38 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs @@ -64,6 +64,7 @@ fn html_escape(text: &str) -> String { pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .injected { opacity: 0.65 ; } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 974f54fa01e..2a6cc0cab66 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -80,6 +80,7 @@ fn as_str(self) -> &'static str { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Macro => "macro", SymbolKind::Local => "variable", + SymbolKind::Label => "label", SymbolKind::ValueParam => "value_param", SymbolKind::SelfParam => "self_keyword", 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 db6f32d3397..506ebe60e71 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 4e511baa932..4dd7413ba0b 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 800d894c769..ed452586adb 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 7f18ad297f2..92e7dc3e4f7 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 c843b5085d5..31dad5d4295 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 d26f485162e..e3a0aa317d1 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .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 588e86a34f9..72ff9dd40e9 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .injected { opacity: 0.65 ; } @@ -194,6 +195,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let baz = -baz; let _ = !true; + + 'foo: loop { + break 'foo; + continue 'foo; + } } enum Option<T> { 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 c7589605fa5..8b3dfa69f9b 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; } pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } .lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } .comment { color: #7F9F7F; } .documentation { color: #629755; } .injected { opacity: 0.65 ; } diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index f53d2c3ba41..e0df0d2b522 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -168,6 +168,11 @@ fn main() { let baz = -baz; let _ = !true; + + 'foo: loop { + break 'foo; + continue 'foo; + } } enum Option { diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 9d7dce1d47d..d33a6cb863b 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs @@ -6,8 +6,8 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). use hir::{ - db::HirDatabase, Crate, Field, HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module, - ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, + db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, + Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, }; use syntax::{ ast::{self, AstNode}, @@ -26,7 +26,7 @@ pub enum Definition { Local(Local), TypeParam(TypeParam), LifetimeParam(LifetimeParam), - // FIXME: Label + Label(Label), } impl Definition { @@ -39,6 +39,7 @@ pub fn module(&self, db: &RootDatabase) -> Option { Definition::Local(it) => Some(it.module(db)), Definition::TypeParam(it) => Some(it.module(db)), Definition::LifetimeParam(it) => Some(it.module(db)), + Definition::Label(it) => Some(it.module(db)), } } @@ -51,6 +52,7 @@ pub fn visibility(&self, db: &RootDatabase) -> Option { Definition::Local(_) => None, Definition::TypeParam(_) => None, Definition::LifetimeParam(_) => None, + Definition::Label(_) => None, } } @@ -77,6 +79,7 @@ pub fn name(&self, db: &RootDatabase) -> Option { Definition::Local(it) => it.name(db)?, Definition::TypeParam(it) => it.name(db), Definition::LifetimeParam(it) => it.name(db), + Definition::Label(it) => it.name(db), }; Some(name) } @@ -248,7 +251,10 @@ pub fn classify_lifetime( let def = sema.to_def(&it)?; Some(NameClass::Definition(Definition::LifetimeParam(def))) }, - ast::Label(_it) => None, + ast::Label(it) => { + let def = sema.to_def(&it)?; + Some(NameClass::Definition(Definition::Label(def))) + }, _ => None, } } @@ -370,6 +376,9 @@ pub fn classify_lifetime( let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string()); let parent = lifetime.syntax().parent()?; match parent.kind() { + SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { + sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition) + } SyntaxKind::LIFETIME_ARG | SyntaxKind::SELF_PARAM | SyntaxKind::TYPE_BOUND @@ -387,7 +396,6 @@ pub fn classify_lifetime( .map(Definition::LifetimeParam) .map(NameRefClass::Definition) } - SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None, _ => None, } } diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 1daad1c9890..c2f6a655d4e 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -45,6 +45,7 @@ macro_rules! define_semantic_token_types { (FORMAT_SPECIFIER, "formatSpecifier"), (GENERIC, "generic"), (LIFETIME, "lifetime"), + (LABEL, "label"), (PUNCTUATION, "punctuation"), (SELF_KEYWORD, "selfKeyword"), (TYPE_ALIAS, "typeAlias"), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 5a1ae96aa09..753aad62864 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -46,7 +46,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Local | SymbolKind::SelfParam | SymbolKind::LifetimeParam - | SymbolKind::ValueParam => lsp_types::SymbolKind::Variable, + | SymbolKind::ValueParam + | SymbolKind::Label => lsp_types::SymbolKind::Variable, SymbolKind::Union => lsp_types::SymbolKind::Struct, } } @@ -378,6 +379,7 @@ fn semantic_token_type_and_modifiers( SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, + SymbolKind::Label => semantic_tokens::LABEL, SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,