2 use hir::{MacroDef, Module, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
4 defs::{classify_name_ref, Definition, NameRefClass},
7 use syntax::{algo, ast, AstNode, Direction, SyntaxNode, SyntaxToken, T};
10 assist_context::{AssistBuilder, AssistContext, Assists},
14 // Assist: expand_glob_import
16 // Expands glob imports.
26 // fn qux(bar: Bar, baz: Baz) {}
35 // use foo::{Baz, Bar};
37 // fn qux(bar: Bar, baz: Baz) {}
39 pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let star = ctx.find_token_at_offset(T![*])?;
41 let (parent, mod_path) = find_parent_and_path(&star)?;
42 let target_module = match ctx.sema.resolve_path(&mod_path)? {
43 PathResolution::Def(ModuleDef::Module(it)) => it,
47 let current_scope = ctx.sema.scope(&star.parent());
48 let current_module = current_scope.module()?;
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 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone());
56 AssistId("expand_glob_import", AssistKind::RefactorRewrite),
60 replace_ast(builder, parent, mod_path, names_to_import);
65 fn find_parent_and_path(
67 ) -> Option<(Either<ast::UseTree, ast::UseTreeList>, ast::Path)> {
68 return star.ancestors().find_map(|n| {
69 find_use_tree_list(n.clone())
70 .and_then(|(u, p)| Some((Either::Right(u), p)))
71 .or_else(|| find_use_tree(n).and_then(|(u, p)| Some((Either::Left(u), p))))
74 fn find_use_tree_list(n: SyntaxNode) -> Option<(ast::UseTreeList, ast::Path)> {
75 let use_tree_list = ast::UseTreeList::cast(n)?;
76 let path = use_tree_list.parent_use_tree().path()?;
77 Some((use_tree_list, path))
80 fn find_use_tree(n: SyntaxNode) -> Option<(ast::UseTree, ast::Path)> {
81 let use_tree = ast::UseTree::cast(n)?;
82 let path = use_tree.path()?;
83 Some((use_tree, path))
87 #[derive(Debug, PartialEq, Clone)]
93 #[derive(Debug, Clone)]
101 fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
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) }),
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),
115 if let Definition::ModuleDef(ModuleDef::Trait(tr)) = def {
119 .find(|other_tr_id| tr == other_tr_id.to_owned().into())
126 let search_scope = SearchScope::single_file(ctx.frange.file_id);
127 !def.find_usages(&ctx.sema, Some(search_scope)).is_empty()
131 #[derive(Debug, Clone)]
132 struct Refs(Vec<Ref>);
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())
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())
147 visible_from: Option<Module>,
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();
154 // looks for name refs in parent use block's siblings
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 })?;
173 [Direction::Prev, Direction::Next]
176 parent_use_item_syntax
177 .siblings(dir.to_owned())
178 .filter(|n| ast::Use::can_cast(n.kind()))
181 .filter_map(|n| Some(n.descendants().filter_map(ast::NameRef::cast)))
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)),
192 fn find_names_to_import(
194 current_scope: SemanticsScope,
195 refs_in_target: Refs,
196 imported_defs: Vec<Def>,
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()
203 builder: &mut AssistBuilder,
204 parent: Either<ast::UseTree, ast::UseTreeList>,
206 names_to_import: Vec<Name>,
208 let existing_use_trees = match parent.clone() {
209 Either::Left(_) => vec![],
210 Either::Right(u) => u
214 n.star_token().is_none())
218 let new_use_trees: Vec<ast::UseTree> = names_to_import
220 .map(|n| ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false))
223 let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat();
225 match use_trees.as_slice() {
227 if let Some(end_path) = name.path() {
228 let replacement = ast::make::use_tree(
229 ast::make::path_from_text(&format!("{}::{}", path, end_path)),
236 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
237 replacement.syntax(),
239 .into_text_edit(builder.text_edit_builder());
243 let replacement = match parent {
244 Either::Left(_) => ast::make::use_tree(
246 Some(ast::make::use_tree_list(names.to_owned())),
252 Either::Right(_) => ast::make::use_tree_list(names.to_owned()).syntax().clone(),
256 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
259 .into_text_edit(builder.text_edit_builder());
266 use crate::tests::{check_assist, check_assist_not_applicable};
271 fn expanding_glob_import() {
285 fn qux(bar: Bar, baz: Baz) {
298 use foo::{Baz, Bar, f};
300 fn qux(bar: Bar, baz: Baz) {
308 fn expanding_glob_import_with_existing_explicit_names() {
322 fn qux(bar: Bar, baz: Baz) {
335 use foo::{f, Baz, Bar};
337 fn qux(bar: Bar, baz: Baz) {
345 fn expanding_glob_import_with_existing_uses_in_same_module() {
360 fn qux(bar: Bar, baz: Baz) {
376 fn qux(bar: Bar, baz: Baz) {
384 fn expanding_nested_glob_import() {
402 use foo::{bar::{*<|>, f}, baz::*};
404 fn qux(bar: Bar, baz: Baz) {
424 use foo::{bar::{f, Baz, Bar}, baz::*};
426 fn qux(bar: Bar, baz: Baz) {
450 use foo::{bar::{Bar, Baz, f}, baz::*<|>};
452 fn qux(bar: Bar, baz: Baz) {
472 use foo::{bar::{Bar, Baz, f}, baz::g};
474 fn qux(bar: Bar, baz: Baz) {
512 fn qux(bar: Bar, baz: Baz) {
545 baz::{g, qux::{q, h}}
548 fn qux(bar: Bar, baz: Baz) {
585 baz::{g, qux::{h, q::*<|>}}
588 fn qux(bar: Bar, baz: Baz) {
621 baz::{g, qux::{h, q::j}}
624 fn qux(bar: Bar, baz: Baz) {
661 baz::{g, qux::{q::j, *<|>}}
664 fn qux(bar: Bar, baz: Baz) {
697 baz::{g, qux::{q::j, h}}
700 fn qux(bar: Bar, baz: Baz) {
711 fn expanding_glob_import_with_macro_defs() {
712 // FIXME: this is currently fails because `Definition::find_usages` ignores macros
713 // https://github.com/rust-analyzer/rust-analyzer/issues/3484
716 // expand_glob_import,
718 // //- /lib.rs crate:foo
720 // macro_rules! bar {
726 // //- /main.rs crate:main deps:foo
735 // use foo::{bar, baz};
746 fn expanding_glob_import_with_trait_method_uses() {
750 //- /lib.rs crate:foo
756 //- /main.rs crate:main deps:foo
774 fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
775 check_assist_not_applicable(
786 fn qux(bar: Bar, baz: Baz) {}