fix: Partially fix ide_db::search for crate roots

This commit is contained in:
Lukas Wirth 2021-12-20 17:48:47 +01:00
parent 2ca3834c9f
commit 5206946674
4 changed files with 118 additions and 34 deletions

View file

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

View file

@ -378,7 +378,22 @@ struct Foo;
fn test_hl_self_in_crate_root() { fn test_hl_self_in_crate_root() {
check( check(
r#" 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 @@ use self$0;
expect![[r#" expect![[r#"
Module FileId(0) 0..10 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 //! get a super-set of matches. Then, we we confirm each match using precise
//! name resolution. //! name resolution.
use std::{convert::TryInto, mem}; use std::{convert::TryInto, mem, sync::Arc};
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{ use hir::{
@ -376,32 +376,63 @@ impl<'a> FindUsages<'a> {
} }
}; };
let name = self.def.name(sema.db).or_else(|| { 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| { self.include_self_kw_refs.as_ref().and_then(|ty| {
ty.as_adt() ty.as_adt()
.map(|adt| adt.name(self.sema.db)) .map(|adt| adt.name(self.sema.db))
.or_else(|| ty.as_builtin().map(|builtin| builtin.name())) .or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
}) })
}); };
let name = match name { self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.to_smol_str())
Some(name) => name.to_smol_str(), }
};
let name = match &name {
Some(s) => s.as_str(),
None => return, None => return,
}; };
let name = name.as_str();
for (file_id, search_range) in search_scope { // 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) {
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 text = sema.db.file_text(file_id);
let search_range = let search_range =
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); (text, file_id, search_range)
})
for (idx, _) in text.match_indices(name) {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
} }
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) { for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if match name { if match name {
ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
@ -412,13 +443,9 @@ impl<'a> FindUsages<'a> {
} }
} }
} }
// Search for occurrences of the `Self` referring to our type
if let Some(self_ty) = &self.include_self_kw_refs { if let Some(self_ty) = &self.include_self_kw_refs {
for (idx, _) in text.match_indices("Self") { for offset in match_indices(&text, "Self", search_range) {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
}
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { 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) { if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
return; return;
@ -428,6 +455,37 @@ impl<'a> FindUsages<'a> {
} }
} }
// 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 // search for module `self` references in our module's definition source
match self.def { match self.def {
Definition::Module(module) if self.search_self_mod => { Definition::Module(module) if self.search_self_mod => {
@ -439,18 +497,25 @@ impl<'a> FindUsages<'a> {
ModuleSource::SourceFile(_) => (file_id, None), 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 text = sema.db.file_text(file_id);
let search_range = let search_range =
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
for (idx, _) in text.match_indices("self") { for offset in match_indices(&text, "self", search_range) {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
continue;
}
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_self_module_name_ref(&name_ref, sink) { if self.found_self_module_name_ref(&name_ref, sink) {
return; return;