|
1 | 1 | 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}; |
5 | 3 | use ide_db::{
|
6 | 4 | defs::{classify_name_ref, Definition, NameRefClass},
|
7 |
| - RootDatabase, |
| 5 | + search::SearchScope, |
8 | 6 | };
|
9 |
| -use syntax::{algo, ast, AstNode, SourceFile, SyntaxNode, SyntaxToken, T}; |
| 7 | +use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T}; |
10 | 8 |
|
11 | 9 | use crate::{
|
12 | 10 | assist_context::{AssistBuilder, AssistContext, Assists},
|
@@ -41,16 +39,17 @@ use crate::{
|
41 | 39 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
42 | 40 | let star = ctx.find_token_at_offset(T![*])?;
|
43 | 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 | 43 | PathResolution::Def(ModuleDef::Module(it)) => it,
|
46 | 44 | _ => return None,
|
47 | 45 | };
|
48 | 46 |
|
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()?; |
51 | 49 |
|
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); |
54 | 53 |
|
55 | 54 | let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
|
56 | 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)] |
89 | 88 | enum Def {
|
90 | 89 | ModuleDef(ModuleDef),
|
91 | 90 | MacroDef(MacroDef),
|
92 | 91 | }
|
93 | 92 |
|
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, |
99 | 106 | }
|
100 | 107 | }
|
101 |
| -} |
102 | 108 |
|
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 | + } |
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)] |
| 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()) |
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 |
|
122 |
| -fn find_names_to_import( |
| 144 | +fn find_refs_in_mod( |
123 | 145 | 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 | +} |
158 | 153 |
|
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 | +} |
171 | 191 |
|
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, ¤t_scope).filter_out_by_defs(imported_defs); |
| 199 | + used_refs.0.iter().map(|r| r.visible_name.clone()).collect() |
176 | 200 | }
|
177 | 201 |
|
178 | 202 | fn replace_ast(
|
@@ -685,34 +709,37 @@ fn qux(bar: Bar, baz: Baz) {
|
685 | 709 |
|
686 | 710 | #[test]
|
687 | 711 | 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 | + // ) |
716 | 743 | }
|
717 | 744 |
|
718 | 745 | #[test]
|
|
0 commit comments