]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/expand_glob_import.rs
reenable test
[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.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 =
207         star.ancestors().find_map(|n| if ast::Use::can_cast(n.kind()) { Some(n) } else { None })?;
208
209     Some(
210         [Direction::Prev, Direction::Next]
211             .into_iter()
212             .flat_map(|dir| {
213                 parent_use_item_syntax
214                     .siblings(dir.to_owned())
215                     .filter(|n| ast::Use::can_cast(n.kind()))
216             })
217             .flat_map(|n| n.descendants().filter_map(ast::NameRef::cast))
218             .filter_map(|r| match NameRefClass::classify(&ctx.sema, &r)? {
219                 NameRefClass::Definition(
220                     def @ (Definition::Macro(_)
221                     | Definition::Module(_)
222                     | Definition::Function(_)
223                     | Definition::Adt(_)
224                     | Definition::Variant(_)
225                     | Definition::Const(_)
226                     | Definition::Static(_)
227                     | Definition::Trait(_)
228                     | Definition::TypeAlias(_)),
229                 ) => Some(def),
230                 _ => None,
231             })
232             .collect(),
233     )
234 }
235
236 fn find_names_to_import(
237     ctx: &AssistContext,
238     refs_in_target: Refs,
239     imported_defs: Vec<Definition>,
240 ) -> Vec<Name> {
241     let used_refs = refs_in_target.used_refs(ctx).filter_out_by_defs(imported_defs);
242     used_refs.0.iter().map(|r| r.visible_name.clone()).collect()
243 }
244
245 #[cfg(test)]
246 mod tests {
247     use crate::tests::{check_assist, check_assist_not_applicable};
248
249     use super::*;
250
251     #[test]
252     fn expanding_glob_import() {
253         check_assist(
254             expand_glob_import,
255             r"
256 mod foo {
257     pub struct Bar;
258     pub struct Baz;
259     pub struct Qux;
260
261     pub fn f() {}
262 }
263
264 use foo::*$0;
265
266 fn qux(bar: Bar, baz: Baz) {
267     f();
268 }
269 ",
270             r"
271 mod foo {
272     pub struct Bar;
273     pub struct Baz;
274     pub struct Qux;
275
276     pub fn f() {}
277 }
278
279 use foo::{Baz, Bar, f};
280
281 fn qux(bar: Bar, baz: Baz) {
282     f();
283 }
284 ",
285         )
286     }
287
288     #[test]
289     fn expanding_glob_import_unused() {
290         check_assist(
291             expand_glob_import,
292             r"
293 mod foo {
294     pub struct Bar;
295     pub struct Baz;
296     pub struct Qux;
297
298     pub fn f() {}
299 }
300
301 use foo::*$0;
302
303 fn qux() {}
304 ",
305             r"
306 mod foo {
307     pub struct Bar;
308     pub struct Baz;
309     pub struct Qux;
310
311     pub fn f() {}
312 }
313
314 use foo::{};
315
316 fn qux() {}
317 ",
318         )
319     }
320
321     #[test]
322     fn expanding_glob_import_with_existing_explicit_names() {
323         check_assist(
324             expand_glob_import,
325             r"
326 mod foo {
327     pub struct Bar;
328     pub struct Baz;
329     pub struct Qux;
330
331     pub fn f() {}
332 }
333
334 use foo::{*$0, f};
335
336 fn qux(bar: Bar, baz: Baz) {
337     f();
338 }
339 ",
340             r"
341 mod foo {
342     pub struct Bar;
343     pub struct Baz;
344     pub struct Qux;
345
346     pub fn f() {}
347 }
348
349 use foo::{Baz, Bar, f};
350
351 fn qux(bar: Bar, baz: Baz) {
352     f();
353 }
354 ",
355         )
356     }
357
358     #[test]
359     fn expanding_glob_import_with_existing_uses_in_same_module() {
360         check_assist(
361             expand_glob_import,
362             r"
363 mod foo {
364     pub struct Bar;
365     pub struct Baz;
366     pub struct Qux;
367
368     pub fn f() {}
369 }
370
371 use foo::Bar;
372 use foo::{*$0, f};
373
374 fn qux(bar: Bar, baz: Baz) {
375     f();
376 }
377 ",
378             r"
379 mod foo {
380     pub struct Bar;
381     pub struct Baz;
382     pub struct Qux;
383
384     pub fn f() {}
385 }
386
387 use foo::Bar;
388 use foo::{Baz, f};
389
390 fn qux(bar: Bar, baz: Baz) {
391     f();
392 }
393 ",
394         )
395     }
396
397     #[test]
398     fn expanding_nested_glob_import() {
399         check_assist(
400             expand_glob_import,
401             r"
402 mod foo {
403     pub mod bar {
404         pub struct Bar;
405         pub struct Baz;
406         pub struct Qux;
407
408         pub fn f() {}
409     }
410
411     pub mod baz {
412         pub fn g() {}
413     }
414 }
415
416 use foo::{bar::{*$0, f}, baz::*};
417
418 fn qux(bar: Bar, baz: Baz) {
419     f();
420     g();
421 }
422 ",
423             r"
424 mod foo {
425     pub mod bar {
426         pub struct Bar;
427         pub struct Baz;
428         pub struct Qux;
429
430         pub fn f() {}
431     }
432
433     pub mod baz {
434         pub fn g() {}
435     }
436 }
437
438 use foo::{bar::{Baz, Bar, f}, baz::*};
439
440 fn qux(bar: Bar, baz: Baz) {
441     f();
442     g();
443 }
444 ",
445         );
446
447         check_assist(
448             expand_glob_import,
449             r"
450 mod foo {
451     pub mod bar {
452         pub struct Bar;
453         pub struct Baz;
454         pub struct Qux;
455
456         pub fn f() {}
457     }
458
459     pub mod baz {
460         pub fn g() {}
461     }
462 }
463
464 use foo::{bar::{Bar, Baz, f}, baz::*$0};
465
466 fn qux(bar: Bar, baz: Baz) {
467     f();
468     g();
469 }
470 ",
471             r"
472 mod foo {
473     pub mod bar {
474         pub struct Bar;
475         pub struct Baz;
476         pub struct Qux;
477
478         pub fn f() {}
479     }
480
481     pub mod baz {
482         pub fn g() {}
483     }
484 }
485
486 use foo::{bar::{Bar, Baz, f}, baz::g};
487
488 fn qux(bar: Bar, baz: Baz) {
489     f();
490     g();
491 }
492 ",
493         );
494
495         check_assist(
496             expand_glob_import,
497             r"
498 mod foo {
499     pub mod bar {
500         pub struct Bar;
501         pub struct Baz;
502         pub struct Qux;
503
504         pub fn f() {}
505     }
506
507     pub mod baz {
508         pub fn g() {}
509
510         pub mod qux {
511             pub fn h() {}
512             pub fn m() {}
513
514             pub mod q {
515                 pub fn j() {}
516             }
517         }
518     }
519 }
520
521 use foo::{
522     bar::{*, f},
523     baz::{g, qux::*$0}
524 };
525
526 fn qux(bar: Bar, baz: Baz) {
527     f();
528     g();
529     h();
530     q::j();
531 }
532 ",
533             r"
534 mod foo {
535     pub mod bar {
536         pub struct Bar;
537         pub struct Baz;
538         pub struct Qux;
539
540         pub fn f() {}
541     }
542
543     pub mod baz {
544         pub fn g() {}
545
546         pub mod qux {
547             pub fn h() {}
548             pub fn m() {}
549
550             pub mod q {
551                 pub fn j() {}
552             }
553         }
554     }
555 }
556
557 use foo::{
558     bar::{*, f},
559     baz::{g, qux::{q, h}}
560 };
561
562 fn qux(bar: Bar, baz: Baz) {
563     f();
564     g();
565     h();
566     q::j();
567 }
568 ",
569         );
570
571         check_assist(
572             expand_glob_import,
573             r"
574 mod foo {
575     pub mod bar {
576         pub struct Bar;
577         pub struct Baz;
578         pub struct Qux;
579
580         pub fn f() {}
581     }
582
583     pub mod baz {
584         pub fn g() {}
585
586         pub mod qux {
587             pub fn h() {}
588             pub fn m() {}
589
590             pub mod q {
591                 pub fn j() {}
592             }
593         }
594     }
595 }
596
597 use foo::{
598     bar::{*, f},
599     baz::{g, qux::{h, q::*$0}}
600 };
601
602 fn qux(bar: Bar, baz: Baz) {
603     f();
604     g();
605     h();
606     j();
607 }
608 ",
609             r"
610 mod foo {
611     pub mod bar {
612         pub struct Bar;
613         pub struct Baz;
614         pub struct Qux;
615
616         pub fn f() {}
617     }
618
619     pub mod baz {
620         pub fn g() {}
621
622         pub mod qux {
623             pub fn h() {}
624             pub fn m() {}
625
626             pub mod q {
627                 pub fn j() {}
628             }
629         }
630     }
631 }
632
633 use foo::{
634     bar::{*, f},
635     baz::{g, qux::{h, q::j}}
636 };
637
638 fn qux(bar: Bar, baz: Baz) {
639     f();
640     g();
641     h();
642     j();
643 }
644 ",
645         );
646
647         check_assist(
648             expand_glob_import,
649             r"
650 mod foo {
651     pub mod bar {
652         pub struct Bar;
653         pub struct Baz;
654         pub struct Qux;
655
656         pub fn f() {}
657     }
658
659     pub mod baz {
660         pub fn g() {}
661
662         pub mod qux {
663             pub fn h() {}
664             pub fn m() {}
665
666             pub mod q {
667                 pub fn j() {}
668             }
669         }
670     }
671 }
672
673 use foo::{
674     bar::{*, f},
675     baz::{g, qux::{q::j, *$0}}
676 };
677
678 fn qux(bar: Bar, baz: Baz) {
679     f();
680     g();
681     h();
682     j();
683 }
684 ",
685             r"
686 mod foo {
687     pub mod bar {
688         pub struct Bar;
689         pub struct Baz;
690         pub struct Qux;
691
692         pub fn f() {}
693     }
694
695     pub mod baz {
696         pub fn g() {}
697
698         pub mod qux {
699             pub fn h() {}
700             pub fn m() {}
701
702             pub mod q {
703                 pub fn j() {}
704             }
705         }
706     }
707 }
708
709 use foo::{
710     bar::{*, f},
711     baz::{g, qux::{q::j, h}}
712 };
713
714 fn qux(bar: Bar, baz: Baz) {
715     f();
716     g();
717     h();
718     j();
719 }
720 ",
721         );
722     }
723
724     #[test]
725     fn expanding_glob_import_with_macro_defs() {
726         check_assist(
727             expand_glob_import,
728             r#"
729 //- /lib.rs crate:foo
730 #[macro_export]
731 macro_rules! bar {
732     () => ()
733 }
734
735 pub fn baz() {}
736
737 //- /main.rs crate:main deps:foo
738 use foo::*$0;
739
740 fn main() {
741     bar!();
742     baz();
743 }
744 "#,
745             r#"
746 use foo::{bar, baz};
747
748 fn main() {
749     bar!();
750     baz();
751 }
752 "#,
753         );
754     }
755
756     #[test]
757     fn expanding_glob_import_with_trait_method_uses() {
758         check_assist(
759             expand_glob_import,
760             r"
761 //- /lib.rs crate:foo
762 pub trait Tr {
763     fn method(&self) {}
764 }
765 impl Tr for () {}
766
767 //- /main.rs crate:main deps:foo
768 use foo::*$0;
769
770 fn main() {
771     ().method();
772 }
773 ",
774             r"
775 use foo::Tr;
776
777 fn main() {
778     ().method();
779 }
780 ",
781         );
782
783         check_assist(
784             expand_glob_import,
785             r"
786 //- /lib.rs crate:foo
787 pub trait Tr {
788     fn method(&self) {}
789 }
790 impl Tr for () {}
791
792 pub trait Tr2 {
793     fn method2(&self) {}
794 }
795 impl Tr2 for () {}
796
797 //- /main.rs crate:main deps:foo
798 use foo::*$0;
799
800 fn main() {
801     ().method();
802 }
803 ",
804             r"
805 use foo::Tr;
806
807 fn main() {
808     ().method();
809 }
810 ",
811         );
812     }
813
814     #[test]
815     fn expanding_is_not_applicable_if_target_module_is_not_accessible_from_current_scope() {
816         check_assist_not_applicable(
817             expand_glob_import,
818             r"
819 mod foo {
820     mod bar {
821         pub struct Bar;
822     }
823 }
824
825 use foo::bar::*$0;
826
827 fn baz(bar: Bar) {}
828 ",
829         );
830
831         check_assist_not_applicable(
832             expand_glob_import,
833             r"
834 mod foo {
835     mod bar {
836         pub mod baz {
837             pub struct Baz;
838         }
839     }
840 }
841
842 use foo::bar::baz::*$0;
843
844 fn qux(baz: Baz) {}
845 ",
846         );
847     }
848
849     #[test]
850     fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
851         check_assist_not_applicable(
852             expand_glob_import,
853             r"
854     mod foo {
855         pub struct Bar;
856         pub struct Baz;
857         pub struct Qux;
858     }
859
860     use foo::Bar$0;
861
862     fn qux(bar: Bar, baz: Baz) {}
863     ",
864         )
865     }
866
867     #[test]
868     fn expanding_glob_import_single_nested_glob_only() {
869         check_assist(
870             expand_glob_import,
871             r"
872 mod foo {
873     pub struct Bar;
874 }
875
876 use foo::{*$0};
877
878 struct Baz {
879     bar: Bar
880 }
881 ",
882             r"
883 mod foo {
884     pub struct Bar;
885 }
886
887 use foo::{Bar};
888
889 struct Baz {
890     bar: Bar
891 }
892 ",
893         );
894     }
895 }