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.
This commit is contained in:
Alex Kladov 2024-04-16 16:10:36 +01:00
parent 1179c3ee83
commit 9bd8eee21e
9 changed files with 167 additions and 132 deletions

View file

@ -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<ReferenceCategory>,
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<Vec<HighlightedRange>> {
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<TextRange> = 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<Vec<HighlightedRange>> {
),
_ => 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<Vec<HighlightedRange>> {
async_token: Option<SyntaxToken>,
body: Option<ast::Expr>,
) -> Option<Vec<HighlightedRange>> {
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::<Vec<_>>();
let mut expected =
annotations.into_iter().map(|(r, access)| (r.range, access)).collect::<Vec<_>>();
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::<Vec<_>>();
.collect();
actual.sort_by_key(|(range, _)| range.start());
expected.sort_by_key(|(range, _)| range.start());

View file

@ -30,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
pub references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>>,
}
#[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<FileId, Vec<(TextRange, ReferenceCategory)>> = usages
.into_iter()
.map(|(file_id, refs)| {
(
@ -77,7 +77,7 @@ pub(crate) fn find_all_refs(
.collect(),
)
})
.collect::<IntMap<_, Vec<_>>>();
.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
"#]],
);
}