]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/handlers/auto_import.rs
Centralize fixture parsing for assists
[rust.git] / crates / ra_assists / src / handlers / auto_import.rs
1 use std::collections::BTreeSet;
2
3 use either::Either;
4 use hir::{
5     AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
6     Type,
7 };
8 use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase};
9 use ra_prof::profile;
10 use ra_syntax::{
11     ast::{self, AstNode},
12     SyntaxNode,
13 };
14 use rustc_hash::FxHashSet;
15
16 use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel};
17
18 // Assist: auto_import
19 //
20 // If the name is unresolved, provides all possible imports for it.
21 //
22 // ```
23 // fn main() {
24 //     let map = HashMap<|>::new();
25 // }
26 // # pub mod std { pub mod collections { pub struct HashMap { } } }
27 // ```
28 // ->
29 // ```
30 // use std::collections::HashMap;
31 //
32 // fn main() {
33 //     let map = HashMap::new();
34 // }
35 // # pub mod std { pub mod collections { pub struct HashMap { } } }
36 // ```
37 pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38     let auto_import_assets = AutoImportAssets::new(&ctx)?;
39     let proposed_imports = auto_import_assets.search_for_imports(ctx.db);
40     if proposed_imports.is_empty() {
41         return None;
42     }
43
44     let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
45     let group = auto_import_assets.get_import_group_message();
46     for import in proposed_imports {
47         acc.add_group(
48             &group,
49             AssistId("auto_import"),
50             format!("Import `{}`", &import),
51             range,
52             |builder| {
53                 insert_use_statement(
54                     &auto_import_assets.syntax_under_caret,
55                     &import,
56                     ctx,
57                     builder.text_edit_builder(),
58                 );
59             },
60         );
61     }
62     Some(())
63 }
64
65 #[derive(Debug)]
66 struct AutoImportAssets {
67     import_candidate: ImportCandidate,
68     module_with_name_to_import: Module,
69     syntax_under_caret: SyntaxNode,
70 }
71
72 impl AutoImportAssets {
73     fn new(ctx: &AssistContext) -> Option<Self> {
74         if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
75             Self::for_regular_path(path_under_caret, &ctx)
76         } else {
77             Self::for_method_call(ctx.find_node_at_offset_with_descend()?, &ctx)
78         }
79     }
80
81     fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option<Self> {
82         let syntax_under_caret = method_call.syntax().to_owned();
83         let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
84         Some(Self {
85             import_candidate: ImportCandidate::for_method_call(&ctx.sema, &method_call)?,
86             module_with_name_to_import,
87             syntax_under_caret,
88         })
89     }
90
91     fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> {
92         let syntax_under_caret = path_under_caret.syntax().to_owned();
93         if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() {
94             return None;
95         }
96
97         let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
98         Some(Self {
99             import_candidate: ImportCandidate::for_regular_path(&ctx.sema, &path_under_caret)?,
100             module_with_name_to_import,
101             syntax_under_caret,
102         })
103     }
104
105     fn get_search_query(&self) -> &str {
106         match &self.import_candidate {
107             ImportCandidate::UnqualifiedName(name) => name,
108             ImportCandidate::QualifierStart(qualifier_start) => qualifier_start,
109             ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name,
110             ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name,
111         }
112     }
113
114     fn get_import_group_message(&self) -> GroupLabel {
115         let name = match &self.import_candidate {
116             ImportCandidate::UnqualifiedName(name) => format!("Import {}", name),
117             ImportCandidate::QualifierStart(qualifier_start) => {
118                 format!("Import {}", qualifier_start)
119             }
120             ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => {
121                 format!("Import a trait for item {}", trait_assoc_item_name)
122             }
123             ImportCandidate::TraitMethod(_, trait_method_name) => {
124                 format!("Import a trait for method {}", trait_method_name)
125             }
126         };
127         GroupLabel(name)
128     }
129
130     fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
131         let _p = profile("auto_import::search_for_imports");
132         let current_crate = self.module_with_name_to_import.krate();
133         ImportsLocator::new(db, current_crate)
134             .find_imports(&self.get_search_query())
135             .into_iter()
136             .filter_map(|candidate| match &self.import_candidate {
137                 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
138                     let located_assoc_item = match candidate {
139                         Either::Left(ModuleDef::Function(located_function)) => located_function
140                             .as_assoc_item(db)
141                             .map(|assoc| assoc.container(db))
142                             .and_then(Self::assoc_to_trait),
143                         Either::Left(ModuleDef::Const(located_const)) => located_const
144                             .as_assoc_item(db)
145                             .map(|assoc| assoc.container(db))
146                             .and_then(Self::assoc_to_trait),
147                         _ => None,
148                     }?;
149
150                     let mut trait_candidates = FxHashSet::default();
151                     trait_candidates.insert(located_assoc_item.into());
152
153                     assoc_item_type
154                         .iterate_path_candidates(
155                             db,
156                             current_crate,
157                             &trait_candidates,
158                             None,
159                             |_, assoc| Self::assoc_to_trait(assoc.container(db)),
160                         )
161                         .map(ModuleDef::from)
162                         .map(Either::Left)
163                 }
164                 ImportCandidate::TraitMethod(function_callee, _) => {
165                     let located_assoc_item =
166                         if let Either::Left(ModuleDef::Function(located_function)) = candidate {
167                             located_function
168                                 .as_assoc_item(db)
169                                 .map(|assoc| assoc.container(db))
170                                 .and_then(Self::assoc_to_trait)
171                         } else {
172                             None
173                         }?;
174
175                     let mut trait_candidates = FxHashSet::default();
176                     trait_candidates.insert(located_assoc_item.into());
177
178                     function_callee
179                         .iterate_method_candidates(
180                             db,
181                             current_crate,
182                             &trait_candidates,
183                             None,
184                             |_, function| {
185                                 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
186                             },
187                         )
188                         .map(ModuleDef::from)
189                         .map(Either::Left)
190                 }
191                 _ => Some(candidate),
192             })
193             .filter_map(|candidate| match candidate {
194                 Either::Left(module_def) => {
195                     self.module_with_name_to_import.find_use_path(db, module_def)
196                 }
197                 Either::Right(macro_def) => {
198                     self.module_with_name_to_import.find_use_path(db, macro_def)
199                 }
200             })
201             .filter(|use_path| !use_path.segments.is_empty())
202             .take(20)
203             .collect::<BTreeSet<_>>()
204     }
205
206     fn assoc_to_trait(assoc: AssocItemContainer) -> Option<Trait> {
207         if let AssocItemContainer::Trait(extracted_trait) = assoc {
208             Some(extracted_trait)
209         } else {
210             None
211         }
212     }
213 }
214
215 #[derive(Debug)]
216 enum ImportCandidate {
217     /// Simple name like 'HashMap'
218     UnqualifiedName(String),
219     /// First part of the qualified name.
220     /// For 'std::collections::HashMap', that will be 'std'.
221     QualifierStart(String),
222     /// A trait associated function (with no self parameter) or associated constant.
223     /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type
224     /// and `String` is the `test_function`
225     TraitAssocItem(Type, String),
226     /// A trait method with self parameter.
227     /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type
228     /// and `String` is the `test_method`
229     TraitMethod(Type, String),
230 }
231
232 impl ImportCandidate {
233     fn for_method_call(
234         sema: &Semantics<RootDatabase>,
235         method_call: &ast::MethodCallExpr,
236     ) -> Option<Self> {
237         if sema.resolve_method_call(method_call).is_some() {
238             return None;
239         }
240         Some(Self::TraitMethod(
241             sema.type_of_expr(&method_call.expr()?)?,
242             method_call.name_ref()?.syntax().to_string(),
243         ))
244     }
245
246     fn for_regular_path(
247         sema: &Semantics<RootDatabase>,
248         path_under_caret: &ast::Path,
249     ) -> Option<Self> {
250         if sema.resolve_path(path_under_caret).is_some() {
251             return None;
252         }
253
254         let segment = path_under_caret.segment()?;
255         if let Some(qualifier) = path_under_caret.qualifier() {
256             let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
257             let qualifier_start_path =
258                 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
259             if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
260                 let qualifier_resolution = if qualifier_start_path == qualifier {
261                     qualifier_start_resolution
262                 } else {
263                     sema.resolve_path(&qualifier)?
264                 };
265                 if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution {
266                     Some(ImportCandidate::TraitAssocItem(
267                         assoc_item_path.ty(sema.db),
268                         segment.syntax().to_string(),
269                     ))
270                 } else {
271                     None
272                 }
273             } else {
274                 Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string()))
275             }
276         } else {
277             Some(ImportCandidate::UnqualifiedName(
278                 segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(),
279             ))
280         }
281     }
282 }
283
284 #[cfg(test)]
285 mod tests {
286     use super::*;
287     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
288
289     #[test]
290     fn applicable_when_found_an_import() {
291         check_assist(
292             auto_import,
293             r"
294             <|>PubStruct
295
296             pub mod PubMod {
297                 pub struct PubStruct;
298             }
299             ",
300             r"
301             use PubMod::PubStruct;
302
303             PubStruct
304
305             pub mod PubMod {
306                 pub struct PubStruct;
307             }
308             ",
309         );
310     }
311
312     #[test]
313     fn applicable_when_found_an_import_in_macros() {
314         check_assist(
315             auto_import,
316             r"
317             macro_rules! foo {
318                 ($i:ident) => { fn foo(a: $i) {} }
319             }
320             foo!(Pub<|>Struct);
321
322             pub mod PubMod {
323                 pub struct PubStruct;
324             }
325             ",
326             r"
327             use PubMod::PubStruct;
328
329             macro_rules! foo {
330                 ($i:ident) => { fn foo(a: $i) {} }
331             }
332             foo!(PubStruct);
333
334             pub mod PubMod {
335                 pub struct PubStruct;
336             }
337             ",
338         );
339     }
340
341     #[test]
342     fn auto_imports_are_merged() {
343         check_assist(
344             auto_import,
345             r"
346             use PubMod::PubStruct1;
347
348             struct Test {
349                 test: Pub<|>Struct2<u8>,
350             }
351
352             pub mod PubMod {
353                 pub struct PubStruct1;
354                 pub struct PubStruct2<T> {
355                     _t: T,
356                 }
357             }
358             ",
359             r"
360             use PubMod::{PubStruct2, PubStruct1};
361
362             struct Test {
363                 test: PubStruct2<u8>,
364             }
365
366             pub mod PubMod {
367                 pub struct PubStruct1;
368                 pub struct PubStruct2<T> {
369                     _t: T,
370                 }
371             }
372             ",
373         );
374     }
375
376     #[test]
377     fn applicable_when_found_multiple_imports() {
378         check_assist(
379             auto_import,
380             r"
381             PubSt<|>ruct
382
383             pub mod PubMod1 {
384                 pub struct PubStruct;
385             }
386             pub mod PubMod2 {
387                 pub struct PubStruct;
388             }
389             pub mod PubMod3 {
390                 pub struct PubStruct;
391             }
392             ",
393             r"
394             use PubMod3::PubStruct;
395
396             PubStruct
397
398             pub mod PubMod1 {
399                 pub struct PubStruct;
400             }
401             pub mod PubMod2 {
402                 pub struct PubStruct;
403             }
404             pub mod PubMod3 {
405                 pub struct PubStruct;
406             }
407             ",
408         );
409     }
410
411     #[test]
412     fn not_applicable_for_already_imported_types() {
413         check_assist_not_applicable(
414             auto_import,
415             r"
416             use PubMod::PubStruct;
417
418             PubStruct<|>
419
420             pub mod PubMod {
421                 pub struct PubStruct;
422             }
423             ",
424         );
425     }
426
427     #[test]
428     fn not_applicable_for_types_with_private_paths() {
429         check_assist_not_applicable(
430             auto_import,
431             r"
432             PrivateStruct<|>
433
434             pub mod PubMod {
435                 struct PrivateStruct;
436             }
437             ",
438         );
439     }
440
441     #[test]
442     fn not_applicable_when_no_imports_found() {
443         check_assist_not_applicable(
444             auto_import,
445             "
446             PubStruct<|>",
447         );
448     }
449
450     #[test]
451     fn not_applicable_in_import_statements() {
452         check_assist_not_applicable(
453             auto_import,
454             r"
455             use PubStruct<|>;
456
457             pub mod PubMod {
458                 pub struct PubStruct;
459             }",
460         );
461     }
462
463     #[test]
464     fn function_import() {
465         check_assist(
466             auto_import,
467             r"
468             test_function<|>
469
470             pub mod PubMod {
471                 pub fn test_function() {};
472             }
473             ",
474             r"
475             use PubMod::test_function;
476
477             test_function
478
479             pub mod PubMod {
480                 pub fn test_function() {};
481             }
482             ",
483         );
484     }
485
486     #[test]
487     fn macro_import() {
488         check_assist(
489             auto_import,
490             r"
491 //- /lib.rs crate:crate_with_macro
492 #[macro_export]
493 macro_rules! foo {
494     () => ()
495 }
496
497 //- /main.rs crate:main deps:crate_with_macro
498 fn main() {
499     foo<|>
500 }
501 ",
502             r"use crate_with_macro::foo;
503
504 fn main() {
505     foo
506 }
507 ",
508         );
509     }
510
511     #[test]
512     fn auto_import_target() {
513         check_assist_target(
514             auto_import,
515             r"
516             struct AssistInfo {
517                 group_label: Option<<|>GroupLabel>,
518             }
519
520             mod m { pub struct GroupLabel; }
521             ",
522             "GroupLabel",
523         )
524     }
525
526     #[test]
527     fn not_applicable_when_path_start_is_imported() {
528         check_assist_not_applicable(
529             auto_import,
530             r"
531             pub mod mod1 {
532                 pub mod mod2 {
533                     pub mod mod3 {
534                         pub struct TestStruct;
535                     }
536                 }
537             }
538
539             use mod1::mod2;
540             fn main() {
541                 mod2::mod3::TestStruct<|>
542             }
543             ",
544         );
545     }
546
547     #[test]
548     fn not_applicable_for_imported_function() {
549         check_assist_not_applicable(
550             auto_import,
551             r"
552             pub mod test_mod {
553                 pub fn test_function() {}
554             }
555
556             use test_mod::test_function;
557             fn main() {
558                 test_function<|>
559             }
560             ",
561         );
562     }
563
564     #[test]
565     fn associated_struct_function() {
566         check_assist(
567             auto_import,
568             r"
569             mod test_mod {
570                 pub struct TestStruct {}
571                 impl TestStruct {
572                     pub fn test_function() {}
573                 }
574             }
575
576             fn main() {
577                 TestStruct::test_function<|>
578             }
579             ",
580             r"
581             use test_mod::TestStruct;
582
583             mod test_mod {
584                 pub struct TestStruct {}
585                 impl TestStruct {
586                     pub fn test_function() {}
587                 }
588             }
589
590             fn main() {
591                 TestStruct::test_function
592             }
593             ",
594         );
595     }
596
597     #[test]
598     fn associated_struct_const() {
599         check_assist(
600             auto_import,
601             r"
602             mod test_mod {
603                 pub struct TestStruct {}
604                 impl TestStruct {
605                     const TEST_CONST: u8 = 42;
606                 }
607             }
608
609             fn main() {
610                 TestStruct::TEST_CONST<|>
611             }
612             ",
613             r"
614             use test_mod::TestStruct;
615
616             mod test_mod {
617                 pub struct TestStruct {}
618                 impl TestStruct {
619                     const TEST_CONST: u8 = 42;
620                 }
621             }
622
623             fn main() {
624                 TestStruct::TEST_CONST
625             }
626             ",
627         );
628     }
629
630     #[test]
631     fn associated_trait_function() {
632         check_assist(
633             auto_import,
634             r"
635             mod test_mod {
636                 pub trait TestTrait {
637                     fn test_function();
638                 }
639                 pub struct TestStruct {}
640                 impl TestTrait for TestStruct {
641                     fn test_function() {}
642                 }
643             }
644
645             fn main() {
646                 test_mod::TestStruct::test_function<|>
647             }
648             ",
649             r"
650             use test_mod::TestTrait;
651
652             mod test_mod {
653                 pub trait TestTrait {
654                     fn test_function();
655                 }
656                 pub struct TestStruct {}
657                 impl TestTrait for TestStruct {
658                     fn test_function() {}
659                 }
660             }
661
662             fn main() {
663                 test_mod::TestStruct::test_function
664             }
665             ",
666         );
667     }
668
669     #[test]
670     fn not_applicable_for_imported_trait_for_function() {
671         check_assist_not_applicable(
672             auto_import,
673             r"
674             mod test_mod {
675                 pub trait TestTrait {
676                     fn test_function();
677                 }
678                 pub trait TestTrait2 {
679                     fn test_function();
680                 }
681                 pub enum TestEnum {
682                     One,
683                     Two,
684                 }
685                 impl TestTrait2 for TestEnum {
686                     fn test_function() {}
687                 }
688                 impl TestTrait for TestEnum {
689                     fn test_function() {}
690                 }
691             }
692
693             use test_mod::TestTrait2;
694             fn main() {
695                 test_mod::TestEnum::test_function<|>;
696             }
697             ",
698         )
699     }
700
701     #[test]
702     fn associated_trait_const() {
703         check_assist(
704             auto_import,
705             r"
706             mod test_mod {
707                 pub trait TestTrait {
708                     const TEST_CONST: u8;
709                 }
710                 pub struct TestStruct {}
711                 impl TestTrait for TestStruct {
712                     const TEST_CONST: u8 = 42;
713                 }
714             }
715
716             fn main() {
717                 test_mod::TestStruct::TEST_CONST<|>
718             }
719             ",
720             r"
721             use test_mod::TestTrait;
722
723             mod test_mod {
724                 pub trait TestTrait {
725                     const TEST_CONST: u8;
726                 }
727                 pub struct TestStruct {}
728                 impl TestTrait for TestStruct {
729                     const TEST_CONST: u8 = 42;
730                 }
731             }
732
733             fn main() {
734                 test_mod::TestStruct::TEST_CONST
735             }
736             ",
737         );
738     }
739
740     #[test]
741     fn not_applicable_for_imported_trait_for_const() {
742         check_assist_not_applicable(
743             auto_import,
744             r"
745             mod test_mod {
746                 pub trait TestTrait {
747                     const TEST_CONST: u8;
748                 }
749                 pub trait TestTrait2 {
750                     const TEST_CONST: f64;
751                 }
752                 pub enum TestEnum {
753                     One,
754                     Two,
755                 }
756                 impl TestTrait2 for TestEnum {
757                     const TEST_CONST: f64 = 42.0;
758                 }
759                 impl TestTrait for TestEnum {
760                     const TEST_CONST: u8 = 42;
761                 }
762             }
763
764             use test_mod::TestTrait2;
765             fn main() {
766                 test_mod::TestEnum::TEST_CONST<|>;
767             }
768             ",
769         )
770     }
771
772     #[test]
773     fn trait_method() {
774         check_assist(
775             auto_import,
776             r"
777             mod test_mod {
778                 pub trait TestTrait {
779                     fn test_method(&self);
780                 }
781                 pub struct TestStruct {}
782                 impl TestTrait for TestStruct {
783                     fn test_method(&self) {}
784                 }
785             }
786
787             fn main() {
788                 let test_struct = test_mod::TestStruct {};
789                 test_struct.test_meth<|>od()
790             }
791             ",
792             r"
793             use test_mod::TestTrait;
794
795             mod test_mod {
796                 pub trait TestTrait {
797                     fn test_method(&self);
798                 }
799                 pub struct TestStruct {}
800                 impl TestTrait for TestStruct {
801                     fn test_method(&self) {}
802                 }
803             }
804
805             fn main() {
806                 let test_struct = test_mod::TestStruct {};
807                 test_struct.test_method()
808             }
809             ",
810         );
811     }
812
813     #[test]
814     fn not_applicable_for_imported_trait_for_method() {
815         check_assist_not_applicable(
816             auto_import,
817             r"
818             mod test_mod {
819                 pub trait TestTrait {
820                     fn test_method(&self);
821                 }
822                 pub trait TestTrait2 {
823                     fn test_method(&self);
824                 }
825                 pub enum TestEnum {
826                     One,
827                     Two,
828                 }
829                 impl TestTrait2 for TestEnum {
830                     fn test_method(&self) {}
831                 }
832                 impl TestTrait for TestEnum {
833                     fn test_method(&self) {}
834                 }
835             }
836
837             use test_mod::TestTrait2;
838             fn main() {
839                 let one = test_mod::TestEnum::One;
840                 one.test<|>_method();
841             }
842             ",
843         )
844     }
845
846     #[test]
847     fn dep_import() {
848         check_assist(
849             auto_import,
850             r"
851 //- /lib.rs crate:dep
852 pub struct Struct;
853
854 //- /main.rs crate:main deps:dep
855 fn main() {
856     Struct<|>
857 }
858 ",
859             r"use dep::Struct;
860
861 fn main() {
862     Struct
863 }
864 ",
865         );
866     }
867
868     #[test]
869     fn whole_segment() {
870         // Tests that only imports whose last segment matches the identifier get suggested.
871         check_assist(
872             auto_import,
873             r"
874 //- /lib.rs crate:dep
875 pub mod fmt {
876     pub trait Display {}
877 }
878
879 pub fn panic_fmt() {}
880
881 //- /main.rs crate:main deps:dep
882 struct S;
883
884 impl f<|>mt::Display for S {}
885 ",
886             r"use dep::fmt;
887
888 struct S;
889
890 impl fmt::Display for S {}
891 ",
892         );
893     }
894
895     #[test]
896     fn macro_generated() {
897         // Tests that macro-generated items are suggested from external crates.
898         check_assist(
899             auto_import,
900             r"
901 //- /lib.rs crate:dep
902 macro_rules! mac {
903     () => {
904         pub struct Cheese;
905     };
906 }
907
908 mac!();
909
910 //- /main.rs crate:main deps:dep
911 fn main() {
912     Cheese<|>;
913 }
914 ",
915             r"use dep::Cheese;
916
917 fn main() {
918     Cheese;
919 }
920 ",
921         );
922     }
923
924     #[test]
925     fn casing() {
926         // Tests that differently cased names don't interfere and we only suggest the matching one.
927         check_assist(
928             auto_import,
929             r"
930 //- /lib.rs crate:dep
931 pub struct FMT;
932 pub struct fmt;
933
934 //- /main.rs crate:main deps:dep
935 fn main() {
936     FMT<|>;
937 }
938 ",
939             r"use dep::FMT;
940
941 fn main() {
942     FMT;
943 }
944 ",
945         );
946     }
947 }