aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--crates/ide_db/src/call_info.rs527
-rw-r--r--crates/ide_db/src/call_info/tests.rs523
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs639
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs620
-rw-r--r--crates/ide_db/src/line_index.rs131
-rw-r--r--crates/ide_db/src/line_index/tests.rs128
-rw-r--r--crates/ide_db/src/traits.rs148
-rw-r--r--crates/ide_db/src/traits/tests.rs144
-rw-r--r--docs/dev/README.md16
10 files changed, 1439 insertions, 1445 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9ffe5f364..8dcea6ea4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -802,9 +802,9 @@ checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
802 802
803[[package]] 803[[package]]
804name = "libloading" 804name = "libloading"
805version = "0.6.5" 805version = "0.6.6"
806source = "registry+https://github.com/rust-lang/crates.io-index" 806source = "registry+https://github.com/rust-lang/crates.io-index"
807checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" 807checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
808dependencies = [ 808dependencies = [
809 "cfg-if 1.0.0", 809 "cfg-if 1.0.0",
810 "winapi 0.3.9", 810 "winapi 0.3.9",
@@ -1555,9 +1555,9 @@ dependencies = [
1555 1555
1556[[package]] 1556[[package]]
1557name = "serde_json" 1557name = "serde_json"
1558version = "1.0.59" 1558version = "1.0.60"
1559source = "registry+https://github.com/rust-lang/crates.io-index" 1559source = "registry+https://github.com/rust-lang/crates.io-index"
1560checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" 1560checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
1561dependencies = [ 1561dependencies = [
1562 "itoa", 1562 "itoa",
1563 "ryu", 1563 "ryu",
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 83a602b9a..615fa7b0e 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -228,529 +228,4 @@ impl FnCallNode {
228} 228}
229 229
230#[cfg(test)] 230#[cfg(test)]
231mod tests { 231mod tests;
232 use crate::RootDatabase;
233 use base_db::{fixture::ChangeFixture, FilePosition};
234 use expect_test::{expect, Expect};
235 use test_utils::{mark, RangeOrOffset};
236
237 /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
238 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
239 let change_fixture = ChangeFixture::parse(ra_fixture);
240 let mut database = RootDatabase::default();
241 database.apply_change(change_fixture.change);
242 let (file_id, range_or_offset) =
243 change_fixture.file_position.expect("expected a marker (<|>)");
244 let offset = match range_or_offset {
245 RangeOrOffset::Range(_) => panic!(),
246 RangeOrOffset::Offset(it) => it,
247 };
248 (database, FilePosition { file_id, offset })
249 }
250
251 fn check(ra_fixture: &str, expect: Expect) {
252 let (db, position) = position(ra_fixture);
253 let call_info = crate::call_info::call_info(&db, position);
254 let actual = match call_info {
255 Some(call_info) => {
256 let docs = match &call_info.doc {
257 None => "".to_string(),
258 Some(docs) => format!("{}\n------\n", docs.as_str()),
259 };
260 let params = call_info
261 .parameter_labels()
262 .enumerate()
263 .map(|(i, param)| {
264 if Some(i) == call_info.active_parameter {
265 format!("<{}>", param)
266 } else {
267 param.to_string()
268 }
269 })
270 .collect::<Vec<_>>()
271 .join(", ");
272 format!("{}{}\n({})\n", docs, call_info.signature, params)
273 }
274 None => String::new(),
275 };
276 expect.assert_eq(&actual);
277 }
278
279 #[test]
280 fn test_fn_signature_two_args() {
281 check(
282 r#"
283fn foo(x: u32, y: u32) -> u32 {x + y}
284fn bar() { foo(<|>3, ); }
285"#,
286 expect![[r#"
287 fn foo(x: u32, y: u32) -> u32
288 (<x: u32>, y: u32)
289 "#]],
290 );
291 check(
292 r#"
293fn foo(x: u32, y: u32) -> u32 {x + y}
294fn bar() { foo(3<|>, ); }
295"#,
296 expect![[r#"
297 fn foo(x: u32, y: u32) -> u32
298 (<x: u32>, y: u32)
299 "#]],
300 );
301 check(
302 r#"
303fn foo(x: u32, y: u32) -> u32 {x + y}
304fn bar() { foo(3,<|> ); }
305"#,
306 expect![[r#"
307 fn foo(x: u32, y: u32) -> u32
308 (x: u32, <y: u32>)
309 "#]],
310 );
311 check(
312 r#"
313fn foo(x: u32, y: u32) -> u32 {x + y}
314fn bar() { foo(3, <|>); }
315"#,
316 expect![[r#"
317 fn foo(x: u32, y: u32) -> u32
318 (x: u32, <y: u32>)
319 "#]],
320 );
321 }
322
323 #[test]
324 fn test_fn_signature_two_args_empty() {
325 check(
326 r#"
327fn foo(x: u32, y: u32) -> u32 {x + y}
328fn bar() { foo(<|>); }
329"#,
330 expect![[r#"
331 fn foo(x: u32, y: u32) -> u32
332 (<x: u32>, y: u32)
333 "#]],
334 );
335 }
336
337 #[test]
338 fn test_fn_signature_two_args_first_generics() {
339 check(
340 r#"
341fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
342 where T: Copy + Display, U: Debug
343{ x + y }
344
345fn bar() { foo(<|>3, ); }
346"#,
347 expect![[r#"
348 fn foo(x: i32, y: {unknown}) -> u32
349 (<x: i32>, y: {unknown})
350 "#]],
351 );
352 }
353
354 #[test]
355 fn test_fn_signature_no_params() {
356 check(
357 r#"
358fn foo<T>() -> T where T: Copy + Display {}
359fn bar() { foo(<|>); }
360"#,
361 expect![[r#"
362 fn foo() -> {unknown}
363 ()
364 "#]],
365 );
366 }
367
368 #[test]
369 fn test_fn_signature_for_impl() {
370 check(
371 r#"
372struct F;
373impl F { pub fn new() { } }
374fn bar() {
375 let _ : F = F::new(<|>);
376}
377"#,
378 expect![[r#"
379 fn new()
380 ()
381 "#]],
382 );
383 }
384
385 #[test]
386 fn test_fn_signature_for_method_self() {
387 check(
388 r#"
389struct S;
390impl S { pub fn do_it(&self) {} }
391
392fn bar() {
393 let s: S = S;
394 s.do_it(<|>);
395}
396"#,
397 expect![[r#"
398 fn do_it(&self)
399 ()
400 "#]],
401 );
402 }
403
404 #[test]
405 fn test_fn_signature_for_method_with_arg() {
406 check(
407 r#"
408struct S;
409impl S {
410 fn foo(&self, x: i32) {}
411}
412
413fn main() { S.foo(<|>); }
414"#,
415 expect![[r#"
416 fn foo(&self, x: i32)
417 (<x: i32>)
418 "#]],
419 );
420 }
421
422 #[test]
423 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
424 check(
425 r#"
426struct S;
427impl S {
428 fn foo(&self, x: i32) {}
429}
430
431fn main() { S::foo(<|>); }
432"#,
433 expect![[r#"
434 fn foo(self: &S, x: i32)
435 (<self: &S>, x: i32)
436 "#]],
437 );
438 }
439
440 #[test]
441 fn test_fn_signature_with_docs_simple() {
442 check(
443 r#"
444/// test
445// non-doc-comment
446fn foo(j: u32) -> u32 {
447 j
448}
449
450fn bar() {
451 let _ = foo(<|>);
452}
453"#,
454 expect![[r#"
455 test
456 ------
457 fn foo(j: u32) -> u32
458 (<j: u32>)
459 "#]],
460 );
461 }
462
463 #[test]
464 fn test_fn_signature_with_docs() {
465 check(
466 r#"
467/// Adds one to the number given.
468///
469/// # Examples
470///
471/// ```
472/// let five = 5;
473///
474/// assert_eq!(6, my_crate::add_one(5));
475/// ```
476pub fn add_one(x: i32) -> i32 {
477 x + 1
478}
479
480pub fn do() {
481 add_one(<|>
482}"#,
483 expect![[r##"
484 Adds one to the number given.
485
486 # Examples
487
488 ```
489 let five = 5;
490
491 assert_eq!(6, my_crate::add_one(5));
492 ```
493 ------
494 fn add_one(x: i32) -> i32
495 (<x: i32>)
496 "##]],
497 );
498 }
499
500 #[test]
501 fn test_fn_signature_with_docs_impl() {
502 check(
503 r#"
504struct addr;
505impl addr {
506 /// Adds one to the number given.
507 ///
508 /// # Examples
509 ///
510 /// ```
511 /// let five = 5;
512 ///
513 /// assert_eq!(6, my_crate::add_one(5));
514 /// ```
515 pub fn add_one(x: i32) -> i32 {
516 x + 1
517 }
518}
519
520pub fn do_it() {
521 addr {};
522 addr::add_one(<|>);
523}
524"#,
525 expect![[r##"
526 Adds one to the number given.
527
528 # Examples
529
530 ```
531 let five = 5;
532
533 assert_eq!(6, my_crate::add_one(5));
534 ```
535 ------
536 fn add_one(x: i32) -> i32
537 (<x: i32>)
538 "##]],
539 );
540 }
541
542 #[test]
543 fn test_fn_signature_with_docs_from_actix() {
544 check(
545 r#"
546struct WriteHandler<E>;
547
548impl<E> WriteHandler<E> {
549 /// Method is called when writer emits error.
550 ///
551 /// If this method returns `ErrorAction::Continue` writer processing
552 /// continues otherwise stream processing stops.
553 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
554 Running::Stop
555 }
556
557 /// Method is called when writer finishes.
558 ///
559 /// By default this method stops actor's `Context`.
560 fn finished(&mut self, ctx: &mut Self::Context) {
561 ctx.stop()
562 }
563}
564
565pub fn foo(mut r: WriteHandler<()>) {
566 r.finished(<|>);
567}
568"#,
569 expect![[r#"
570 Method is called when writer finishes.
571
572 By default this method stops actor's `Context`.
573 ------
574 fn finished(&mut self, ctx: &mut {unknown})
575 (<ctx: &mut {unknown}>)
576 "#]],
577 );
578 }
579
580 #[test]
581 fn call_info_bad_offset() {
582 mark::check!(call_info_bad_offset);
583 check(
584 r#"
585fn foo(x: u32, y: u32) -> u32 {x + y}
586fn bar() { foo <|> (3, ); }
587"#,
588 expect![[""]],
589 );
590 }
591
592 #[test]
593 fn test_nested_method_in_lambda() {
594 check(
595 r#"
596struct Foo;
597impl Foo { fn bar(&self, _: u32) { } }
598
599fn bar(_: u32) { }
600
601fn main() {
602 let foo = Foo;
603 std::thread::spawn(move || foo.bar(<|>));
604}
605"#,
606 expect![[r#"
607 fn bar(&self, _: u32)
608 (<_: u32>)
609 "#]],
610 );
611 }
612
613 #[test]
614 fn works_for_tuple_structs() {
615 check(
616 r#"
617/// A cool tuple struct
618struct S(u32, i32);
619fn main() {
620 let s = S(0, <|>);
621}
622"#,
623 expect![[r#"
624 A cool tuple struct
625 ------
626 struct S(u32, i32)
627 (u32, <i32>)
628 "#]],
629 );
630 }
631
632 #[test]
633 fn generic_struct() {
634 check(
635 r#"
636struct S<T>(T);
637fn main() {
638 let s = S(<|>);
639}
640"#,
641 expect![[r#"
642 struct S({unknown})
643 (<{unknown}>)
644 "#]],
645 );
646 }
647
648 #[test]
649 fn works_for_enum_variants() {
650 check(
651 r#"
652enum E {
653 /// A Variant
654 A(i32),
655 /// Another
656 B,
657 /// And C
658 C { a: i32, b: i32 }
659}
660
661fn main() {
662 let a = E::A(<|>);
663}
664"#,
665 expect![[r#"
666 A Variant
667 ------
668 enum E::A(i32)
669 (<i32>)
670 "#]],
671 );
672 }
673
674 #[test]
675 fn cant_call_struct_record() {
676 check(
677 r#"
678struct S { x: u32, y: i32 }
679fn main() {
680 let s = S(<|>);
681}
682"#,
683 expect![[""]],
684 );
685 }
686
687 #[test]
688 fn cant_call_enum_record() {
689 check(
690 r#"
691enum E {
692 /// A Variant
693 A(i32),
694 /// Another
695 B,
696 /// And C
697 C { a: i32, b: i32 }
698}
699
700fn main() {
701 let a = E::C(<|>);
702}
703"#,
704 expect![[""]],
705 );
706 }
707
708 #[test]
709 fn fn_signature_for_call_in_macro() {
710 check(
711 r#"
712macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
713fn foo() { }
714id! {
715 fn bar() { foo(<|>); }
716}
717"#,
718 expect![[r#"
719 fn foo()
720 ()
721 "#]],
722 );
723 }
724
725 #[test]
726 fn call_info_for_lambdas() {
727 check(
728 r#"
729struct S;
730fn foo(s: S) -> i32 { 92 }
731fn main() {
732 (|s| foo(s))(<|>)
733}
734 "#,
735 expect![[r#"
736 (S) -> i32
737 (<S>)
738 "#]],
739 )
740 }
741
742 #[test]
743 fn call_info_for_fn_ptr() {
744 check(
745 r#"
746fn main(f: fn(i32, f64) -> char) {
747 f(0, <|>)
748}
749 "#,
750 expect![[r#"
751 (i32, f64) -> char
752 (i32, <f64>)
753 "#]],
754 )
755 }
756}
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
new file mode 100644
index 000000000..9335aeaa5
--- /dev/null
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -0,0 +1,523 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset};
5
6/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
8 let change_fixture = ChangeFixture::parse(ra_fixture);
9 let mut database = RootDatabase::default();
10 database.apply_change(change_fixture.change);
11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
12 let offset = match range_or_offset {
13 RangeOrOffset::Range(_) => panic!(),
14 RangeOrOffset::Offset(it) => it,
15 };
16 (database, FilePosition { file_id, offset })
17}
18
19fn check(ra_fixture: &str, expect: Expect) {
20 let (db, position) = position(ra_fixture);
21 let call_info = crate::call_info::call_info(&db, position);
22 let actual = match call_info {
23 Some(call_info) => {
24 let docs = match &call_info.doc {
25 None => "".to_string(),
26 Some(docs) => format!("{}\n------\n", docs.as_str()),
27 };
28 let params = call_info
29 .parameter_labels()
30 .enumerate()
31 .map(|(i, param)| {
32 if Some(i) == call_info.active_parameter {
33 format!("<{}>", param)
34 } else {
35 param.to_string()
36 }
37 })
38 .collect::<Vec<_>>()
39 .join(", ");
40 format!("{}{}\n({})\n", docs, call_info.signature, params)
41 }
42 None => String::new(),
43 };
44 expect.assert_eq(&actual);
45}
46
47#[test]
48fn test_fn_signature_two_args() {
49 check(
50 r#"
51fn foo(x: u32, y: u32) -> u32 {x + y}
52fn bar() { foo(<|>3, ); }
53"#,
54 expect![[r#"
55 fn foo(x: u32, y: u32) -> u32
56 (<x: u32>, y: u32)
57 "#]],
58 );
59 check(
60 r#"
61fn foo(x: u32, y: u32) -> u32 {x + y}
62fn bar() { foo(3<|>, ); }
63"#,
64 expect![[r#"
65 fn foo(x: u32, y: u32) -> u32
66 (<x: u32>, y: u32)
67 "#]],
68 );
69 check(
70 r#"
71fn foo(x: u32, y: u32) -> u32 {x + y}
72fn bar() { foo(3,<|> ); }
73"#,
74 expect![[r#"
75 fn foo(x: u32, y: u32) -> u32
76 (x: u32, <y: u32>)
77 "#]],
78 );
79 check(
80 r#"
81fn foo(x: u32, y: u32) -> u32 {x + y}
82fn bar() { foo(3, <|>); }
83"#,
84 expect![[r#"
85 fn foo(x: u32, y: u32) -> u32
86 (x: u32, <y: u32>)
87 "#]],
88 );
89}
90
91#[test]
92fn test_fn_signature_two_args_empty() {
93 check(
94 r#"
95fn foo(x: u32, y: u32) -> u32 {x + y}
96fn bar() { foo(<|>); }
97"#,
98 expect![[r#"
99 fn foo(x: u32, y: u32) -> u32
100 (<x: u32>, y: u32)
101 "#]],
102 );
103}
104
105#[test]
106fn test_fn_signature_two_args_first_generics() {
107 check(
108 r#"
109fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
110 where T: Copy + Display, U: Debug
111{ x + y }
112
113fn bar() { foo(<|>3, ); }
114"#,
115 expect![[r#"
116 fn foo(x: i32, y: {unknown}) -> u32
117 (<x: i32>, y: {unknown})
118 "#]],
119 );
120}
121
122#[test]
123fn test_fn_signature_no_params() {
124 check(
125 r#"
126fn foo<T>() -> T where T: Copy + Display {}
127fn bar() { foo(<|>); }
128"#,
129 expect![[r#"
130 fn foo() -> {unknown}
131 ()
132 "#]],
133 );
134}
135
136#[test]
137fn test_fn_signature_for_impl() {
138 check(
139 r#"
140struct F;
141impl F { pub fn new() { } }
142fn bar() {
143 let _ : F = F::new(<|>);
144}
145"#,
146 expect![[r#"
147 fn new()
148 ()
149 "#]],
150 );
151}
152
153#[test]
154fn test_fn_signature_for_method_self() {
155 check(
156 r#"
157struct S;
158impl S { pub fn do_it(&self) {} }
159
160fn bar() {
161 let s: S = S;
162 s.do_it(<|>);
163}
164"#,
165 expect![[r#"
166 fn do_it(&self)
167 ()
168 "#]],
169 );
170}
171
172#[test]
173fn test_fn_signature_for_method_with_arg() {
174 check(
175 r#"
176struct S;
177impl S {
178 fn foo(&self, x: i32) {}
179}
180
181fn main() { S.foo(<|>); }
182"#,
183 expect![[r#"
184 fn foo(&self, x: i32)
185 (<x: i32>)
186 "#]],
187 );
188}
189
190#[test]
191fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
192 check(
193 r#"
194struct S;
195impl S {
196 fn foo(&self, x: i32) {}
197}
198
199fn main() { S::foo(<|>); }
200"#,
201 expect![[r#"
202 fn foo(self: &S, x: i32)
203 (<self: &S>, x: i32)
204 "#]],
205 );
206}
207
208#[test]
209fn test_fn_signature_with_docs_simple() {
210 check(
211 r#"
212/// test
213// non-doc-comment
214fn foo(j: u32) -> u32 {
215 j
216}
217
218fn bar() {
219 let _ = foo(<|>);
220}
221"#,
222 expect![[r#"
223 test
224 ------
225 fn foo(j: u32) -> u32
226 (<j: u32>)
227 "#]],
228 );
229}
230
231#[test]
232fn test_fn_signature_with_docs() {
233 check(
234 r#"
235/// Adds one to the number given.
236///
237/// # Examples
238///
239/// ```
240/// let five = 5;
241///
242/// assert_eq!(6, my_crate::add_one(5));
243/// ```
244pub fn add_one(x: i32) -> i32 {
245 x + 1
246}
247
248pub fn do() {
249 add_one(<|>
250}"#,
251 expect![[r##"
252 Adds one to the number given.
253
254 # Examples
255
256 ```
257 let five = 5;
258
259 assert_eq!(6, my_crate::add_one(5));
260 ```
261 ------
262 fn add_one(x: i32) -> i32
263 (<x: i32>)
264 "##]],
265 );
266}
267
268#[test]
269fn test_fn_signature_with_docs_impl() {
270 check(
271 r#"
272struct addr;
273impl addr {
274 /// Adds one to the number given.
275 ///
276 /// # Examples
277 ///
278 /// ```
279 /// let five = 5;
280 ///
281 /// assert_eq!(6, my_crate::add_one(5));
282 /// ```
283 pub fn add_one(x: i32) -> i32 {
284 x + 1
285 }
286}
287
288pub fn do_it() {
289 addr {};
290 addr::add_one(<|>);
291}
292"#,
293 expect![[r##"
294 Adds one to the number given.
295
296 # Examples
297
298 ```
299 let five = 5;
300
301 assert_eq!(6, my_crate::add_one(5));
302 ```
303 ------
304 fn add_one(x: i32) -> i32
305 (<x: i32>)
306 "##]],
307 );
308}
309
310#[test]
311fn test_fn_signature_with_docs_from_actix() {
312 check(
313 r#"
314struct WriteHandler<E>;
315
316impl<E> WriteHandler<E> {
317 /// Method is called when writer emits error.
318 ///
319 /// If this method returns `ErrorAction::Continue` writer processing
320 /// continues otherwise stream processing stops.
321 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
322 Running::Stop
323 }
324
325 /// Method is called when writer finishes.
326 ///
327 /// By default this method stops actor's `Context`.
328 fn finished(&mut self, ctx: &mut Self::Context) {
329 ctx.stop()
330 }
331}
332
333pub fn foo(mut r: WriteHandler<()>) {
334 r.finished(<|>);
335}
336"#,
337 expect![[r#"
338 Method is called when writer finishes.
339
340 By default this method stops actor's `Context`.
341 ------
342 fn finished(&mut self, ctx: &mut {unknown})
343 (<ctx: &mut {unknown}>)
344 "#]],
345 );
346}
347
348#[test]
349fn call_info_bad_offset() {
350 mark::check!(call_info_bad_offset);
351 check(
352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y}
354fn bar() { foo <|> (3, ); }
355"#,
356 expect![[""]],
357 );
358}
359
360#[test]
361fn test_nested_method_in_lambda() {
362 check(
363 r#"
364struct Foo;
365impl Foo { fn bar(&self, _: u32) { } }
366
367fn bar(_: u32) { }
368
369fn main() {
370 let foo = Foo;
371 std::thread::spawn(move || foo.bar(<|>));
372}
373"#,
374 expect![[r#"
375 fn bar(&self, _: u32)
376 (<_: u32>)
377 "#]],
378 );
379}
380
381#[test]
382fn works_for_tuple_structs() {
383 check(
384 r#"
385/// A cool tuple struct
386struct S(u32, i32);
387fn main() {
388 let s = S(0, <|>);
389}
390"#,
391 expect![[r#"
392 A cool tuple struct
393 ------
394 struct S(u32, i32)
395 (u32, <i32>)
396 "#]],
397 );
398}
399
400#[test]
401fn generic_struct() {
402 check(
403 r#"
404struct S<T>(T);
405fn main() {
406 let s = S(<|>);
407}
408"#,
409 expect![[r#"
410 struct S({unknown})
411 (<{unknown}>)
412 "#]],
413 );
414}
415
416#[test]
417fn works_for_enum_variants() {
418 check(
419 r#"
420enum E {
421 /// A Variant
422 A(i32),
423 /// Another
424 B,
425 /// And C
426 C { a: i32, b: i32 }
427}
428
429fn main() {
430 let a = E::A(<|>);
431}
432"#,
433 expect![[r#"
434 A Variant
435 ------
436 enum E::A(i32)
437 (<i32>)
438 "#]],
439 );
440}
441
442#[test]
443fn cant_call_struct_record() {
444 check(
445 r#"
446struct S { x: u32, y: i32 }
447fn main() {
448 let s = S(<|>);
449}
450"#,
451 expect![[""]],
452 );
453}
454
455#[test]
456fn cant_call_enum_record() {
457 check(
458 r#"
459enum E {
460 /// A Variant
461 A(i32),
462 /// Another
463 B,
464 /// And C
465 C { a: i32, b: i32 }
466}
467
468fn main() {
469 let a = E::C(<|>);
470}
471"#,
472 expect![[""]],
473 );
474}
475
476#[test]
477fn fn_signature_for_call_in_macro() {
478 check(
479 r#"
480macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
481fn foo() { }
482id! {
483 fn bar() { foo(<|>); }
484}
485"#,
486 expect![[r#"
487 fn foo()
488 ()
489 "#]],
490 );
491}
492
493#[test]
494fn call_info_for_lambdas() {
495 check(
496 r#"
497struct S;
498fn foo(s: S) -> i32 { 92 }
499fn main() {
500 (|s| foo(s))(<|>)
501}
502 "#,
503 expect![[r#"
504 (S) -> i32
505 (<S>)
506 "#]],
507 )
508}
509
510#[test]
511fn call_info_for_fn_ptr() {
512 check(
513 r#"
514fn main(f: fn(i32, f64) -> char) {
515 f(0, <|>)
516}
517 "#,
518 expect![[r#"
519 (i32, f64) -> char
520 (i32, <f64>)
521 "#]],
522 )
523}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 08d246c16..040843990 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -573,641 +573,4 @@ fn find_insert_position(
573} 573}
574 574
575#[cfg(test)] 575#[cfg(test)]
576mod tests { 576mod tests;
577 use super::*;
578
579 use test_utils::assert_eq_text;
580
581 #[test]
582 fn insert_existing() {
583 check_full("std::fs", "use std::fs;", "use std::fs;")
584 }
585
586 #[test]
587 fn insert_start() {
588 check_none(
589 "std::bar::AA",
590 r"
591use std::bar::B;
592use std::bar::D;
593use std::bar::F;
594use std::bar::G;",
595 r"
596use std::bar::AA;
597use std::bar::B;
598use std::bar::D;
599use std::bar::F;
600use std::bar::G;",
601 )
602 }
603
604 #[test]
605 fn insert_start_indent() {
606 mark::check!(insert_use_indent_after);
607 check_none(
608 "std::bar::AA",
609 r"
610 use std::bar::B;
611 use std::bar::D;",
612 r"
613 use std::bar::AA;
614 use std::bar::B;
615 use std::bar::D;",
616 )
617 }
618
619 #[test]
620 fn insert_middle() {
621 check_none(
622 "std::bar::EE",
623 r"
624use std::bar::A;
625use std::bar::D;
626use std::bar::F;
627use std::bar::G;",
628 r"
629use std::bar::A;
630use std::bar::D;
631use std::bar::EE;
632use std::bar::F;
633use std::bar::G;",
634 )
635 }
636
637 #[test]
638 fn insert_middle_indent() {
639 check_none(
640 "std::bar::EE",
641 r"
642 use std::bar::A;
643 use std::bar::D;
644 use std::bar::F;
645 use std::bar::G;",
646 r"
647 use std::bar::A;
648 use std::bar::D;
649 use std::bar::EE;
650 use std::bar::F;
651 use std::bar::G;",
652 )
653 }
654
655 #[test]
656 fn insert_end() {
657 check_none(
658 "std::bar::ZZ",
659 r"
660use std::bar::A;
661use std::bar::D;
662use std::bar::F;
663use std::bar::G;",
664 r"
665use std::bar::A;
666use std::bar::D;
667use std::bar::F;
668use std::bar::G;
669use std::bar::ZZ;",
670 )
671 }
672
673 #[test]
674 fn insert_end_indent() {
675 mark::check!(insert_use_indent_before);
676 check_none(
677 "std::bar::ZZ",
678 r"
679 use std::bar::A;
680 use std::bar::D;
681 use std::bar::F;
682 use std::bar::G;",
683 r"
684 use std::bar::A;
685 use std::bar::D;
686 use std::bar::F;
687 use std::bar::G;
688 use std::bar::ZZ;",
689 )
690 }
691
692 #[test]
693 fn insert_middle_nested() {
694 check_none(
695 "std::bar::EE",
696 r"
697use std::bar::A;
698use std::bar::{D, Z}; // example of weird imports due to user
699use std::bar::F;
700use std::bar::G;",
701 r"
702use std::bar::A;
703use std::bar::EE;
704use std::bar::{D, Z}; // example of weird imports due to user
705use std::bar::F;
706use std::bar::G;",
707 )
708 }
709
710 #[test]
711 fn insert_middle_groups() {
712 check_none(
713 "foo::bar::GG",
714 r"
715 use std::bar::A;
716 use std::bar::D;
717
718 use foo::bar::F;
719 use foo::bar::H;",
720 r"
721 use std::bar::A;
722 use std::bar::D;
723
724 use foo::bar::F;
725 use foo::bar::GG;
726 use foo::bar::H;",
727 )
728 }
729
730 #[test]
731 fn insert_first_matching_group() {
732 check_none(
733 "foo::bar::GG",
734 r"
735 use foo::bar::A;
736 use foo::bar::D;
737
738 use std;
739
740 use foo::bar::F;
741 use foo::bar::H;",
742 r"
743 use foo::bar::A;
744 use foo::bar::D;
745 use foo::bar::GG;
746
747 use std;
748
749 use foo::bar::F;
750 use foo::bar::H;",
751 )
752 }
753
754 #[test]
755 fn insert_missing_group_std() {
756 check_none(
757 "std::fmt",
758 r"
759 use foo::bar::A;
760 use foo::bar::D;",
761 r"
762 use std::fmt;
763
764 use foo::bar::A;
765 use foo::bar::D;",
766 )
767 }
768
769 #[test]
770 fn insert_missing_group_self() {
771 check_none(
772 "self::fmt",
773 r"
774use foo::bar::A;
775use foo::bar::D;",
776 r"
777use foo::bar::A;
778use foo::bar::D;
779
780use self::fmt;",
781 )
782 }
783
784 #[test]
785 fn insert_no_imports() {
786 check_full(
787 "foo::bar",
788 "fn main() {}",
789 r"use foo::bar;
790
791fn main() {}",
792 )
793 }
794
795 #[test]
796 fn insert_empty_file() {
797 // empty files will get two trailing newlines
798 // this is due to the test case insert_no_imports above
799 check_full(
800 "foo::bar",
801 "",
802 r"use foo::bar;
803
804",
805 )
806 }
807
808 #[test]
809 fn insert_empty_module() {
810 mark::check!(insert_use_no_indent_after);
811 check(
812 "foo::bar",
813 "mod x {}",
814 r"{
815 use foo::bar;
816}",
817 None,
818 true,
819 )
820 }
821
822 #[test]
823 fn insert_after_inner_attr() {
824 check_full(
825 "foo::bar",
826 r"#![allow(unused_imports)]",
827 r"#![allow(unused_imports)]
828
829use foo::bar;",
830 )
831 }
832
833 #[test]
834 fn insert_after_inner_attr2() {
835 check_full(
836 "foo::bar",
837 r"#![allow(unused_imports)]
838
839#![no_std]
840fn main() {}",
841 r"#![allow(unused_imports)]
842
843#![no_std]
844
845use foo::bar;
846fn main() {}",
847 );
848 }
849
850 #[test]
851 fn inserts_after_single_line_inner_comments() {
852 check_none(
853 "foo::bar::Baz",
854 "//! Single line inner comments do not allow any code before them.",
855 r#"//! Single line inner comments do not allow any code before them.
856
857use foo::bar::Baz;"#,
858 );
859 }
860
861 #[test]
862 fn inserts_after_multiline_inner_comments() {
863 check_none(
864 "foo::bar::Baz",
865 r#"/*! Multiline inner comments do not allow any code before them. */
866
867/*! Still an inner comment, cannot place any code before. */
868fn main() {}"#,
869 r#"/*! Multiline inner comments do not allow any code before them. */
870
871/*! Still an inner comment, cannot place any code before. */
872
873use foo::bar::Baz;
874fn main() {}"#,
875 )
876 }
877
878 #[test]
879 fn inserts_after_all_inner_items() {
880 check_none(
881 "foo::bar::Baz",
882 r#"#![allow(unused_imports)]
883/*! Multiline line comment 2 */
884
885
886//! Single line comment 1
887#![no_std]
888//! Single line comment 2
889fn main() {}"#,
890 r#"#![allow(unused_imports)]
891/*! Multiline line comment 2 */
892
893
894//! Single line comment 1
895#![no_std]
896//! Single line comment 2
897
898use foo::bar::Baz;
899fn main() {}"#,
900 )
901 }
902
903 #[test]
904 fn merge_groups() {
905 check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
906 }
907
908 #[test]
909 fn merge_groups_last() {
910 check_last(
911 "std::io",
912 r"use std::fmt::{Result, Display};",
913 r"use std::fmt::{Result, Display};
914use std::io;",
915 )
916 }
917
918 #[test]
919 fn merge_last_into_self() {
920 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
921 }
922
923 #[test]
924 fn merge_groups_full() {
925 check_full(
926 "std::io",
927 r"use std::fmt::{Result, Display};",
928 r"use std::{fmt::{Result, Display}, io};",
929 )
930 }
931
932 #[test]
933 fn merge_groups_long_full() {
934 check_full(
935 "std::foo::bar::Baz",
936 r"use std::foo::bar::Qux;",
937 r"use std::foo::bar::{Baz, Qux};",
938 )
939 }
940
941 #[test]
942 fn merge_groups_long_last() {
943 check_last(
944 "std::foo::bar::Baz",
945 r"use std::foo::bar::Qux;",
946 r"use std::foo::bar::{Baz, Qux};",
947 )
948 }
949
950 #[test]
951 fn merge_groups_long_full_list() {
952 check_full(
953 "std::foo::bar::Baz",
954 r"use std::foo::bar::{Qux, Quux};",
955 r"use std::foo::bar::{Baz, Quux, Qux};",
956 )
957 }
958
959 #[test]
960 fn merge_groups_long_last_list() {
961 check_last(
962 "std::foo::bar::Baz",
963 r"use std::foo::bar::{Qux, Quux};",
964 r"use std::foo::bar::{Baz, Quux, Qux};",
965 )
966 }
967
968 #[test]
969 fn merge_groups_long_full_nested() {
970 check_full(
971 "std::foo::bar::Baz",
972 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
973 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
974 )
975 }
976
977 #[test]
978 fn merge_groups_long_last_nested() {
979 check_last(
980 "std::foo::bar::Baz",
981 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
982 r"use std::foo::bar::Baz;
983use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
984 )
985 }
986
987 #[test]
988 fn merge_groups_full_nested_deep() {
989 check_full(
990 "std::foo::bar::quux::Baz",
991 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
992 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
993 )
994 }
995
996 #[test]
997 fn merge_groups_full_nested_long() {
998 check_full(
999 "std::foo::bar::Baz",
1000 r"use std::{foo::bar::Qux};",
1001 r"use std::{foo::bar::{Baz, Qux}};",
1002 );
1003 }
1004
1005 #[test]
1006 fn merge_groups_last_nested_long() {
1007 check_full(
1008 "std::foo::bar::Baz",
1009 r"use std::{foo::bar::Qux};",
1010 r"use std::{foo::bar::{Baz, Qux}};",
1011 );
1012 }
1013
1014 #[test]
1015 fn merge_groups_skip_pub() {
1016 check_full(
1017 "std::io",
1018 r"pub use std::fmt::{Result, Display};",
1019 r"pub use std::fmt::{Result, Display};
1020use std::io;",
1021 )
1022 }
1023
1024 #[test]
1025 fn merge_groups_skip_pub_crate() {
1026 check_full(
1027 "std::io",
1028 r"pub(crate) use std::fmt::{Result, Display};",
1029 r"pub(crate) use std::fmt::{Result, Display};
1030use std::io;",
1031 )
1032 }
1033
1034 #[test]
1035 #[ignore] // FIXME: Support this
1036 fn split_out_merge() {
1037 check_last(
1038 "std::fmt::Result",
1039 r"use std::{fmt, io};",
1040 r"use std::fmt::{self, Result};
1041use std::io;",
1042 )
1043 }
1044
1045 #[test]
1046 fn merge_into_module_import() {
1047 check_full(
1048 "std::fmt::Result",
1049 r"use std::{fmt, io};",
1050 r"use std::{fmt::{self, Result}, io};",
1051 )
1052 }
1053
1054 #[test]
1055 fn merge_groups_self() {
1056 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
1057 }
1058
1059 #[test]
1060 fn merge_mod_into_glob() {
1061 check_full(
1062 "token::TokenKind",
1063 r"use token::TokenKind::*;",
1064 r"use token::TokenKind::{*, self};",
1065 )
1066 // FIXME: have it emit `use token::TokenKind::{self, *}`?
1067 }
1068
1069 #[test]
1070 fn merge_self_glob() {
1071 check_full("self", r"use self::*;", r"use self::{*, self};")
1072 // FIXME: have it emit `use {self, *}`?
1073 }
1074
1075 #[test]
1076 fn merge_glob_nested() {
1077 check_full(
1078 "foo::bar::quux::Fez",
1079 r"use foo::bar::{Baz, quux::*};",
1080 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
1081 )
1082 }
1083
1084 #[test]
1085 fn merge_nested_considers_first_segments() {
1086 check_full(
1087 "hir_ty::display::write_bounds_like_dyn_trait",
1088 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
1089 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
1090 );
1091 }
1092
1093 #[test]
1094 fn skip_merge_last_too_long() {
1095 check_last(
1096 "foo::bar",
1097 r"use foo::bar::baz::Qux;",
1098 r"use foo::bar;
1099use foo::bar::baz::Qux;",
1100 );
1101 }
1102
1103 #[test]
1104 fn skip_merge_last_too_long2() {
1105 check_last(
1106 "foo::bar::baz::Qux",
1107 r"use foo::bar;",
1108 r"use foo::bar;
1109use foo::bar::baz::Qux;",
1110 );
1111 }
1112
1113 #[test]
1114 fn insert_short_before_long() {
1115 check_none(
1116 "foo::bar",
1117 r"use foo::bar::baz::Qux;",
1118 r"use foo::bar;
1119use foo::bar::baz::Qux;",
1120 );
1121 }
1122
1123 #[test]
1124 fn merge_last_fail() {
1125 check_merge_only_fail(
1126 r"use foo::bar::{baz::{Qux, Fez}};",
1127 r"use foo::bar::{baaz::{Quux, Feez}};",
1128 MergeBehaviour::Last,
1129 );
1130 }
1131
1132 #[test]
1133 fn merge_last_fail1() {
1134 check_merge_only_fail(
1135 r"use foo::bar::{baz::{Qux, Fez}};",
1136 r"use foo::bar::baaz::{Quux, Feez};",
1137 MergeBehaviour::Last,
1138 );
1139 }
1140
1141 #[test]
1142 fn merge_last_fail2() {
1143 check_merge_only_fail(
1144 r"use foo::bar::baz::{Qux, Fez};",
1145 r"use foo::bar::{baaz::{Quux, Feez}};",
1146 MergeBehaviour::Last,
1147 );
1148 }
1149
1150 #[test]
1151 fn merge_last_fail3() {
1152 check_merge_only_fail(
1153 r"use foo::bar::baz::{Qux, Fez};",
1154 r"use foo::bar::baaz::{Quux, Feez};",
1155 MergeBehaviour::Last,
1156 );
1157 }
1158
1159 fn check(
1160 path: &str,
1161 ra_fixture_before: &str,
1162 ra_fixture_after: &str,
1163 mb: Option<MergeBehaviour>,
1164 module: bool,
1165 ) {
1166 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
1167 if module {
1168 syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
1169 }
1170 let file = super::ImportScope::from(syntax).unwrap();
1171 let path = ast::SourceFile::parse(&format!("use {};", path))
1172 .tree()
1173 .syntax()
1174 .descendants()
1175 .find_map(ast::Path::cast)
1176 .unwrap();
1177
1178 let rewriter = insert_use(&file, path, mb);
1179 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
1180 assert_eq_text!(&result, ra_fixture_after);
1181 }
1182
1183 fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1184 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
1185 }
1186
1187 fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1188 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
1189 }
1190
1191 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
1192 check(path, ra_fixture_before, ra_fixture_after, None, false)
1193 }
1194
1195 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
1196 let use0 = ast::SourceFile::parse(ra_fixture0)
1197 .tree()
1198 .syntax()
1199 .descendants()
1200 .find_map(ast::Use::cast)
1201 .unwrap();
1202
1203 let use1 = ast::SourceFile::parse(ra_fixture1)
1204 .tree()
1205 .syntax()
1206 .descendants()
1207 .find_map(ast::Use::cast)
1208 .unwrap();
1209
1210 let result = try_merge_imports(&use0, &use1, mb);
1211 assert_eq!(result.map(|u| u.to_string()), None);
1212 }
1213}
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
new file mode 100644
index 000000000..86bfa5b41
--- /dev/null
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -0,0 +1,620 @@
1use super::*;
2
3use test_utils::assert_eq_text;
4
5#[test]
6fn insert_existing() {
7 check_full("std::fs", "use std::fs;", "use std::fs;")
8}
9
10#[test]
11fn insert_start() {
12 check_none(
13 "std::bar::AA",
14 r"
15use std::bar::B;
16use std::bar::D;
17use std::bar::F;
18use std::bar::G;",
19 r"
20use std::bar::AA;
21use std::bar::B;
22use std::bar::D;
23use std::bar::F;
24use std::bar::G;",
25 )
26}
27
28#[test]
29fn insert_start_indent() {
30 mark::check!(insert_use_indent_after);
31 check_none(
32 "std::bar::AA",
33 r"
34 use std::bar::B;
35 use std::bar::D;",
36 r"
37 use std::bar::AA;
38 use std::bar::B;
39 use std::bar::D;",
40 )
41}
42
43#[test]
44fn insert_middle() {
45 check_none(
46 "std::bar::EE",
47 r"
48use std::bar::A;
49use std::bar::D;
50use std::bar::F;
51use std::bar::G;",
52 r"
53use std::bar::A;
54use std::bar::D;
55use std::bar::EE;
56use std::bar::F;
57use std::bar::G;",
58 )
59}
60
61#[test]
62fn insert_middle_indent() {
63 check_none(
64 "std::bar::EE",
65 r"
66 use std::bar::A;
67 use std::bar::D;
68 use std::bar::F;
69 use std::bar::G;",
70 r"
71 use std::bar::A;
72 use std::bar::D;
73 use std::bar::EE;
74 use std::bar::F;
75 use std::bar::G;",
76 )
77}
78
79#[test]
80fn insert_end() {
81 check_none(
82 "std::bar::ZZ",
83 r"
84use std::bar::A;
85use std::bar::D;
86use std::bar::F;
87use std::bar::G;",
88 r"
89use std::bar::A;
90use std::bar::D;
91use std::bar::F;
92use std::bar::G;
93use std::bar::ZZ;",
94 )
95}
96
97#[test]
98fn insert_end_indent() {
99 mark::check!(insert_use_indent_before);
100 check_none(
101 "std::bar::ZZ",
102 r"
103 use std::bar::A;
104 use std::bar::D;
105 use std::bar::F;
106 use std::bar::G;",
107 r"
108 use std::bar::A;
109 use std::bar::D;
110 use std::bar::F;
111 use std::bar::G;
112 use std::bar::ZZ;",
113 )
114}
115
116#[test]
117fn insert_middle_nested() {
118 check_none(
119 "std::bar::EE",
120 r"
121use std::bar::A;
122use std::bar::{D, Z}; // example of weird imports due to user
123use std::bar::F;
124use std::bar::G;",
125 r"
126use std::bar::A;
127use std::bar::EE;
128use std::bar::{D, Z}; // example of weird imports due to user
129use std::bar::F;
130use std::bar::G;",
131 )
132}
133
134#[test]
135fn insert_middle_groups() {
136 check_none(
137 "foo::bar::GG",
138 r"
139 use std::bar::A;
140 use std::bar::D;
141
142 use foo::bar::F;
143 use foo::bar::H;",
144 r"
145 use std::bar::A;
146 use std::bar::D;
147
148 use foo::bar::F;
149 use foo::bar::GG;
150 use foo::bar::H;",
151 )
152}
153
154#[test]
155fn insert_first_matching_group() {
156 check_none(
157 "foo::bar::GG",
158 r"
159 use foo::bar::A;
160 use foo::bar::D;
161
162 use std;
163
164 use foo::bar::F;
165 use foo::bar::H;",
166 r"
167 use foo::bar::A;
168 use foo::bar::D;
169 use foo::bar::GG;
170
171 use std;
172
173 use foo::bar::F;
174 use foo::bar::H;",
175 )
176}
177
178#[test]
179fn insert_missing_group_std() {
180 check_none(
181 "std::fmt",
182 r"
183 use foo::bar::A;
184 use foo::bar::D;",
185 r"
186 use std::fmt;
187
188 use foo::bar::A;
189 use foo::bar::D;",
190 )
191}
192
193#[test]
194fn insert_missing_group_self() {
195 check_none(
196 "self::fmt",
197 r"
198use foo::bar::A;
199use foo::bar::D;",
200 r"
201use foo::bar::A;
202use foo::bar::D;
203
204use self::fmt;",
205 )
206}
207
208#[test]
209fn insert_no_imports() {
210 check_full(
211 "foo::bar",
212 "fn main() {}",
213 r"use foo::bar;
214
215fn main() {}",
216 )
217}
218
219#[test]
220fn insert_empty_file() {
221 // empty files will get two trailing newlines
222 // this is due to the test case insert_no_imports above
223 check_full(
224 "foo::bar",
225 "",
226 r"use foo::bar;
227
228",
229 )
230}
231
232#[test]
233fn insert_empty_module() {
234 mark::check!(insert_use_no_indent_after);
235 check(
236 "foo::bar",
237 "mod x {}",
238 r"{
239 use foo::bar;
240}",
241 None,
242 true,
243 )
244}
245
246#[test]
247fn insert_after_inner_attr() {
248 check_full(
249 "foo::bar",
250 r"#![allow(unused_imports)]",
251 r"#![allow(unused_imports)]
252
253use foo::bar;",
254 )
255}
256
257#[test]
258fn insert_after_inner_attr2() {
259 check_full(
260 "foo::bar",
261 r"#![allow(unused_imports)]
262
263#![no_std]
264fn main() {}",
265 r"#![allow(unused_imports)]
266
267#![no_std]
268
269use foo::bar;
270fn main() {}",
271 );
272}
273
274#[test]
275fn inserts_after_single_line_inner_comments() {
276 check_none(
277 "foo::bar::Baz",
278 "//! Single line inner comments do not allow any code before them.",
279 r#"//! Single line inner comments do not allow any code before them.
280
281use foo::bar::Baz;"#,
282 );
283}
284
285#[test]
286fn inserts_after_multiline_inner_comments() {
287 check_none(
288 "foo::bar::Baz",
289 r#"/*! Multiline inner comments do not allow any code before them. */
290
291/*! Still an inner comment, cannot place any code before. */
292fn main() {}"#,
293 r#"/*! Multiline inner comments do not allow any code before them. */
294
295/*! Still an inner comment, cannot place any code before. */
296
297use foo::bar::Baz;
298fn main() {}"#,
299 )
300}
301
302#[test]
303fn inserts_after_all_inner_items() {
304 check_none(
305 "foo::bar::Baz",
306 r#"#![allow(unused_imports)]
307/*! Multiline line comment 2 */
308
309
310//! Single line comment 1
311#![no_std]
312//! Single line comment 2
313fn main() {}"#,
314 r#"#![allow(unused_imports)]
315/*! Multiline line comment 2 */
316
317
318//! Single line comment 1
319#![no_std]
320//! Single line comment 2
321
322use foo::bar::Baz;
323fn main() {}"#,
324 )
325}
326
327#[test]
328fn merge_groups() {
329 check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
330}
331
332#[test]
333fn merge_groups_last() {
334 check_last(
335 "std::io",
336 r"use std::fmt::{Result, Display};",
337 r"use std::fmt::{Result, Display};
338use std::io;",
339 )
340}
341
342#[test]
343fn merge_last_into_self() {
344 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
345}
346
347#[test]
348fn merge_groups_full() {
349 check_full(
350 "std::io",
351 r"use std::fmt::{Result, Display};",
352 r"use std::{fmt::{Result, Display}, io};",
353 )
354}
355
356#[test]
357fn merge_groups_long_full() {
358 check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
359}
360
361#[test]
362fn merge_groups_long_last() {
363 check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
364}
365
366#[test]
367fn merge_groups_long_full_list() {
368 check_full(
369 "std::foo::bar::Baz",
370 r"use std::foo::bar::{Qux, Quux};",
371 r"use std::foo::bar::{Baz, Quux, Qux};",
372 )
373}
374
375#[test]
376fn merge_groups_long_last_list() {
377 check_last(
378 "std::foo::bar::Baz",
379 r"use std::foo::bar::{Qux, Quux};",
380 r"use std::foo::bar::{Baz, Quux, Qux};",
381 )
382}
383
384#[test]
385fn merge_groups_long_full_nested() {
386 check_full(
387 "std::foo::bar::Baz",
388 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
389 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
390 )
391}
392
393#[test]
394fn merge_groups_long_last_nested() {
395 check_last(
396 "std::foo::bar::Baz",
397 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
398 r"use std::foo::bar::Baz;
399use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
400 )
401}
402
403#[test]
404fn merge_groups_full_nested_deep() {
405 check_full(
406 "std::foo::bar::quux::Baz",
407 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
408 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
409 )
410}
411
412#[test]
413fn merge_groups_full_nested_long() {
414 check_full(
415 "std::foo::bar::Baz",
416 r"use std::{foo::bar::Qux};",
417 r"use std::{foo::bar::{Baz, Qux}};",
418 );
419}
420
421#[test]
422fn merge_groups_last_nested_long() {
423 check_full(
424 "std::foo::bar::Baz",
425 r"use std::{foo::bar::Qux};",
426 r"use std::{foo::bar::{Baz, Qux}};",
427 );
428}
429
430#[test]
431fn merge_groups_skip_pub() {
432 check_full(
433 "std::io",
434 r"pub use std::fmt::{Result, Display};",
435 r"pub use std::fmt::{Result, Display};
436use std::io;",
437 )
438}
439
440#[test]
441fn merge_groups_skip_pub_crate() {
442 check_full(
443 "std::io",
444 r"pub(crate) use std::fmt::{Result, Display};",
445 r"pub(crate) use std::fmt::{Result, Display};
446use std::io;",
447 )
448}
449
450#[test]
451#[ignore] // FIXME: Support this
452fn split_out_merge() {
453 check_last(
454 "std::fmt::Result",
455 r"use std::{fmt, io};",
456 r"use std::fmt::{self, Result};
457use std::io;",
458 )
459}
460
461#[test]
462fn merge_into_module_import() {
463 check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};")
464}
465
466#[test]
467fn merge_groups_self() {
468 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
469}
470
471#[test]
472fn merge_mod_into_glob() {
473 check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};")
474 // FIXME: have it emit `use token::TokenKind::{self, *}`?
475}
476
477#[test]
478fn merge_self_glob() {
479 check_full("self", r"use self::*;", r"use self::{*, self};")
480 // FIXME: have it emit `use {self, *}`?
481}
482
483#[test]
484fn merge_glob_nested() {
485 check_full(
486 "foo::bar::quux::Fez",
487 r"use foo::bar::{Baz, quux::*};",
488 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
489 )
490}
491
492#[test]
493fn merge_nested_considers_first_segments() {
494 check_full(
495 "hir_ty::display::write_bounds_like_dyn_trait",
496 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
497 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
498 );
499}
500
501#[test]
502fn skip_merge_last_too_long() {
503 check_last(
504 "foo::bar",
505 r"use foo::bar::baz::Qux;",
506 r"use foo::bar;
507use foo::bar::baz::Qux;",
508 );
509}
510
511#[test]
512fn skip_merge_last_too_long2() {
513 check_last(
514 "foo::bar::baz::Qux",
515 r"use foo::bar;",
516 r"use foo::bar;
517use foo::bar::baz::Qux;",
518 );
519}
520
521#[test]
522fn insert_short_before_long() {
523 check_none(
524 "foo::bar",
525 r"use foo::bar::baz::Qux;",
526 r"use foo::bar;
527use foo::bar::baz::Qux;",
528 );
529}
530
531#[test]
532fn merge_last_fail() {
533 check_merge_only_fail(
534 r"use foo::bar::{baz::{Qux, Fez}};",
535 r"use foo::bar::{baaz::{Quux, Feez}};",
536 MergeBehaviour::Last,
537 );
538}
539
540#[test]
541fn merge_last_fail1() {
542 check_merge_only_fail(
543 r"use foo::bar::{baz::{Qux, Fez}};",
544 r"use foo::bar::baaz::{Quux, Feez};",
545 MergeBehaviour::Last,
546 );
547}
548
549#[test]
550fn merge_last_fail2() {
551 check_merge_only_fail(
552 r"use foo::bar::baz::{Qux, Fez};",
553 r"use foo::bar::{baaz::{Quux, Feez}};",
554 MergeBehaviour::Last,
555 );
556}
557
558#[test]
559fn merge_last_fail3() {
560 check_merge_only_fail(
561 r"use foo::bar::baz::{Qux, Fez};",
562 r"use foo::bar::baaz::{Quux, Feez};",
563 MergeBehaviour::Last,
564 );
565}
566
567fn check(
568 path: &str,
569 ra_fixture_before: &str,
570 ra_fixture_after: &str,
571 mb: Option<MergeBehaviour>,
572 module: bool,
573) {
574 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
575 if module {
576 syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
577 }
578 let file = super::ImportScope::from(syntax).unwrap();
579 let path = ast::SourceFile::parse(&format!("use {};", path))
580 .tree()
581 .syntax()
582 .descendants()
583 .find_map(ast::Path::cast)
584 .unwrap();
585
586 let rewriter = insert_use(&file, path, mb);
587 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
588 assert_eq_text!(&result, ra_fixture_after);
589}
590
591fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
592 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
593}
594
595fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
596 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
597}
598
599fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
600 check(path, ra_fixture_before, ra_fixture_after, None, false)
601}
602
603fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
604 let use0 = ast::SourceFile::parse(ra_fixture0)
605 .tree()
606 .syntax()
607 .descendants()
608 .find_map(ast::Use::cast)
609 .unwrap();
610
611 let use1 = ast::SourceFile::parse(ra_fixture1)
612 .tree()
613 .syntax()
614 .descendants()
615 .find_map(ast::Use::cast)
616 .unwrap();
617
618 let result = try_merge_imports(&use0, &use1, mb);
619 assert_eq!(result.map(|u| u.to_string()), None);
620}
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs
index a381f7fb8..41226305e 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -149,133 +149,4 @@ impl LineIndex {
149} 149}
150 150
151#[cfg(test)] 151#[cfg(test)]
152mod tests { 152mod tests;
153 use super::*;
154
155 #[test]
156 fn test_line_index() {
157 let text = "hello\nworld";
158 let index = LineIndex::new(text);
159 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
160 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
161 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
162 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
163 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
164 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
165 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
166 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
167 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
168
169 let text = "\nhello\nworld";
170 let index = LineIndex::new(text);
171 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
172 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
173 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
174 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
175 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
176 }
177
178 #[test]
179 fn test_char_len() {
180 assert_eq!('メ'.len_utf8(), 3);
181 assert_eq!('メ'.len_utf16(), 1);
182 }
183
184 #[test]
185 fn test_empty_index() {
186 let col_index = LineIndex::new(
187 "
188const C: char = 'x';
189",
190 );
191 assert_eq!(col_index.utf16_lines.len(), 0);
192 }
193
194 #[test]
195 fn test_single_char() {
196 let col_index = LineIndex::new(
197 "
198const C: char = 'メ';
199",
200 );
201
202 assert_eq!(col_index.utf16_lines.len(), 1);
203 assert_eq!(col_index.utf16_lines[&1].len(), 1);
204 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
205
206 // UTF-8 to UTF-16, no changes
207 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
208
209 // UTF-8 to UTF-16
210 assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
211
212 // UTF-16 to UTF-8, no changes
213 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
214
215 // UTF-16 to UTF-8
216 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
217
218 let col_index = LineIndex::new("a𐐏b");
219 assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
220 }
221
222 #[test]
223 fn test_string() {
224 let col_index = LineIndex::new(
225 "
226const C: char = \"メ メ\";
227",
228 );
229
230 assert_eq!(col_index.utf16_lines.len(), 1);
231 assert_eq!(col_index.utf16_lines[&1].len(), 2);
232 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
233 assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
234
235 // UTF-8 to UTF-16
236 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
237
238 assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
239 assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
240
241 assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
242
243 // UTF-16 to UTF-8
244 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
245
246 // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
247 assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
248 assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
249 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
250
251 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
252 }
253
254 #[test]
255 fn test_splitlines() {
256 fn r(lo: u32, hi: u32) -> TextRange {
257 TextRange::new(lo.into(), hi.into())
258 }
259
260 let text = "a\nbb\nccc\n";
261 let line_index = LineIndex::new(text);
262
263 let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
264 let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
265 assert_eq!(actual, expected);
266
267 let text = "";
268 let line_index = LineIndex::new(text);
269
270 let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
271 let expected = vec![];
272 assert_eq!(actual, expected);
273
274 let text = "\n";
275 let line_index = LineIndex::new(text);
276
277 let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
278 let expected = vec![r(0, 1)];
279 assert_eq!(actual, expected)
280 }
281}
diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs
new file mode 100644
index 000000000..05f7484e8
--- /dev/null
+++ b/crates/ide_db/src/line_index/tests.rs
@@ -0,0 +1,128 @@
1use super::*;
2
3#[test]
4fn test_line_index() {
5 let text = "hello\nworld";
6 let index = LineIndex::new(text);
7 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
8 assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
9 assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
10 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
11 assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
12 assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
13 assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
14 assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
15 assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
16
17 let text = "\nhello\nworld";
18 let index = LineIndex::new(text);
19 assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
20 assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
21 assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
22 assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
23 assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
24}
25
26#[test]
27fn test_char_len() {
28 assert_eq!('メ'.len_utf8(), 3);
29 assert_eq!('メ'.len_utf16(), 1);
30}
31
32#[test]
33fn test_empty_index() {
34 let col_index = LineIndex::new(
35 "
36const C: char = 'x';
37",
38 );
39 assert_eq!(col_index.utf16_lines.len(), 0);
40}
41
42#[test]
43fn test_single_char() {
44 let col_index = LineIndex::new(
45 "
46const C: char = 'メ';
47",
48 );
49
50 assert_eq!(col_index.utf16_lines.len(), 1);
51 assert_eq!(col_index.utf16_lines[&1].len(), 1);
52 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
53
54 // UTF-8 to UTF-16, no changes
55 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
56
57 // UTF-8 to UTF-16
58 assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
59
60 // UTF-16 to UTF-8, no changes
61 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
62
63 // UTF-16 to UTF-8
64 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
65
66 let col_index = LineIndex::new("a𐐏b");
67 assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
68}
69
70#[test]
71fn test_string() {
72 let col_index = LineIndex::new(
73 "
74const C: char = \"メ メ\";
75",
76 );
77
78 assert_eq!(col_index.utf16_lines.len(), 1);
79 assert_eq!(col_index.utf16_lines[&1].len(), 2);
80 assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
81 assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
82
83 // UTF-8 to UTF-16
84 assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
85
86 assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
87 assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
88
89 assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
90
91 // UTF-16 to UTF-8
92 assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
93
94 // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
95 assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
96 assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
97 assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
98
99 assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
100}
101
102#[test]
103fn test_splitlines() {
104 fn r(lo: u32, hi: u32) -> TextRange {
105 TextRange::new(lo.into(), hi.into())
106 }
107
108 let text = "a\nbb\nccc\n";
109 let line_index = LineIndex::new(text);
110
111 let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
112 let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
113 assert_eq!(actual, expected);
114
115 let text = "";
116 let line_index = LineIndex::new(text);
117
118 let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
119 let expected = vec![];
120 assert_eq!(actual, expected);
121
122 let text = "\n";
123 let line_index = LineIndex::new(text);
124
125 let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
126 let expected = vec![r(0, 1)];
127 assert_eq!(actual, expected)
128}
diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs
index f57b6dd91..78a43f587 100644
--- a/crates/ide_db/src/traits.rs
+++ b/crates/ide_db/src/traits.rs
@@ -78,150 +78,4 @@ pub fn get_missing_assoc_items(
78} 78}
79 79
80#[cfg(test)] 80#[cfg(test)]
81mod tests { 81mod tests;
82 use crate::RootDatabase;
83 use base_db::{fixture::ChangeFixture, FilePosition};
84 use expect_test::{expect, Expect};
85 use hir::Semantics;
86 use syntax::ast::{self, AstNode};
87 use test_utils::RangeOrOffset;
88
89 /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
90 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
91 let change_fixture = ChangeFixture::parse(ra_fixture);
92 let mut database = RootDatabase::default();
93 database.apply_change(change_fixture.change);
94 let (file_id, range_or_offset) =
95 change_fixture.file_position.expect("expected a marker (<|>)");
96 let offset = match range_or_offset {
97 RangeOrOffset::Range(_) => panic!(),
98 RangeOrOffset::Offset(it) => it,
99 };
100 (database, FilePosition { file_id, offset })
101 }
102
103 fn check_trait(ra_fixture: &str, expect: Expect) {
104 let (db, position) = position(ra_fixture);
105 let sema = Semantics::new(&db);
106 let file = sema.parse(position.file_id);
107 let impl_block: ast::Impl =
108 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
109 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
110 let actual = match trait_ {
111 Some(trait_) => trait_.name(&db).to_string(),
112 None => String::new(),
113 };
114 expect.assert_eq(&actual);
115 }
116
117 fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
118 let (db, position) = position(ra_fixture);
119 let sema = Semantics::new(&db);
120 let file = sema.parse(position.file_id);
121 let impl_block: ast::Impl =
122 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
123 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
124 let actual = items
125 .into_iter()
126 .map(|item| item.name(&db).unwrap().to_string())
127 .collect::<Vec<_>>()
128 .join("\n");
129 expect.assert_eq(&actual);
130 }
131
132 #[test]
133 fn resolve_trait() {
134 check_trait(
135 r#"
136pub trait Foo {
137 fn bar();
138}
139impl Foo for u8 {
140 <|>
141}
142 "#,
143 expect![["Foo"]],
144 );
145 check_trait(
146 r#"
147pub trait Foo {
148 fn bar();
149}
150impl Foo for u8 {
151 fn bar() {
152 fn baz() {
153 <|>
154 }
155 baz();
156 }
157}
158 "#,
159 expect![["Foo"]],
160 );
161 check_trait(
162 r#"
163pub trait Foo {
164 fn bar();
165}
166pub struct Bar;
167impl Bar {
168 <|>
169}
170 "#,
171 expect![[""]],
172 );
173 }
174
175 #[test]
176 fn missing_assoc_items() {
177 check_missing_assoc(
178 r#"
179pub trait Foo {
180 const FOO: u8;
181 fn bar();
182}
183impl Foo for u8 {
184 <|>
185}"#,
186 expect![[r#"
187 FOO
188 bar"#]],
189 );
190
191 check_missing_assoc(
192 r#"
193pub trait Foo {
194 const FOO: u8;
195 fn bar();
196}
197impl Foo for u8 {
198 const FOO: u8 = 10;
199 <|>
200}"#,
201 expect![[r#"
202 bar"#]],
203 );
204
205 check_missing_assoc(
206 r#"
207pub trait Foo {
208 const FOO: u8;
209 fn bar();
210}
211impl Foo for u8 {
212 const FOO: u8 = 10;
213 fn bar() {<|>}
214}"#,
215 expect![[r#""#]],
216 );
217
218 check_missing_assoc(
219 r#"
220pub struct Foo;
221impl Foo {
222 fn bar() {<|>}
223}"#,
224 expect![[r#""#]],
225 );
226 }
227}
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
new file mode 100644
index 000000000..09c7ac3ec
--- /dev/null
+++ b/crates/ide_db/src/traits/tests.rs
@@ -0,0 +1,144 @@
1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect};
4use hir::Semantics;
5use syntax::ast::{self, AstNode};
6use test_utils::RangeOrOffset;
7
8/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let change_fixture = ChangeFixture::parse(ra_fixture);
11 let mut database = RootDatabase::default();
12 database.apply_change(change_fixture.change);
13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
14 let offset = match range_or_offset {
15 RangeOrOffset::Range(_) => panic!(),
16 RangeOrOffset::Offset(it) => it,
17 };
18 (database, FilePosition { file_id, offset })
19}
20
21fn check_trait(ra_fixture: &str, expect: Expect) {
22 let (db, position) = position(ra_fixture);
23 let sema = Semantics::new(&db);
24 let file = sema.parse(position.file_id);
25 let impl_block: ast::Impl =
26 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
27 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
28 let actual = match trait_ {
29 Some(trait_) => trait_.name(&db).to_string(),
30 None => String::new(),
31 };
32 expect.assert_eq(&actual);
33}
34
35fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
36 let (db, position) = position(ra_fixture);
37 let sema = Semantics::new(&db);
38 let file = sema.parse(position.file_id);
39 let impl_block: ast::Impl =
40 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
41 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
42 let actual = items
43 .into_iter()
44 .map(|item| item.name(&db).unwrap().to_string())
45 .collect::<Vec<_>>()
46 .join("\n");
47 expect.assert_eq(&actual);
48}
49
50#[test]
51fn resolve_trait() {
52 check_trait(
53 r#"
54pub trait Foo {
55 fn bar();
56}
57impl Foo for u8 {
58 <|>
59}
60 "#,
61 expect![["Foo"]],
62 );
63 check_trait(
64 r#"
65pub trait Foo {
66 fn bar();
67}
68impl Foo for u8 {
69 fn bar() {
70 fn baz() {
71 <|>
72 }
73 baz();
74 }
75}
76 "#,
77 expect![["Foo"]],
78 );
79 check_trait(
80 r#"
81pub trait Foo {
82 fn bar();
83}
84pub struct Bar;
85impl Bar {
86 <|>
87}
88 "#,
89 expect![[""]],
90 );
91}
92
93#[test]
94fn missing_assoc_items() {
95 check_missing_assoc(
96 r#"
97pub trait Foo {
98 const FOO: u8;
99 fn bar();
100}
101impl Foo for u8 {
102 <|>
103}"#,
104 expect![[r#"
105 FOO
106 bar"#]],
107 );
108
109 check_missing_assoc(
110 r#"
111pub trait Foo {
112 const FOO: u8;
113 fn bar();
114}
115impl Foo for u8 {
116 const FOO: u8 = 10;
117 <|>
118}"#,
119 expect![[r#"
120 bar"#]],
121 );
122
123 check_missing_assoc(
124 r#"
125pub trait Foo {
126 const FOO: u8;
127 fn bar();
128}
129impl Foo for u8 {
130 const FOO: u8 = 10;
131 fn bar() {<|>}
132}"#,
133 expect![[r#""#]],
134 );
135
136 check_missing_assoc(
137 r#"
138pub struct Foo;
139impl Foo {
140 fn bar() {<|>}
141}"#,
142 expect![[r#""#]],
143 );
144}
diff --git a/docs/dev/README.md b/docs/dev/README.md
index abb387e8e..ca324493f 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -170,6 +170,22 @@ In general, API is centered around UI concerns -- the result of the call is what
170The results are 100% Rust specific though. 170The results are 100% Rust specific though.
171Shout outs to LSP developers for popularizing the idea that "UI" is a good place to draw a boundary at. 171Shout outs to LSP developers for popularizing the idea that "UI" is a good place to draw a boundary at.
172 172
173## LSP is sateless
174
175The protocol is implemented in the mostly stateless way.
176A good mental model is HTTP, which doesn't store per-client state, and instead relies on devices like cookies to maintain an illusion of state.
177If some action requires multi-step protocol, each step should be self-contained.
178
179A good example here is code action resolving process.
180TO display the lightbulb, we compute the list of code actions without computing edits.
181Figuring out the edit is done in a separate `codeAction/resolve` call.
182Rather than storing some `lazy_edit: Box<dyn FnOnce() -> Edit>` somewhere, we use a string ID of action to re-compute the list of actions during the resolve process.
183(See [this post](https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html) for more details.)
184The benefit here is that, generally speaking, the state of the world might change between `codeAction` and `codeAction` resolve requests, so any closure we store might become invalid.
185
186While we don't currently implement any complicated refactors with complex GUI, I imagine we'd use the same techniques for refactors.
187After clicking each "Next" button during refactor, the client would send all the info which server needs to re-recreate the context from scratch.
188
173## CI 189## CI
174 190
175CI does not test rust-analyzer, CI is a core part of rust-analyzer, and is maintained with above average standard of quality. 191CI does not test rust-analyzer, CI is a core part of rust-analyzer, and is maintained with above average standard of quality.