]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/helpers/insert_use/tests.rs
01894630a8f321d337b750a6ec3f19188ce61b8d
[rust.git] / crates / ide_db / src / helpers / insert_use / tests.rs
1 use super::*;
2
3 use hir::PrefixKind;
4 use test_utils::{assert_eq_text, extract_range_or_offset, CURSOR_MARKER};
5
6 #[test]
7 fn respects_cfg_attr_fn() {
8     check(
9         r"bar::Bar",
10         r#"
11 #[cfg(test)]
12 fn foo() {$0}
13 "#,
14         r#"
15 #[cfg(test)]
16 fn foo() {
17 use bar::Bar;
18 }
19 "#,
20         ImportGranularity::Crate,
21     );
22 }
23
24 #[test]
25 fn respects_cfg_attr_const() {
26     check(
27         r"bar::Bar",
28         r#"
29 #[cfg(test)]
30 const FOO: Bar = {$0};
31 "#,
32         r#"
33 #[cfg(test)]
34 const FOO: Bar = {
35 use bar::Bar;
36 };
37 "#,
38         ImportGranularity::Crate,
39     );
40 }
41
42 #[test]
43 fn insert_skips_lone_glob_imports() {
44     check(
45         "use foo::baz::A",
46         r"
47 use foo::bar::*;
48 ",
49         r"
50 use foo::bar::*;
51 use foo::baz::A;
52 ",
53         ImportGranularity::Crate,
54     );
55 }
56
57 #[test]
58 fn insert_not_group() {
59     cov_mark::check!(insert_no_grouping_last);
60     check_with_config(
61         "use external_crate2::bar::A",
62         r"
63 use std::bar::B;
64 use external_crate::bar::A;
65 use crate::bar::A;
66 use self::bar::A;
67 use super::bar::A;",
68         r"
69 use std::bar::B;
70 use external_crate::bar::A;
71 use crate::bar::A;
72 use self::bar::A;
73 use super::bar::A;
74 use external_crate2::bar::A;",
75         &InsertUseConfig {
76             granularity: ImportGranularity::Item,
77             enforce_granularity: true,
78             prefix_kind: PrefixKind::Plain,
79             group: false,
80             skip_glob_imports: true,
81         },
82     );
83 }
84
85 #[test]
86 fn insert_not_group_empty() {
87     cov_mark::check!(insert_no_grouping_last2);
88     check_with_config(
89         "use external_crate2::bar::A",
90         r"",
91         r"use external_crate2::bar::A;
92
93 ",
94         &InsertUseConfig {
95             granularity: ImportGranularity::Item,
96             enforce_granularity: true,
97             prefix_kind: PrefixKind::Plain,
98             group: false,
99             skip_glob_imports: true,
100         },
101     );
102 }
103
104 #[test]
105 fn insert_existing() {
106     check_crate("std::fs", "use std::fs;", "use std::fs;")
107 }
108
109 #[test]
110 fn insert_start() {
111     check_none(
112         "std::bar::AA",
113         r"
114 use std::bar::B;
115 use std::bar::D;
116 use std::bar::F;
117 use std::bar::G;",
118         r"
119 use std::bar::AA;
120 use std::bar::B;
121 use std::bar::D;
122 use std::bar::F;
123 use std::bar::G;",
124     )
125 }
126
127 #[test]
128 fn insert_start_indent() {
129     check_none(
130         "std::bar::AA",
131         r"
132     use std::bar::B;
133     use std::bar::C;",
134         r"
135     use std::bar::AA;
136     use std::bar::B;
137     use std::bar::C;",
138     );
139 }
140
141 #[test]
142 fn insert_middle() {
143     cov_mark::check!(insert_group);
144     check_none(
145         "std::bar::EE",
146         r"
147 use std::bar::A;
148 use std::bar::D;
149 use std::bar::F;
150 use std::bar::G;",
151         r"
152 use std::bar::A;
153 use std::bar::D;
154 use std::bar::EE;
155 use std::bar::F;
156 use std::bar::G;",
157     )
158 }
159
160 #[test]
161 fn insert_middle_indent() {
162     check_none(
163         "std::bar::EE",
164         r"
165     use std::bar::A;
166     use std::bar::D;
167     use std::bar::F;
168     use std::bar::G;",
169         r"
170     use std::bar::A;
171     use std::bar::D;
172     use std::bar::EE;
173     use std::bar::F;
174     use std::bar::G;",
175     )
176 }
177
178 #[test]
179 fn insert_end() {
180     cov_mark::check!(insert_group_last);
181     check_none(
182         "std::bar::ZZ",
183         r"
184 use std::bar::A;
185 use std::bar::D;
186 use std::bar::F;
187 use std::bar::G;",
188         r"
189 use std::bar::A;
190 use std::bar::D;
191 use std::bar::F;
192 use std::bar::G;
193 use std::bar::ZZ;",
194     )
195 }
196
197 #[test]
198 fn insert_end_indent() {
199     check_none(
200         "std::bar::ZZ",
201         r"
202     use std::bar::A;
203     use std::bar::D;
204     use std::bar::F;
205     use std::bar::G;",
206         r"
207     use std::bar::A;
208     use std::bar::D;
209     use std::bar::F;
210     use std::bar::G;
211     use std::bar::ZZ;",
212     )
213 }
214
215 #[test]
216 fn insert_middle_nested() {
217     check_none(
218         "std::bar::EE",
219         r"
220 use std::bar::A;
221 use std::bar::{D, Z}; // example of weird imports due to user
222 use std::bar::F;
223 use std::bar::G;",
224         r"
225 use std::bar::A;
226 use std::bar::EE;
227 use std::bar::{D, Z}; // example of weird imports due to user
228 use std::bar::F;
229 use std::bar::G;",
230     )
231 }
232
233 #[test]
234 fn insert_middle_groups() {
235     check_none(
236         "foo::bar::GG",
237         r"
238     use std::bar::A;
239     use std::bar::D;
240
241     use foo::bar::F;
242     use foo::bar::H;",
243         r"
244     use std::bar::A;
245     use std::bar::D;
246
247     use foo::bar::F;
248     use foo::bar::GG;
249     use foo::bar::H;",
250     )
251 }
252
253 #[test]
254 fn insert_first_matching_group() {
255     check_none(
256         "foo::bar::GG",
257         r"
258     use foo::bar::A;
259     use foo::bar::D;
260
261     use std;
262
263     use foo::bar::F;
264     use foo::bar::H;",
265         r"
266     use foo::bar::A;
267     use foo::bar::D;
268     use foo::bar::GG;
269
270     use std;
271
272     use foo::bar::F;
273     use foo::bar::H;",
274     )
275 }
276
277 #[test]
278 fn insert_missing_group_std() {
279     cov_mark::check!(insert_group_new_group);
280     check_none(
281         "std::fmt",
282         r"
283     use foo::bar::A;
284     use foo::bar::D;",
285         r"
286     use std::fmt;
287
288     use foo::bar::A;
289     use foo::bar::D;",
290     )
291 }
292
293 #[test]
294 fn insert_missing_group_self() {
295     cov_mark::check!(insert_group_no_group);
296     check_none(
297         "self::fmt",
298         r"
299 use foo::bar::A;
300 use foo::bar::D;",
301         r"
302 use foo::bar::A;
303 use foo::bar::D;
304
305 use self::fmt;",
306     )
307 }
308
309 #[test]
310 fn insert_no_imports() {
311     check_crate(
312         "foo::bar",
313         "fn main() {}",
314         r"use foo::bar;
315
316 fn main() {}",
317     )
318 }
319
320 #[test]
321 fn insert_empty_file() {
322     cov_mark::check!(insert_group_empty_file);
323     // empty files will get two trailing newlines
324     // this is due to the test case insert_no_imports above
325     check_crate(
326         "foo::bar",
327         "",
328         r"use foo::bar;
329
330 ",
331     )
332 }
333
334 #[test]
335 fn insert_empty_module() {
336     cov_mark::check!(insert_group_empty_module);
337     check(
338         "foo::bar",
339         r"
340 mod x {$0}
341 ",
342         r"
343 mod x {
344     use foo::bar;
345 }
346 ",
347         ImportGranularity::Item,
348     )
349 }
350
351 #[test]
352 fn insert_after_inner_attr() {
353     cov_mark::check!(insert_group_empty_inner_attr);
354     check_crate(
355         "foo::bar",
356         r"#![allow(unused_imports)]",
357         r"#![allow(unused_imports)]
358
359 use foo::bar;",
360     )
361 }
362
363 #[test]
364 fn insert_after_inner_attr2() {
365     check_crate(
366         "foo::bar",
367         r"#![allow(unused_imports)]
368
369 #![no_std]
370 fn main() {}",
371         r"#![allow(unused_imports)]
372
373 #![no_std]
374
375 use foo::bar;
376 fn main() {}",
377     );
378 }
379
380 #[test]
381 fn inserts_after_single_line_inner_comments() {
382     check_none(
383         "foo::bar::Baz",
384         "//! Single line inner comments do not allow any code before them.",
385         r#"//! Single line inner comments do not allow any code before them.
386
387 use foo::bar::Baz;"#,
388     );
389 }
390
391 #[test]
392 fn inserts_after_multiline_inner_comments() {
393     check_none(
394         "foo::bar::Baz",
395         r#"/*! Multiline inner comments do not allow any code before them. */
396
397 /*! Still an inner comment, cannot place any code before. */
398 fn main() {}"#,
399         r#"/*! Multiline inner comments do not allow any code before them. */
400
401 /*! Still an inner comment, cannot place any code before. */
402
403 use foo::bar::Baz;
404 fn main() {}"#,
405     )
406 }
407
408 #[test]
409 fn inserts_after_all_inner_items() {
410     check_none(
411         "foo::bar::Baz",
412         r#"#![allow(unused_imports)]
413 /*! Multiline line comment 2 */
414
415
416 //! Single line comment 1
417 #![no_std]
418 //! Single line comment 2
419 fn main() {}"#,
420         r#"#![allow(unused_imports)]
421 /*! Multiline line comment 2 */
422
423
424 //! Single line comment 1
425 #![no_std]
426 //! Single line comment 2
427
428 use foo::bar::Baz;
429 fn main() {}"#,
430     )
431 }
432
433 #[test]
434 fn merge_groups() {
435     check_module("std::io", r"use std::fmt;", r"use std::{fmt, io};")
436 }
437
438 #[test]
439 fn merge_groups_last() {
440     check_module(
441         "std::io",
442         r"use std::fmt::{Result, Display};",
443         r"use std::fmt::{Result, Display};
444 use std::io;",
445     )
446 }
447
448 #[test]
449 fn merge_last_into_self() {
450     check_module("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
451 }
452
453 #[test]
454 fn merge_groups_full() {
455     check_crate(
456         "std::io",
457         r"use std::fmt::{Result, Display};",
458         r"use std::{fmt::{Result, Display}, io};",
459     )
460 }
461
462 #[test]
463 fn merge_groups_long_full() {
464     check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
465 }
466
467 #[test]
468 fn merge_groups_long_last() {
469     check_module(
470         "std::foo::bar::Baz",
471         r"use std::foo::bar::Qux;",
472         r"use std::foo::bar::{Baz, Qux};",
473     )
474 }
475
476 #[test]
477 fn merge_groups_long_full_list() {
478     check_crate(
479         "std::foo::bar::Baz",
480         r"use std::foo::bar::{Qux, Quux};",
481         r"use std::foo::bar::{Baz, Quux, Qux};",
482     )
483 }
484
485 #[test]
486 fn merge_groups_long_last_list() {
487     check_module(
488         "std::foo::bar::Baz",
489         r"use std::foo::bar::{Qux, Quux};",
490         r"use std::foo::bar::{Baz, Quux, Qux};",
491     )
492 }
493
494 #[test]
495 fn merge_groups_long_full_nested() {
496     check_crate(
497         "std::foo::bar::Baz",
498         r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
499         r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
500     )
501 }
502
503 #[test]
504 fn merge_groups_long_last_nested() {
505     check_module(
506         "std::foo::bar::Baz",
507         r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
508         r"use std::foo::bar::Baz;
509 use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
510     )
511 }
512
513 #[test]
514 fn merge_groups_full_nested_deep() {
515     check_crate(
516         "std::foo::bar::quux::Baz",
517         r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
518         r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
519     )
520 }
521
522 #[test]
523 fn merge_groups_full_nested_long() {
524     check_crate(
525         "std::foo::bar::Baz",
526         r"use std::{foo::bar::Qux};",
527         r"use std::{foo::bar::{Baz, Qux}};",
528     );
529 }
530
531 #[test]
532 fn merge_groups_last_nested_long() {
533     check_crate(
534         "std::foo::bar::Baz",
535         r"use std::{foo::bar::Qux};",
536         r"use std::{foo::bar::{Baz, Qux}};",
537     );
538 }
539
540 #[test]
541 fn merge_groups_skip_pub() {
542     check_crate(
543         "std::io",
544         r"pub use std::fmt::{Result, Display};",
545         r"pub use std::fmt::{Result, Display};
546 use std::io;",
547     )
548 }
549
550 #[test]
551 fn merge_groups_skip_pub_crate() {
552     check_crate(
553         "std::io",
554         r"pub(crate) use std::fmt::{Result, Display};",
555         r"pub(crate) use std::fmt::{Result, Display};
556 use std::io;",
557     )
558 }
559
560 #[test]
561 fn merge_groups_skip_attributed() {
562     check_crate(
563         "std::io",
564         r#"
565 #[cfg(feature = "gated")] use std::fmt::{Result, Display};
566 "#,
567         r#"
568 #[cfg(feature = "gated")] use std::fmt::{Result, Display};
569 use std::io;
570 "#,
571     )
572 }
573
574 #[test]
575 fn split_out_merge() {
576     // FIXME: This is suboptimal, we want to get `use std::fmt::{self, Result}`
577     // instead.
578     check_module(
579         "std::fmt::Result",
580         r"use std::{fmt, io};",
581         r"use std::fmt::Result;
582 use std::{fmt, io};",
583     )
584 }
585
586 #[test]
587 fn merge_into_module_import() {
588     check_crate("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};")
589 }
590
591 #[test]
592 fn merge_groups_self() {
593     check_crate("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
594 }
595
596 #[test]
597 fn merge_mod_into_glob() {
598     check_with_config(
599         "token::TokenKind",
600         r"use token::TokenKind::*;",
601         r"use token::TokenKind::{*, self};",
602         &InsertUseConfig {
603             granularity: ImportGranularity::Crate,
604             enforce_granularity: true,
605             prefix_kind: PrefixKind::Plain,
606             group: false,
607             skip_glob_imports: false,
608         },
609     )
610     // FIXME: have it emit `use token::TokenKind::{self, *}`?
611 }
612
613 #[test]
614 fn merge_self_glob() {
615     check_with_config(
616         "self",
617         r"use self::*;",
618         r"use self::{*, self};",
619         &InsertUseConfig {
620             granularity: ImportGranularity::Crate,
621             enforce_granularity: true,
622             prefix_kind: PrefixKind::Plain,
623             group: false,
624             skip_glob_imports: false,
625         },
626     )
627     // FIXME: have it emit `use {self, *}`?
628 }
629
630 #[test]
631 fn merge_glob_nested() {
632     check_crate(
633         "foo::bar::quux::Fez",
634         r"use foo::bar::{Baz, quux::*};",
635         r"use foo::bar::{Baz, quux::{self::*, Fez}};",
636     )
637 }
638
639 #[test]
640 fn merge_nested_considers_first_segments() {
641     check_crate(
642         "hir_ty::display::write_bounds_like_dyn_trait",
643         r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
644         r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
645     );
646 }
647
648 #[test]
649 fn skip_merge_last_too_long() {
650     check_module(
651         "foo::bar",
652         r"use foo::bar::baz::Qux;",
653         r"use foo::bar;
654 use foo::bar::baz::Qux;",
655     );
656 }
657
658 #[test]
659 fn skip_merge_last_too_long2() {
660     check_module(
661         "foo::bar::baz::Qux",
662         r"use foo::bar;",
663         r"use foo::bar;
664 use foo::bar::baz::Qux;",
665     );
666 }
667
668 #[test]
669 fn insert_short_before_long() {
670     check_none(
671         "foo::bar",
672         r"use foo::bar::baz::Qux;",
673         r"use foo::bar;
674 use foo::bar::baz::Qux;",
675     );
676 }
677
678 #[test]
679 fn merge_last_fail() {
680     check_merge_only_fail(
681         r"use foo::bar::{baz::{Qux, Fez}};",
682         r"use foo::bar::{baaz::{Quux, Feez}};",
683         MergeBehavior::Module,
684     );
685 }
686
687 #[test]
688 fn merge_last_fail1() {
689     check_merge_only_fail(
690         r"use foo::bar::{baz::{Qux, Fez}};",
691         r"use foo::bar::baaz::{Quux, Feez};",
692         MergeBehavior::Module,
693     );
694 }
695
696 #[test]
697 fn merge_last_fail2() {
698     check_merge_only_fail(
699         r"use foo::bar::baz::{Qux, Fez};",
700         r"use foo::bar::{baaz::{Quux, Feez}};",
701         MergeBehavior::Module,
702     );
703 }
704
705 #[test]
706 fn merge_last_fail3() {
707     check_merge_only_fail(
708         r"use foo::bar::baz::{Qux, Fez};",
709         r"use foo::bar::baaz::{Quux, Feez};",
710         MergeBehavior::Module,
711     );
712 }
713
714 #[test]
715 fn guess_empty() {
716     check_guess("", ImportGranularityGuess::Unknown);
717 }
718
719 #[test]
720 fn guess_single() {
721     check_guess(r"use foo::{baz::{qux, quux}, bar};", ImportGranularityGuess::Crate);
722     check_guess(r"use foo::bar;", ImportGranularityGuess::Unknown);
723     check_guess(r"use foo::bar::{baz, qux};", ImportGranularityGuess::CrateOrModule);
724 }
725
726 #[test]
727 fn guess_unknown() {
728     check_guess(
729         r"
730 use foo::bar::baz;
731 use oof::rab::xuq;
732 ",
733         ImportGranularityGuess::Unknown,
734     );
735 }
736
737 #[test]
738 fn guess_item() {
739     check_guess(
740         r"
741 use foo::bar::baz;
742 use foo::bar::qux;
743 ",
744         ImportGranularityGuess::Item,
745     );
746     check_guess(
747         r"
748 use foo::bar::Bar;
749 use foo::baz;
750 ",
751         ImportGranularityGuess::Item,
752     );
753 }
754
755 #[test]
756 fn guess_module() {
757     check_guess(
758         r"
759 use foo::bar::baz;
760 use foo::bar::{qux, quux};
761 ",
762         ImportGranularityGuess::Module,
763     );
764     // this is a rather odd case, technically this file isn't following any style properly.
765     check_guess(
766         r"
767 use foo::bar::baz;
768 use foo::{baz::{qux, quux}, bar};
769 ",
770         ImportGranularityGuess::Module,
771     );
772     check_guess(
773         r"
774 use foo::bar::Bar;
775 use foo::baz::Baz;
776 use foo::{Foo, Qux};
777 ",
778         ImportGranularityGuess::Module,
779     );
780 }
781
782 #[test]
783 fn guess_crate_or_module() {
784     check_guess(
785         r"
786 use foo::bar::baz;
787 use oof::bar::{qux, quux};
788 ",
789         ImportGranularityGuess::CrateOrModule,
790     );
791 }
792
793 #[test]
794 fn guess_crate() {
795     check_guess(
796         r"
797 use frob::bar::baz;
798 use foo::{baz::{qux, quux}, bar};
799 ",
800         ImportGranularityGuess::Crate,
801     );
802 }
803
804 #[test]
805 fn guess_skips_differing_vis() {
806     check_guess(
807         r"
808 use foo::bar::baz;
809 pub use foo::bar::qux;
810 ",
811         ImportGranularityGuess::Unknown,
812     );
813 }
814
815 #[test]
816 fn guess_skips_differing_attrs() {
817     check_guess(
818         r"
819 pub use foo::bar::baz;
820 #[doc(hidden)]
821 pub use foo::bar::qux;
822 ",
823         ImportGranularityGuess::Unknown,
824     );
825 }
826
827 #[test]
828 fn guess_grouping_matters() {
829     check_guess(
830         r"
831 use foo::bar::baz;
832 use oof::bar::baz;
833 use foo::bar::qux;
834 ",
835         ImportGranularityGuess::Unknown,
836     );
837 }
838
839 fn check_with_config(
840     path: &str,
841     ra_fixture_before: &str,
842     ra_fixture_after: &str,
843     config: &InsertUseConfig,
844 ) {
845     let (text, pos) = if ra_fixture_before.contains(CURSOR_MARKER) {
846         let (range_or_offset, text) = extract_range_or_offset(ra_fixture_before);
847         (text, Some(range_or_offset))
848     } else {
849         (ra_fixture_before.to_owned(), None)
850     };
851     let syntax = ast::SourceFile::parse(&text).tree().syntax().clone_for_update();
852     let file = pos
853         .and_then(|pos| syntax.token_at_offset(pos.expect_offset()).next()?.parent())
854         .and_then(|it| super::ImportScope::find_insert_use_container(&it))
855         .or_else(|| super::ImportScope::from(syntax))
856         .unwrap();
857     let path = ast::SourceFile::parse(&format!("use {};", path))
858         .tree()
859         .syntax()
860         .descendants()
861         .find_map(ast::Path::cast)
862         .unwrap();
863
864     insert_use(&file, path, config);
865     let result = file.as_syntax_node().ancestors().last().unwrap().to_string();
866     assert_eq_text!(ra_fixture_after, &result);
867 }
868
869 fn check(
870     path: &str,
871     ra_fixture_before: &str,
872     ra_fixture_after: &str,
873     granularity: ImportGranularity,
874 ) {
875     check_with_config(
876         path,
877         ra_fixture_before,
878         ra_fixture_after,
879         &InsertUseConfig {
880             granularity,
881             enforce_granularity: true,
882             prefix_kind: PrefixKind::Plain,
883             group: true,
884             skip_glob_imports: true,
885         },
886     )
887 }
888
889 fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
890     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Crate)
891 }
892
893 fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
894     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Module)
895 }
896
897 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
898     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Item)
899 }
900
901 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {
902     let use0 = ast::SourceFile::parse(ra_fixture0)
903         .tree()
904         .syntax()
905         .descendants()
906         .find_map(ast::Use::cast)
907         .unwrap();
908
909     let use1 = ast::SourceFile::parse(ra_fixture1)
910         .tree()
911         .syntax()
912         .descendants()
913         .find_map(ast::Use::cast)
914         .unwrap();
915
916     let result = try_merge_imports(&use0, &use1, mb);
917     assert_eq!(result.map(|u| u.to_string()), None);
918 }
919
920 fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) {
921     let syntax = ast::SourceFile::parse(ra_fixture).tree().syntax().clone();
922     let file = super::ImportScope::from(syntax).unwrap();
923     assert_eq!(file.guess_granularity_from_scope(), expected);
924 }