2 use hir::{AssocItem, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef};
4 defs::{Definition, NameRefClass},
10 ted, AstNode, Direction, SyntaxNode, SyntaxToken, T,
14 assist_context::{AssistContext, Assists},
18 // Assist: expand_glob_import
20 // Expands glob imports.
30 // fn qux(bar: Bar, baz: Baz) {}
39 // use foo::{Baz, Bar};
41 // fn qux(bar: Bar, baz: Baz) {}
43 pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
44 let star = ctx.find_token_syntax_at_offset(T![*])?;
45 let use_tree = star.parent().and_then(ast::UseTree::cast)?;
46 let (parent, mod_path) = find_parent_and_path(&star)?;
47 let target_module = match ctx.sema.resolve_path(&mod_path)? {
48 PathResolution::Def(ModuleDef::Module(it)) => it,
52 let current_scope = ctx.sema.scope(&star.parent()?);
53 let current_module = current_scope.module()?;
55 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
56 let imported_defs = find_imported_defs(ctx, star)?;
58 let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
60 AssistId("expand_glob_import", AssistKind::RefactorRewrite),
64 let use_tree = builder.make_mut(use_tree);
66 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
67 let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
68 let path = make::ext::ident_path(&n.to_string());
69 make::use_tree(path, None, None, false)
73 match use_tree.star_token() {
75 let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
77 ted::replace(star, expanded.syntax())
79 let without_braces = expanded
81 .children_with_tokens()
82 .filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
84 ted::replace_with_many(star, without_braces)
93 fn find_parent_and_path(
95 ) -> Option<(Either<ast::UseTree, ast::UseTreeList>, ast::Path)> {
96 return star.ancestors().find_map(|n| {
97 find_use_tree_list(n.clone())
98 .map(|(u, p)| (Either::Right(u), p))
99 .or_else(|| find_use_tree(n).map(|(u, p)| (Either::Left(u), p)))
102 fn find_use_tree_list(n: SyntaxNode) -> Option<(ast::UseTreeList, ast::Path)> {
103 let use_tree_list = ast::UseTreeList::cast(n)?;
104 let path = use_tree_list.parent_use_tree().path()?;
105 Some((use_tree_list, path))
108 fn find_use_tree(n: SyntaxNode) -> Option<(ast::UseTree, ast::Path)> {
109 let use_tree = ast::UseTree::cast(n)?;
110 let path = use_tree.path()?;
111 Some((use_tree, path))
115 fn def_is_referenced_in(def: Definition, ctx: &AssistContext) -> bool {
116 let search_scope = SearchScope::single_file(ctx.file_id());
117 def.usages(&ctx.sema).in_scope(search_scope).at_least_one()
120 #[derive(Debug, Clone)]
128 fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
130 ScopeDef::ModuleDef(def) => {
131 Some(Ref { visible_name: name, def: Definition::from(def) })
133 ScopeDef::MacroDef(def) => {
134 Some(Ref { visible_name: name, def: Definition::Macro(def) })
141 #[derive(Debug, Clone)]
142 struct Refs(Vec<Ref>);
145 fn used_refs(&self, ctx: &AssistContext) -> Refs {
151 if let Definition::Trait(tr) = r.def {
152 if tr.items(ctx.db()).into_iter().any(|ai| {
153 if let AssocItem::Function(f) = ai {
154 def_is_referenced_in(Definition::Function(f), ctx)
163 def_is_referenced_in(r.def, ctx)
169 fn filter_out_by_defs(&self, defs: Vec<Definition>) -> Refs {
170 Refs(self.0.clone().into_iter().filter(|r| !defs.contains(&r.def)).collect())
177 visible_from: Option<Module>,
179 if let Some(from) = visible_from {
180 if !is_mod_visible_from(ctx, module, from) {
185 let module_scope = module.scope(ctx.db(), visible_from);
186 let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
190 fn is_mod_visible_from(ctx: &AssistContext, module: Module, from: Module) -> bool {
191 match module.parent(ctx.db()) {
193 module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into())
194 && is_mod_visible_from(ctx, parent, from)
200 // looks for name refs in parent use block's siblings
210 // ↓ ---------------
213 // ↑ ---------------
214 fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Definition>> {
215 let parent_use_item_syntax =
216 star.ancestors().find_map(|n| if ast::Use::can_cast(n.kind()) { Some(n) } else { None })?;
219 [Direction::Prev, Direction::Next]
222 parent_use_item_syntax
223 .siblings(dir.to_owned())
224 .filter(|n| ast::Use::can_cast(n.kind()))
226 .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
227 .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
228 NameRefClass::Definition(
229 def @ (Definition::Macro(_)
230 | Definition::Module(_)
231 | Definition::Function(_)
233 | Definition::Variant(_)
234 | Definition::Const(_)
235 | Definition::Static(_)
236 | Definition::Trait(_)
237 | Definition::TypeAlias(_)),
245 fn find_names_to_import(
247 refs_in_target: Refs,
248 imported_defs: Vec<Definition>,
250 let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
251 used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
256 use crate::tests::{check_assist, check_assist_not_applicable};
261 fn expanding_glob_import() {
275 fn qux(bar: Bar, baz: Baz) {
288 use foo::{Baz, Bar, f};
290 fn qux(bar: Bar, baz: Baz) {
298 fn expanding_glob_import_unused() {
331 fn expanding_glob_import_with_existing_explicit_names() {
345 fn qux(bar: Bar, baz: Baz) {
358 use foo::{Baz, Bar, f};
360 fn qux(bar: Bar, baz: Baz) {
368 fn expanding_glob_import_with_existing_uses_in_same_module() {
383 fn qux(bar: Bar, baz: Baz) {
399 fn qux(bar: Bar, baz: Baz) {
407 fn expanding_nested_glob_import() {
425 use foo::{bar::{*$0, f}, baz::*};
427 fn qux(bar: Bar, baz: Baz) {
447 use foo::{bar::{Baz, Bar, f}, baz::*};
449 fn qux(bar: Bar, baz: Baz) {
473 use foo::{bar::{Bar, Baz, f}, baz::*$0};
475 fn qux(bar: Bar, baz: Baz) {
495 use foo::{bar::{Bar, Baz, f}, baz::g};
497 fn qux(bar: Bar, baz: Baz) {
535 fn qux(bar: Bar, baz: Baz) {
568 baz::{g, qux::{q, h}}
571 fn qux(bar: Bar, baz: Baz) {
608 baz::{g, qux::{h, q::*$0}}
611 fn qux(bar: Bar, baz: Baz) {
644 baz::{g, qux::{h, q::j}}
647 fn qux(bar: Bar, baz: Baz) {
684 baz::{g, qux::{q::j, *$0}}
687 fn qux(bar: Bar, baz: Baz) {
720 baz::{g, qux::{q::j, h}}
723 fn qux(bar: Bar, baz: Baz) {
734 fn expanding_glob_import_with_macro_defs() {
735 // FIXME: this is currently fails because `Definition::find_usages` ignores macros
736 // https://github.com/rust-analyzer/rust-analyzer/issues/3484
739 // expand_glob_import,
741 // //- /lib.rs crate:foo
743 // macro_rules! bar {
749 // //- /main.rs crate:main deps:foo
758 // use foo::{bar, baz};
769 fn expanding_glob_import_with_trait_method_uses() {
773 //- /lib.rs crate:foo
779 //- /main.rs crate:main deps:foo
798 //- /lib.rs crate:foo
809 //- /main.rs crate:main deps:foo
827 fn expanding_is_not_applicable_if_target_module_is_not_accessible_from_current_scope() {
828 check_assist_not_applicable(
843 check_assist_not_applicable(
854 use foo::bar::baz::*$0;
862 fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
863 check_assist_not_applicable(
874 fn qux(bar: Bar, baz: Baz) {}
880 fn expanding_glob_import_single_nested_glob_only() {