From 5206946674c2f6b15d476eced4faa442e607e049 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Dec 2021 17:48:47 +0100 Subject: [PATCH] fix: Partially fix `ide_db::search` for crate roots --- crates/base_db/src/input.rs | 4 + crates/ide/src/highlight_related.rs | 17 +++- crates/ide/src/references.rs | 2 +- crates/ide_db/src/search.rs | 129 +++++++++++++++++++++------- 4 files changed, 118 insertions(+), 34 deletions(-) diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index f182427e478..08d3bc93ffe 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs @@ -97,6 +97,10 @@ impl CrateName { 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 { diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index de6afaef8e5..054b8f297b5 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -378,7 +378,22 @@ struct Foo; 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 "#, ); } diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index c5fe7d30c96..949c6dc686b 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -756,7 +756,7 @@ use self$0; expect![[r#" Module FileId(0) 0..10 - (no references) + FileId(0) 4..8 "#]], ); } diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 0d0f7b78d81..dcfc2b7bfc7 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -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 @@ impl<'a> FindUsages<'a> { } }; - 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 + '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, + scope: &'a SearchScope, + ) -> impl Iterator, 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 @@ impl<'a> FindUsages<'a> { } } } + // 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 @@ 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 match self.def { Definition::Module(module) if self.search_self_mod => { @@ -439,18 +497,25 @@ impl<'a> FindUsages<'a> { 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;