Skip to content

Commit 0847bc8

Browse files
committed
Use Definition::find_usages for finding used items in expand glob import
1 parent 11d048a commit 0847bc8

File tree

2 files changed

+137
-114
lines changed

2 files changed

+137
-114
lines changed

crates/assists/src/assist_context.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ impl<'a> AssistContext<'a> {
7373
self.sema.db
7474
}
7575

76-
pub(crate) fn source_file(&self) -> &SourceFile {
77-
&self.source_file
78-
}
79-
8076
// NB, this ignores active selection.
8177
pub(crate) fn offset(&self) -> TextSize {
8278
self.frange.range.start()

crates/assists/src/handlers/expand_glob_import.rs

Lines changed: 137 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use either::Either;
2-
use std::iter::successors;
3-
4-
use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
2+
use hir::{MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
53
use ide_db::{
64
defs::{classify_name_ref, Definition, NameRefClass},
7-
RootDatabase,
5+
search::SearchScope,
86
};
9-
use syntax::{algo, ast, AstNode, SourceFile, SyntaxNode, SyntaxToken, T};
7+
use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T};
108

119
use crate::{
1210
assist_context::{AssistBuilder, AssistContext, Assists},
@@ -41,16 +39,17 @@ use crate::{
4139
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
4240
let star = ctx.find_token_at_offset(T![*])?;
4341
let (parent, mod_path) = find_parent_and_path(&star)?;
44-
let module = match ctx.sema.resolve_path(&mod_path)? {
42+
let target_module = match ctx.sema.resolve_path(&mod_path)? {
4543
PathResolution::Def(ModuleDef::Module(it)) => it,
4644
_ => return None,
4745
};
4846

49-
let source_file = ctx.source_file();
50-
let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());
47+
let current_scope = ctx.sema.scope(&star.parent());
48+
let current_module = current_scope.module()?;
5149

52-
let defs_in_mod = find_defs_in_mod(ctx, scope, module)?;
53-
let names_to_import = find_names_to_import(ctx, source_file, defs_in_mod);
50+
let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
51+
let imported_defs = find_imported_defs(ctx, star)?;
52+
let names_to_import = find_names_to_import(ctx, current_scope, refs_in_target, imported_defs);
5453

5554
let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
5655
acc.add(
@@ -85,94 +84,119 @@ fn find_parent_and_path(
8584
}
8685
}
8786

88-
#[derive(PartialEq)]
87+
#[derive(Debug, PartialEq, Clone)]
8988
enum Def {
9089
ModuleDef(ModuleDef),
9190
MacroDef(MacroDef),
9291
}
9392

94-
impl Def {
95-
fn name(&self, db: &RootDatabase) -> Option<Name> {
96-
match self {
97-
Def::ModuleDef(def) => def.name(db),
98-
Def::MacroDef(def) => def.name(db),
93+
#[derive(Debug, Clone)]
94+
struct Ref {
95+
// could be alias
96+
visible_name: Name,
97+
def: Def,
98+
}
99+
100+
impl Ref {
101+
fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
102+
match scope_def {
103+
ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, def: Def::ModuleDef(def) }),
104+
ScopeDef::MacroDef(def) => Some(Ref { visible_name: name, def: Def::MacroDef(def) }),
105+
_ => None,
99106
}
100107
}
101-
}
102108

103-
fn find_defs_in_mod(
104-
ctx: &AssistContext,
105-
from: SemanticsScope<'_>,
106-
module: hir::Module,
107-
) -> Option<Vec<Def>> {
108-
let module_scope = module.scope(ctx.db(), from.module());
109-
110-
let mut defs = vec![];
111-
for (_, def) in module_scope {
112-
match def {
113-
ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
114-
ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
115-
_ => continue,
109+
fn is_referenced_in(&self, ctx: &AssistContext, scope: &SemanticsScope) -> bool {
110+
let def = match self.def {
111+
Def::ModuleDef(def) => Definition::ModuleDef(def),
112+
Def::MacroDef(def) => Definition::Macro(def),
113+
};
114+
115+
if let Definition::ModuleDef(ModuleDef::Trait(tr)) = def {
116+
if scope
117+
.traits_in_scope()
118+
.into_iter()
119+
.find(|other_tr_id| tr == other_tr_id.to_owned().into())
120+
.is_some()
121+
{
122+
return true;
123+
}
116124
}
125+
126+
let search_scope = SearchScope::single_file(ctx.frange.file_id);
127+
!def.find_usages(&ctx.sema, Some(search_scope)).is_empty()
128+
}
129+
}
130+
131+
#[derive(Debug, Clone)]
132+
struct Refs(Vec<Ref>);
133+
134+
impl Refs {
135+
fn used_refs(&self, ctx: &AssistContext, scope: &SemanticsScope) -> Refs {
136+
Refs(self.0.clone().into_iter().filter(|r| r.is_referenced_in(ctx, scope)).collect())
117137
}
118138

119-
Some(defs)
139+
fn filter_out_by_defs(&self, defs: Vec<Def>) -> Refs {
140+
Refs(self.0.clone().into_iter().filter(|r| !defs.contains(&r.def)).collect())
141+
}
120142
}
121143

122-
fn find_names_to_import(
144+
fn find_refs_in_mod(
123145
ctx: &AssistContext,
124-
source_file: &SourceFile,
125-
defs_in_mod: Vec<Def>,
126-
) -> Vec<Name> {
127-
let (name_refs_in_use_item, name_refs_in_source) = source_file
128-
.syntax()
129-
.descendants()
130-
.filter_map(|n| {
131-
let name_ref = ast::NameRef::cast(n.clone())?;
132-
let name_ref_class = classify_name_ref(&ctx.sema, &name_ref)?;
133-
let is_in_use_item =
134-
successors(n.parent(), |n| n.parent()).find_map(ast::Use::cast).is_some();
135-
Some((name_ref_class, is_in_use_item))
136-
})
137-
.partition::<Vec<_>, _>(|&(_, is_in_use_item)| is_in_use_item);
138-
139-
let name_refs_to_import: Vec<NameRefClass> = name_refs_in_source
140-
.into_iter()
141-
.filter_map(|(r, _)| {
142-
if name_refs_in_use_item.contains(&(r.clone(), true)) {
143-
// already imported
144-
return None;
145-
}
146-
Some(r)
147-
})
148-
.collect();
149-
150-
let defs_in_source_file = name_refs_to_import
151-
.into_iter()
152-
.filter_map(|rc| match rc {
153-
NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
154-
NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
155-
_ => None,
156-
})
157-
.collect::<Vec<Def>>();
146+
module: Module,
147+
visible_from: Option<Module>,
148+
) -> Option<Refs> {
149+
let module_scope = module.scope(ctx.db(), visible_from);
150+
let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
151+
Some(Refs(refs))
152+
}
158153

159-
defs_in_mod
160-
.iter()
161-
.filter(|def| {
162-
if let Def::ModuleDef(ModuleDef::Trait(tr)) = def {
163-
for item in tr.items(ctx.db()) {
164-
if let AssocItem::Function(f) = item {
165-
if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) {
166-
return true;
167-
}
168-
}
169-
}
170-
}
154+
// looks for name refs in parent use block's siblings
155+
//
156+
// mod bar {
157+
// mod qux {
158+
// struct Qux;
159+
// }
160+
//
161+
// pub use qux::Qux;
162+
// }
163+
//
164+
// ↓ ---------------
165+
// use foo::*<|>;
166+
// use baz::Baz;
167+
// ↑ ---------------
168+
fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>> {
169+
let parent_use_item_syntax =
170+
star.ancestors().find_map(|n| if ast::Use::can_cast(n.kind()) { Some(n) } else { None })?;
171+
172+
Some(
173+
[Direction::Prev, Direction::Next]
174+
.iter()
175+
.map(|dir| {
176+
parent_use_item_syntax
177+
.siblings(dir.to_owned())
178+
.filter(|n| ast::Use::can_cast(n.kind()))
179+
})
180+
.flatten()
181+
.filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
182+
.flatten()
183+
.filter_map(|r| match classify_name_ref(&ctx.sema, &r)? {
184+
NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
185+
NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
186+
_ => None,
187+
})
188+
.collect(),
189+
)
190+
}
171191

172-
defs_in_source_file.contains(def)
173-
})
174-
.filter_map(|d| d.name(ctx.db()))
175-
.collect()
192+
fn find_names_to_import(
193+
ctx: &AssistContext,
194+
current_scope: SemanticsScope,
195+
refs_in_target: Refs,
196+
imported_defs: Vec<Def>,
197+
) -> Vec<Name> {
198+
let used_refs = refs_in_target.used_refs(ctx, &current_scope).filter_out_by_defs(imported_defs);
199+
used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
176200
}
177201

178202
fn replace_ast(
@@ -685,34 +709,37 @@ fn qux(bar: Bar, baz: Baz) {
685709

686710
#[test]
687711
fn expanding_glob_import_with_macro_defs() {
688-
check_assist(
689-
expand_glob_import,
690-
r"
691-
//- /lib.rs crate:foo
692-
#[macro_export]
693-
macro_rules! bar {
694-
() => ()
695-
}
696-
697-
pub fn baz() {}
698-
699-
//- /main.rs crate:main deps:foo
700-
use foo::*<|>;
701-
702-
fn main() {
703-
bar!();
704-
baz();
705-
}
706-
",
707-
r"
708-
use foo::{bar, baz};
709-
710-
fn main() {
711-
bar!();
712-
baz();
713-
}
714-
",
715-
)
712+
// TODO: this is currently fails because `Definition::find_usages` ignores macros
713+
// https://github.com/rust-analyzer/rust-analyzer/issues/3484
714+
//
715+
// check_assist(
716+
// expand_glob_import,
717+
// r"
718+
// //- /lib.rs crate:foo
719+
// #[macro_export]
720+
// macro_rules! bar {
721+
// () => ()
722+
// }
723+
724+
// pub fn baz() {}
725+
726+
// //- /main.rs crate:main deps:foo
727+
// use foo::*<|>;
728+
729+
// fn main() {
730+
// bar!();
731+
// baz();
732+
// }
733+
// ",
734+
// r"
735+
// use foo::{bar, baz};
736+
737+
// fn main() {
738+
// bar!();
739+
// baz();
740+
// }
741+
// ",
742+
// )
716743
}
717744

718745
#[test]

0 commit comments

Comments
 (0)