Add associated data into fst

This commit is contained in:
Kirill Bulatov 2021-01-02 02:05:09 +02:00
parent 1bfc3a50c0
commit 63d83fa385

View file

@ -7,9 +7,7 @@ use fst::{self, Streamer};
use hir_expand::name::Name; use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap}; use indexmap::{map::Entry, IndexMap};
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use rustc_hash::{FxHashSet, FxHasher};
use smallvec::SmallVec;
use syntax::SmolStr;
use crate::{ use crate::{
db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@ -25,6 +23,8 @@ pub struct ImportInfo {
pub path: ImportPath, pub path: ImportPath,
/// The module containing this item. /// The module containing this item.
pub container: ModuleId, pub container: ModuleId,
/// Whether the import is a trait associated item or not.
pub is_assoc_item: bool,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -64,10 +64,6 @@ pub struct ImportMap {
/// the index of the first one. /// the index of the first one.
importables: Vec<ItemInNs>, importables: Vec<ItemInNs>,
fst: fst::Map<Vec<u8>>, fst: fst::Map<Vec<u8>>,
/// Maps names of associated items to the item's ID. Only includes items whose defining trait is
/// exported.
assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
} }
impl ImportMap { impl ImportMap {
@ -108,14 +104,22 @@ impl ImportMap {
for item in per_ns.iter_items() { for item in per_ns.iter_items() {
let path = mk_path(); let path = mk_path();
let path_len = path.len();
let import_info = ImportInfo { path, container: module, is_assoc_item: false };
// If we've added a path to a trait, add the trait's associated items to the assoc map.
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
import_map.collect_trait_assoc_items(db, tr, &import_info);
}
match import_map.map.entry(item) { match import_map.map.entry(item) {
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(ImportInfo { path, container: module }); entry.insert(import_info);
} }
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
// If the new path is shorter, prefer that one. // If the new path is shorter, prefer that one.
if path.len() < entry.get().path.len() { if path_len < entry.get().path.len() {
*entry.get_mut() = ImportInfo { path, container: module }; *entry.get_mut() = import_info;
} else { } else {
continue; continue;
} }
@ -128,11 +132,6 @@ impl ImportMap {
if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
worklist.push((mod_id, mk_path())); worklist.push((mod_id, mk_path()));
} }
// If we've added a path to a trait, add the trait's methods to the method map.
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
import_map.collect_trait_methods(db, tr);
}
} }
} }
} }
@ -153,12 +152,10 @@ impl ImportMap {
} }
} }
let start = last_batch_start; let key = fst_path(&importables[last_batch_start].1.path);
builder.insert(key, last_batch_start as u64).unwrap();
last_batch_start = idx + 1; last_batch_start = idx + 1;
let key = fst_path(&importables[start].1.path);
builder.insert(key, start as u64).unwrap();
} }
import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
@ -176,10 +173,22 @@ impl ImportMap {
self.map.get(&item) self.map.get(&item)
} }
fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { fn collect_trait_assoc_items(
let data = db.trait_data(tr); &mut self,
for (name, item) in data.items.iter() { db: &dyn DefDatabase,
self.assoc_map.entry(name.to_string().into()).or_default().push(*item); tr: TraitId,
import_info: &ImportInfo,
) {
for (assoc_item_name, item) in db.trait_data(tr).items.iter() {
let assoc_item = ItemInNs::Types(match item.clone() {
AssocItemId::FunctionId(f) => f.into(),
AssocItemId::ConstId(c) => c.into(),
AssocItemId::TypeAliasId(t) => t.into(),
});
let mut assoc_item_info = import_info.to_owned();
assoc_item_info.path.segments.push(assoc_item_name.to_owned());
assoc_item_info.is_assoc_item = true;
self.map.insert(assoc_item, assoc_item_info);
} }
} }
} }
@ -304,11 +313,11 @@ impl Query {
} }
} }
fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { fn import_matches_query(import: &ImportInfo, query: &Query, enforce_lowercase: bool) -> bool {
let mut input = if query.name_only { let mut input = if import.is_assoc_item || query.name_only {
input_path.segments.last().unwrap().to_string() import.path.segments.last().unwrap().to_string()
} else { } else {
input_path.to_string() import.path.to_string()
}; };
if enforce_lowercase || !query.case_sensitive { if enforce_lowercase || !query.case_sensitive {
input.make_ascii_lowercase(); input.make_ascii_lowercase();
@ -366,13 +375,13 @@ pub fn search_dependencies<'a>(
let import_map = &import_maps[indexed_value.index]; let import_map = &import_maps[indexed_value.index];
let importables = &import_map.importables[indexed_value.value as usize..]; let importables = &import_map.importables[indexed_value.value as usize..];
// Path shared by the importable items in this group. let common_importable_data = &import_map.map[&importables[0]];
let common_importables_path = &import_map.map[&importables[0]].path; if !import_matches_query(common_importable_data, &query, true) {
if !contains_query(&query, common_importables_path, true) {
continue; continue;
} }
let common_importables_path_fst = fst_path(common_importables_path); // Path shared by the importable items in this group.
let common_importables_path_fst = fst_path(&common_importable_data.path);
// Add the items from this `ModPath` group. Those are all subsequent items in // Add the items from this `ModPath` group. Those are all subsequent items in
// `importables` whose paths match `path`. // `importables` whose paths match `path`.
let iter = importables let iter = importables
@ -387,7 +396,7 @@ pub fn search_dependencies<'a>(
}) })
.filter(|item| { .filter(|item| {
!query.case_sensitive // we've already checked the common importables path case-insensitively !query.case_sensitive // we've already checked the common importables path case-insensitively
|| contains_query(&query, &import_map.map[item].path, false) || import_matches_query(&import_map.map[item], &query, false)
}); });
res.extend(iter); res.extend(iter);
@ -398,19 +407,6 @@ pub fn search_dependencies<'a>(
} }
} }
// Add all exported associated items whose names match the query (exactly).
for map in &import_maps {
if let Some(v) = map.assoc_map.get(&*query.query) {
res.extend(v.iter().map(|&assoc| {
ItemInNs::Types(match assoc {
AssocItemId::FunctionId(it) => it.into(),
AssocItemId::ConstId(it) => it.into(),
AssocItemId::TypeAliasId(it) => it.into(),
})
}));
}
}
res res
} }
@ -755,7 +751,7 @@ mod tests {
//- /dep.rs crate:dep //- /dep.rs crate:dep
pub mod fmt { pub mod fmt {
pub trait Display { pub trait Display {
fn fmttt(); fn format();
} }
} }
"#; "#;
@ -767,7 +763,7 @@ mod tests {
expect![[r#" expect![[r#"
dep::fmt (t) dep::fmt (t)
dep::fmt::Display (t) dep::fmt::Display (t)
dep::fmt::Display::fmttt (f) dep::fmt::Display::format (f)
"#]], "#]],
); );
} }
@ -808,8 +804,8 @@ mod tests {
dep::Fmt (v) dep::Fmt (v)
dep::Fmt (m) dep::Fmt (m)
dep::fmt::Display (t) dep::fmt::Display (t)
dep::format (f)
dep::fmt::Display::fmt (f) dep::fmt::Display::fmt (f)
dep::format (f)
"#]], "#]],
); );