From 9bd8eee21e2d2425bfd2946849b45b91f1313174 Mon Sep 17 00:00:00 2001 From: Alex Kladov Date: Tue, 16 Apr 2024 16:10:36 +0100 Subject: [PATCH] ide: improve ReferenceCategoryType It is bitset semantically --- many categorical things can be true about a reference at the same time. In parciular, a reference can be a "test" and a "write" at the same time. --- Cargo.lock | 1 + .../src/handlers/extract_function.rs | 8 +- .../src/handlers/remove_unused_imports.rs | 2 +- crates/ide-db/Cargo.toml | 1 + crates/ide-db/src/search.rs | 85 ++++++++------- crates/ide/src/highlight_related.rs | 83 ++++++++------ crates/ide/src/references.rs | 102 +++++++++--------- crates/rust-analyzer/src/handlers/request.rs | 6 +- crates/rust-analyzer/src/lsp/to_proto.rs | 11 +- 9 files changed, 167 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20bd36b894a0..a6e460134f21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,6 +695,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", + "bitflags 2.4.2", "cov-mark", "crossbeam-channel", "either", diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index d111005c2ec4..34326294d2e1 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1149,8 +1149,14 @@ fn reference_is_exclusive( node: &dyn HasTokenAtOffset, ctx: &AssistContext<'_>, ) -> bool { + // FIXME: this quite an incorrect way to go about doing this :-) + // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, + // but doesn't necessary fully reflect all the intricacies of the underlying language semantics + // The correct approach here would be to expose this entire analysis as a method on some hir + // type. Something like `body.free_variables(statement_range)`. + // we directly modify variable with set: `n = 0`, `n += 1` - if reference.category == Some(ReferenceCategory::Write) { + if reference.category.contains(ReferenceCategory::WRITE) { return true; } diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index d67b259d2f5f..0f0f13bbc80b 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -145,7 +145,7 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec, + pub category: ReferenceCategory, } #[derive(Debug, Clone)] @@ -124,17 +124,16 @@ impl FileReferenceNode { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ReferenceCategory { - // FIXME: Add this variant and delete the `retain_adt_literal_usages` function. - // Create - Write, - Read, - Import, - // FIXME: Some day should be able to search in doc comments. Would probably - // need to switch from enum to bitflags then? - // DocComment - Test, +bitflags::bitflags! { + #[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)] + pub struct ReferenceCategory: u8 { + // FIXME: Add this variant and delete the `retain_adt_literal_usages` function. + // const CREATE = 1 << 0; + const WRITE = 1 << 0; + const READ = 1 << 1; + const IMPORT = 1 << 2; + const TEST = 1 << 3; + } } /// Generally, `search_scope` returns files that might contain references for the element. @@ -660,7 +659,7 @@ impl<'a> FindUsages<'a> { let reference = FileReference { range, name: FileReferenceNode::NameRef(name_ref.clone()), - category: None, + category: ReferenceCategory::empty(), }; sink(file_id, reference) } @@ -676,10 +675,15 @@ impl<'a> FindUsages<'a> { match NameRefClass::classify(self.sema, name_ref) { Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); + let category = if is_name_ref_in_import(name_ref) { + ReferenceCategory::IMPORT + } else { + ReferenceCategory::empty() + }; let reference = FileReference { range, name: FileReferenceNode::NameRef(name_ref.clone()), - category: is_name_ref_in_import(name_ref).then_some(ReferenceCategory::Import), + category, }; sink(file_id, reference) } @@ -700,7 +704,7 @@ impl<'a> FindUsages<'a> { let reference = FileReference { range, name: FileReferenceNode::FormatStringEntry(token, range), - category: Some(ReferenceCategory::Read), + category: ReferenceCategory::READ, }; sink(file_id, reference) } @@ -719,7 +723,7 @@ impl<'a> FindUsages<'a> { let reference = FileReference { range, name: FileReferenceNode::Lifetime(lifetime.clone()), - category: None, + category: ReferenceCategory::empty(), }; sink(file_id, reference) } @@ -817,7 +821,7 @@ impl<'a> FindUsages<'a> { range, name: FileReferenceNode::Name(name.clone()), // FIXME: mutable patterns should have `Write` access - category: Some(ReferenceCategory::Read), + category: ReferenceCategory::READ, }; sink(file_id, reference) } @@ -826,7 +830,7 @@ impl<'a> FindUsages<'a> { let reference = FileReference { range, name: FileReferenceNode::Name(name.clone()), - category: None, + category: ReferenceCategory::empty(), }; sink(file_id, reference) } @@ -851,7 +855,7 @@ impl<'a> FindUsages<'a> { let reference = FileReference { range, name: FileReferenceNode::Name(name.clone()), - category: None, + category: ReferenceCategory::empty(), }; sink(file_id, reference) } @@ -875,38 +879,41 @@ impl ReferenceCategory { sema: &Semantics<'_, RootDatabase>, def: &Definition, r: &ast::NameRef, - ) -> Option { + ) -> ReferenceCategory { + let mut result = ReferenceCategory::empty(); if is_name_ref_in_test(sema, r) { - return Some(ReferenceCategory::Test); + result |= ReferenceCategory::TEST; } // Only Locals and Fields have accesses for now. if !matches!(def, Definition::Local(_) | Definition::Field(_)) { - return is_name_ref_in_import(r).then_some(ReferenceCategory::Import); + if is_name_ref_in_import(r) { + result |= ReferenceCategory::IMPORT; + } + return result; } let mode = r.syntax().ancestors().find_map(|node| { - match_ast! { - match node { - ast::BinExpr(expr) => { - if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) { - // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). - // FIXME: This is not terribly accurate. - if let Some(lhs) = expr.lhs() { - if lhs.syntax().text_range().end() == r.syntax().text_range().end() { - return Some(ReferenceCategory::Write); + match_ast! { + match node { + ast::BinExpr(expr) => { + if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) { + // If the variable or field ends on the LHS's end then it's a Write + // (covers fields and locals). FIXME: This is not terribly accurate. + if let Some(lhs) = expr.lhs() { + if lhs.syntax().text_range().end() == r.syntax().text_range().end() { + return Some(ReferenceCategory::WRITE) + } } } - } - Some(ReferenceCategory::Read) - }, - _ => None + Some(ReferenceCategory::READ) + }, + _ => None, + } } - } - }); + }).unwrap_or(ReferenceCategory::READ); - // Default Locals and Fields to read - mode.or(Some(ReferenceCategory::Read)) + result | mode } } diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index e20e0b67f4b3..6f32ce76b22c 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -26,7 +26,7 @@ pub struct HighlightedRange { // FIXME: This needs to be more precise. Reference category makes sense only // for references, but we also have defs. And things like exit points are // neither. - pub category: Option, + pub category: ReferenceCategory, } #[derive(Default, Clone)] @@ -113,7 +113,11 @@ fn highlight_closure_captures( range, category, }); - let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + let category = if local.is_mut(sema.db) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; local .sources(sema.db) .into_iter() @@ -137,7 +141,9 @@ fn highlight_references( { match resolution.map(Definition::from) { Some(def) => iter::once(def).collect(), - None => return Some(vec![HighlightedRange { range, category: None }]), + None => { + return Some(vec![HighlightedRange { range, category: ReferenceCategory::empty() }]) + } } } else { find_defs(sema, token.clone()) @@ -211,7 +217,11 @@ fn highlight_references( // highlight the defs themselves match def { Definition::Local(local) => { - let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + let category = if local.is_mut(sema.db) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; local .sources(sema.db) .into_iter() @@ -238,8 +248,11 @@ fn highlight_references( continue; } let hl_range = nav.focus_range.map(|range| { - let category = matches!(def, Definition::Local(l) if l.is_mut(sema.db)) - .then_some(ReferenceCategory::Write); + let category = if matches!(def, Definition::Local(l) if l.is_mut(sema.db)) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; HighlightedRange { range, category } }); if let Some(hl_range) = hl_range { @@ -272,24 +285,30 @@ fn highlight_exit_points( def_ranges .into_iter() .flatten() - .map(|range| HighlightedRange { category: None, range }), + .map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), ); let body = body?; walk_expr(&body, &mut |expr| match expr { ast::Expr::ReturnExpr(expr) => { if let Some(token) = expr.return_token() { - highlights.push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } ast::Expr::TryExpr(try_) => { if let Some(token) = try_.question_mark_token() { - highlights.push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => { if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) { highlights.push(HighlightedRange { - category: None, + category: ReferenceCategory::empty(), range: expr.syntax().text_range(), }); } @@ -309,7 +328,7 @@ fn highlight_exit_points( .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), _ => tail.syntax().text_range(), }; - highlights.push(HighlightedRange { category: None, range }) + highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) }); } Some(highlights) @@ -354,7 +373,9 @@ fn highlight_break_points(token: SyntaxToken) -> Option> { token.map(|tok| tok.text_range()), label.as_ref().map(|it| it.syntax().text_range()), ); - highlights.extend(range.map(|range| HighlightedRange { category: None, range })); + highlights.extend( + range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), + ); for_each_break_and_continue_expr(label, body, &mut |expr| { let range: Option = match (cursor_token_kind, expr) { (T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => { @@ -372,7 +393,9 @@ fn highlight_break_points(token: SyntaxToken) -> Option> { ), _ => None, }; - highlights.extend(range.map(|range| HighlightedRange { category: None, range })); + highlights.extend( + range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), + ); }); Some(highlights) } @@ -430,14 +453,18 @@ fn highlight_yield_points(token: SyntaxToken) -> Option> { async_token: Option, body: Option, ) -> Option> { - let mut highlights = - vec![HighlightedRange { category: None, range: async_token?.text_range() }]; + let mut highlights = vec![HighlightedRange { + category: ReferenceCategory::empty(), + range: async_token?.text_range(), + }]; if let Some(body) = body { walk_expr(&body, &mut |expr| { if let ast::Expr::AwaitExpr(expr) = expr { if let Some(token) = expr.await_token() { - highlights - .push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } }); @@ -481,6 +508,8 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe #[cfg(test)] mod tests { + use itertools::Itertools; + use crate::fixture; use super::*; @@ -504,28 +533,18 @@ mod tests { let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default(); - let mut expected = annotations - .into_iter() - .map(|(r, access)| (r.range, (!access.is_empty()).then_some(access))) - .collect::>(); + let mut expected = + annotations.into_iter().map(|(r, access)| (r.range, access)).collect::>(); - let mut actual = hls + let mut actual: Vec<(TextRange, String)> = hls .into_iter() .map(|hl| { ( hl.range, - hl.category.map(|it| { - match it { - ReferenceCategory::Read => "read", - ReferenceCategory::Write => "write", - ReferenceCategory::Import => "import", - ReferenceCategory::Test => "test", - } - .to_owned() - }), + hl.category.iter_names().map(|(name, _flag)| name.to_lowercase()).join(","), ) }) - .collect::>(); + .collect(); actual.sort_by_key(|(range, _)| range.start()); expected.sort_by_key(|(range, _)| range.start()); diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index fef2aba3c616..8eb4bfe6d0d9 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -30,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { pub declaration: Option, - pub references: IntMap)>>, + pub references: IntMap>, } #[derive(Debug, Clone)] @@ -66,7 +66,7 @@ pub(crate) fn find_all_refs( retain_adt_literal_usages(&mut usages, def, sema); } - let mut references = usages + let mut references: IntMap> = usages .into_iter() .map(|(file_id, refs)| { ( @@ -77,7 +77,7 @@ pub(crate) fn find_all_refs( .collect(), ) }) - .collect::>>(); + .collect(); let declaration = match def { Definition::Module(module) => { Some(NavigationTarget::from_module_to_decl(sema.db, module)) @@ -93,7 +93,7 @@ pub(crate) fn find_all_refs( references .entry(extra_ref.file_id) .or_default() - .push((extra_ref.focus_or_full_range(), None)); + .push((extra_ref.focus_or_full_range(), ReferenceCategory::empty())); } Declaration { is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)), @@ -300,7 +300,7 @@ fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool { #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::{base_db::FileId, search::ReferenceCategory}; + use ide_db::base_db::FileId; use stdx::format_to; use crate::{fixture, SearchScope}; @@ -324,7 +324,7 @@ fn test() { test_func Function FileId(0) 0..17 3..12 FileId(0) 35..44 - FileId(0) 75..84 Test + FileId(0) 75..84 test "#]], ); @@ -345,7 +345,7 @@ fn test() { test_func Function FileId(0) 0..17 3..12 FileId(0) 35..44 - FileId(0) 96..105 Test + FileId(0) 96..105 test "#]], ); } @@ -600,12 +600,12 @@ fn main() { i = 5; }"#, expect![[r#" - i Local FileId(0) 20..25 24..25 Write + i Local FileId(0) 20..25 24..25 write - FileId(0) 50..51 Write - FileId(0) 54..55 Read - FileId(0) 76..77 Write - FileId(0) 94..95 Write + FileId(0) 50..51 write + FileId(0) 54..55 read + FileId(0) 76..77 write + FileId(0) 94..95 write "#]], ); } @@ -626,8 +626,8 @@ fn bar() { expect![[r#" spam Local FileId(0) 19..23 19..23 - FileId(0) 34..38 Read - FileId(0) 41..45 Read + FileId(0) 34..38 read + FileId(0) 41..45 read "#]], ); } @@ -641,7 +641,7 @@ fn foo(i : u32) -> u32 { i$0 } expect![[r#" i ValueParam FileId(0) 7..8 7..8 - FileId(0) 25..26 Read + FileId(0) 25..26 read "#]], ); } @@ -655,7 +655,7 @@ fn foo(i$0 : u32) -> u32 { i } expect![[r#" i ValueParam FileId(0) 7..8 7..8 - FileId(0) 25..26 Read + FileId(0) 25..26 read "#]], ); } @@ -676,7 +676,7 @@ fn main(s: Foo) { expect![[r#" spam Field FileId(0) 17..30 21..25 - FileId(0) 67..71 Read + FileId(0) 67..71 read "#]], ); } @@ -824,7 +824,7 @@ pub struct Foo { expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(0) 14..17 Import + FileId(0) 14..17 import "#]], ); } @@ -842,7 +842,7 @@ use self$0; expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(1) 4..8 Import + FileId(1) 4..8 import "#]], ); } @@ -857,7 +857,7 @@ use self$0; expect![[r#" Module FileId(0) 0..10 - FileId(0) 4..8 Import + FileId(0) 4..8 import "#]], ); } @@ -885,7 +885,7 @@ pub(super) struct Foo$0 { expect![[r#" Foo Struct FileId(2) 0..41 18..21 some - FileId(1) 20..23 Import + FileId(1) 20..23 import FileId(1) 47..50 "#]], ); @@ -960,10 +960,10 @@ fn foo() { } "#, expect![[r#" - i Local FileId(0) 19..24 23..24 Write + i Local FileId(0) 19..24 23..24 write - FileId(0) 34..35 Write - FileId(0) 38..39 Read + FileId(0) 34..35 write + FileId(0) 38..39 read "#]], ); } @@ -984,8 +984,8 @@ fn foo() { expect![[r#" f Field FileId(0) 15..21 15..16 - FileId(0) 55..56 Read - FileId(0) 68..69 Write + FileId(0) 55..56 read + FileId(0) 68..69 write "#]], ); } @@ -1002,7 +1002,7 @@ fn foo() { expect![[r#" i Local FileId(0) 19..20 19..20 - FileId(0) 26..27 Write + FileId(0) 26..27 write "#]], ); } @@ -1048,7 +1048,7 @@ fn g() { f(); } expect![[r#" f Function FileId(0) 22..31 25..26 - FileId(1) 11..12 Import + FileId(1) 11..12 import FileId(1) 24..25 "#]], ); @@ -1071,7 +1071,7 @@ fn f(s: S) { expect![[r#" field Field FileId(0) 15..24 15..20 - FileId(0) 68..73 Read + FileId(0) 68..73 read "#]], ); } @@ -1095,7 +1095,7 @@ fn f(e: En) { expect![[r#" field Field FileId(0) 32..41 32..37 - FileId(0) 102..107 Read + FileId(0) 102..107 read "#]], ); } @@ -1119,7 +1119,7 @@ fn f() -> m::En { expect![[r#" field Field FileId(0) 56..65 56..61 - FileId(0) 125..130 Read + FileId(0) 125..130 read "#]], ); } @@ -1144,8 +1144,8 @@ impl Foo { expect![[r#" self SelfParam FileId(0) 47..51 47..51 - FileId(0) 71..75 Read - FileId(0) 152..156 Read + FileId(0) 71..75 read + FileId(0) 152..156 read "#]], ); } @@ -1165,7 +1165,7 @@ impl Foo { expect![[r#" self SelfParam FileId(0) 47..51 47..51 - FileId(0) 63..67 Read + FileId(0) 63..67 read "#]], ); } @@ -1185,16 +1185,16 @@ impl Foo { if let Some(decl) = refs.declaration { format_to!(actual, "{}", decl.nav.debug_render()); if decl.is_mut { - format_to!(actual, " {:?}", ReferenceCategory::Write) + format_to!(actual, " write",) } actual += "\n\n"; } for (file_id, references) in &refs.references { - for (range, access) in references { + for (range, category) in references { format_to!(actual, "{:?} {:?}", file_id, range); - if let Some(access) = access { - format_to!(actual, " {:?}", access); + for (name, _flag) in category.iter_names() { + format_to!(actual, " {}", name.to_lowercase()); } actual += "\n"; } @@ -1281,7 +1281,7 @@ fn main() { expect![[r#" a Local FileId(0) 59..60 59..60 - FileId(0) 80..81 Read + FileId(0) 80..81 read "#]], ); } @@ -1299,7 +1299,7 @@ fn main() { expect![[r#" a Local FileId(0) 59..60 59..60 - FileId(0) 80..81 Read + FileId(0) 80..81 read "#]], ); } @@ -1479,7 +1479,7 @@ fn test$0() { expect![[r#" test Function FileId(0) 0..33 11..15 - FileId(0) 24..28 Test + FileId(0) 24..28 test "#]], ); } @@ -1538,9 +1538,9 @@ pub use level1::Foo; expect![[r#" Foo Struct FileId(0) 0..15 11..14 - FileId(1) 16..19 Import - FileId(2) 16..19 Import - FileId(3) 16..19 Import + FileId(1) 16..19 import + FileId(2) 16..19 import + FileId(3) 16..19 import "#]], ); } @@ -1568,7 +1568,7 @@ lib::foo!(); expect![[r#" foo Macro FileId(1) 0..61 29..32 - FileId(0) 46..49 Import + FileId(0) 46..49 import FileId(2) 0..3 FileId(3) 5..8 "#]], @@ -1731,7 +1731,7 @@ struct Foo; expect![[r#" derive_identity Derive FileId(2) 1..107 45..60 - FileId(0) 17..31 Import + FileId(0) 17..31 import FileId(0) 56..70 "#]], ); @@ -2055,7 +2055,7 @@ fn method() {} expect![[r#" method Field FileId(0) 60..70 60..66 - FileId(0) 136..142 Read + FileId(0) 136..142 read "#]], ); check( @@ -2101,7 +2101,7 @@ fn method() {} expect![[r#" method Field FileId(0) 60..70 60..66 - FileId(0) 136..142 Read + FileId(0) 136..142 read "#]], ); check( @@ -2160,9 +2160,9 @@ fn test() { expect![[r#" a Local FileId(0) 20..21 20..21 - FileId(0) 56..57 Read - FileId(0) 60..61 Read - FileId(0) 68..69 Read + FileId(0) 56..57 read + FileId(0) 60..61 read + FileId(0) 68..69 read "#]], ); } diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 06c981663210..9c86704ba3e4 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1164,8 +1164,8 @@ pub(crate) fn handle_references( .flat_map(|(file_id, refs)| { refs.into_iter() .filter(|&(_, category)| { - (!exclude_imports || category != Some(ReferenceCategory::Import)) - && (!exclude_tests || category != Some(ReferenceCategory::Test)) + (!exclude_imports || !category.contains(ReferenceCategory::IMPORT)) + && (!exclude_tests || !category.contains(ReferenceCategory::TEST)) }) .map(move |(range, _)| FileRange { file_id, range }) }) @@ -1452,7 +1452,7 @@ pub(crate) fn handle_document_highlight( .into_iter() .map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight { range: to_proto::range(&line_index, range), - kind: category.and_then(to_proto::document_highlight_kind), + kind: to_proto::document_highlight_kind(category), }) .collect(); Ok(Some(res)) diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 7e03458c5293..d02f4612dc1e 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -92,12 +92,13 @@ pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolK pub(crate) fn document_highlight_kind( category: ReferenceCategory, ) -> Option { - match category { - ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ), - ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE), - ReferenceCategory::Import => None, - ReferenceCategory::Test => None, + if category.contains(ReferenceCategory::WRITE) { + return Some(lsp_types::DocumentHighlightKind::WRITE); } + if category.contains(ReferenceCategory::READ) { + return Some(lsp_types::DocumentHighlightKind::READ); + } + None } pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {