mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-27 20:35:09 +00:00
Auto merge of #17083 - matklad:matklad/test-search, r=lnicola
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:
commit
d07f0240fd
9 changed files with 188 additions and 132 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -695,6 +695,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"base-db",
|
"base-db",
|
||||||
|
"bitflags 2.4.2",
|
||||||
"cov-mark",
|
"cov-mark",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"either",
|
"either",
|
||||||
|
|
|
@ -1149,8 +1149,14 @@ fn reference_is_exclusive(
|
||||||
node: &dyn HasTokenAtOffset,
|
node: &dyn HasTokenAtOffset,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
) -> bool {
|
) -> 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`
|
// we directly modify variable with set: `n = 0`, `n += 1`
|
||||||
if reference.category == Some(ReferenceCategory::Write) {
|
if reference.category.contains(ReferenceCategory::WRITE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec<Sea
|
||||||
for scope in scopes {
|
for scope in scopes {
|
||||||
let mut search_non_import = |_, r: FileReference| {
|
let mut search_non_import = |_, r: FileReference| {
|
||||||
// The import itself is a use; we must skip that.
|
// The import itself is a use; we must skip that.
|
||||||
if r.category != Some(ReferenceCategory::Import) {
|
if !r.category.contains(ReferenceCategory::IMPORT) {
|
||||||
found = true;
|
found = true;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,6 +26,7 @@ indexmap.workspace = true
|
||||||
memchr = "2.6.4"
|
memchr = "2.6.4"
|
||||||
triomphe.workspace = true
|
triomphe.workspace = true
|
||||||
nohash-hasher.workspace = true
|
nohash-hasher.workspace = true
|
||||||
|
bitflags.workspace = true
|
||||||
|
|
||||||
# local deps
|
# local deps
|
||||||
base-db.workspace = true
|
base-db.workspace = true
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub struct FileReference {
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
/// The node of the reference in the (macro-)file
|
/// The node of the reference in the (macro-)file
|
||||||
pub name: FileReferenceNode,
|
pub name: FileReferenceNode,
|
||||||
pub category: Option<ReferenceCategory>,
|
pub category: ReferenceCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -124,17 +124,16 @@ impl FileReferenceNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
bitflags::bitflags! {
|
||||||
pub enum ReferenceCategory {
|
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
|
||||||
// FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
|
pub struct ReferenceCategory: u8 {
|
||||||
// Create
|
// FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
|
||||||
Write,
|
// const CREATE = 1 << 0;
|
||||||
Read,
|
const WRITE = 1 << 0;
|
||||||
Import,
|
const READ = 1 << 1;
|
||||||
// FIXME: Some day should be able to search in doc comments. Would probably
|
const IMPORT = 1 << 2;
|
||||||
// need to switch from enum to bitflags then?
|
const TEST = 1 << 3;
|
||||||
// DocComment
|
}
|
||||||
Test,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generally, `search_scope` returns files that might contain references for the element.
|
/// Generally, `search_scope` returns files that might contain references for the element.
|
||||||
|
@ -660,7 +659,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::NameRef(name_ref.clone()),
|
name: FileReferenceNode::NameRef(name_ref.clone()),
|
||||||
category: None,
|
category: ReferenceCategory::empty(),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -676,10 +675,15 @@ impl<'a> FindUsages<'a> {
|
||||||
match NameRefClass::classify(self.sema, name_ref) {
|
match NameRefClass::classify(self.sema, name_ref) {
|
||||||
Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
|
Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
|
||||||
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
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 {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::NameRef(name_ref.clone()),
|
name: FileReferenceNode::NameRef(name_ref.clone()),
|
||||||
category: is_name_ref_in_import(name_ref).then_some(ReferenceCategory::Import),
|
category,
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -700,7 +704,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::FormatStringEntry(token, range),
|
name: FileReferenceNode::FormatStringEntry(token, range),
|
||||||
category: Some(ReferenceCategory::Read),
|
category: ReferenceCategory::READ,
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -719,7 +723,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::Lifetime(lifetime.clone()),
|
name: FileReferenceNode::Lifetime(lifetime.clone()),
|
||||||
category: None,
|
category: ReferenceCategory::empty(),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -817,7 +821,7 @@ impl<'a> FindUsages<'a> {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::Name(name.clone()),
|
name: FileReferenceNode::Name(name.clone()),
|
||||||
// FIXME: mutable patterns should have `Write` access
|
// FIXME: mutable patterns should have `Write` access
|
||||||
category: Some(ReferenceCategory::Read),
|
category: ReferenceCategory::READ,
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -826,7 +830,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::Name(name.clone()),
|
name: FileReferenceNode::Name(name.clone()),
|
||||||
category: None,
|
category: ReferenceCategory::empty(),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -851,7 +855,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::Name(name.clone()),
|
name: FileReferenceNode::Name(name.clone()),
|
||||||
category: None,
|
category: ReferenceCategory::empty(),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -875,38 +879,41 @@ impl ReferenceCategory {
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
def: &Definition,
|
def: &Definition,
|
||||||
r: &ast::NameRef,
|
r: &ast::NameRef,
|
||||||
) -> Option<ReferenceCategory> {
|
) -> ReferenceCategory {
|
||||||
|
let mut result = ReferenceCategory::empty();
|
||||||
if is_name_ref_in_test(sema, r) {
|
if is_name_ref_in_test(sema, r) {
|
||||||
return Some(ReferenceCategory::Test);
|
result |= ReferenceCategory::TEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only Locals and Fields have accesses for now.
|
// Only Locals and Fields have accesses for now.
|
||||||
if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
|
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| {
|
let mode = r.syntax().ancestors().find_map(|node| {
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::BinExpr(expr) => {
|
ast::BinExpr(expr) => {
|
||||||
if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
|
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).
|
// If the variable or field ends on the LHS's end then it's a Write
|
||||||
// FIXME: This is not terribly accurate.
|
// (covers fields and locals). FIXME: This is not terribly accurate.
|
||||||
if let Some(lhs) = expr.lhs() {
|
if let Some(lhs) = expr.lhs() {
|
||||||
if lhs.syntax().text_range().end() == r.syntax().text_range().end() {
|
if lhs.syntax().text_range().end() == r.syntax().text_range().end() {
|
||||||
return Some(ReferenceCategory::Write);
|
return Some(ReferenceCategory::WRITE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Some(ReferenceCategory::READ)
|
||||||
Some(ReferenceCategory::Read)
|
},
|
||||||
},
|
_ => None,
|
||||||
_ => None
|
}
|
||||||
}
|
}
|
||||||
}
|
}).unwrap_or(ReferenceCategory::READ);
|
||||||
});
|
|
||||||
|
|
||||||
// Default Locals and Fields to read
|
result | mode
|
||||||
mode.or(Some(ReferenceCategory::Read))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub struct HighlightedRange {
|
||||||
// FIXME: This needs to be more precise. Reference category makes sense only
|
// 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
|
// for references, but we also have defs. And things like exit points are
|
||||||
// neither.
|
// neither.
|
||||||
pub category: Option<ReferenceCategory>,
|
pub category: ReferenceCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
|
@ -113,7 +113,11 @@ fn highlight_closure_captures(
|
||||||
range,
|
range,
|
||||||
category,
|
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
|
local
|
||||||
.sources(sema.db)
|
.sources(sema.db)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -137,7 +141,9 @@ fn highlight_references(
|
||||||
{
|
{
|
||||||
match resolution.map(Definition::from) {
|
match resolution.map(Definition::from) {
|
||||||
Some(def) => iter::once(def).collect(),
|
Some(def) => iter::once(def).collect(),
|
||||||
None => return Some(vec![HighlightedRange { range, category: None }]),
|
None => {
|
||||||
|
return Some(vec![HighlightedRange { range, category: ReferenceCategory::empty() }])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
find_defs(sema, token.clone())
|
find_defs(sema, token.clone())
|
||||||
|
@ -211,7 +217,11 @@ fn highlight_references(
|
||||||
// highlight the defs themselves
|
// highlight the defs themselves
|
||||||
match def {
|
match def {
|
||||||
Definition::Local(local) => {
|
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
|
local
|
||||||
.sources(sema.db)
|
.sources(sema.db)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -238,8 +248,11 @@ fn highlight_references(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let hl_range = nav.focus_range.map(|range| {
|
let hl_range = nav.focus_range.map(|range| {
|
||||||
let category = matches!(def, Definition::Local(l) if l.is_mut(sema.db))
|
let category = if matches!(def, Definition::Local(l) if l.is_mut(sema.db)) {
|
||||||
.then_some(ReferenceCategory::Write);
|
ReferenceCategory::WRITE
|
||||||
|
} else {
|
||||||
|
ReferenceCategory::empty()
|
||||||
|
};
|
||||||
HighlightedRange { range, category }
|
HighlightedRange { range, category }
|
||||||
});
|
});
|
||||||
if let Some(hl_range) = hl_range {
|
if let Some(hl_range) = hl_range {
|
||||||
|
@ -272,24 +285,30 @@ fn highlight_exit_points(
|
||||||
def_ranges
|
def_ranges
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|range| HighlightedRange { category: None, range }),
|
.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }),
|
||||||
);
|
);
|
||||||
let body = body?;
|
let body = body?;
|
||||||
walk_expr(&body, &mut |expr| match expr {
|
walk_expr(&body, &mut |expr| match expr {
|
||||||
ast::Expr::ReturnExpr(expr) => {
|
ast::Expr::ReturnExpr(expr) => {
|
||||||
if let Some(token) = expr.return_token() {
|
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_) => {
|
ast::Expr::TryExpr(try_) => {
|
||||||
if let Some(token) = try_.question_mark_token() {
|
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(_) => {
|
ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => {
|
||||||
if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) {
|
if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) {
|
||||||
highlights.push(HighlightedRange {
|
highlights.push(HighlightedRange {
|
||||||
category: None,
|
category: ReferenceCategory::empty(),
|
||||||
range: expr.syntax().text_range(),
|
range: expr.syntax().text_range(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -309,7 +328,7 @@ fn highlight_exit_points(
|
||||||
.map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
|
.map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()),
|
||||||
_ => tail.syntax().text_range(),
|
_ => tail.syntax().text_range(),
|
||||||
};
|
};
|
||||||
highlights.push(HighlightedRange { category: None, range })
|
highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(highlights)
|
Some(highlights)
|
||||||
|
@ -354,7 +373,9 @@ fn highlight_break_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
|
||||||
token.map(|tok| tok.text_range()),
|
token.map(|tok| tok.text_range()),
|
||||||
label.as_ref().map(|it| it.syntax().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| {
|
for_each_break_and_continue_expr(label, body, &mut |expr| {
|
||||||
let range: Option<TextRange> = match (cursor_token_kind, expr) {
|
let range: Option<TextRange> = match (cursor_token_kind, expr) {
|
||||||
(T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => {
|
(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,
|
_ => None,
|
||||||
};
|
};
|
||||||
highlights.extend(range.map(|range| HighlightedRange { category: None, range }));
|
highlights.extend(
|
||||||
|
range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
Some(highlights)
|
Some(highlights)
|
||||||
}
|
}
|
||||||
|
@ -430,14 +453,18 @@ fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
|
||||||
async_token: Option<SyntaxToken>,
|
async_token: Option<SyntaxToken>,
|
||||||
body: Option<ast::Expr>,
|
body: Option<ast::Expr>,
|
||||||
) -> Option<Vec<HighlightedRange>> {
|
) -> Option<Vec<HighlightedRange>> {
|
||||||
let mut highlights =
|
let mut highlights = vec![HighlightedRange {
|
||||||
vec![HighlightedRange { category: None, range: async_token?.text_range() }];
|
category: ReferenceCategory::empty(),
|
||||||
|
range: async_token?.text_range(),
|
||||||
|
}];
|
||||||
if let Some(body) = body {
|
if let Some(body) = body {
|
||||||
walk_expr(&body, &mut |expr| {
|
walk_expr(&body, &mut |expr| {
|
||||||
if let ast::Expr::AwaitExpr(expr) = expr {
|
if let ast::Expr::AwaitExpr(expr) = expr {
|
||||||
if let Some(token) = expr.await_token() {
|
if let Some(token) = expr.await_token() {
|
||||||
highlights
|
highlights.push(HighlightedRange {
|
||||||
.push(HighlightedRange { category: None, range: token.text_range() });
|
category: ReferenceCategory::empty(),
|
||||||
|
range: token.text_range(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -481,6 +508,8 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::fixture;
|
use crate::fixture;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -504,28 +533,18 @@ mod tests {
|
||||||
|
|
||||||
let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
|
let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
|
||||||
|
|
||||||
let mut expected = annotations
|
let mut expected =
|
||||||
.into_iter()
|
annotations.into_iter().map(|(r, access)| (r.range, access)).collect::<Vec<_>>();
|
||||||
.map(|(r, access)| (r.range, (!access.is_empty()).then_some(access)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut actual = hls
|
let mut actual: Vec<(TextRange, String)> = hls
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|hl| {
|
.map(|hl| {
|
||||||
(
|
(
|
||||||
hl.range,
|
hl.range,
|
||||||
hl.category.map(|it| {
|
hl.category.iter_names().map(|(name, _flag)| name.to_lowercase()).join(","),
|
||||||
match it {
|
|
||||||
ReferenceCategory::Read => "read",
|
|
||||||
ReferenceCategory::Write => "write",
|
|
||||||
ReferenceCategory::Import => "import",
|
|
||||||
ReferenceCategory::Test => "test",
|
|
||||||
}
|
|
||||||
.to_owned()
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
actual.sort_by_key(|(range, _)| range.start());
|
actual.sort_by_key(|(range, _)| range.start());
|
||||||
expected.sort_by_key(|(range, _)| range.start());
|
expected.sort_by_key(|(range, _)| range.start());
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ReferenceSearchResult {
|
pub struct ReferenceSearchResult {
|
||||||
pub declaration: Option<Declaration>,
|
pub declaration: Option<Declaration>,
|
||||||
pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
|
pub references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -66,7 +66,7 @@ pub(crate) fn find_all_refs(
|
||||||
retain_adt_literal_usages(&mut usages, def, sema);
|
retain_adt_literal_usages(&mut usages, def, sema);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut references = usages
|
let mut references: IntMap<FileId, Vec<(TextRange, ReferenceCategory)>> = usages
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(file_id, refs)| {
|
.map(|(file_id, refs)| {
|
||||||
(
|
(
|
||||||
|
@ -77,7 +77,7 @@ pub(crate) fn find_all_refs(
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<IntMap<_, Vec<_>>>();
|
.collect();
|
||||||
let declaration = match def {
|
let declaration = match def {
|
||||||
Definition::Module(module) => {
|
Definition::Module(module) => {
|
||||||
Some(NavigationTarget::from_module_to_decl(sema.db, module))
|
Some(NavigationTarget::from_module_to_decl(sema.db, module))
|
||||||
|
@ -93,7 +93,7 @@ pub(crate) fn find_all_refs(
|
||||||
references
|
references
|
||||||
.entry(extra_ref.file_id)
|
.entry(extra_ref.file_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((extra_ref.focus_or_full_range(), None));
|
.push((extra_ref.focus_or_full_range(), ReferenceCategory::empty()));
|
||||||
}
|
}
|
||||||
Declaration {
|
Declaration {
|
||||||
is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
use ide_db::{base_db::FileId, search::ReferenceCategory};
|
use ide_db::base_db::FileId;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
|
|
||||||
use crate::{fixture, SearchScope};
|
use crate::{fixture, SearchScope};
|
||||||
|
@ -324,7 +324,7 @@ fn test() {
|
||||||
test_func Function FileId(0) 0..17 3..12
|
test_func Function FileId(0) 0..17 3..12
|
||||||
|
|
||||||
FileId(0) 35..44
|
FileId(0) 35..44
|
||||||
FileId(0) 75..84 Test
|
FileId(0) 75..84 test
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -345,7 +345,28 @@ fn test() {
|
||||||
test_func Function FileId(0) 0..17 3..12
|
test_func Function FileId(0) 0..17 3..12
|
||||||
|
|
||||||
FileId(0) 35..44
|
FileId(0) 35..44
|
||||||
FileId(0) 96..105 Test
|
FileId(0) 96..105 test
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_access() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S { f$0: u32 }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut x = S { f: 92 };
|
||||||
|
x.f = 92;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
f Field FileId(0) 11..17 11..12
|
||||||
|
|
||||||
|
FileId(0) 61..62 read test
|
||||||
|
FileId(0) 76..77 write test
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -600,12 +621,12 @@ fn main() {
|
||||||
i = 5;
|
i = 5;
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
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) 50..51 write
|
||||||
FileId(0) 54..55 Read
|
FileId(0) 54..55 read
|
||||||
FileId(0) 76..77 Write
|
FileId(0) 76..77 write
|
||||||
FileId(0) 94..95 Write
|
FileId(0) 94..95 write
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -626,8 +647,8 @@ fn bar() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
spam Local FileId(0) 19..23 19..23
|
spam Local FileId(0) 19..23 19..23
|
||||||
|
|
||||||
FileId(0) 34..38 Read
|
FileId(0) 34..38 read
|
||||||
FileId(0) 41..45 Read
|
FileId(0) 41..45 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -641,7 +662,7 @@ fn foo(i : u32) -> u32 { i$0 }
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
i ValueParam FileId(0) 7..8 7..8
|
i ValueParam FileId(0) 7..8 7..8
|
||||||
|
|
||||||
FileId(0) 25..26 Read
|
FileId(0) 25..26 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -655,7 +676,7 @@ fn foo(i$0 : u32) -> u32 { i }
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
i ValueParam FileId(0) 7..8 7..8
|
i ValueParam FileId(0) 7..8 7..8
|
||||||
|
|
||||||
FileId(0) 25..26 Read
|
FileId(0) 25..26 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -676,7 +697,7 @@ fn main(s: Foo) {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
spam Field FileId(0) 17..30 21..25
|
spam Field FileId(0) 17..30 21..25
|
||||||
|
|
||||||
FileId(0) 67..71 Read
|
FileId(0) 67..71 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -824,7 +845,7 @@ pub struct Foo {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
foo Module FileId(0) 0..8 4..7
|
foo Module FileId(0) 0..8 4..7
|
||||||
|
|
||||||
FileId(0) 14..17 Import
|
FileId(0) 14..17 import
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -842,7 +863,7 @@ use self$0;
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
foo Module FileId(0) 0..8 4..7
|
foo Module FileId(0) 0..8 4..7
|
||||||
|
|
||||||
FileId(1) 4..8 Import
|
FileId(1) 4..8 import
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -857,7 +878,7 @@ use self$0;
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Module FileId(0) 0..10
|
Module FileId(0) 0..10
|
||||||
|
|
||||||
FileId(0) 4..8 Import
|
FileId(0) 4..8 import
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -885,7 +906,7 @@ pub(super) struct Foo$0 {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Foo Struct FileId(2) 0..41 18..21 some
|
Foo Struct FileId(2) 0..41 18..21 some
|
||||||
|
|
||||||
FileId(1) 20..23 Import
|
FileId(1) 20..23 import
|
||||||
FileId(1) 47..50
|
FileId(1) 47..50
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -960,10 +981,10 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
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) 34..35 write
|
||||||
FileId(0) 38..39 Read
|
FileId(0) 38..39 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -984,8 +1005,8 @@ fn foo() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
f Field FileId(0) 15..21 15..16
|
f Field FileId(0) 15..21 15..16
|
||||||
|
|
||||||
FileId(0) 55..56 Read
|
FileId(0) 55..56 read
|
||||||
FileId(0) 68..69 Write
|
FileId(0) 68..69 write
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1002,7 +1023,7 @@ fn foo() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
i Local FileId(0) 19..20 19..20
|
i Local FileId(0) 19..20 19..20
|
||||||
|
|
||||||
FileId(0) 26..27 Write
|
FileId(0) 26..27 write
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1048,7 +1069,7 @@ fn g() { f(); }
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
f Function FileId(0) 22..31 25..26
|
f Function FileId(0) 22..31 25..26
|
||||||
|
|
||||||
FileId(1) 11..12 Import
|
FileId(1) 11..12 import
|
||||||
FileId(1) 24..25
|
FileId(1) 24..25
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -1071,7 +1092,7 @@ fn f(s: S) {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
field Field FileId(0) 15..24 15..20
|
field Field FileId(0) 15..24 15..20
|
||||||
|
|
||||||
FileId(0) 68..73 Read
|
FileId(0) 68..73 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1095,7 +1116,7 @@ fn f(e: En) {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
field Field FileId(0) 32..41 32..37
|
field Field FileId(0) 32..41 32..37
|
||||||
|
|
||||||
FileId(0) 102..107 Read
|
FileId(0) 102..107 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1119,7 +1140,7 @@ fn f() -> m::En {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
field Field FileId(0) 56..65 56..61
|
field Field FileId(0) 56..65 56..61
|
||||||
|
|
||||||
FileId(0) 125..130 Read
|
FileId(0) 125..130 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1144,8 +1165,8 @@ impl Foo {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
self SelfParam FileId(0) 47..51 47..51
|
self SelfParam FileId(0) 47..51 47..51
|
||||||
|
|
||||||
FileId(0) 71..75 Read
|
FileId(0) 71..75 read
|
||||||
FileId(0) 152..156 Read
|
FileId(0) 152..156 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1165,7 +1186,7 @@ impl Foo {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
self SelfParam FileId(0) 47..51 47..51
|
self SelfParam FileId(0) 47..51 47..51
|
||||||
|
|
||||||
FileId(0) 63..67 Read
|
FileId(0) 63..67 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1185,16 +1206,16 @@ impl Foo {
|
||||||
if let Some(decl) = refs.declaration {
|
if let Some(decl) = refs.declaration {
|
||||||
format_to!(actual, "{}", decl.nav.debug_render());
|
format_to!(actual, "{}", decl.nav.debug_render());
|
||||||
if decl.is_mut {
|
if decl.is_mut {
|
||||||
format_to!(actual, " {:?}", ReferenceCategory::Write)
|
format_to!(actual, " write",)
|
||||||
}
|
}
|
||||||
actual += "\n\n";
|
actual += "\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (file_id, references) in &refs.references {
|
for (file_id, references) in &refs.references {
|
||||||
for (range, access) in references {
|
for (range, category) in references {
|
||||||
format_to!(actual, "{:?} {:?}", file_id, range);
|
format_to!(actual, "{:?} {:?}", file_id, range);
|
||||||
if let Some(access) = access {
|
for (name, _flag) in category.iter_names() {
|
||||||
format_to!(actual, " {:?}", access);
|
format_to!(actual, " {}", name.to_lowercase());
|
||||||
}
|
}
|
||||||
actual += "\n";
|
actual += "\n";
|
||||||
}
|
}
|
||||||
|
@ -1281,7 +1302,7 @@ fn main() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
a Local FileId(0) 59..60 59..60
|
a Local FileId(0) 59..60 59..60
|
||||||
|
|
||||||
FileId(0) 80..81 Read
|
FileId(0) 80..81 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1299,7 +1320,7 @@ fn main() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
a Local FileId(0) 59..60 59..60
|
a Local FileId(0) 59..60 59..60
|
||||||
|
|
||||||
FileId(0) 80..81 Read
|
FileId(0) 80..81 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1479,7 +1500,7 @@ fn test$0() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
test Function FileId(0) 0..33 11..15
|
test Function FileId(0) 0..33 11..15
|
||||||
|
|
||||||
FileId(0) 24..28 Test
|
FileId(0) 24..28 test
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1538,9 +1559,9 @@ pub use level1::Foo;
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Foo Struct FileId(0) 0..15 11..14
|
Foo Struct FileId(0) 0..15 11..14
|
||||||
|
|
||||||
FileId(1) 16..19 Import
|
FileId(1) 16..19 import
|
||||||
FileId(2) 16..19 Import
|
FileId(2) 16..19 import
|
||||||
FileId(3) 16..19 Import
|
FileId(3) 16..19 import
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1568,7 +1589,7 @@ lib::foo!();
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
foo Macro FileId(1) 0..61 29..32
|
foo Macro FileId(1) 0..61 29..32
|
||||||
|
|
||||||
FileId(0) 46..49 Import
|
FileId(0) 46..49 import
|
||||||
FileId(2) 0..3
|
FileId(2) 0..3
|
||||||
FileId(3) 5..8
|
FileId(3) 5..8
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -1731,7 +1752,7 @@ struct Foo;
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
derive_identity Derive FileId(2) 1..107 45..60
|
derive_identity Derive FileId(2) 1..107 45..60
|
||||||
|
|
||||||
FileId(0) 17..31 Import
|
FileId(0) 17..31 import
|
||||||
FileId(0) 56..70
|
FileId(0) 56..70
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -2055,7 +2076,7 @@ fn method() {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
method Field FileId(0) 60..70 60..66
|
method Field FileId(0) 60..70 60..66
|
||||||
|
|
||||||
FileId(0) 136..142 Read
|
FileId(0) 136..142 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
|
@ -2101,7 +2122,7 @@ fn method() {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
method Field FileId(0) 60..70 60..66
|
method Field FileId(0) 60..70 60..66
|
||||||
|
|
||||||
FileId(0) 136..142 Read
|
FileId(0) 136..142 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
|
@ -2160,9 +2181,9 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
a Local FileId(0) 20..21 20..21
|
a Local FileId(0) 20..21 20..21
|
||||||
|
|
||||||
FileId(0) 56..57 Read
|
FileId(0) 56..57 read
|
||||||
FileId(0) 60..61 Read
|
FileId(0) 60..61 read
|
||||||
FileId(0) 68..69 Read
|
FileId(0) 68..69 read
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1164,8 +1164,8 @@ pub(crate) fn handle_references(
|
||||||
.flat_map(|(file_id, refs)| {
|
.flat_map(|(file_id, refs)| {
|
||||||
refs.into_iter()
|
refs.into_iter()
|
||||||
.filter(|&(_, category)| {
|
.filter(|&(_, category)| {
|
||||||
(!exclude_imports || category != Some(ReferenceCategory::Import))
|
(!exclude_imports || !category.contains(ReferenceCategory::IMPORT))
|
||||||
&& (!exclude_tests || category != Some(ReferenceCategory::Test))
|
&& (!exclude_tests || !category.contains(ReferenceCategory::TEST))
|
||||||
})
|
})
|
||||||
.map(move |(range, _)| FileRange { file_id, range })
|
.map(move |(range, _)| FileRange { file_id, range })
|
||||||
})
|
})
|
||||||
|
@ -1452,7 +1452,7 @@ pub(crate) fn handle_document_highlight(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight {
|
.map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight {
|
||||||
range: to_proto::range(&line_index, range),
|
range: to_proto::range(&line_index, range),
|
||||||
kind: category.and_then(to_proto::document_highlight_kind),
|
kind: to_proto::document_highlight_kind(category),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
|
|
|
@ -92,12 +92,13 @@ pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolK
|
||||||
pub(crate) fn document_highlight_kind(
|
pub(crate) fn document_highlight_kind(
|
||||||
category: ReferenceCategory,
|
category: ReferenceCategory,
|
||||||
) -> Option<lsp_types::DocumentHighlightKind> {
|
) -> Option<lsp_types::DocumentHighlightKind> {
|
||||||
match category {
|
if category.contains(ReferenceCategory::WRITE) {
|
||||||
ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
|
return Some(lsp_types::DocumentHighlightKind::WRITE);
|
||||||
ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
|
|
||||||
ReferenceCategory::Import => None,
|
|
||||||
ReferenceCategory::Test => None,
|
|
||||||
}
|
}
|
||||||
|
if category.contains(ReferenceCategory::READ) {
|
||||||
|
return Some(lsp_types::DocumentHighlightKind::READ);
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
|
pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
|
||||||
|
|
Loading…
Reference in a new issue