11069: fix: Partially fix `ide_db::search` for crate roots r=Veykril a=Veykril

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10007
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-12-20 16:50:35 +00:00 committed by GitHub
commit d3aa2579cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 34 deletions

View file

@ -97,6 +97,10 @@ pub fn new(name: &str) -> Result<CrateName, &str> {
pub fn normalize_dashes(name: &str) -> CrateName {
Self(SmolStr::new(name.replace('-', "_")))
}
pub fn as_smol_str(&self) -> &SmolStr {
&self.0
}
}
impl fmt::Display for CrateName {

View file

@ -378,7 +378,22 @@ fn test_hl_module() {
fn test_hl_self_in_crate_root() {
check(
r#"
use self$0;
use crate$0;
//^^^^^
use self;
//^^^^
mod __ {
use super;
//^^^^^
}
"#,
);
check(
r#"
//- /main.rs crate:main deps:lib
use lib$0;
//^^^
//- /lib.rs crate:lib
"#,
);
}

View file

@ -756,7 +756,7 @@ fn test_find_all_refs_decl_module_on_self_crate_root() {
expect![[r#"
Module FileId(0) 0..10
(no references)
FileId(0) 4..8
"#]],
);
}

View file

@ -4,7 +4,7 @@
//! get a super-set of matches. Then, we we confirm each match using precise
//! name resolution.
use std::{convert::TryInto, mem};
use std::{convert::TryInto, mem, sync::Arc};
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{
@ -376,32 +376,63 @@ fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
}
};
let name = self.def.name(sema.db).or_else(|| {
self.include_self_kw_refs.as_ref().and_then(|ty| {
ty.as_adt()
.map(|adt| adt.name(self.sema.db))
.or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
})
});
let name = match name {
Some(name) => name.to_smol_str(),
let name = match self.def {
// special case crate modules as these do not have a proper name
Definition::Module(module) if module.crate_root(self.sema.db) == module => {
// FIXME: This assumes the crate name is always equal to its display name when it really isn't
module
.krate()
.display_name(self.sema.db)
.map(|crate_name| crate_name.crate_name().as_smol_str().clone())
}
_ => {
let self_kw_refs = || {
self.include_self_kw_refs.as_ref().and_then(|ty| {
ty.as_adt()
.map(|adt| adt.name(self.sema.db))
.or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
})
};
self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.to_smol_str())
}
};
let name = match &name {
Some(s) => s.as_str(),
None => return,
};
let name = name.as_str();
for (file_id, search_range) in search_scope {
let text = sema.db.file_text(file_id);
let search_range =
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
for (idx, _) in text.match_indices(name) {
// these can't be closures because rust infers the lifetimes wrong ...
fn match_indices<'a>(
text: &'a str,
name: &'a str,
search_range: TextRange,
) -> impl Iterator<Item = TextSize> + 'a {
text.match_indices(name).filter_map(move |(idx, _)| {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
return None;
}
Some(offset)
})
}
fn scope_files<'a>(
sema: &'a Semantics<RootDatabase>,
scope: &'a SearchScope,
) -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a {
scope.entries.iter().map(|(&file_id, &search_range)| {
let text = sema.db.file_text(file_id);
let search_range =
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
(text, file_id, search_range)
})
}
for (text, file_id, search_range) in scope_files(sema, &search_scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
for offset in match_indices(&text, name, search_range) {
for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if match name {
ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
@ -412,13 +443,9 @@ fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
}
}
}
// Search for occurrences of the `Self` referring to our type
if let Some(self_ty) = &self.include_self_kw_refs {
for (idx, _) in text.match_indices("Self") {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
}
for offset in match_indices(&text, "Self", search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
return;
@ -428,6 +455,37 @@ fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
}
}
// Search for `super` and `crate` resolving to our module
match self.def {
Definition::Module(module) => {
let scope = search_scope.intersection(&SearchScope::module(self.sema.db, module));
let is_crate_root = module.crate_root(self.sema.db) == module;
for (text, file_id, search_range) in scope_files(sema, &scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
for offset in match_indices(&text, "super", search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_name_ref(&name_ref, sink) {
return;
}
}
}
if is_crate_root {
for offset in match_indices(&text, "crate", search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_name_ref(&name_ref, sink) {
return;
}
}
}
}
}
}
_ => (),
}
// search for module `self` references in our module's definition source
match self.def {
Definition::Module(module) if self.search_self_mod => {
@ -439,18 +497,25 @@ fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
ModuleSource::SourceFile(_) => (file_id, None),
};
let search_range = if let Some(&range) = search_scope.entries.get(&file_id) {
match (range, search_range) {
(None, range) | (range, None) => range,
(Some(range), Some(search_range)) => match range.intersect(search_range) {
Some(range) => Some(range),
None => return,
},
}
} else {
return;
};
let text = sema.db.file_text(file_id);
let search_range =
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
for (idx, _) in text.match_indices("self") {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
}
for offset in match_indices(&text, "self", search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_self_module_name_ref(&name_ref, sink) {
return;