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