]> git.lizzy.rs Git - rust.git/blob - crates/ide-assists/src/handlers/expand_glob_import.rs
943c1d90e63659830d017e9ab4b102a594de62f1
[rust.git] / crates / ide-assists / src / handlers / expand_glob_import.rs
1 use either::Either;
2 use hir::{AssocItem, HasVisibility, Module, ModuleDef, Name, PathResolution, ScopeDef};
3 use ide_db::{
4     defs::{Definition, NameRefClass},
5     search::SearchScope,
6 };
7 use stdx::never;
8 use syntax::{
9     ast::{self, make},
10     ted, AstNode, Direction, SyntaxNode, SyntaxToken, T,
11 };
12
13 use crate::{
14     assist_context::{AssistContext, Assists},
15     AssistId, AssistKind,
16 };
17
18 // Assist: expand_glob_import
19 //
20 // Expands glob imports.
21 //
22 // ```
23 // mod foo {
24 //     pub struct Bar;
25 //     pub struct Baz;
26 // }
27 //
28 // use foo::*$0;
29 //
30 // fn qux(bar: Bar, baz: Baz) {}
31 // ```
32 // ->
33 // ```
34 // mod foo {
35 //     pub struct Bar;
36 //     pub struct Baz;
37 // }
38 //
39 // use foo::{Baz, Bar};
40 //
41 // fn qux(bar: Bar, baz: Baz) {}
42 // ```
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,
49         _ => return None,
50     };
51
52     let current_scope = ctx.sema.scope(&star.parent()?)?;
53     let current_module = current_scope.module();
54
55     let refs_in_target = find_refs_in_mod(ctx, target_module, current_module)?;
56     let imported_defs = find_imported_defs(ctx, star)?;
57
58     let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
59     acc.add(
60         AssistId("expand_glob_import", AssistKind::RefactorRewrite),
61         "Expand glob import",
62         target.text_range(),
63         |builder| {
64             let use_tree = builder.make_mut(use_tree);
65
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)
70             }))
71             .clone_for_update();
72
73             match use_tree.star_token() {
74                 Some(star) => {
75                     let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
76                     if needs_braces {
77                         ted::replace(star, expanded.syntax())
78                     } else {
79                         let without_braces = expanded
80                             .syntax()
81                             .children_with_tokens()
82                             .filter(|child| !matches!(child.kind(), T!['{'] | T!['}']))
83                             .collect();
84                         ted::replace_with_many(star, without_braces)
85                     }
86                 }
87                 None => never!(),
88             }
89         },
90     )
91 }
92
93 fn find_parent_and_path(
94     star: &SyntaxToken,
95 ) -> Option<(Either<ast::UseTree, ast::UseTreeList>, ast::Path)> {
96     return star.parent_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)))
100     });
101
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))
106     }
107
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))
112     }
113 }
114
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()
118 }
119
120 #[derive(Debug, Clone)]
121 struct Ref {
122     // could be alias
123     visible_name: Name,
124     def: Definition,
125 }
126
127 impl Ref {
128     fn from_scope_def(name: Name, scope_def: ScopeDef) -> Option<Self> {
129         match scope_def {
130             ScopeDef::ModuleDef(def) => {
131                 Some(Ref { visible_name: name, def: Definition::from(def) })
132             }
133             _ => None,
134         }
135     }
136 }
137
138 #[derive(Debug, Clone)]
139 struct Refs(Vec<Ref>);
140
141 impl Refs {
142     fn used_refs(&self, ctx: &AssistContext<'_>) -> Refs {
143         Refs(
144             self.0
145                 .clone()
146                 .into_iter()
147                 .filter(|r| {
148                     if let Definition::Trait(tr) = r.def {
149                         if tr.items(ctx.db()).into_iter().any(|ai| {
150                             if let AssocItem::Function(f) = ai {
151                                 def_is_referenced_in(Definition::Function(f), ctx)
152                             } else {
153                                 false
154                             }
155                         }) {
156                             return true;
157                         }
158                     }
159
160                     def_is_referenced_in(r.def, ctx)
161                 })
162                 .collect(),
163         )
164     }
165
166     fn filter_out_by_defs(&self, defs: Vec<Definition>) -> Refs {
167         Refs(self.0.clone().into_iter().filter(|r| !defs.contains(&r.def)).collect())
168     }
169 }
170
171 fn find_refs_in_mod(ctx: &AssistContext<'_>, module: Module, visible_from: Module) -> Option<Refs> {
172     if !is_mod_visible_from(ctx, module, visible_from) {
173         return None;
174     }
175
176     let module_scope = module.scope(ctx.db(), Some(visible_from));
177     let refs = module_scope.into_iter().filter_map(|(n, d)| Ref::from_scope_def(n, d)).collect();
178     Some(Refs(refs))
179 }
180
181 fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool {
182     match module.parent(ctx.db()) {
183         Some(parent) => {
184             module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into())
185                 && is_mod_visible_from(ctx, parent, from)
186         }
187         None => true,
188     }
189 }
190
191 // looks for name refs in parent use block's siblings
192 //
193 // mod bar {
194 //     mod qux {
195 //         struct Qux;
196 //     }
197 //
198 //     pub use qux::Qux;
199 // }
200 //
201 // ↓ ---------------
202 // use foo::*$0;
203 // use baz::Baz;
204 // ↑ ---------------
205 fn find_imported_defs(ctx: &AssistContext<'_>, star: SyntaxToken) -> Option<Vec<Definition>> {
206     let parent_use_item_syntax = star.parent_ancestors().find_map(|n| {
207         if ast::Use::can_cast(n.kind()) {
208             Some(n)
209         } else {
210             None
211         }
212     })?;
213
214     Some(
215         [Direction::Prev, Direction::Next]
216             .into_iter()
217             .flat_map(|dir| {
218                 parent_use_item_syntax
219                     .siblings(dir.to_owned())
220                     .filter(|n| ast::Use::can_cast(n.kind()))
221             })
222             .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
223             .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
224                 NameRefClass::Definition(
225                     def @ (Definition::Macro(_)
226                     | Definition::Module(_)
227                     | Definition::Function(_)
228                     | Definition::Adt(_)
229                     | Definition::Variant(_)
230                     | Definition::Const(_)
231                     | Definition::Static(_)
232                     | Definition::Trait(_)
233                     | Definition::TypeAlias(_)),
234                 ) => Some(def),
235                 _ => None,
236             })
237             .collect(),
238     )
239 }
240
241 fn find_names_to_import(
242     ctx: &AssistContext<'_>,
243     refs_in_target: Refs,
244     imported_defs: Vec<Definition>,
245 ) -> Vec<Name> {
246     let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
247     used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
248 }
249
250 #[cfg(test)]
251 mod tests {
252     use crate::tests::{check_assist, check_assist_not_applicable};
253
254     use super::*;
255
256     #[test]
257     fn expanding_glob_import() {
258         check_assist(
259             expand_glob_import,
260             r"
261 mod foo {
262     pub struct Bar;
263     pub struct Baz;
264     pub struct Qux;
265
266     pub fn f() {}
267 }
268
269 use foo::*$0;
270
271 fn qux(bar: Bar, baz: Baz) {
272     f();
273 }
274 ",
275             r"
276 mod foo {
277     pub struct Bar;
278     pub struct Baz;
279     pub struct Qux;
280
281     pub fn f() {}
282 }
283
284 use foo::{Baz, Bar, f};
285
286 fn qux(bar: Bar, baz: Baz) {
287     f();
288 }
289 ",
290         )
291     }
292
293     #[test]
294     fn expanding_glob_import_unused() {
295         check_assist(
296             expand_glob_import,
297             r"
298 mod foo {
299     pub struct Bar;
300     pub struct Baz;
301     pub struct Qux;
302
303     pub fn f() {}
304 }
305
306 use foo::*$0;
307
308 fn qux() {}
309 ",
310             r"
311 mod foo {
312     pub struct Bar;
313     pub struct Baz;
314     pub struct Qux;
315
316     pub fn f() {}
317 }
318
319 use foo::{};
320
321 fn qux() {}
322 ",
323         )
324     }
325
326     #[test]
327     fn expanding_glob_import_with_existing_explicit_names() {
328         check_assist(
329             expand_glob_import,
330             r"
331 mod foo {
332     pub struct Bar;
333     pub struct Baz;
334     pub struct Qux;
335
336     pub fn f() {}
337 }
338
339 use foo::{*$0, f};
340
341 fn qux(bar: Bar, baz: Baz) {
342     f();
343 }
344 ",
345             r"
346 mod foo {
347     pub struct Bar;
348     pub struct Baz;
349     pub struct Qux;
350
351     pub fn f() {}
352 }
353
354 use foo::{Baz, Bar, f};
355
356 fn qux(bar: Bar, baz: Baz) {
357     f();
358 }
359 ",
360         )
361     }
362
363     #[test]
364     fn expanding_glob_import_with_existing_uses_in_same_module() {
365         check_assist(
366             expand_glob_import,
367             r"
368 mod foo {
369     pub struct Bar;
370     pub struct Baz;
371     pub struct Qux;
372
373     pub fn f() {}
374 }
375
376 use foo::Bar;
377 use foo::{*$0, f};
378
379 fn qux(bar: Bar, baz: Baz) {
380     f();
381 }
382 ",
383             r"
384 mod foo {
385     pub struct Bar;
386     pub struct Baz;
387     pub struct Qux;
388
389     pub fn f() {}
390 }
391
392 use foo::Bar;
393 use foo::{Baz, f};
394
395 fn qux(bar: Bar, baz: Baz) {
396     f();
397 }
398 ",
399         )
400     }
401
402     #[test]
403     fn expanding_nested_glob_import() {
404         check_assist(
405             expand_glob_import,
406             r"
407 mod foo {
408     pub mod bar {
409         pub struct Bar;
410         pub struct Baz;
411         pub struct Qux;
412
413         pub fn f() {}
414     }
415
416     pub mod baz {
417         pub fn g() {}
418     }
419 }
420
421 use foo::{bar::{*$0, f}, baz::*};
422
423 fn qux(bar: Bar, baz: Baz) {
424     f();
425     g();
426 }
427 ",
428             r"
429 mod foo {
430     pub mod bar {
431         pub struct Bar;
432         pub struct Baz;
433         pub struct Qux;
434
435         pub fn f() {}
436     }
437
438     pub mod baz {
439         pub fn g() {}
440     }
441 }
442
443 use foo::{bar::{Baz, Bar, f}, baz::*};
444
445 fn qux(bar: Bar, baz: Baz) {
446     f();
447     g();
448 }
449 ",
450         );
451
452         check_assist(
453             expand_glob_import,
454             r"
455 mod foo {
456     pub mod bar {
457         pub struct Bar;
458         pub struct Baz;
459         pub struct Qux;
460
461         pub fn f() {}
462     }
463
464     pub mod baz {
465         pub fn g() {}
466     }
467 }
468
469 use foo::{bar::{Bar, Baz, f}, baz::*$0};
470
471 fn qux(bar: Bar, baz: Baz) {
472     f();
473     g();
474 }
475 ",
476             r"
477 mod foo {
478     pub mod bar {
479         pub struct Bar;
480         pub struct Baz;
481         pub struct Qux;
482
483         pub fn f() {}
484     }
485
486     pub mod baz {
487         pub fn g() {}
488     }
489 }
490
491 use foo::{bar::{Bar, Baz, f}, baz::g};
492
493 fn qux(bar: Bar, baz: Baz) {
494     f();
495     g();
496 }
497 ",
498         );
499
500         check_assist(
501             expand_glob_import,
502             r"
503 mod foo {
504     pub mod bar {
505         pub struct Bar;
506         pub struct Baz;
507         pub struct Qux;
508
509         pub fn f() {}
510     }
511
512     pub mod baz {
513         pub fn g() {}
514
515         pub mod qux {
516             pub fn h() {}
517             pub fn m() {}
518
519             pub mod q {
520                 pub fn j() {}
521             }
522         }
523     }
524 }
525
526 use foo::{
527     bar::{*, f},
528     baz::{g, qux::*$0}
529 };
530
531 fn qux(bar: Bar, baz: Baz) {
532     f();
533     g();
534     h();
535     q::j();
536 }
537 ",
538             r"
539 mod foo {
540     pub mod bar {
541         pub struct Bar;
542         pub struct Baz;
543         pub struct Qux;
544
545         pub fn f() {}
546     }
547
548     pub mod baz {
549         pub fn g() {}
550
551         pub mod qux {
552             pub fn h() {}
553             pub fn m() {}
554
555             pub mod q {
556                 pub fn j() {}
557             }
558         }
559     }
560 }
561
562 use foo::{
563     bar::{*, f},
564     baz::{g, qux::{q, h}}
565 };
566
567 fn qux(bar: Bar, baz: Baz) {
568     f();
569     g();
570     h();
571     q::j();
572 }
573 ",
574         );
575
576         check_assist(
577             expand_glob_import,
578             r"
579 mod foo {
580     pub mod bar {
581         pub struct Bar;
582         pub struct Baz;
583         pub struct Qux;
584
585         pub fn f() {}
586     }
587
588     pub mod baz {
589         pub fn g() {}
590
591         pub mod qux {
592             pub fn h() {}
593             pub fn m() {}
594
595             pub mod q {
596                 pub fn j() {}
597             }
598         }
599     }
600 }
601
602 use foo::{
603     bar::{*, f},
604     baz::{g, qux::{h, q::*$0}}
605 };
606
607 fn qux(bar: Bar, baz: Baz) {
608     f();
609     g();
610     h();
611     j();
612 }
613 ",
614             r"
615 mod foo {
616     pub mod bar {
617         pub struct Bar;
618         pub struct Baz;
619         pub struct Qux;
620
621         pub fn f() {}
622     }
623
624     pub mod baz {
625         pub fn g() {}
626
627         pub mod qux {
628             pub fn h() {}
629             pub fn m() {}
630
631             pub mod q {
632                 pub fn j() {}
633             }
634         }
635     }
636 }
637
638 use foo::{
639     bar::{*, f},
640     baz::{g, qux::{h, q::j}}
641 };
642
643 fn qux(bar: Bar, baz: Baz) {
644     f();
645     g();
646     h();
647     j();
648 }
649 ",
650         );
651
652         check_assist(
653             expand_glob_import,
654             r"
655 mod foo {
656     pub mod bar {
657         pub struct Bar;
658         pub struct Baz;
659         pub struct Qux;
660
661         pub fn f() {}
662     }
663
664     pub mod baz {
665         pub fn g() {}
666
667         pub mod qux {
668             pub fn h() {}
669             pub fn m() {}
670
671             pub mod q {
672                 pub fn j() {}
673             }
674         }
675     }
676 }
677
678 use foo::{
679     bar::{*, f},
680     baz::{g, qux::{q::j, *$0}}
681 };
682
683 fn qux(bar: Bar, baz: Baz) {
684     f();
685     g();
686     h();
687     j();
688 }
689 ",
690             r"
691 mod foo {
692     pub mod bar {
693         pub struct Bar;
694         pub struct Baz;
695         pub struct Qux;
696
697         pub fn f() {}
698     }
699
700     pub mod baz {
701         pub fn g() {}
702
703         pub mod qux {
704             pub fn h() {}
705             pub fn m() {}
706
707             pub mod q {
708                 pub fn j() {}
709             }
710         }
711     }
712 }
713
714 use foo::{
715     bar::{*, f},
716     baz::{g, qux::{q::j, h}}
717 };
718
719 fn qux(bar: Bar, baz: Baz) {
720     f();
721     g();
722     h();
723     j();
724 }
725 ",
726         );
727     }
728
729     #[test]
730     fn expanding_glob_import_with_macro_defs() {
731         check_assist(
732             expand_glob_import,
733             r#"
734 //- /lib.rs crate:foo
735 #[macro_export]
736 macro_rules! bar {
737     () => ()
738 }
739
740 pub fn baz() {}
741
742 //- /main.rs crate:main deps:foo
743 use foo::*$0;
744
745 fn main() {
746     bar!();
747     baz();
748 }
749 "#,
750             r#"
751 use foo::{bar, baz};
752
753 fn main() {
754     bar!();
755     baz();
756 }
757 "#,
758         );
759     }
760
761     #[test]
762     fn expanding_glob_import_with_trait_method_uses() {
763         check_assist(
764             expand_glob_import,
765             r"
766 //- /lib.rs crate:foo
767 pub trait Tr {
768     fn method(&self) {}
769 }
770 impl Tr for () {}
771
772 //- /main.rs crate:main deps:foo
773 use foo::*$0;
774
775 fn main() {
776     ().method();
777 }
778 ",
779             r"
780 use foo::Tr;
781
782 fn main() {
783     ().method();
784 }
785 ",
786         );
787
788         check_assist(
789             expand_glob_import,
790             r"
791 //- /lib.rs crate:foo
792 pub trait Tr {
793     fn method(&self) {}
794 }
795 impl Tr for () {}
796
797 pub trait Tr2 {
798     fn method2(&self) {}
799 }
800 impl Tr2 for () {}
801
802 //- /main.rs crate:main deps:foo
803 use foo::*$0;
804
805 fn main() {
806     ().method();
807 }
808 ",
809             r"
810 use foo::Tr;
811
812 fn main() {
813     ().method();
814 }
815 ",
816         );
817     }
818
819     #[test]
820     fn expanding_is_not_applicable_if_target_module_is_not_accessible_from_current_scope() {
821         check_assist_not_applicable(
822             expand_glob_import,
823             r"
824 mod foo {
825     mod bar {
826         pub struct Bar;
827     }
828 }
829
830 use foo::bar::*$0;
831
832 fn baz(bar: Bar) {}
833 ",
834         );
835
836         check_assist_not_applicable(
837             expand_glob_import,
838             r"
839 mod foo {
840     mod bar {
841         pub mod baz {
842             pub struct Baz;
843         }
844     }
845 }
846
847 use foo::bar::baz::*$0;
848
849 fn qux(baz: Baz) {}
850 ",
851         );
852     }
853
854     #[test]
855     fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
856         check_assist_not_applicable(
857             expand_glob_import,
858             r"
859     mod foo {
860         pub struct Bar;
861         pub struct Baz;
862         pub struct Qux;
863     }
864
865     use foo::Bar$0;
866
867     fn qux(bar: Bar, baz: Baz) {}
868     ",
869         )
870     }
871
872     #[test]
873     fn expanding_glob_import_single_nested_glob_only() {
874         check_assist(
875             expand_glob_import,
876             r"
877 mod foo {
878     pub struct Bar;
879 }
880
881 use foo::{*$0};
882
883 struct Baz {
884     bar: Bar
885 }
886 ",
887             r"
888 mod foo {
889     pub struct Bar;
890 }
891
892 use foo::{Bar};
893
894 struct Baz {
895     bar: Bar
896 }
897 ",
898         );
899     }
900 }