This commit is contained in:
Lukas Wirth 2021-07-15 21:28:30 +02:00
parent ed73460971
commit 4450365ec8
4 changed files with 111 additions and 112 deletions

View File

@ -105,31 +105,24 @@ fn impls_for_trait_item(
#[cfg(test)]
mod tests {
use ide_db::base_db::FileRange;
use itertools::Itertools;
use crate::fixture;
fn check(ra_fixture: &str) {
let (analysis, position, annotations) = fixture::annotations(ra_fixture);
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
let key = |frange: &FileRange| (frange.file_id, frange.range.start());
let cmp = |frange: &FileRange| (frange.file_id, frange.range.start());
let mut expected = annotations
.into_iter()
.map(|(range, data)| {
assert!(data.is_empty());
range
})
.collect::<Vec<_>>();
expected.sort_by_key(key);
let mut actual = navs
let actual = navs
.into_iter()
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
.sorted_by_key(cmp)
.collect::<Vec<_>>();
actual.sort_by_key(key);
let expected =
expected.into_iter().map(|(range, _)| range).sorted_by_key(cmp).collect::<Vec<_>>();
assert_eq!(expected, actual);
}

View File

@ -1,6 +1,6 @@
//! rust-analyzer is lazy and doesn't not compute anything unless asked. This
//! rust-analyzer is lazy and doesn't compute anything unless asked. This
//! sometimes is counter productive when, for example, the first goto definition
//! request takes longer to compute. This modules implemented prepopulating of
//! request takes longer to compute. This modules implemented prepopulation of
//! various caches, it's not really advanced at the moment.
use hir::db::DefDatabase;
@ -27,7 +27,7 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
let topo = &graph.crates_in_topological_order();
cb(PrimeCachesProgress::Started);
// Take care to emit the finish signal even when the computation is canceled.
// Take care to emit the finish signal even when the computation is canceled.
let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
// FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.

View File

@ -13,7 +13,7 @@
use ide_db::{
base_db::FileId,
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceAccess, SearchScope},
search::{ReferenceAccess, SearchScope, UsageSearchResult},
RootDatabase,
};
use rustc_hash::FxHashMap;
@ -56,48 +56,20 @@ pub(crate) fn find_all_refs(
let _p = profile::span("find_all_refs");
let syntax = sema.parse(position.file_id).syntax().clone();
let (def, is_literal_search) =
if let Some(name) = get_name_of_item_declaration(&syntax, position) {
(
match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
Definition::Field(field_ref)
}
},
true,
)
} else {
(find_def(sema, &syntax, position.offset)?, false)
};
let mut is_literal_search = false;
let def = if let Some(name) = name_for_constructor_search(&syntax, position) {
is_literal_search = true;
match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
Definition::Field(field_ref)
}
}
} else {
find_def(sema, &syntax, position.offset)?
};
let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
if is_literal_search {
// filter for constructor-literals
let refs = usages.references.values_mut();
match def {
Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(enum_))) => {
refs.for_each(|it| {
it.retain(|reference| {
reference
.name
.as_name_ref()
.map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))
})
});
usages.references.retain(|_, it| !it.is_empty());
}
Definition::ModuleDef(hir::ModuleDef::Adt(_) | hir::ModuleDef::Variant(_)) => {
refs.for_each(|it| {
it.retain(|reference| {
reference.name.as_name_ref().map_or(false, is_lit_name_ref)
})
});
usages.references.retain(|_, it| !it.is_empty());
}
_ => {}
}
}
let declaration = match def {
Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
Some(NavigationTarget::from_module_to_decl(sema.db, module))
@ -108,6 +80,10 @@ pub(crate) fn find_all_refs(
let decl_range = nav.focus_or_full_range();
Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
});
if is_literal_search {
retain_adt_literal_usages(&mut usages, def, sema);
}
let references = usages
.into_iter()
.map(|(file_id, refs)| {
@ -174,7 +150,37 @@ pub(crate) fn decl_access(
None
}
fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
/// Filter out all non-literal usages for adt-defs
fn retain_adt_literal_usages(
usages: &mut UsageSearchResult,
def: Definition,
sema: &Semantics<RootDatabase>,
) {
let refs = usages.references.values_mut();
match def {
Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(enum_))) => {
refs.for_each(|it| {
it.retain(|reference| {
reference
.name
.as_name_ref()
.map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))
})
});
usages.references.retain(|_, it| !it.is_empty());
}
Definition::ModuleDef(hir::ModuleDef::Adt(_) | hir::ModuleDef::Variant(_)) => {
refs.for_each(|it| {
it.retain(|reference| reference.name.as_name_ref().map_or(false, is_lit_name_ref))
});
usages.references.retain(|_, it| !it.is_empty());
}
_ => {}
}
}
/// Returns `Some` if the cursor is at a position for an item to search for all its constructor/literal usages
fn name_for_constructor_search(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
let token = syntax.token_at_offset(position.offset).right_biased()?;
let token_parent = token.parent()?;
let kind = token.kind();

View File

@ -17,7 +17,7 @@
use rustc_hash::FxHashMap;
use syntax::{
ast::{self, HasFormatSpecifier},
AstNode, AstToken, Direction, NodeOrToken,
match_ast, AstNode, AstToken, Direction, NodeOrToken,
SyntaxKind::*,
SyntaxNode, TextRange, WalkEvent, T,
};
@ -159,15 +159,16 @@ pub(crate) fn highlight(
// Determine the root based on the given range.
let (root, range_to_highlight) = {
let source_file = sema.parse(file_id);
let source_file = source_file.syntax();
match range_to_highlight {
Some(range) => {
let node = match source_file.syntax().covering_element(range) {
let node = match source_file.covering_element(range) {
NodeOrToken::Node(it) => it,
NodeOrToken::Token(it) => it.parent().unwrap(),
NodeOrToken::Token(it) => it.parent().unwrap_or_else(|| source_file.clone()),
};
(node, range)
}
None => (source_file.syntax().clone(), source_file.syntax().text_range()),
None => (source_file.clone(), source_file.text_range()),
}
};
@ -211,62 +212,61 @@ fn traverse(
continue;
}
// Track "inside macro" state
match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
WalkEvent::Enter(Some(mc)) => {
if let Some(range) = macro_call_range(&mc) {
hl.add(HlRange {
range,
highlight: HlTag::Symbol(SymbolKind::Macro).into(),
binding_hash: None,
});
}
current_macro_call = Some(mc.clone());
continue;
}
WalkEvent::Leave(Some(mc)) => {
assert_eq!(current_macro_call, Some(mc));
current_macro_call = None;
}
_ => (),
}
match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) {
WalkEvent::Enter(Some(item)) => {
if sema.is_attr_macro_call(&item) {
current_attr_macro_call = Some(item);
match event.clone() {
WalkEvent::Enter(NodeOrToken::Node(node)) => {
match_ast! {
match node {
ast::MacroCall(mcall) => {
if let Some(range) = macro_call_range(&mcall) {
hl.add(HlRange {
range,
highlight: HlTag::Symbol(SymbolKind::Macro).into(),
binding_hash: None,
});
}
current_macro_call = Some(mcall);
continue;
},
ast::Macro(mac) => {
macro_highlighter.init();
current_macro = Some(mac);
continue;
},
ast::Item(item) => {
if sema.is_attr_macro_call(&item) {
current_attr_macro_call = Some(item);
}
},
ast::Attr(__) => inside_attribute = true,
_ => ()
}
}
}
WalkEvent::Leave(Some(item)) => {
if current_attr_macro_call == Some(item) {
current_attr_macro_call = None;
WalkEvent::Leave(NodeOrToken::Node(node)) => {
match_ast! {
match node {
ast::MacroCall(mcall) => {
assert_eq!(current_macro_call, Some(mcall));
current_macro_call = None;
},
ast::Macro(mac) => {
assert_eq!(current_macro, Some(mac));
current_macro = None;
macro_highlighter = MacroHighlighter::default();
},
ast::Item(item) => {
if current_attr_macro_call == Some(item) {
current_attr_macro_call = None;
}
},
ast::Attr(__) => inside_attribute = false,
_ => ()
}
}
}
_ => (),
}
match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
WalkEvent::Enter(Some(mac)) => {
macro_highlighter.init();
current_macro = Some(mac);
continue;
}
WalkEvent::Leave(Some(mac)) => {
assert_eq!(current_macro, Some(mac));
current_macro = None;
macro_highlighter = MacroHighlighter::default();
}
_ => (),
}
match &event {
WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
inside_attribute = true
}
WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
inside_attribute = false
}
_ => (),
}
let element = match event {
WalkEvent::Enter(it) => it,
WalkEvent::Leave(it) => {