aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/assist_context.rs4
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs247
2 files changed, 137 insertions, 114 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 11c171fc2..bf520069e 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -73,10 +73,6 @@ impl<'a> AssistContext<'a> {
73 self.sema.db 73 self.sema.db
74 } 74 }
75 75
76 pub(crate) fn source_file(&self) -> &SourceFile {
77 &self.source_file
78 }
79
80 // NB, this ignores active selection. 76 // NB, this ignores active selection.
81 pub(crate) fn offset(&self) -> TextSize { 77 pub(crate) fn offset(&self) -> TextSize {
82 self.frange.range.start() 78 self.frange.range.start()
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index f9a08e84e..64267a03f 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -1,12 +1,10 @@
1use either::Either; 1use either::Either;
2use std::iter::successors; 2use hir::{MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
3
4use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
5use ide_db::{ 3use ide_db::{
6 defs::{classify_name_ref, Definition, NameRefClass}, 4 defs::{classify_name_ref, Definition, NameRefClass},
7 RootDatabase, 5 search::SearchScope,
8}; 6};
9use syntax::{algo, ast, AstNode, SourceFile, SyntaxNode, SyntaxToken, T}; 7use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T};
10 8
11use crate::{ 9use crate::{
12 assist_context::{AssistBuilder, AssistContext, Assists}, 10 assist_context::{AssistBuilder, AssistContext, Assists},
@@ -41,16 +39,17 @@ use crate::{
41pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let star = ctx.find_token_at_offset(T![*])?; 40 let star = ctx.find_token_at_offset(T![*])?;
43 let (parent, mod_path) = find_parent_and_path(&star)?; 41 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)? {
45 PathResolution::Def(ModuleDef::Module(it)) => it, 43 PathResolution::Def(ModuleDef::Module(it)) => it,
46 _ => return None, 44 _ => return None,
47 }; 45 };
48 46
49 let source_file = ctx.source_file(); 47 let current_scope = ctx.sema.scope(&star.parent());
50 let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset()); 48 let current_module = current_scope.module()?;
51 49
52 let defs_in_mod = find_defs_in_mod(ctx, scope, module)?; 50 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
53 let names_to_import = find_names_to_import(ctx, source_file, defs_in_mod); 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);
54 53
55 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); 54 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
56 acc.add( 55 acc.add(
@@ -85,94 +84,119 @@ fn find_parent_and_path(
85 } 84 }
86} 85}
87 86
88#[derive(PartialEq)] 87#[derive(Debug, PartialEq, Clone)]
89enum Def { 88enum Def {
90 ModuleDef(ModuleDef), 89 ModuleDef(ModuleDef),
91 MacroDef(MacroDef), 90 MacroDef(MacroDef),
92} 91}
93 92
94impl Def { 93#[derive(Debug, Clone)]
95 fn name(&self, db: &RootDatabase) -> Option<Name> { 94struct Ref {
96 match self { 95 // could be alias
97 Def::ModuleDef(def) => def.name(db), 96 visible_name: Name,
98 Def::MacroDef(def) => def.name(db), 97 def: Def,
98}
99
100impl 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,
99 } 106 }
100 } 107 }
101}
102 108
103fn find_defs_in_mod( 109 fn is_referenced_in(&self, ctx: &AssistContext, scope: &SemanticsScope) -> bool {
104 ctx: &AssistContext, 110 let def = match self.def {
105 from: SemanticsScope<'_>, 111 Def::ModuleDef(def) => Definition::ModuleDef(def),
106 module: hir::Module, 112 Def::MacroDef(def) => Definition::Macro(def),
107) -> Option<Vec<Def>> { 113 };
108 let module_scope = module.scope(ctx.db(), from.module()); 114
109 115 if let Definition::ModuleDef(ModuleDef::Trait(tr)) = def {
110 let mut defs = vec![]; 116 if scope
111 for (_, def) in module_scope { 117 .traits_in_scope()
112 match def { 118 .into_iter()
113 ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)), 119 .find(|other_tr_id| tr == other_tr_id.to_owned().into())
114 ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)), 120 .is_some()
115 _ => continue, 121 {
122 return true;
123 }
116 } 124 }
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)]
132struct Refs(Vec<Ref>);
133
134impl 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())
117 } 137 }
118 138
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 }
120} 142}
121 143
122fn find_names_to_import( 144fn find_refs_in_mod(
123 ctx: &AssistContext, 145 ctx: &AssistContext,
124 source_file: &SourceFile, 146 module: Module,
125 defs_in_mod: Vec<Def>, 147 visible_from: Option<Module>,
126) -> Vec<Name> { 148) -> Option<Refs> {
127 let (name_refs_in_use_item, name_refs_in_source) = source_file 149 let module_scope = module.scope(ctx.db(), visible_from);
128 .syntax() 150 let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
129 .descendants() 151 Some(Refs(refs))
130 .filter_map(|n| { 152}
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>>();
158 153
159 defs_in_mod 154// looks for name refs in parent use block's siblings
160 .iter() 155//
161 .filter(|def| { 156// mod bar {
162 if let Def::ModuleDef(ModuleDef::Trait(tr)) = def { 157// mod qux {
163 for item in tr.items(ctx.db()) { 158// struct Qux;
164 if let AssocItem::Function(f) = item { 159// }
165 if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) { 160//
166 return true; 161// pub use qux::Qux;
167 } 162// }
168 } 163//
169 } 164// ↓ ---------------
170 } 165// use foo::*<|>;
166// use baz::Baz;
167// ↑ ---------------
168fn 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}
171 191
172 defs_in_source_file.contains(def) 192fn find_names_to_import(
173 }) 193 ctx: &AssistContext,
174 .filter_map(|d| d.name(ctx.db())) 194 current_scope: SemanticsScope,
175 .collect() 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()
176} 200}
177 201
178fn replace_ast( 202fn replace_ast(
@@ -685,34 +709,37 @@ fn qux(bar: Bar, baz: Baz) {
685 709
686 #[test] 710 #[test]
687 fn expanding_glob_import_with_macro_defs() { 711 fn expanding_glob_import_with_macro_defs() {
688 check_assist( 712 // TODO: this is currently fails because `Definition::find_usages` ignores macros
689 expand_glob_import, 713 // https://github.com/rust-analyzer/rust-analyzer/issues/3484
690 r" 714 //
691//- /lib.rs crate:foo 715 // check_assist(
692#[macro_export] 716 // expand_glob_import,
693macro_rules! bar { 717 // r"
694 () => () 718 // //- /lib.rs crate:foo
695} 719 // #[macro_export]
696 720 // macro_rules! bar {
697pub fn baz() {} 721 // () => ()
698 722 // }
699//- /main.rs crate:main deps:foo 723
700use foo::*<|>; 724 // pub fn baz() {}
701 725
702fn main() { 726 // //- /main.rs crate:main deps:foo
703 bar!(); 727 // use foo::*<|>;
704 baz(); 728
705} 729 // fn main() {
706", 730 // bar!();
707 r" 731 // baz();
708use foo::{bar, baz}; 732 // }
709 733 // ",
710fn main() { 734 // r"
711 bar!(); 735 // use foo::{bar, baz};
712 baz(); 736
713} 737 // fn main() {
714", 738 // bar!();
715 ) 739 // baz();
740 // }
741 // ",
742 // )
716 } 743 }
717 744
718 #[test] 745 #[test]