aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock32
-rw-r--r--crates/ra_ide/src/inlay_hints.rs976
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs17
-rw-r--r--crates/test_utils/src/lib.rs30
-rw-r--r--editors/code/rust.tmGrammar.json4
5 files changed, 319 insertions, 740 deletions
diff --git a/Cargo.lock b/Cargo.lock
index eb03caa83..e1d2475e2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -52,17 +52,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
52checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 52checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
53 53
54[[package]] 54[[package]]
55name = "atty"
56version = "0.2.14"
57source = "registry+https://github.com/rust-lang/crates.io-index"
58checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
59dependencies = [
60 "hermit-abi",
61 "libc",
62 "winapi 0.3.9",
63]
64
65[[package]]
66name = "autocfg" 55name = "autocfg"
67version = "1.0.0" 56version = "1.0.0"
68source = "registry+https://github.com/rust-lang/crates.io-index" 57source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -196,18 +185,6 @@ dependencies = [
196] 185]
197 186
198[[package]] 187[[package]]
199name = "clicolors-control"
200version = "1.0.1"
201source = "registry+https://github.com/rust-lang/crates.io-index"
202checksum = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
203dependencies = [
204 "atty",
205 "lazy_static",
206 "libc",
207 "winapi 0.3.9",
208]
209
210[[package]]
211name = "cloudabi" 188name = "cloudabi"
212version = "0.1.0" 189version = "0.1.0"
213source = "registry+https://github.com/rust-lang/crates.io-index" 190source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -218,11 +195,10 @@ dependencies = [
218 195
219[[package]] 196[[package]]
220name = "console" 197name = "console"
221version = "0.10.3" 198version = "0.11.3"
222source = "registry+https://github.com/rust-lang/crates.io-index" 199source = "registry+https://github.com/rust-lang/crates.io-index"
223checksum = "2586208b33573b7f76ccfbe5adb076394c88deaf81b84d7213969805b0a952a7" 200checksum = "8c0994e656bba7b922d8dd1245db90672ffb701e684e45be58f20719d69abc5a"
224dependencies = [ 201dependencies = [
225 "clicolors-control",
226 "encode_unicode", 202 "encode_unicode",
227 "lazy_static", 203 "lazy_static",
228 "libc", 204 "libc",
@@ -553,9 +529,9 @@ dependencies = [
553 529
554[[package]] 530[[package]]
555name = "insta" 531name = "insta"
556version = "0.16.0" 532version = "0.16.1"
557source = "registry+https://github.com/rust-lang/crates.io-index" 533source = "registry+https://github.com/rust-lang/crates.io-index"
558checksum = "8386e795fb3927131ea4cede203c529a333652eb6dc4ff29616b832b27e9b096" 534checksum = "617e921abc813f96a3b00958c079e7bf1e2db998f8a04f1546dd967373a418ee"
559dependencies = [ 535dependencies = [
560 "console", 536 "console",
561 "difference", 537 "difference",
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index c87652555..62d364bfa 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{FileId, FunctionSignature}; 9use crate::{FileId, FunctionSignature};
@@ -112,7 +112,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 112 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 113 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 114 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 115 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 116 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 117 if ty.is_unknown() {
118 return None; 118 return None;
@@ -345,583 +345,251 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
345 345
346#[cfg(test)] 346#[cfg(test)]
347mod tests { 347mod tests {
348 use crate::inlay_hints::InlayHintsConfig; 348 use expect::{expect, Expect};
349 use insta::assert_debug_snapshot; 349 use test_utils::extract_annotations;
350 350
351 use crate::mock_analysis::single_file; 351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352
353 fn check(ra_fixture: &str) {
354 check_with_config(ra_fixture, InlayHintsConfig::default());
355 }
356
357 fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual);
364 }
365
366 fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints)
370 }
352 371
353 #[test] 372 #[test]
354 fn param_hints_only() { 373 fn param_hints_only() {
355 let (analysis, file_id) = single_file( 374 check_with_config(
356 r#" 375 r#"
357 fn foo(a: i32, b: i32) -> i32 { a + b } 376fn foo(a: i32, b: i32) -> i32 { a + b }
358 fn main() { 377fn main() {
359 let _x = foo(4, 4); 378 let _x = foo(
360 }"#, 379 4,
361 ); 380 //^ a
362 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 381 4,
363 [ 382 //^ b
364 InlayHint { 383 );
365 range: 69..70, 384}"#,
366 kind: ParameterHint, 385 InlayHintsConfig {
367 label: "a", 386 parameter_hints: true,
368 }, 387 type_hints: false,
369 InlayHint { 388 chaining_hints: false,
370 range: 72..73, 389 max_length: None,
371 kind: ParameterHint,
372 label: "b",
373 }, 390 },
374 ] 391 );
375 "###);
376 } 392 }
377 393
378 #[test] 394 #[test]
379 fn hints_disabled() { 395 fn hints_disabled() {
380 let (analysis, file_id) = single_file( 396 check_with_config(
381 r#" 397 r#"
382 fn foo(a: i32, b: i32) -> i32 { a + b } 398fn foo(a: i32, b: i32) -> i32 { a + b }
383 fn main() { 399fn main() {
384 let _x = foo(4, 4); 400 let _x = foo(4, 4);
385 }"#, 401}"#,
402 InlayHintsConfig {
403 type_hints: false,
404 parameter_hints: false,
405 chaining_hints: false,
406 max_length: None,
407 },
386 ); 408 );
387 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
388 } 409 }
389 410
390 #[test] 411 #[test]
391 fn type_hints_only() { 412 fn type_hints_only() {
392 let (analysis, file_id) = single_file( 413 check_with_config(
393 r#" 414 r#"
394 fn foo(a: i32, b: i32) -> i32 { a + b } 415fn foo(a: i32, b: i32) -> i32 { a + b }
395 fn main() { 416fn main() {
396 let _x = foo(4, 4); 417 let _x = foo(4, 4);
397 }"#, 418 //^^ i32
398 ); 419}"#,
399 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 420 InlayHintsConfig {
400 [ 421 type_hints: true,
401 InlayHint { 422 parameter_hints: false,
402 range: 60..62, 423 chaining_hints: false,
403 kind: TypeHint, 424 max_length: None,
404 label: "i32",
405 }, 425 },
406 ] 426 );
407 "###);
408 } 427 }
428
409 #[test] 429 #[test]
410 fn default_generic_types_should_not_be_displayed() { 430 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 431 check(
412 r#" 432 r#"
413struct Test<K, T = u8> { 433struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 434
418fn main() { 435fn main() {
419 let zz = Test { t: 23u8, k: 33 }; 436 let zz = Test { t: 23u8, k: 33 };
437 //^^ Test<i32>
420 let zz_ref = &zz; 438 let zz_ref = &zz;
439 //^^^^^^ &Test<i32>
421}"#, 440}"#,
422 ); 441 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 68..70,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 106..112,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 442 }
440 443
441 #[test] 444 #[test]
442 fn let_statement() { 445 fn let_statement() {
443 let (analysis, file_id) = single_file( 446 check(
444 r#" 447 r#"
445#[derive(PartialEq)] 448#[derive(PartialEq)]
446enum CustomOption<T> { 449enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 450
451#[derive(PartialEq)] 451#[derive(PartialEq)]
452struct Test { 452struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 453
457fn main() { 454fn main() {
458 struct InnerStruct {} 455 struct InnerStruct {}
459 456
460 let test = 54; 457 let test = 54;
458 //^^^^ i32
461 let test: i32 = 33; 459 let test: i32 = 33;
462 let mut test = 33; 460 let mut test = 33;
461 //^^^^^^^^ i32
463 let _ = 22; 462 let _ = 22;
464 let test = "test"; 463 let test = "test";
464 //^^^^ &str
465 let test = InnerStruct {}; 465 let test = InnerStruct {};
466 466
467 let test = vec![222]; 467 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 468
475 let test = (42, 'a'); 469 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 470 //^^^^ (i32, char)
471 let (a, (b, (c,)) = (2, (3, (9.2,));
472 //^ i32 ^ i32 ^ f64
477 let &x = &92; 473 let &x = &92;
474 //^ i32
478}"#, 475}"#,
479 ); 476 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 192..196,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 235..243,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 274..278,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 538..542,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 565..566,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 569..570,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 572..573,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 576..577,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 579..580,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 583..584,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 626..627,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 477 }
542 478
543 #[test] 479 #[test]
544 fn closure_parameters() { 480 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 481 check(
546 r#" 482 r#"
547fn main() { 483fn main() {
548 let mut start = 0; 484 let mut start = 0;
549 (0..2).for_each(|increment| { 485 //^^^^^^^^^ i32
550 start += increment; 486 (0..2).for_each(|increment| { start += increment; });
551 }); 487 //^^^^^^^^^ i32
488
489 let multiply =
490 //^^^^^^^^ |…| -> i32
491 | a, b| a * b
492 //^ i32 ^ i32
493 ;
552 494
553 let multiply = |a, b, c, d| a * b * c * d; 495 let _: i32 = multiply(1, 2);
554 let _: i32 = multiply(1, 2, 3, 4);
555 let multiply_ref = &multiply; 496 let multiply_ref = &multiply;
497 //^^^^^^^^^^^^ &|…| -> i32
556 498
557 let return_42 = || 42; 499 let return_42 = || 42;
500 //^^^^^^^^^ || -> i32
558}"#, 501}"#,
559 ); 502 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 20..29,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 56..65,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 114..122,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 126..127,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 129..130,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 132..133,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 135..136,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 200..212,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 235..244,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 503 }
612 504
613 #[test] 505 #[test]
614 fn for_expression() { 506 fn for_expression() {
615 let (analysis, file_id) = single_file( 507 check(
616 r#" 508 r#"
617fn main() { 509fn main() {
618 let mut start = 0; 510 let mut start = 0;
619 for increment in 0..2 { 511 //^^^^^^^^^ i32
620 start += increment; 512 for increment in 0..2 { start += increment; }
621 } 513 //^^^^^^^^^ i32
622}"#, 514}"#,
623 ); 515 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 20..29,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 43..52,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 516 }
641 517
642 #[test] 518 #[test]
643 fn if_expr() { 519 fn if_expr() {
644 let (analysis, file_id) = single_file( 520 check(
645 r#" 521 r#"
646#[derive(PartialEq)] 522enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 523use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 524
658use CustomOption::*; 525struct Test { a: Option<u32>, b: u8 }
659 526
660fn main() { 527fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 528 let test = Some(Test { a: Some(3), b: 1 });
529 //^^^^ Option<Test>
662 if let None = &test {}; 530 if let None = &test {};
663 if let test = &test {}; 531 if let test = &test {};
532 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 533 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 534 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 535 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 536 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 537 if let Some(Test { a: x, b: y }) = &test {};
538 //^ &Option<u32> ^ &u8
539 if let Some(Test { a: Some(x), b: y }) = &test {};
540 //^ &u32 ^ &u8
541 if let Some(Test { a: None, b: y }) = &test {};
542 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 543 if let Some(Test { b: y, .. }) = &test {};
670 544 //^ &u8
671 if test == None {} 545 if test == None {}
672}"#, 546}"#,
673 ); 547 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 187..191,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 266..270,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 299..303,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 340..341,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 343..344,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 386..387,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 392..393,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 440..441,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 447..448,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 499..500,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 542..543,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 548 }
736 549
737 #[test] 550 #[test]
738 fn while_expr() { 551 fn while_expr() {
739 let (analysis, file_id) = single_file( 552 check(
740 r#" 553 r#"
741#[derive(PartialEq)] 554enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 555use Option::*;
743 None,
744 Some(T),
745}
746 556
747#[derive(PartialEq)] 557struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 558
755fn main() { 559fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 560 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 561 //^^^^ Option<Test>
758 while let test = &test {}; 562 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 563 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 564}"#,
768 ); 565 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 187..191,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 272..276,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 308..312,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 352..353,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 355..356,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 401..402,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 407..408,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 458..459,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 465..466,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 520..521,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 566..567,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 566 }
831 567
832 #[test] 568 #[test]
833 fn match_arm_list() { 569 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 570 check(
835 r#" 571 r#"
836#[derive(PartialEq)] 572enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 573use Option::*;
838 None,
839 Some(T),
840}
841
842#[derive(PartialEq)]
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847 574
848use CustomOption::*; 575struct Test { a: Option<u32>, b: u8 }
849 576
850fn main() { 577fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 578 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 579 None => (),
853 test => (), 580 test => (),
854 Some(test) => (), 581 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 582 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 583 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 584 _ => {}
861 } 585 }
862}"#, 586}"#,
863 ); 587 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 251..255,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 276..280,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 309..310,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 312..313,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 347..348,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 353..354,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 393..394,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 400..401,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 444..445,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 479..480,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 588 }
921 589
922 #[test] 590 #[test]
923 fn hint_truncation() { 591 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 592 check_with_config(
925 r#" 593 r#"
926struct Smol<T>(T); 594struct Smol<T>(T);
927 595
@@ -929,52 +597,26 @@ struct VeryLongOuterName<T>(T);
929 597
930fn main() { 598fn main() {
931 let a = Smol(0u32); 599 let a = Smol(0u32);
600 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 601 let b = VeryLongOuterName(0usize);
602 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 603 let c = Smol(Smol(0u32))
604 //^ Smol<Smol<…>>
934}"#, 605}"#,
935 ); 606 InlayHintsConfig { max_length: Some(8), ..Default::default() },
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 73..74,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 97..98,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 136..137,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 ); 607 );
957 } 608 }
958 609
959 #[test] 610 #[test]
960 fn function_call_parameter_hint() { 611 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 612 check(
962 r#" 613 r#"
963enum CustomOption<T> { 614enum Option<T> { None, Some(T) }
964 None, 615use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 616
969struct FileId {} 617struct FileId {}
970struct SmolStr {} 618struct SmolStr {}
971 619
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 620struct TextRange {}
979struct SyntaxKind {} 621struct SyntaxKind {}
980struct NavigationTarget {} 622struct NavigationTarget {}
@@ -982,18 +624,15 @@ struct NavigationTarget {}
982struct Test {} 624struct Test {}
983 625
984impl Test { 626impl Test {
985 fn method(&self, mut param: i32) -> i32 { 627 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 628
989 fn from_syntax( 629 fn from_syntax(
990 file_id: FileId, 630 file_id: FileId,
991 name: SmolStr, 631 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 632 focus_range: Option<TextRange>,
993 full_range: TextRange, 633 full_range: TextRange,
994 kind: SyntaxKind, 634 kind: SyntaxKind,
995 docs: CustomOption<String>, 635 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 636 ) -> NavigationTarget {
998 NavigationTarget {} 637 NavigationTarget {}
999 } 638 }
@@ -1005,108 +644,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 644
1006fn main() { 645fn main() {
1007 let not_literal = 1; 646 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 647 //^^^^^^^^^^^ i32
648 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
649 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 650 let t: Test = Test {};
1010 t.method(123); 651 t.method(123);
1011 Test::method(&t, 3456); 652 //^^^ param
1012 653 Test::method(&t, 3456);
654 //^^ &self ^^^^ param
1013 Test::from_syntax( 655 Test::from_syntax(
1014 FileId {}, 656 FileId {},
657 //^^^^^^^^^ file_id
1015 "impl".into(), 658 "impl".into(),
659 //^^^^^^^^^^^^^ name
1016 None, 660 None,
661 //^^^^ focus_range
1017 TextRange {}, 662 TextRange {},
663 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 664 SyntaxKind {},
665 //^^^^^^^^^^^^^ kind
1019 None, 666 None,
1020 None, 667 //^^^^ docs
1021 ); 668 );
1022}"#, 669}"#,
1023 ); 670 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 797..808,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 841..842,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 844..845,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 847..854,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 859..870,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 913..916,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 936..938,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 940..944,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 979..988,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 998..1011,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1021..1025,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1035..1047,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1057..1070,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1080..1084,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1094..1098,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 671 }
1106 672
1107 #[test] 673 #[test]
1108 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 675 check_with_config(
1110 r#" 676 r#"
1111fn map(f: i32) {} 677fn map(f: i32) {}
1112fn filter(predicate: i32) {} 678fn filter(predicate: i32) {}
@@ -1187,23 +753,16 @@ fn main() {
1187 let _: f64 = a.div_euclid(b); 753 let _: f64 = a.div_euclid(b);
1188 let _: f64 = a.abs_sub(b); 754 let _: f64 = a.abs_sub(b);
1189}"#, 755}"#,
1190 ); 756 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 ); 757 );
1196 } 758 }
1197 759
1198 #[test] 760 #[test]
1199 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 762 check_with_config(
1201 r#" 763 r#"
1202enum CustomResult<T, E> { 764enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 765use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 766
1208struct SyntheticSyntax; 767struct SyntheticSyntax;
1209 768
@@ -1213,136 +772,157 @@ fn main() {
1213 Err(SyntheticSyntax) => (), 772 Err(SyntheticSyntax) => (),
1214 } 773 }
1215}"#, 774}"#,
1216 ); 775 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 ); 776 );
1222 } 777 }
1223 778
1224 #[test] 779 #[test]
1225 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
1226 let (analysis, file_id) = single_file( 781 check_expect(
1227 r#" 782 r#"
1228 struct A(B); 783struct A(B);
1229 impl A { fn into_b(self) -> B { self.0 } } 784impl A { fn into_b(self) -> B { self.0 } }
1230 struct B(C); 785struct B(C);
1231 impl B { fn into_c(self) -> C { self.0 } } 786impl B { fn into_c(self) -> C { self.0 } }
1232 struct C; 787struct C;
1233 788
1234 fn main() { 789fn main() {
1235 let c = A(B(C)) 790 let c = A(B(C))
1236 .into_b() // This is a comment 791 .into_b() // This is a comment
1237 .into_c(); 792 .into_c();
1238 }"#, 793}
794"#,
795 InlayHintsConfig {
796 parameter_hints: false,
797 type_hints: false,
798 chaining_hints: true,
799 max_length: None,
800 },
801 expect![[r#"
802 [
803 InlayHint {
804 range: 147..172,
805 kind: ChainingHint,
806 label: "B",
807 },
808 InlayHint {
809 range: 147..154,
810 kind: ChainingHint,
811 label: "A",
812 },
813 ]
814 "#]],
1239 ); 815 );
1240 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1241 [
1242 InlayHint {
1243 range: 147..172,
1244 kind: ChainingHint,
1245 label: "B",
1246 },
1247 InlayHint {
1248 range: 147..154,
1249 kind: ChainingHint,
1250 label: "A",
1251 },
1252 ]
1253 "###);
1254 } 816 }
1255 817
1256 #[test] 818 #[test]
1257 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
1258 let (analysis, file_id) = single_file( 820 check_with_config(
1259 r#" 821 r#"
1260 struct A(B); 822struct A(B);
1261 impl A { fn into_b(self) -> B { self.0 } } 823impl A { fn into_b(self) -> B { self.0 } }
1262 struct B(C); 824struct B(C);
1263 impl B { fn into_c(self) -> C { self.0 } } 825impl B { fn into_c(self) -> C { self.0 } }
1264 struct C; 826struct C;
1265 827
1266 fn main() { 828fn main() {
1267 let c = A(B(C)).into_b().into_c(); 829 let c = A(B(C)).into_b().into_c();
1268 }"#, 830}"#,
831 InlayHintsConfig {
832 parameter_hints: false,
833 type_hints: false,
834 chaining_hints: true,
835 max_length: None,
836 },
1269 ); 837 );
1270 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1271 } 838 }
1272 839
1273 #[test] 840 #[test]
1274 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
1275 let (analysis, file_id) = single_file( 842 check_expect(
1276 r#" 843 r#"
1277 struct A { pub b: B } 844struct A { pub b: B }
1278 struct B { pub c: C } 845struct B { pub c: C }
1279 struct C(pub bool); 846struct C(pub bool);
1280 struct D; 847struct D;
1281 848
1282 impl D { 849impl D {
1283 fn foo(&self) -> i32 { 42 } 850 fn foo(&self) -> i32 { 42 }
1284 } 851}
1285 852
1286 fn main() { 853fn main() {
1287 let x = A { b: B { c: C(true) } } 854 let x = A { b: B { c: C(true) } }
1288 .b 855 .b
1289 .c 856 .c
1290 .0; 857 .0;
1291 let x = D 858 let x = D
1292 .foo(); 859 .foo();
1293 }"#, 860}"#,
861 InlayHintsConfig {
862 parameter_hints: false,
863 type_hints: false,
864 chaining_hints: true,
865 max_length: None,
866 },
867 expect![[r#"
868 [
869 InlayHint {
870 range: 143..190,
871 kind: ChainingHint,
872 label: "C",
873 },
874 InlayHint {
875 range: 143..179,
876 kind: ChainingHint,
877 label: "B",
878 },
879 ]
880 "#]],
1294 ); 881 );
1295 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1296 [
1297 InlayHint {
1298 range: 143..190,
1299 kind: ChainingHint,
1300 label: "C",
1301 },
1302 InlayHint {
1303 range: 143..179,
1304 kind: ChainingHint,
1305 label: "B",
1306 },
1307 ]
1308 "###);
1309 } 882 }
1310 883
1311 #[test] 884 #[test]
1312 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
1313 let (analysis, file_id) = single_file( 886 check_expect(
1314 r#" 887 r#"
1315 struct A<T>(T); 888struct A<T>(T);
1316 struct B<T>(T); 889struct B<T>(T);
1317 struct C<T>(T); 890struct C<T>(T);
1318 struct X<T,R>(T, R); 891struct X<T,R>(T, R);
1319 892
1320 impl<T> A<T> { 893impl<T> A<T> {
1321 fn new(t: T) -> Self { A(t) } 894 fn new(t: T) -> Self { A(t) }
1322 fn into_b(self) -> B<T> { B(self.0) } 895 fn into_b(self) -> B<T> { B(self.0) }
1323 } 896}
1324 impl<T> B<T> { 897impl<T> B<T> {
1325 fn into_c(self) -> C<T> { C(self.0) } 898 fn into_c(self) -> C<T> { C(self.0) }
1326 } 899}
1327 fn main() { 900fn main() {
1328 let c = A::new(X(42, true)) 901 let c = A::new(X(42, true))
1329 .into_b() 902 .into_b()
1330 .into_c(); 903 .into_c();
1331 }"#, 904}
905"#,
906 InlayHintsConfig {
907 parameter_hints: false,
908 type_hints: false,
909 chaining_hints: true,
910 max_length: None,
911 },
912 expect![[r#"
913 [
914 InlayHint {
915 range: 246..283,
916 kind: ChainingHint,
917 label: "B<X<i32, bool>>",
918 },
919 InlayHint {
920 range: 246..265,
921 kind: ChainingHint,
922 label: "A<X<i32, bool>>",
923 },
924 ]
925 "#]],
1332 ); 926 );
1333 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1334 [
1335 InlayHint {
1336 range: 246..283,
1337 kind: ChainingHint,
1338 label: "B<X<i32, bool>>",
1339 },
1340 InlayHint {
1341 range: 246..265,
1342 kind: ChainingHint,
1343 label: "A<X<i32, bool>>",
1344 },
1345 ]
1346 "###);
1347 } 927 }
1348} 928}
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 3b124020d..47a1d393d 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,6 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ffi::OsStr, ops, path::Path, process::Command}; 3use std::{
4 ffi::OsStr,
5 ops,
6 path::{Path, PathBuf},
7 process::Command,
8};
4 9
5use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
6use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
@@ -308,9 +313,13 @@ pub fn load_extern_resources(
308 if let Ok(message) = message { 313 if let Ok(message) = message {
309 match message { 314 match message {
310 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 315 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
311 let out_dir = AbsPathBuf::assert(out_dir); 316 // cargo_metadata crate returns default (empty) path for
312 res.out_dirs.insert(package_id.clone(), out_dir); 317 // older cargos, which is not absolute, so work around that.
313 res.cfgs.insert(package_id, cfgs); 318 if out_dir != PathBuf::default() {
319 let out_dir = AbsPathBuf::assert(out_dir);
320 res.out_dirs.insert(package_id.clone(), out_dir);
321 res.cfgs.insert(package_id, cfgs);
322 }
314 } 323 }
315 Message::CompilerArtifact(message) => { 324 Message::CompilerArtifact(message) => {
316 if message.target.kind.contains(&"proc-macro".to_string()) { 325 if message.target.kind.contains(&"proc-macro".to_string()) {
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index f9d6c6c96..e32a0a0c3 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -11,7 +11,7 @@ pub mod mark;
11mod fixture; 11mod fixture;
12 12
13use std::{ 13use std::{
14 convert::TryInto, 14 convert::{TryFrom, TryInto},
15 env, fs, 15 env, fs,
16 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
17}; 17};
@@ -169,10 +169,9 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
169 for line in lines_with_ends(text) { 169 for line in lines_with_ends(text) {
170 if let Some(idx) = line.find("//^") { 170 if let Some(idx) = line.find("//^") {
171 let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); 171 let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]);
172 let marker_and_data = &line[idx + "//".len()..]; 172 for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) {
173 let len = marker_and_data.chars().take_while(|&it| it == '^').count(); 173 res.push((line_range + offset, text))
174 let data = marker_and_data[len..].trim().to_string(); 174 }
175 res.push((TextRange::at(offset, len.try_into().unwrap()), data))
176 } 175 }
177 prev_line_start = Some(line_start); 176 prev_line_start = Some(line_start);
178 line_start += TextSize::of(line); 177 line_start += TextSize::of(line);
@@ -180,13 +179,28 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
180 res 179 res
181} 180}
182 181
182fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> {
183 let mut res = Vec::new();
184 let mut offset: TextSize = 0.into();
185 while !line.is_empty() {
186 let len = line.chars().take_while(|&it| it == '^').count();
187 assert!(len > 0);
188 let range = TextRange::at(offset, len.try_into().unwrap());
189 let next = line[len..].find('^').map_or(line.len(), |it| it + len);
190 res.push((range, line[len..][..next - len].trim().to_string()));
191 line = &line[next..];
192 offset += TextSize::try_from(next).unwrap();
193 }
194 res
195}
196
183#[test] 197#[test]
184fn test_extract_annotations() { 198fn test_extract_annotations() {
185 let text = stdx::trim_indent( 199 let text = stdx::trim_indent(
186 r#" 200 r#"
187fn main() { 201fn main() {
188 let x = 92; 202 let (x, y) = (9, 2);
189 //^ def 203 //^ def ^ def
190 zoo + 1 204 zoo + 1
191} //^^^ i32 205} //^^^ i32
192 "#, 206 "#,
@@ -195,7 +209,7 @@ fn main() {
195 .into_iter() 209 .into_iter()
196 .map(|(range, ann)| (&text[range], ann)) 210 .map(|(range, ann)| (&text[range], ann))
197 .collect::<Vec<_>>(); 211 .collect::<Vec<_>>();
198 assert_eq!(res, vec![("x", "def".into()), ("zoo", "i32".into()),]); 212 assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]);
199} 213}
200 214
201// Comparison functionality borrowed from cargo: 215// Comparison functionality borrowed from cargo:
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index ab87cd39f..0be2583db 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -268,7 +268,7 @@
268 "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!" 268 "match": "\\b(log|error|warn|info|debug|trace|log_enabled)!"
269 }, 269 },
270 { 270 {
271 "comment": "Invokation of a macro", 271 "comment": "Invocation of a macro",
272 "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]", 272 "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]",
273 "captures": { 273 "captures": {
274 "1": { 274 "1": {
@@ -683,4 +683,4 @@
683 ] 683 ]
684 } 684 }
685 } 685 }
686} \ No newline at end of file 686}