aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/auto_import.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/auto_import.rs')
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs970
1 files changed, 970 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
new file mode 100644
index 000000000..e93901cb3
--- /dev/null
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -0,0 +1,970 @@
1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast,
5};
6use syntax::{ast, AstNode, SyntaxNode};
7
8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
9
10// Feature: Auto Import
11//
12// Using the `auto-import` assist it is possible to insert missing imports for unresolved items.
13// When inserting an import it will do so in a structured manner by keeping imports grouped,
14// separated by a newline in the following order:
15//
16// - `std` and `core`
17// - External Crates
18// - Current Crate, paths prefixed by `crate`
19// - Current Module, paths prefixed by `self`
20// - Super Module, paths prefixed by `super`
21//
22// Example:
23// ```rust
24// use std::fs::File;
25//
26// use itertools::Itertools;
27// use syntax::ast;
28//
29// use crate::utils::insert_use;
30//
31// use self::auto_import;
32//
33// use super::AssistContext;
34// ```
35//
36// .Merge Behaviour
37//
38// It is possible to configure how use-trees are merged with the `importMergeBehaviour` setting.
39// It has the following configurations:
40//
41// - `full`: This setting will cause auto-import to always completely merge use-trees that share the
42// same path prefix while also merging inner trees that share the same path-prefix. This kind of
43// nesting is only supported in Rust versions later than 1.24.
44// - `last`: This setting will cause auto-import to merge use-trees as long as the resulting tree
45// will only contain a nesting of single segment paths at the very end.
46// - `none`: This setting will cause auto-import to never merge use-trees keeping them as simple
47// paths.
48//
49// In `VS Code` the configuration for this is `rust-analyzer.assist.importMergeBehaviour`.
50//
51// .Import Prefix
52//
53// The style of imports in the same crate is configurable through the `importPrefix` setting.
54// It has the following configurations:
55//
56// - `by_crate`: This setting will force paths to be always absolute, starting with the `crate`
57// prefix, unless the item is defined outside of the current crate.
58// - `by_self`: This setting will force paths that are relative to the current module to always
59// start with `self`. This will result in paths that always start with either `crate`, `self`,
60// `super` or an extern crate identifier.
61// - `plain`: This setting does not impose any restrictions in imports.
62//
63// In `VS Code` the configuration for this is `rust-analyzer.assist.importPrefix`.
64
65// Assist: auto_import
66//
67// If the name is unresolved, provides all possible imports for it.
68//
69// ```
70// fn main() {
71// let map = HashMap$0::new();
72// }
73// # pub mod std { pub mod collections { pub struct HashMap { } } }
74// ```
75// ->
76// ```
77// use std::collections::HashMap;
78//
79// fn main() {
80// let map = HashMap::new();
81// }
82// # pub mod std { pub mod collections { pub struct HashMap { } } }
83// ```
84pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85 let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
86 let proposed_imports =
87 import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
88 if proposed_imports.is_empty() {
89 return None;
90 }
91
92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for (import, _) in proposed_imports {
96 acc.add_group(
97 &group,
98 AssistId("auto_import", AssistKind::QuickFix),
99 format!("Import `{}`", &import),
100 range,
101 |builder| {
102 let rewriter =
103 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge);
104 builder.rewrite(rewriter);
105 },
106 );
107 }
108 Some(())
109}
110
111pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
112 if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
113 ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
114 .zip(Some(path_under_caret.syntax().clone()))
115 } else if let Some(method_under_caret) =
116 ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
117 {
118 ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
119 .zip(Some(method_under_caret.syntax().clone()))
120 } else {
121 None
122 }
123}
124
125fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
126 let name = match import_candidate {
127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
128 ImportCandidate::TraitAssocItem(candidate) => {
129 format!("Import a trait for item {}", candidate.name.text())
130 }
131 ImportCandidate::TraitMethod(candidate) => {
132 format!("Import a trait for method {}", candidate.name.text())
133 }
134 };
135 GroupLabel(name)
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
142
143 #[test]
144 fn applicable_when_found_an_import_partial() {
145 check_assist(
146 auto_import,
147 r"
148 mod std {
149 pub mod fmt {
150 pub struct Formatter;
151 }
152 }
153
154 use std::fmt;
155
156 $0Formatter
157 ",
158 r"
159 mod std {
160 pub mod fmt {
161 pub struct Formatter;
162 }
163 }
164
165 use std::fmt::{self, Formatter};
166
167 Formatter
168 ",
169 );
170 }
171
172 #[test]
173 fn applicable_when_found_an_import() {
174 check_assist(
175 auto_import,
176 r"
177 $0PubStruct
178
179 pub mod PubMod {
180 pub struct PubStruct;
181 }
182 ",
183 r"
184 use PubMod::PubStruct;
185
186 PubStruct
187
188 pub mod PubMod {
189 pub struct PubStruct;
190 }
191 ",
192 );
193 }
194
195 #[test]
196 fn applicable_when_found_an_import_in_macros() {
197 check_assist(
198 auto_import,
199 r"
200 macro_rules! foo {
201 ($i:ident) => { fn foo(a: $i) {} }
202 }
203 foo!(Pub$0Struct);
204
205 pub mod PubMod {
206 pub struct PubStruct;
207 }
208 ",
209 r"
210 use PubMod::PubStruct;
211
212 macro_rules! foo {
213 ($i:ident) => { fn foo(a: $i) {} }
214 }
215 foo!(PubStruct);
216
217 pub mod PubMod {
218 pub struct PubStruct;
219 }
220 ",
221 );
222 }
223
224 #[test]
225 fn auto_imports_are_merged() {
226 check_assist(
227 auto_import,
228 r"
229 use PubMod::PubStruct1;
230
231 struct Test {
232 test: Pub$0Struct2<u8>,
233 }
234
235 pub mod PubMod {
236 pub struct PubStruct1;
237 pub struct PubStruct2<T> {
238 _t: T,
239 }
240 }
241 ",
242 r"
243 use PubMod::{PubStruct1, PubStruct2};
244
245 struct Test {
246 test: PubStruct2<u8>,
247 }
248
249 pub mod PubMod {
250 pub struct PubStruct1;
251 pub struct PubStruct2<T> {
252 _t: T,
253 }
254 }
255 ",
256 );
257 }
258
259 #[test]
260 fn applicable_when_found_multiple_imports() {
261 check_assist(
262 auto_import,
263 r"
264 PubSt$0ruct
265
266 pub mod PubMod1 {
267 pub struct PubStruct;
268 }
269 pub mod PubMod2 {
270 pub struct PubStruct;
271 }
272 pub mod PubMod3 {
273 pub struct PubStruct;
274 }
275 ",
276 r"
277 use PubMod3::PubStruct;
278
279 PubStruct
280
281 pub mod PubMod1 {
282 pub struct PubStruct;
283 }
284 pub mod PubMod2 {
285 pub struct PubStruct;
286 }
287 pub mod PubMod3 {
288 pub struct PubStruct;
289 }
290 ",
291 );
292 }
293
294 #[test]
295 fn not_applicable_for_already_imported_types() {
296 check_assist_not_applicable(
297 auto_import,
298 r"
299 use PubMod::PubStruct;
300
301 PubStruct$0
302
303 pub mod PubMod {
304 pub struct PubStruct;
305 }
306 ",
307 );
308 }
309
310 #[test]
311 fn not_applicable_for_types_with_private_paths() {
312 check_assist_not_applicable(
313 auto_import,
314 r"
315 PrivateStruct$0
316
317 pub mod PubMod {
318 struct PrivateStruct;
319 }
320 ",
321 );
322 }
323
324 #[test]
325 fn not_applicable_when_no_imports_found() {
326 check_assist_not_applicable(
327 auto_import,
328 "
329 PubStruct$0",
330 );
331 }
332
333 #[test]
334 fn not_applicable_in_import_statements() {
335 check_assist_not_applicable(
336 auto_import,
337 r"
338 use PubStruct$0;
339
340 pub mod PubMod {
341 pub struct PubStruct;
342 }",
343 );
344 }
345
346 #[test]
347 fn function_import() {
348 check_assist(
349 auto_import,
350 r"
351 test_function$0
352
353 pub mod PubMod {
354 pub fn test_function() {};
355 }
356 ",
357 r"
358 use PubMod::test_function;
359
360 test_function
361
362 pub mod PubMod {
363 pub fn test_function() {};
364 }
365 ",
366 );
367 }
368
369 #[test]
370 fn macro_import() {
371 check_assist(
372 auto_import,
373 r"
374//- /lib.rs crate:crate_with_macro
375#[macro_export]
376macro_rules! foo {
377 () => ()
378}
379
380//- /main.rs crate:main deps:crate_with_macro
381fn main() {
382 foo$0
383}
384",
385 r"use crate_with_macro::foo;
386
387fn main() {
388 foo
389}
390",
391 );
392 }
393
394 #[test]
395 fn auto_import_target() {
396 check_assist_target(
397 auto_import,
398 r"
399 struct AssistInfo {
400 group_label: Option<$0GroupLabel>,
401 }
402
403 mod m { pub struct GroupLabel; }
404 ",
405 "GroupLabel",
406 )
407 }
408
409 #[test]
410 fn not_applicable_when_path_start_is_imported() {
411 check_assist_not_applicable(
412 auto_import,
413 r"
414 pub mod mod1 {
415 pub mod mod2 {
416 pub mod mod3 {
417 pub struct TestStruct;
418 }
419 }
420 }
421
422 use mod1::mod2;
423 fn main() {
424 mod2::mod3::TestStruct$0
425 }
426 ",
427 );
428 }
429
430 #[test]
431 fn not_applicable_for_imported_function() {
432 check_assist_not_applicable(
433 auto_import,
434 r"
435 pub mod test_mod {
436 pub fn test_function() {}
437 }
438
439 use test_mod::test_function;
440 fn main() {
441 test_function$0
442 }
443 ",
444 );
445 }
446
447 #[test]
448 fn associated_struct_function() {
449 check_assist(
450 auto_import,
451 r"
452 mod test_mod {
453 pub struct TestStruct {}
454 impl TestStruct {
455 pub fn test_function() {}
456 }
457 }
458
459 fn main() {
460 TestStruct::test_function$0
461 }
462 ",
463 r"
464 use test_mod::TestStruct;
465
466 mod test_mod {
467 pub struct TestStruct {}
468 impl TestStruct {
469 pub fn test_function() {}
470 }
471 }
472
473 fn main() {
474 TestStruct::test_function
475 }
476 ",
477 );
478 }
479
480 #[test]
481 fn associated_struct_const() {
482 check_assist(
483 auto_import,
484 r"
485 mod test_mod {
486 pub struct TestStruct {}
487 impl TestStruct {
488 const TEST_CONST: u8 = 42;
489 }
490 }
491
492 fn main() {
493 TestStruct::TEST_CONST$0
494 }
495 ",
496 r"
497 use test_mod::TestStruct;
498
499 mod test_mod {
500 pub struct TestStruct {}
501 impl TestStruct {
502 const TEST_CONST: u8 = 42;
503 }
504 }
505
506 fn main() {
507 TestStruct::TEST_CONST
508 }
509 ",
510 );
511 }
512
513 #[test]
514 fn associated_trait_function() {
515 check_assist(
516 auto_import,
517 r"
518 mod test_mod {
519 pub trait TestTrait {
520 fn test_function();
521 }
522 pub struct TestStruct {}
523 impl TestTrait for TestStruct {
524 fn test_function() {}
525 }
526 }
527
528 fn main() {
529 test_mod::TestStruct::test_function$0
530 }
531 ",
532 r"
533 use test_mod::TestTrait;
534
535 mod test_mod {
536 pub trait TestTrait {
537 fn test_function();
538 }
539 pub struct TestStruct {}
540 impl TestTrait for TestStruct {
541 fn test_function() {}
542 }
543 }
544
545 fn main() {
546 test_mod::TestStruct::test_function
547 }
548 ",
549 );
550 }
551
552 #[test]
553 fn not_applicable_for_imported_trait_for_function() {
554 check_assist_not_applicable(
555 auto_import,
556 r"
557 mod test_mod {
558 pub trait TestTrait {
559 fn test_function();
560 }
561 pub trait TestTrait2 {
562 fn test_function();
563 }
564 pub enum TestEnum {
565 One,
566 Two,
567 }
568 impl TestTrait2 for TestEnum {
569 fn test_function() {}
570 }
571 impl TestTrait for TestEnum {
572 fn test_function() {}
573 }
574 }
575
576 use test_mod::TestTrait2;
577 fn main() {
578 test_mod::TestEnum::test_function$0;
579 }
580 ",
581 )
582 }
583
584 #[test]
585 fn associated_trait_const() {
586 check_assist(
587 auto_import,
588 r"
589 mod test_mod {
590 pub trait TestTrait {
591 const TEST_CONST: u8;
592 }
593 pub struct TestStruct {}
594 impl TestTrait for TestStruct {
595 const TEST_CONST: u8 = 42;
596 }
597 }
598
599 fn main() {
600 test_mod::TestStruct::TEST_CONST$0
601 }
602 ",
603 r"
604 use test_mod::TestTrait;
605
606 mod test_mod {
607 pub trait TestTrait {
608 const TEST_CONST: u8;
609 }
610 pub struct TestStruct {}
611 impl TestTrait for TestStruct {
612 const TEST_CONST: u8 = 42;
613 }
614 }
615
616 fn main() {
617 test_mod::TestStruct::TEST_CONST
618 }
619 ",
620 );
621 }
622
623 #[test]
624 fn not_applicable_for_imported_trait_for_const() {
625 check_assist_not_applicable(
626 auto_import,
627 r"
628 mod test_mod {
629 pub trait TestTrait {
630 const TEST_CONST: u8;
631 }
632 pub trait TestTrait2 {
633 const TEST_CONST: f64;
634 }
635 pub enum TestEnum {
636 One,
637 Two,
638 }
639 impl TestTrait2 for TestEnum {
640 const TEST_CONST: f64 = 42.0;
641 }
642 impl TestTrait for TestEnum {
643 const TEST_CONST: u8 = 42;
644 }
645 }
646
647 use test_mod::TestTrait2;
648 fn main() {
649 test_mod::TestEnum::TEST_CONST$0;
650 }
651 ",
652 )
653 }
654
655 #[test]
656 fn trait_method() {
657 check_assist(
658 auto_import,
659 r"
660 mod test_mod {
661 pub trait TestTrait {
662 fn test_method(&self);
663 }
664 pub struct TestStruct {}
665 impl TestTrait for TestStruct {
666 fn test_method(&self) {}
667 }
668 }
669
670 fn main() {
671 let test_struct = test_mod::TestStruct {};
672 test_struct.test_meth$0od()
673 }
674 ",
675 r"
676 use test_mod::TestTrait;
677
678 mod test_mod {
679 pub trait TestTrait {
680 fn test_method(&self);
681 }
682 pub struct TestStruct {}
683 impl TestTrait for TestStruct {
684 fn test_method(&self) {}
685 }
686 }
687
688 fn main() {
689 let test_struct = test_mod::TestStruct {};
690 test_struct.test_method()
691 }
692 ",
693 );
694 }
695
696 #[test]
697 fn trait_method_cross_crate() {
698 check_assist(
699 auto_import,
700 r"
701 //- /main.rs crate:main deps:dep
702 fn main() {
703 let test_struct = dep::test_mod::TestStruct {};
704 test_struct.test_meth$0od()
705 }
706 //- /dep.rs crate:dep
707 pub mod test_mod {
708 pub trait TestTrait {
709 fn test_method(&self);
710 }
711 pub struct TestStruct {}
712 impl TestTrait for TestStruct {
713 fn test_method(&self) {}
714 }
715 }
716 ",
717 r"
718 use dep::test_mod::TestTrait;
719
720 fn main() {
721 let test_struct = dep::test_mod::TestStruct {};
722 test_struct.test_method()
723 }
724 ",
725 );
726 }
727
728 #[test]
729 fn assoc_fn_cross_crate() {
730 check_assist(
731 auto_import,
732 r"
733 //- /main.rs crate:main deps:dep
734 fn main() {
735 dep::test_mod::TestStruct::test_func$0tion
736 }
737 //- /dep.rs crate:dep
738 pub mod test_mod {
739 pub trait TestTrait {
740 fn test_function();
741 }
742 pub struct TestStruct {}
743 impl TestTrait for TestStruct {
744 fn test_function() {}
745 }
746 }
747 ",
748 r"
749 use dep::test_mod::TestTrait;
750
751 fn main() {
752 dep::test_mod::TestStruct::test_function
753 }
754 ",
755 );
756 }
757
758 #[test]
759 fn assoc_const_cross_crate() {
760 check_assist(
761 auto_import,
762 r"
763 //- /main.rs crate:main deps:dep
764 fn main() {
765 dep::test_mod::TestStruct::CONST$0
766 }
767 //- /dep.rs crate:dep
768 pub mod test_mod {
769 pub trait TestTrait {
770 const CONST: bool;
771 }
772 pub struct TestStruct {}
773 impl TestTrait for TestStruct {
774 const CONST: bool = true;
775 }
776 }
777 ",
778 r"
779 use dep::test_mod::TestTrait;
780
781 fn main() {
782 dep::test_mod::TestStruct::CONST
783 }
784 ",
785 );
786 }
787
788 #[test]
789 fn assoc_fn_as_method_cross_crate() {
790 check_assist_not_applicable(
791 auto_import,
792 r"
793 //- /main.rs crate:main deps:dep
794 fn main() {
795 let test_struct = dep::test_mod::TestStruct {};
796 test_struct.test_func$0tion()
797 }
798 //- /dep.rs crate:dep
799 pub mod test_mod {
800 pub trait TestTrait {
801 fn test_function();
802 }
803 pub struct TestStruct {}
804 impl TestTrait for TestStruct {
805 fn test_function() {}
806 }
807 }
808 ",
809 );
810 }
811
812 #[test]
813 fn private_trait_cross_crate() {
814 check_assist_not_applicable(
815 auto_import,
816 r"
817 //- /main.rs crate:main deps:dep
818 fn main() {
819 let test_struct = dep::test_mod::TestStruct {};
820 test_struct.test_meth$0od()
821 }
822 //- /dep.rs crate:dep
823 pub mod test_mod {
824 trait TestTrait {
825 fn test_method(&self);
826 }
827 pub struct TestStruct {}
828 impl TestTrait for TestStruct {
829 fn test_method(&self) {}
830 }
831 }
832 ",
833 );
834 }
835
836 #[test]
837 fn not_applicable_for_imported_trait_for_method() {
838 check_assist_not_applicable(
839 auto_import,
840 r"
841 mod test_mod {
842 pub trait TestTrait {
843 fn test_method(&self);
844 }
845 pub trait TestTrait2 {
846 fn test_method(&self);
847 }
848 pub enum TestEnum {
849 One,
850 Two,
851 }
852 impl TestTrait2 for TestEnum {
853 fn test_method(&self) {}
854 }
855 impl TestTrait for TestEnum {
856 fn test_method(&self) {}
857 }
858 }
859
860 use test_mod::TestTrait2;
861 fn main() {
862 let one = test_mod::TestEnum::One;
863 one.test$0_method();
864 }
865 ",
866 )
867 }
868
869 #[test]
870 fn dep_import() {
871 check_assist(
872 auto_import,
873 r"
874//- /lib.rs crate:dep
875pub struct Struct;
876
877//- /main.rs crate:main deps:dep
878fn main() {
879 Struct$0
880}
881",
882 r"use dep::Struct;
883
884fn main() {
885 Struct
886}
887",
888 );
889 }
890
891 #[test]
892 fn whole_segment() {
893 // Tests that only imports whose last segment matches the identifier get suggested.
894 check_assist(
895 auto_import,
896 r"
897//- /lib.rs crate:dep
898pub mod fmt {
899 pub trait Display {}
900}
901
902pub fn panic_fmt() {}
903
904//- /main.rs crate:main deps:dep
905struct S;
906
907impl f$0mt::Display for S {}
908",
909 r"use dep::fmt;
910
911struct S;
912
913impl fmt::Display for S {}
914",
915 );
916 }
917
918 #[test]
919 fn macro_generated() {
920 // Tests that macro-generated items are suggested from external crates.
921 check_assist(
922 auto_import,
923 r"
924//- /lib.rs crate:dep
925macro_rules! mac {
926 () => {
927 pub struct Cheese;
928 };
929}
930
931mac!();
932
933//- /main.rs crate:main deps:dep
934fn main() {
935 Cheese$0;
936}
937",
938 r"use dep::Cheese;
939
940fn main() {
941 Cheese;
942}
943",
944 );
945 }
946
947 #[test]
948 fn casing() {
949 // Tests that differently cased names don't interfere and we only suggest the matching one.
950 check_assist(
951 auto_import,
952 r"
953//- /lib.rs crate:dep
954pub struct FMT;
955pub struct fmt;
956
957//- /main.rs crate:main deps:dep
958fn main() {
959 FMT$0;
960}
961",
962 r"use dep::FMT;
963
964fn main() {
965 FMT;
966}
967",
968 );
969 }
970}